文字列の列の各行における特定の文字の出現数を計算するにはどうすればよいですか?


103

特定の変数にテキスト文字列が含まれているdata.frameがあります。個々の文字列内の特定の文字の出現回数をカウントしたいと思います。

例:

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

文字列での "a"の出現回数を含むq.dataの新しい列を作成したい(つまり、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

回答:


141

ストリンガーパッケージは、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

1
提起された問題で成功するためには、メイン引数の周りにas.character()が必要ですが、あなたの方がはるかに高速でした。
IRTFM 2012

1
@DWin-それは事実ですがstringsAsFactors = FALSE、データフレームを定義するときに追加することでその問題を回避しました。
Dason 2012

すみません、はっきりしていませんでした。私は実際にティム・リフに応答していて、彼の機能が問題を提起してエラーをスローしたことを彼に話していました。彼はあなたの問題の再定義を使ったかもしれませんが、彼はそうは言いませんでした。
IRTFM 2012

ええ、私もstringsAsFactors=TRUEコンプでしましたが、これについては触れませんでした
ティム・リフ

因子の文字列の検索は機能します。つまり、str_count(d $ factor_column、 'A')は機能しますが、その逆は機能しません
Nitro

65

ベースRを残したくない場合は、かなり簡潔で表現力豊かな可能性があります。

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

2
わかりました-多分それはあなたがとを数回一緒に使用したときに初めて表現力を感じるでしょうが、そのコンボは私がプラグインに値すると思ったほど強力です。regmatchesgregexpr
Josh O'Brien

regmatches比較的新しいです。2.14で導入されました。
Dason、2012

regmatchesビットは必要ないと思います。関数gregexprは、xの各要素に対して一致した出現のインデックスを含むリストを返します。
savagent 2014

@savagent-各文字列の一致数を計算するために使用するコードを共有していただけませんか?
ジョシュ・オブライエン

1
申し訳ありませんが、-1を忘れていました。各行に少なくとも1つの一致、sapply(gregexpr( "g"、q.data $ string)、length)がある場合にのみ機能します。
savagent 2014

17
nchar(as.character(q.data$string)) -nchar( gsub("a", "", q.data$string))
[1] 2 1 0

ncharに渡す前に、factor変数を文字に強制変換していることに注意してください。正規表現関数は内部でそれを行うように見えます。

これがベンチマークの結果です(テストのサイズを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

2
これは回答の中で最速のソリューションですfixed=TRUEが、オプションをに渡すことにより、ベンチマークで最大30%高速化されますgsub必要にfixed=TRUEなる場合もあります(つまり、カウントする文字がのような正規表現アサーションとして解釈される可能性がある場合)。.
C8H10N4O2


5

stringiパッケージには、機能を提供stri_countし、stri_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),]

ここに画像の説明を入力してください



2

私は誰かがもっと上手くできると確信していますが、これはうまくいきます:

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")

最初のエラーと2番目のエラーでエラーが発生するようです...(これらすべてをベンチマークしようとしました。)
IRTFM

1

あなたは文字列の除算を使うことができます

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

これにより、1、3、1、0が得られます。正規表現や単語全体で文字列分割を使用することもできます。


0

IMHOが最も簡単でクリーンな方法です。

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`

それはどのように行われますか?私にとってlengths(gregexpr('a', q.data$string))2 1 1、ではなく、を返します2 1 0
FinnÅrupNielsen


0

さらに別のbase Rオプションは次のとおりです。

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

[1] 2 1 0

-1

次の式はその役割を果たし、文字だけでなく記号にも使用できます。

式は次のように機能します。

1:データフレームq.dataの列にラッププライを使用して、列2の行を反復します( "lapply(q.data [、2]、")、

2:列2の各行に関数「function(x){sum( 'a' == strsplit(as.character(x)、 '')[[1]])}」が適用されます。この関数は、列2(x)の各行の値を取り、文字に変換し(たとえば、それが因子である場合)、すべての文字で文字列を分割します( "strsplit(as.character(x)、 ' ') ")。その結果、列2の各行の文字列値の各文字を含むベクトルがあります。

3:ベクトルの各ベクトル値は、カウントする目的の文字(この場合は "a"( "'a' =="))と比較されます。この操作は、TrueとFalseの値のベクトル「c(True、False、True、....)」のベクトルを返します。ベクトルの値が、カウントする必要な文字と一致するとTrueになります。

4:文字「a」が行に表示される合計時間は、ベクトル「sum(....)」内のすべての「True」値の合計として計算されます。

5:次に、「unlist」関数を適用して、「lapply」関数の結果をアンパックし、データフレームの新しい列に割り当てます( "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

1
あなたの答えは、それが正確に単純な表現ではないので、特に新しいユーザーにとっては、それが何をするかを説明することではるかに良くなります。
Khaine775

コメントをありがとう@ Khaine775と投稿の説明がないことをお詫びします。私は投稿を編集し、それがどのように機能するかをよりよく説明するためにいくつかのコメントを追加しました。
bacnqn

-2
s <- "aababacababaaathhhhhslsls jsjsjjsaa ghhaalll"
p <- "a"
s2 <- gsub(p,"",s)
numOcc <- nchar(s) - nchar(s2)

効率的ではないかもしれませんが、私の目的を解決します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.