抓取包含特殊字符和大写字母的列

时间:2016-04-25 21:26:40

标签: regex r

我有一个数据框,我正在尝试遍历数据框以识别那些包含特殊字符或全都是大写字母的列。

我尝试了一些东西,但没有任何东西,我是苹果来捕捉循环中的列名。

data = data.frame(one=c(1,3,5,1,3,5,1,3,5,1,3,5), two=c(1,3,5,1,3,5,1,3,5,1,3,5), 
                thr=c("A","B","D","E","F","G","H","I","J","H","I","J"),
                fou=c("A","B","D","A","B","D","A","B","D","A","B","D"),
                fiv=c(1,3,5,1,3,5,1,3,5,1,3,5), 
                six=c("A","B","D","E","F","G","H","I","J","H","I","J"),
                sev=c("A","B","D","A","B","D","A","B","D","A","B","D"),
                eig=c("A","B","D","A","B","D","A","B","D","A","B","D"),
                nin=c(1.24,3.52,5.33,1.44,3.11,5.33,1.55,3.66,5.33,1.32,3.54,5.77),
                ten=c(1:12),
                ele=rep(1,12),
                twe=c(1,2,1,2,1,2,1,2,1,2,1,2), 
                thir=c("THiS","THAT34","T(&*(", "!!!","@$#","$Q%J","who","THIS","this","this","this","this"),
                stringsAsFactors = FALSE)
data

colls <- c()

    spec=c("$","%","&")
    for( col in names(data) ) {
      if( length(strings[stringr::str_detect(data[,col], spec)]) >= 1 ){
          print("HORRAY")
colls <- c(collls, col) 
      }
      else print ("NOOOOOOOOOO")
    }


    for( col in names(data) ) {
      if( any(data[,col]) %in% spec ){
        print("HORRAY") 
colls <- c(collls, col)
      }
      else print ("NOOOOOOOOOO")
    }

任何人都可以解决这个问题的好方法。

编辑:

最终目标是使用符合该条件的列名称的向量。抱歉我的问题很糟糕,但希望这对我正在尝试做的事情有所帮助

2 个答案:

答案 0 :(得分:2)

我会使用grep()来搜索您感兴趣的模式。请参阅here

[:upper:]匹配任何大写字母。

将它与锚点(^,$)组合并匹配一次或多次(+)会得到^[[:upper:]]+$,并且只应在大写字母中完全匹配条目。

以下内容与您的玩具数据集中的特殊字符相匹配(但不保证与您的真实数据集中的所有特殊字符匹配,即换页,回车)

[:punct:] #Matches标点符号 - ! “#$%&amp;'()* +, - 。/ :;&lt; =&gt;?@ [\ _] ^ _` {|}〜。

请注意,您可以手动定义特殊字符,而不是使用[:punct:]

我们可以在数据集的第一行尝试生成的代码:

#Using grepl() rather than grep() so that we return a list of logical values.
grepl(x= data[1,], pattern = "^[[:upper:]]+$|[[:punct:]]")
[1] FALSE FALSE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE

这给了我们预期的响应,除了第9列的值为1.24。这里小数点被识别为标点符号并被标记为匹配。 我们可以添加一个“负向前瞻断言” - (?!\\.) - 在他们甚至被测试为标点字符之前删除任何考虑期间。注意我们使用\来逃避这段时间。

grepl(x= data[1,], perl = TRUE, pattern = "(?!\\.)(^[[:upper:]]+$|[[:punct:]])")
[1] FALSE FALSE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE

这会返回更好的响应 - 它现在不再匹配小数位。注意:这可能不是您想要的,因为此模式也不会匹配字符字段中的任何完整停止。您需要进一步细化模式。

不是使用'for循环'来重复数据帧中每一行的代码,而是使用矢量化而不是“更像R”。

为此,我们必须将脚本转换为我们将使用apply()

调用的函数
myFunction <- function(x){
      matches <- grepl(x= x, perl = TRUE, pattern = "(?!\\.)(^[[:upper:]]+$|[[:punct:]])")
  #Given a set of logical vectors 'matches', is at least one of the values true? using any()
  return(any(matches))
}

apply(X = data, 1, myFunction)

上面的1指示apply()重复行而不是列。

[1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE

在示例数据集中,所有行都有一个包含特殊字符的条目或包含所有大写字母的字符串。这并不奇怪,因为示例数据集中的许多列都是单个大写字母的列表。

如果您只是对第13栏中哪些值符合规定标准感兴趣,可以使用:

matches <- grepl(x= data$thir, perl = TRUE, pattern = "(?!\\.)(^[[:upper:]]+$|[[:punct:]])")
matches
 [1] FALSE FALSE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE FALSE FALSE FALSE FALSE

在匹配的行上对数据框进行子集化:

data[matches,]
  one two thr fou fiv six sev eig  nin ten ele twe  thir
3   5   5   D   D   5   D   D   D 5.33   3   1   1 T(&*(
4   1   1   E   A   1   E   A   A 1.44   4   1   2   !!!
5   3   3   F   B   3   F   B   B 3.11   5   1   1   @$#
6   5   5   G   D   5   G   D   D 5.33   6   1   2  $Q%J
8   3   3   I   B   3   I   B   B 3.66   8   1   2  THIS

要在不匹配的行上对数据框进行子集化:

data[!matches,]
   one two thr fou fiv six sev eig  nin ten ele twe   thir
1    1   1   A   A   1   A   A   A 1.24   1   1   1   THiS
2    3   3   B   B   3   B   B   B 3.52   2   1   2 THAT34
7    1   1   H   A   1   H   A   A 1.55   7   1   1    who
9    5   5   J   D   5   J   D   D 5.33   9   1   1   this
10   1   1   H   A   1   H   A   A 1.32  10   1   2   this
11   3   3   I   B   3   I   B   B 3.54  11   1   1   this
12   5   5   J   D   5   J   D   D 5.77  12   1   2   this

请注意,使用的正则表达式与THAT34不匹配,因为它不是完全由大写字母组成,最后的数字为34。

编辑:

要获取一个列名列表,列出符合上述编辑标准的列,请使用上述myFunction

colnames(data)[apply(X = data, 2, myFunction)]
"thr"  "fou"  "six"  "sev"  "eig"  "thir"

apply()中的数字从1更改为2,以便在列而不是行之间重复。我们将apply(),一个逻辑匹配列表(TRUE或FALSE)的输出传递给colnames(数据) - 这将通过子集返回匹配的列名。

答案 1 :(得分:1)

我会将数据折叠成字符串(每行一个字符串)

strings = apply(data, 1, paste, collapse = "")
contains_only_caps = strings == toupper(strings)
strings[contains_only_caps]
# [1] "33BB3BBB3.52 212THAT34" "55DD5DDD5.33 311T(&*("  "11EA1EAA1.44 412!!!"   "33FB3FBB3.11 511@$#"   
# [5] "55GD5GDD5.33 612$Q%J"   "33IB3IBB3.66 812THIS"  


# escaping special characters
spec=c("\\$","%","\\&")
contains_spec = stringr::str_detect(strings, pattern = paste(spec, collapse = "|"))

strings[contains_spec]
# [1] "55DD5DDD5.33 311T(&*(" "33FB3FBB3.11 511@$#"   "55GD5GDD5.33 612$Q%J" 

您还可以使用whichcontains_spec上的contains_only_caps来获取原始数据框的相应行号。我认为使用字符串而不是行方式数据框元素会更快 - 只要你想搜索整个字符串,而不是某些条件的特定列。