Як обчислити кількість зустрічань заданого символу в кожному рядку стовпця рядків?


103

У мене є 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

Відповіді:


141

Пакет 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

1
Ваш був набагато швидшим, хоча для головного аргументу потрібен as.character (), щоб досягти успіху з поставленою проблемою.
IRTFM

1
@DWin - Це правда, але я уникну цього питання, додаючи stringsAsFactors = FALSEпри визначенні кадру даних.
Дасон

Вибачте, що я не зрозумів Я насправді відповідав на темф риф і казав йому, що його функція спричинила помилку з поставленою проблемою. Він, можливо, використав ваше переосмислення проблеми, але він цього не сказав.
IRTFM

так, я теж робив stringsAsFactors=TRUEна своєму комп, але не згадував про це
tim riffe

Пошук рядка у факторі буде працювати, тобто str_count (d $ factor_column, 'A'), але не навпаки
Nitro

65

Якщо ви не хочете залишати базу R, ось досить коротка та виразна можливість:

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

2
Гаразд - можливо, це буде відчувати себе виразним лише після того, як ви кілька разів використали regmatchesта gregexprразом, але це комбо досить потужне, що я вважав, що це заслуговує плагіну.
Josh O'Brien

regmatchesє відносно новим. Він був введений у 2.14.
Дейсон

Я не думаю, що вам потрібні не багато регламентів. Функція gregexpr повертає список з індексами збіжених подій для кожного елемента x.
дикунство

@savagent - Ви б не хотіли поділитися кодом, який ви використали для обчислення кількості збігів у кожній рядку?
Josh O'Brien

1
Вибачте, я забув про -1. Він працює лише у тому випадку, якщо кожен рядок має принаймні одну відповідність, sapply (gregexpr ("g", q.data $ string), length).
дикунство

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

Зауважте, що я примушую факторну змінну до символу, перш ніж перейти до nchar. Функції регулярного вираження роблять це внутрішньо.

Ось базові результати (із зменшеним розміром тесту до 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

3
Це найшвидше рішення у відповідях, але це робиться на 30% швидше у вашому орієнтирі, передаючи необов'язкове fixed=TRUEзначення gsub. Існують також випадки, коли fixed=TRUEце буде потрібно (наприклад, коли символ, який ви хочете порахувати, може бути інтерпретований як твердження регулярного вираження, наприклад .).
C8H10N4O2


5

У stringiпакеті передбачені функції stri_countі stri_count_fixedякі дуже швидкі.

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

орієнтир

Порівняно з найшвидшим підходом з відповіді @ 42- and та з еквівалентною функцією з 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")

Здається, я отримав помилку з першим ... і другим ... (намагався порівняти все це)
IRTFM

1

Ви можете просто використовувати поділ струн

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

Що дасть вам 1, 3, 1, 0. Ви також можете використовувати поділ рядків з регулярними виразами та цілими словами.



0

Питання нижче переміщено сюди, але, схоже, ця сторінка не відповідає безпосередньо на питання Фари Ель Як знайти числа 1 з 101 в R

Отже, я напишу тут відповідь, про всяк випадок.

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


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 функція "функція (x) {sum ('a' == strsplit (as.character (x), '') [[1]])}". Функція бере кожне значення рядка стовпця 2 (x), перетворює в символ (якщо він є, наприклад, фактором), і робить розбиття рядка на кожен символ ("strsplit (as.character (x),") ') "). В результаті у нас є векторний вектор з кожним символом значення рядка для кожного рядка стовпця 2.

3: Кожне значення вектора порівнюється з потрібним символом, який слід підрахувати, в цьому випадку "a" ("'a' =="). Ця операція поверне вектор істинних та хибних значень "c (True, False, True, ....)", істинних, коли значення у векторі відповідає бажаному символу, який потрібно підрахувати.

4: Загальний раз, коли символ "a" з'являється у рядку, обчислюється як сума всіх значень "True" у векторі "sum (....)".

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)

Може бути не ефективним, але вирішити мою мету.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.