如何计算字符串列的每一行中给定字符的出现次数?

我有一个 data.frame,其中某些变量包含一个文本字符串。我希望计算每个字符串中给定字符的出现次数。

例如:

q.data<-data.frame(number=1:3, string=c("greatgreat", "magic", "not"))

我希望为 q.data 创建一个新列,其中字符串中出现“ a”的次数(即 c (2,1,0))。

我采用的唯一一种复杂的方法是:

string.counter<-function(strings, pattern){
counts<-NULL
for(i in 1:length(strings)){
counts[i]<-length(attr(gregexpr(pattern,strings[i])[[1]], "match.length")[attr(gregexpr(pattern,strings[i])[[1]], "match.length")>0])
}
return(counts)
}


string.counter(strings=q.data$string, pattern="a")


number     string number.of.a
1      1 greatgreat           2
2      2      magic           1
3      3        not           0
168838 次浏览

我相信有人可以做得更好,但这个可以:

sapply(as.character(q.data$string), function(x, letter = "a"){
sum(unlist(strsplit(x, split = "")) == letter)
})
greatgreat      magic        not
2          1          0

或在一个功能:

countLetter <- function(charvec, letter){
sapply(charvec, function(x, letter){
sum(unlist(strsplit(x, split = "")) == letter)
}, letter = letter)
}
countLetter(as.character(q.data$string),"a")

Stringr 包提供了 str_count函数,它似乎可以完成您感兴趣的工作

# Load your example data
q.data<-data.frame(number=1:3, string=c("greatgreat", "magic", "not"), stringsAsFactors = F)
library(stringr)


# Count the number of 'a's in each element of string
q.data$number.of.a <- str_count(q.data$string, "a")
q.data
#  number     string number.of.a
#1      1 greatgreat           2
#2      2      magic           1
#3      3        not           0

如果你不想离开基数 R,这里有一个相当简洁明了的可能性:

x <- q.data$string
lengths(regmatches(x, gregexpr("a", x)))
# [1] 2 1 0
nchar(as.character(q.data$string)) -nchar( gsub("a", "", q.data$string))
[1] 2 1 0

请注意,在传递给 nchar 之前,我将 factor 变量强制转换为 string。正则表达式函数似乎在内部执行此操作。

下面是基准测试结果(将测试的大小放大到3000行)

 q.data<-q.data[rep(1:NROW(q.data), 1000),]
str(q.data)
'data.frame':   3000 obs. of  3 variables:
$ number     : int  1 2 3 1 2 3 1 2 3 1 ...
$ string     : Factor w/ 3 levels "greatgreat","magic",..: 1 2 3 1 2 3 1 2 3 1 ...
$ number.of.a: int  2 1 0 2 1 0 2 1 0 2 ...


benchmark( Dason = { q.data$number.of.a <- str_count(as.character(q.data$string), "a") },
Tim = {resT <- sapply(as.character(q.data$string), function(x, letter = "a"){
sum(unlist(strsplit(x, split = "")) == letter) }) },


DWin = {resW <- nchar(as.character(q.data$string)) -nchar( gsub("a", "", q.data$string))},
Josh = {x <- sapply(regmatches(q.data$string, gregexpr("g",q.data$string )), length)}, replications=100)
#-----------------------
test replications elapsed  relative user.self sys.self user.child sys.child
1 Dason          100   4.173  9.959427     2.985    1.204          0         0
3  DWin          100   0.419  1.000000     0.417    0.003          0         0
4  Josh          100  18.635 44.474940    17.883    0.827          0         0
2   Tim          100   3.705  8.842482     3.646    0.072          0         0
s <- "aababacababaaathhhhhslsls jsjsjjsaa ghhaalll"
p <- "a"
s2 <- gsub(p,"",s)
numOcc <- nchar(s) - nchar(s2)

也许不是最有效率的,但是解决了我的目标

另一个不错的选择是使用 CharToRaw:

sum(charToRaw("abc.d.aa") == charToRaw('.'))

恕我直言,最简单、最干净的方法是:

q.data$number.of.a <- lengths(gregexpr('a', q.data$string))


#  number     string number.of.a`
#1      1 greatgreat           2`
#2      2      magic           1`
#3      3        not           0`

你可以用弦分法

