Làm thế nào để tính số lần xuất hiện của một ký tự đã cho trong mỗi hàng của cột chuỗi?


103

Tôi có data.frame trong đó các biến nhất định chứa một chuỗi văn bản. Tôi muốn đếm số lần xuất hiện của một ký tự nhất định trong mỗi chuỗi riêng lẻ.

Thí dụ:

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

Tôi muốn tạo một cột mới cho q.data với số lần xuất hiện của "a" trong chuỗi (tức là. C (2,1,0)).

Cách tiếp cận phức tạp duy nhất mà tôi đã quản lý là:

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

Câu trả lời:


141

Gói stringr cung cấp str_counthàm dường như thực hiện những gì bạn quan tâm

# 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
Yours nhanh hơn nhiều mặc dù nó cần một as.character () xung quanh đối số chính để giải quyết vấn đề được đặt ra.
IRTFM

1
@DWin - Điều đó đúng nhưng tôi đã tránh được vấn đề đó bằng cách thêm stringsAsFactors = FALSEkhi xác định khung dữ liệu.
Dason

Xin lỗi tôi đã không rõ ràng. Tôi thực sự đã phản hồi với tim riffe và nói với anh ấy rằng chức năng của anh ấy đã gặp lỗi với vấn đề được đặt ra. Anh ấy có thể đã sử dụng định nghĩa lại của bạn về vấn đề nhưng anh ấy đã không nói như vậy.
IRTFM

vâng, tôi cũng đã làm, stringsAsFactors=TRUEtrên comp của tôi, nhưng không đề cập này
tim riffe

Tìm kiếm một chuỗi trong một yếu tố sẽ hoạt động tức là str_count (d $ factor_column, 'A') nhưng không phải ngược lại
Nitro

65

Nếu bạn không muốn rời cơ sở R, đây là một khả năng khá ngắn gọn và dễ hiểu:

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

2
OK - có thể điều đó sẽ chỉ cảm thấy rõ ràng khi bạn đã sử dụng regmatchesgregexprcùng nhau một vài lần, nhưng kết hợp đó đủ mạnh để tôi nghĩ rằng nó xứng đáng được cắm.
Josh O'Brien

regmatcheslà tương đối mới. Nó được giới thiệu trong 2.14.
Dason

Tôi không nghĩ rằng bạn cần bit regmatch. Hàm gregexpr trả về một danh sách với các chỉ số của các lần xuất hiện phù hợp cho mỗi phần tử của x.
man rợ

@savagent - Bạn có phiền chia sẻ mã mà bạn sử dụng để tính số kết quả phù hợp trong mỗi chuỗi không?
Josh O'Brien

1
Xin lỗi, tôi đã quên về -1. Nó chỉ hoạt động nếu mỗi dòng có ít nhất một khớp, sapply (gregexpr ("g", q.data $ string), length).
dã man

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

Lưu ý rằng tôi buộc biến yếu tố thành ký tự, trước khi chuyển sang nchar. Các hàm regex dường như thực hiện điều đó bên trong.

Đây là kết quả điểm chuẩn (với kích thước bài kiểm tra được mở rộng thành 3000 hàng)

 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
Đây là giải pháp nhanh nhất trong các câu trả lời nhưng được thực hiện nhanh hơn ~ 30% trên điểm chuẩn của bạn bằng cách chuyển tùy chọn fixed=TRUEđến gsub. Cũng có những trường hợp fixed=TRUEđược yêu cầu (tức là khi ký tự bạn muốn đếm có thể được hiểu là một xác nhận regex chẳng hạn .).
C8H10N4O2

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

là một lựa chọn tốt.


5

Các stringigói cung cấp các chức năng stri_countstri_count_fixedđó là rất nhanh.

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

điểm chuẩn

So với cách tiếp cận nhanh nhất từ câu trả lời của @ 42-hàm tương đương từ stringrgói cho một vectơ có 30.000 phần tử.

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)

dữ liệu

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

nhập mô tả hình ảnh ở đây



2

Tôi chắc ai đó có thể làm tốt hơn, nhưng điều này hiệu quả:

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

hoặc trong một hàm:

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

Tôi dường như gặp lỗi với lỗi đầu tiên ... và lỗi thứ hai ... (đang cố gắng chuẩn tất cả những thứ này.)
IRTFM 14/09/12

1

Bạn chỉ có thể sử dụng phân chia chuỗi

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

Điều này sẽ cho bạn 1, 3, 1, 0. Bạn cũng có thể sử dụng phép chia chuỗi với các biểu thức chính quy và toàn bộ từ.


0

Cách dễ nhất và sạch nhất IMHO là:

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`

Điều đó được thực hiện như thế nào? Đối với tôi, lengths(gregexpr('a', q.data$string))trả lại 2 1 1chứ không phải 2 1 0.
Finn Årup Nielsen


0

Tuy nhiên, một base Rlựa chọn khác có thể là:

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

[1] 2 1 0

-1

Biểu thức tiếp theo thực hiện công việc và cũng hoạt động cho các biểu tượng, không chỉ chữ cái.

Biểu thức hoạt động như sau:

1: nó sử dụng lapply trên các cột của khung dữ liệu q.data để lặp qua các hàng của cột 2 ("lapply (q.data [, 2],"),

2: nó áp dụng cho mỗi hàng của cột 2 một hàm "function (x) {sum ('a' == strsplit (as.character (x), '') [[1]])}". Hàm nhận từng giá trị hàng của cột 2 (x), chuyển đổi thành ký tự (ví dụ: trong trường hợp nó là một hệ số) và nó thực hiện việc tách chuỗi trên mọi ký tự ("strsplit (as.character (x), ' ') "). Kết quả là chúng ta có một vectơ aa với mỗi ký tự của giá trị chuỗi cho mỗi hàng của cột 2.

3: Mỗi giá trị vector của vector được so sánh với ký tự mong muốn được đếm, trong trường hợp này là "a" ("'a' =="). Thao tác này sẽ trả về một vectơ có giá trị True và False "c (True, False, True, ....)", là True khi giá trị trong vectơ khớp với ký tự mong muốn được đếm.

4: Tổng số lần ký tự 'a' xuất hiện trong hàng được tính bằng tổng của tất cả các giá trị 'Đúng' trong vectơ "sum (....)".

5: Sau đó, nó được áp dụng hàm "unlist" để giải nén kết quả của hàm "lapply" và gán nó vào một cột mới trong dataframe ("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
Câu trả lời của bạn sẽ tốt hơn rất nhiều nếu có những gì nó làm được, đặc biệt là đối với những người dùng mới vì nó không chính xác là một biểu thức đơn giản .
Khaine775

Cảm ơn @ Khaine775 về nhận xét của bạn và tôi xin lỗi vì thiếu mô tả của bài đăng. Tôi đã chỉnh sửa bài đăng và thêm một số nhận xét để mô tả tốt hơn về cách nó hoạt động.
bacnqn

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

Có thể không phải là một trong những hiệu quả nhưng giải quyết mục đích của tôi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.