Bir dizgi sütununun her satırında belirli bir karakterin oluşum sayısı nasıl hesaplanır?


105

Belirli değişkenlerin metin dizesi içerdiği bir data.frame var. Her bir dizede belirli bir karakterin oluşum sayısını saymak istiyorum.

Misal:

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

Q.data için dizede "a" oluşum sayısı ile yeni bir sütun oluşturmak istiyorum (yani c (2,1,0)).

Yönettiğim tek kıvrımlı yaklaşım şudur:

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

Yanıtlar:


143

Stringr paketi, str_countilgilendiğiniz şeyi yapıyor gibi görünen işlevi sağlar

# 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
Sizinki çok daha hızlıydı, ancak ortaya çıkan problemde başarılı olmak için ana argümanın etrafında bir as.character () gerekiyordu.
IRTFM

1
@DWin - Bu doğru ama stringsAsFactors = FALSEveri çerçevesini tanımlarken ekleyerek bu sorunu önledim .
Dason

Üzgünüm net değildim. Aslında Tim riffe'e yanıt veriyor ve ona işlevinin ortaya çıkan problemle bir hata yaptığını söylüyordum. Sorunu yeniden tanımlamanızı kullanmış olabilir, ancak bunu söylemedi.
IRTFM

evet, ben de stringsAsFactors=TRUEyazdım, ama bundan bahsetmedim
tim riffe

Bir faktörde bir dizeyi aramak işe yarayacaktır, yani str_count (d $ faktör_sütun, 'A') ancak tersi olmaz
Nitro

65

R tabanından ayrılmak istemiyorsanız, işte oldukça kısa ve anlamlı bir olasılık:

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

2
Tamam - belki bu sadece ve birlikte birkaç kez kullandıktan sonra anlamlı gelecektir , ancak bu combo, bir fişi hak ettiğini düşündüğüm kadar güçlü. regmatchesgregexpr
Josh O'Brien

regmatchesnispeten yenidir. 2.14'te tanıtıldı.
Dason

Regmatch'e ihtiyacın olduğunu sanmıyorum. Gregexpr işlevi, her x öğesi için eşleşen oluşumların indislerini içeren bir liste döndürür.
savagent

@savagent - Her dizedeki eşleşme sayısını hesaplamak için kullanacağınız kodu paylaşır mısınız?
Josh O'Brien

1
Üzgünüm, -1'i unuttum. Yalnızca her satırda en az bir eşleşme varsa çalışır, sapply (gregexpr ("g", q.data $ string), uzunluk).
savagent

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

Nchar'a geçmeden önce faktör değişkenini karaktere zorladığıma dikkat edin. Normal ifade işlevleri bunu dahili olarak yapıyor gibi görünmektedir.

İşte karşılaştırma sonuçları (testin boyutu 3000 satıra çıkarılmış)

 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

3
Bu, cevaplardaki en hızlı çözümdür, ancak isteğe bağlı fixed=TRUEolarak geçerek karşılaştırmanızda ~% 30 daha hızlı yapılır gsub. Durumlar da vardır fixed=TRUEolacaktır gerekli (yani saymak istediğiniz karakter gibi regex iddianın olarak yorumlanabilir zaman .).
C8H10N4O2

7

CharToRaw kullanarak başka bir iyi seçenek :

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

6

stringiPaket fonksiyonları sağlar stri_countve stri_count_fixedhangi çok hızlı.

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

kıyaslama

@ 42-'nin cevabından en hızlı yaklaşıma ve 30.000 elemanlı bir vektör için stringrpaketten eşdeğer fonksiyona kıyasla .

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)

veri

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

görüntü açıklamasını buraya girin



2

Eminim birisi daha iyisini yapabilir, ama bu işe yarar:

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

veya bir işlevde:

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


1

Sadece string bölme kullanabilirsin

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

Bu da size 1, 3, 1, 0 verir. Dize bölmesini normal ifadeler ve tam kelimelerle de kullanabilirsiniz.


0

IMHO'nun en kolay ve en temiz yolu:

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`

Bu nasıl yapılır? Benim için lengths(gregexpr('a', q.data$string))döner 2 1 1, değil 2 1 0.
Finn Årup Nielsen


0

Yine başka bir base Rseçenek şunlar olabilir:

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

[1] 2 1 0

-1

Bir sonraki ifade işi yapar ve sadece harfler için değil semboller için de işe yarar.

İfade şu şekilde çalışır:

1: 2. sütunun satırları üzerinde yinelemek için veri çerçevesi q.data sütunlarında lapply kullanır ("lapply (q.data [, 2],"),

2: 2. sütunun her satırına bir işlev "function (x) {sum ('a' == strsplit (as.character (x), '') [[1]])}" uygulanır. İşlev, sütun 2 (x) 'in her satır değerini alır, karaktere dönüştürür (örneğin bir faktör olması durumunda) ve dizeyi her karakterde bölmeyi yapar ("strsplit (as.character (x)," ') "). Sonuç olarak, 2. sütunun her satırı için dize değerinin her karakterini içeren bir vektörümüz var.

3: Vektörün her vektör değeri, sayılması istenen karakterle, bu durumda "a" ("'a' ==") ile karşılaştırılır. Bu işlem, vektördeki değer sayılacak istenen karakterle eşleştiğinde True olan "c (True, False, True, ....)" Doğru ve Yanlış değerlerinin bir vektörünü döndürecektir.

4: Satırda 'a' karakterinin toplam görünme sayısı, "sum (....)" vektöründeki tüm "True" değerlerinin toplamı olarak hesaplanır.

5: Daha sonra, "lapply" işlevinin sonucunu paketten çıkarmak ve bunu veri çerçevesindeki yeni bir sütuna atamak için "listeyi açma" işlevi uygulanır ("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
Cevabınız, özellikle yeni kullanıcılar için tam olarak basit bir ifade olmadığı için ne yaptığının bir açıklamasıyla çok daha iyi olacaktır .
Khaine775

@ Khaine775 yorumunuz için teşekkürler ve gönderinin açıklamasının olmaması için özür dilerim. Gönderiyi düzenledim ve nasıl çalıştığına dair daha iyi bir açıklama için bazı yorumlar ekledim.
bacnqn

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

Etkili olmayabilir ama amacımı çöz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.