2015-05-06 3 views
8

Я пытаюсь выполнить сопоставление слов в большом наборе данных. Мне интересно, есть ли способ ускорить самую медленную операцию в моем рабочем процессе.Быстрое совпадение строк в R

Что я намереваюсь сделать, так это найти расположение совпадений между словарем слов и списком векторов слов.

words <- c("cat", "dog", "snake", "cow") 
scores <- c(1.5, 0.7, 3.5, 4.6) 
dic <- data.frame(words, scores) 

wordList <- list(c("jiraffe", "dog"), c("cat", "elephant"), c("snake", "cow")) 

Самый быстрый способ я нашел до сих пор сделать это, делая это:

matches <- function(wordList) { 
    subD <- which(dic$words %in% wordList) 
} 

Мой желаемый результат:

matches(wordList): 
list(c(2), c(1), c(3, 4)) 

, который я могу позже использовать, чтобы получить средний балл за словоList ячейка по

averageScore <- sapply(matches, function(x) {mean(dic[x, "scores"]}) 

Есть ли более быстрый способ сделать согласование строк, чем то, что я делаю в функции:

subD <- which(dic$words %in% wordList) 

Я попытался dplyr путь, думая, что это может быть быстрее, используя первый «фильтр», чтобы получить подмножество «dic» и применяя на нем «colMeans», но, похоже, он в два раза медленнее.

Кроме того, запуск моей функции совпадений в цикле выполняется так же медленно, как использование «lapply» на нем.

Я что-то не хватает? Есть ли способ, который быстрее, чем оба?

+0

Есть пакеты для быстрого согласования, как 'fastmatch'; и 'chmatch' в пакете' data.table'. Вероятно, они превосходят все, что мы можем взломать в нескольких строках базы R. – Frank

+1

Существует ли абсолютное требование, что вы используете структуру данных списка словных векторов, которые вы сопоставляете? Ваше замедление поступит в структуру цикла for, используемую при вызове sapply(). Я бы векторизовал эту операцию, построив длинноформатную структуру данных, которую вы могли бы легко суммировать с помощью dplyr или тому подобного. Я мог бы написать ответ с этой структурой, если хотите. –

+2

Чтобы развернуть комментарий Форреста, вы можете начать со списка (что удобно для ввода групп, а затем преобразовать в 'mydf <- data.frame (w = unlist (wordList), g = rep (1: length (wordList)), sapply (wordList, length))) 'для более быстрого анализа. – Frank

ответ

9

Вот один вариант:

library(data.table) 
nn <- lengths(wordList) ## Or, for < R-3.2.0, `nn <- sapply(wordList, length)` 
dt <- data.table(grp=rep(seq_along(nn), times=nn), X = unlist(wordList), key="grp") 
dt[,Score:=scores[chmatch(X,words)]] 
dt[!is.na(Score), list(avgScore=mean(Score)), by="grp"] 
# grp avgScore 
# 1: 1  0.70 
# 2: 2  1.50 
# 3: 3  4.05 
+0

Спасибо, Джош, это решение элегантно и намного быстрее, чем у меня! – alexvicegrab

Смежные вопросы