Comment calculer le nombre d'occurrence d'un caractère donné dans chaque ligne d'une colonne de chaînes?


57

J'ai un data.frame dans lequel certaines variables contiennent une chaîne de caractères. Je souhaite compter le nombre d'occurrences d'un caractère donné dans chaque chaîne individuelle.

Exemple: (. C-à-dire (2,1,0))

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

Je souhaite créer une nouvelle colonne pour q.data avec le nombre d'occurence de "a" dans la chaîne.

La seule approche alambiquée que je suis parvenu est:

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 
70

Le paquet stringr fournit la fonction str_count qui semble faire ce qui vous intéresse

# 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

Le vôtre était beaucoup plus rapide bien qu'il ait besoin d'un as.character() autour de l'argument principal pour réussir avec le problème posé. 14 sept.. 122012-09-14 20:09:53

+1

@DWin - C'est vrai mais j'ai évité ce problème en ajoutant 'stringsAsFactors = FALSE' lors de la définition de la trame de données. 14 sept.. 122012-09-14 20:10:54

  0

Désolé, je n'étais pas clair.Je répondais à Tim Riffe et lui disais que sa fonction jetait une erreur sur le problème posé. Il a peut-être utilisé votre redéfinition du problème mais il ne l'a pas dit. 14 sept.. 122012-09-14 20:14:50

  0

ouais, j'ai aussi fait 'stringsAsFactors = TRUE' sur mon ordi, mais je ne l'ai pas mentionné 14 sept.. 122012-09-14 20:31:13

  0

La recherche d'une chaîne dans un facteur fonctionnera ie str_count (d $ factor_column, 'A') mais pas vice-versa 27 sept.. 172017-09-27 19:24:40


2

Je suis sûr que quelqu'un peut faire mieux, mais cela fonctionne:

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

ou dans une fonction:

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

Je semble avoir une erreur avec la première ... et la seconde ... (essayait de comparer tous ces 14 sept.. 122012-09-14 20:04:21

  0

Utilisez 'as.character()' 14 sept.. 122012-09-14 20:10:36

  0

édité pour refléter cela, merci 14 sept.. 122012-09-14 20:31:59


35

Si vous ne voulez pas quitter la base R, voici une possibilité assez succincte et expressif:

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

Mise à jour: Depuis R 3.2.0, on peut utiliser lengths(x) comme plus remplacement efficace pour sapply(x, length). Ainsi, le code ci-dessus peut être simplement

lengths(regmatches(x, gregexpr("g", x))) 
  0

OK - peut-être que cela ne vous paraîtra * expressif * qu'une fois que vous aurez utilisé les 'regmatches' et' gregexpr' ensemble plusieurs fois, mais ce combo est suffisamment puissant pour que je pense qu'il méritait un plug. 14 sept.. 122012-09-14 15:48:41

  0

+1 pour regmatches, je n'étais pas au courant de la fonction. 14 sept.. 122012-09-14 16:06:46

  0

'regmatches' est relativement nouveau. Il a été introduit en 2.14. 15 sept.. 122012-09-15 17:49:41

  0

Je ne pense pas que vous ayez besoin du bit regmatches. La fonction gregexpr renvoie une liste avec les indices des occurrences trouvées pour chaque élément de x. 26 août. 142014-08-26 03:27:20

  0

@savagent - Pourriez-vous partager le code que vous utiliseriez pour calculer le nombre de correspondances dans chaque chaîne? 26 août. 142014-08-26 04:07:30

+1

Désolé, j'ai oublié le -1. Cela ne fonctionne que si chaque ligne a au moins une correspondance, sapply (gregexpr ("g", q.data $ string), length). 26 août. 142014-08-26 04:42:19

  0

@savagent - Yup. C'était à peu près mon processus de pensée aussi;) 26 août. 142014-08-26 05:17:18

  0

Si vous changez la fonction sapply de la longueur à une fonction personnalisée, cela fonctionne également avec les -1. par exemple. 'spply (regmatches (x, gregxpr (" g ", x)), fonction (v) sum (v> 0))' 22 févr.. 162016-02-22 17:07:28


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

Notez que je COERCE la variable de facteur à caractère, avant de passer à nchar. Les fonctions regex semblent le faire en interne.

est ici des résultats de référence (avec une taille plus grande échelle du test à 3000 lignes)

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 
  0

Ceci est la solution la plus rapide dans les réponses mais est faite ~ 30% plus rapide sur votre benchmark en passant l'option 'fixed = TRUE' à' gsub'. Il y a aussi des cas où 'fixed = TRUE' serait ** requis ** (c'est-à-dire, quand le caractère que vous voulez compter pourrait être interprété comme une assertion regex telle que' .'). 16 janv.. 182018-01-16 16:15:43


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

peut ne pas être une efficace mais résoudre mon but.

  0

C'est exactement la même chose que [cette réponse] (http://stackoverflow.com/a/12430764). 06 avril. 162016-04-06 11:29:05


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

est une bonne option.


0

Je compte les caractères de la même manière que Amarjeet. Cependant je préfère le faire en une seule ligne.

HowManySpaces<-nchar(DF$string)-nchar(gsub(" ","",DF$string)) # count spaces in DF$string 

0

Le plus simple et la plus propre à mon humble avis est: il

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`