require(roperators)
my_strings <- c('apple', banana', 'pear', 'melon')
my_strings %s/% 'a'

也可以对正则表达式和整个单词使用字符串除法。

stringi包提供了非常快速的函数 stri_countstri_count_fixed

stringi::stri_count(q.data$string, fixed = "a")
# [1] 2 1 0

基准

与最快的方法相比,从 @ 42-的回答stringr包中的等效函数的载体有30.000个元素。

library(microbenchmark)


benchmark <- microbenchmark(
stringi = stringi::stri_count(test.data$string, fixed = "a"),
baseR = nchar(test.data$string) - nchar(gsub("a", "", test.data$string, fixed = TRUE)),
stringr = str_count(test.data$string, "a")
)


autoplot(benchmark)

资料

q.data <- data.frame(number=1:3, string=c("greatgreat", "magic", "not"), stringsAsFactors = FALSE)
test.data <- q.data[rep(1:NROW(q.data), 10000),]

enter image description here

下面的问题已经移动到这里,但似乎这一页不直接回答法拉艾尔的问题。 如何在 R 中找到101中的数字1

所以,以防万一,我会在这里写一个答案。

library(magrittr)
n %>% # n is a number you'd like to inspect
as.character() %>%
str_count(pattern = "1")

Https://stackoverflow.com/users/8931457/farah-el

https://stackoverflow.com/a/12430764/589165的一个变体是

> nchar(gsub("[^a]", "", q.data$string))
[1] 2 1 0

base R的另一个选择可能是:

lengths(lapply(q.data$string, grepRaw, pattern = "a", all = TRUE, fixed = TRUE))


[1] 2 1 0

下一个表达式完成这项工作,并且也适用于符号,而不仅仅是字母。

这个短语的作用如下:

1: 它对 dataframe q.data 的列使用 lapplication 来迭代列2的行(“ lapplication (q.data [ ,2] ,”) ,

2: 它适用于列2的每一行,一个函数“ function (x){ sum (‘ a’= = strsplit (as.trait (x) ,”)[[1]])}”。 该函数获取列2(x)的每个行值,转换为字符(例如,如果它是一个因子的话) ,并对每个字符进行字符串分割(“ strsplit (as.role (x) ,”)”)。因此,我们得到了一个向量,其中包含列2中每一行的字符串值的每一个字符。

3: 将向量的每个向量值与要计数的所需字符进行比较,在本例中为“ a”(“‘ a’= =”)。此操作将返回 True 和 False 值“ c (True,False,True,... .)”的向量,当向量中的值与要计数的所需字符匹配时,该值为 True。

4: 字符‘ a’在行中出现的总次数计算为向量“ sum (...)”中所有‘ True’值的总和。

5: 然后应用“ unlist”函数来解压“ lapplication”函数的结果,并将其分配给数据框中的一个新列(“ q.data $number.of.a <-unlist (... .”)

q.data$number.of.a<-unlist(lapply(q.data[,2],function(x){sum('a' == strsplit(as.character(x), '')[[1]])}))


>q.data


#  number     string     number.of.a
#1   greatgreat         2
#2      magic           1
#3      not             0

另一个以 R 为基数的答案,不如@IRTFM 和@Finn (或者使用 stringi/stringr的答案)那么好,但比其他答案好:

sapply(strsplit(q.data$string, split=""), function(x) sum(x %in% "a"))


q.data<-data.frame(number=1:3, string=c("greatgreat", "magic", "not"))
q.data<-q.data[rep(1:NROW(q.data), 3000),]
library(rbenchmark)
library(stringr)
library(stringi)


benchmark( Dason = {str_count(q.data$string, "a") },
Tim = {sapply(q.data$string, function(x, letter = "a"){sum(unlist(strsplit(x, split = "")) == letter) }) },
DWin = {nchar(q.data$string) -nchar( gsub("a", "", q.data$string, fixed=TRUE))},
Markus = {stringi::stri_count(q.data$string, fixed = "a")},
Finn={nchar(gsub("[^a]", "", q.data$string))},
tmmfmnk={lengths(lapply(q.data$string, grepRaw, pattern = "a", all = TRUE, fixed = TRUE))},
Josh1 = {sapply(regmatches(q.data$string, gregexpr("g",q.data$string )), length)},
Josh2 = {lengths(regmatches(q.data$string, gregexpr("g",q.data$string )))},
Iago = {sapply(strsplit(q.data$string, split=""), function(x) sum(x %in% "a"))},
replications =100, order = "elapsed")


test replications elapsed relative user.self sys.self user.child sys.child
4  Markus          100   0.076    1.000     0.076    0.000          0         0
3    DWin          100   0.277    3.645     0.277    0.000          0         0
1   Dason          100   0.290    3.816     0.291    0.000          0         0
5    Finn          100   1.057   13.908     1.057    0.000          0         0
9    Iago          100   3.214   42.289     3.215    0.000          0         0
2     Tim          100   6.000   78.947     6.002    0.000          0         0
6 tmmfmnk          100   6.345   83.487     5.760    0.003          0         0
8   Josh2          100  12.542  165.026    12.545    0.000          0         0
7   Josh1          100  13.288  174.842    13.268    0.028          0         0