2015-01-21 17 views
5

Herausgegebendf und dictApportieren Satz Punktzahl basierend auf den Werten der Wörter in einem Wörterbuch

ich einen Datenrahmen enthalten, haben Sätze:

df <- data_frame(text = c("I love pandas", "I hate monkeys", "pandas pandas pandas", "monkeys monkeys")) 

Und ein Wörterbuch Wörter und ihre entsprechenden Werte enthalten:

dict <- data_frame(word = c("love", "hate", "pandas", "monkeys"), 
        score = c(1,-1,1,-1)) 

Ich möchte eine Spalte "Score" ananhängen, die die Bewertung für jeden Satz zusammenfassen würde:

Erwartete Ergebnisse

    text score 
1  I love pandas  2 
2  I hate monkeys -2 
3 pandas pandas pandas  3 
4  monkeys monkeys -2 

aktualisieren

Hier sind die Ergebnisse bisher:

Akrun Methoden

Vorschlag 1

df %>% mutate(score = sapply(strsplit(text, ' '), function(x) with(dict, sum(score[word %in% x])))) 

Beachten Sie, dass für diese Methode zu arbeiten, hatte ich data_frame() verwenden df zu erstellen und dict statt data.frame() sonst erhalte ich: Error in strsplit(text, " ") : non-character argument

Source: local data frame [4 x 2] 

        text score 
1  I love pandas  2 
2  I hate monkeys -2 
3 pandas pandas pandas  1 
4  monkeys monkeys -1 

Dies gilt nicht entfallen mehrere Übereinstimmungen in einer einzelnen Zeichenfolge. Nah am erwarteten Ergebnis, aber noch nicht ganz da.

Vorschlag 2

gezwickt ich ein bisschen eine der Vorschlag des akrun in den Kommentaren an die bearbeitete Postkonto

cbind(df, unnest(stri_split_fixed(df$text, ' '), group) %>% 
     group_by(group) %>% 
     summarise(score = sum(dict$score[dict$word %in% x])) %>% 
     ungroup() %>% select(-group) %>% data.frame()) 

Dies gilt nicht für mehrere Übereinstimmungen in einem String anwenden:

Richard Scriven Methoden

Anregung 1

group_by(df, text) %>% 
mutate(score = sum(dict$score[stri_detect_fixed(text, dict$word)])) 

Nachdem alle Pakete aktualisieren, das funktioniert jetzt (obwohl es nicht für mehrere Übereinstimmungen nicht Konto)

Source: local data frame [4 x 2] 
Groups: text 

        text score 
1  I love pandas  2 
2  I hate monkeys -2 
3 pandas pandas pandas  1 
4  monkeys monkeys -1 

Anregung 2

total <- with(dict, { 
    vapply(df$text, function(X) { 
    sum(score[vapply(word, grepl, logical(1L), x = X, fixed = TRUE)]) 
    }, 1) 
}) 

cbind(df, total) 

Dies gibt die sa me Ergebnisse:

    text total 
1  I love pandas  2 
2  I hate monkeys -2 
3 pandas pandas pandas  1 
4  monkeys monkeys -1 

Anregung 3

s <- strsplit(df$text, " ") 
total <- vapply(s, function(x) sum(with(dict, score[match(x, word, 0L)])), 1) 
cbind(df, total) 

Dies funktioniert tatsächlich:

    text total 
1  I love pandas  2 
2  I hate monkeys -2 
3 pandas pandas pandas  3 
4  monkeys monkeys -2 

Thelatemail Methode

res <- sapply(dict$word, function(x) { 
    sapply(gregexpr(x,df$text),function(y) length(y[y!=-1])) 
}) 

cbind(df, score = rowSums(res * dict$score)) 

Beachten Sie, dass ich den cbind() Teil hinzugefügt habe. Dies entspricht tatsächlich dem erwarteten Ergebnis.

    text score 
1  I love pandas  2 
2  I hate monkeys -2 
3 pandas pandas pandas  3 
4  monkeys monkeys -2 

endgültige Antwort

von akrun Vorschlag inspiriert, hier ist das, was ich als die dplyr -esque Lösung Schreiben endete:

library(dplyr) 
library(tidyr) 
library(stringi) 

bind_cols(df, unnest(stri_split_fixed(df$text, ' '), group) %>% 
      group_by(x) %>% mutate(score = sum(dict$score[dict$word %in% x])) %>% 
      group_by(group) %>% 
      summarise(score = sum(score)) %>% 
      select(-group)) 

Obwohl ich Richard Scriven Vorschlag # implementieren 3 da es am effizientesten ist.

Benchmark

Hier sind die Vorschläge zu viel größere Datensätze angewendet (df von 93 Sätzen und dict von 14K Wörter) mit microbenchmark():

mbm = microbenchmark(
    akrun = df %>% mutate(score = sapply(stri_detect_fixed(text, ' '), function(x) with(dict, sum(score[word %in% x])))), 
    akrun2 = cbind(df, unnest(stri_split_fixed(df$text, ' '), group) %>% group_by(group) %>% summarise(score = sum(dict$score[dict$word %in% x])) %>% ungroup() %>% select(-group) %>% data.frame()), 
    rscriven1 = group_by(df, text) %>% mutate(score = sum(dict$score[stri_detect_fixed(text, dict$word)])), 
    rscriven2 = cbind(df, score = with(dict, { vapply(df$text, function(X) { sum(score[vapply(word, grepl, logical(1L), x = X, fixed = TRUE)])}, 1)})), 
    rscriven3 = cbind(df, score = vapply(strsplit(df$text, " "), function(x) sum(with(dict, score[match(x, word, 0L)])), 1)), 
    thelatemail = cbind(df, score = rowSums(sapply(dict$word, function(x) { sapply(gregexpr(x,df$text),function(y) length(y[y!=-1])) }) * dict$score)), 
    sbeaupre = bind_cols(df, unnest(stri_split_fixed(df$text, ' '), group) %>% group_by(x) %>% mutate(score = sum(dict$score[dict$word %in% x])) %>% group_by(group) %>% summarise(score = sum(score)) %>% select(-group)), 
    times = 10 
) 

Und die Ergebnisse:

enter image description here

+0

Was haben Sie selbst versucht? –

+1

Ich denke, Sie müssen 'strsplit' versuchen. Etwas wie 'sapply (strsplit (df $ text, ''), Funktion (x) mit (dict, sum (score [Wort% in% x]))) – akrun

+0

@akrun. Das ist der Trick. 'df%>% muate (score = sapply (strsplit (Text, ''), Funktion (x) mit (dict, summe (score [wort% in% x]))) ' –

Antwort

6

Update: Hier ist die einfachste Methode dplyr, die ich bisher gefunden habe. Und ich werde eine Funktion hinzufügen, um die Dinge zu beschleunigen. Vorausgesetzt, es gibt keine identischen Sätze in df$text sind, gruppieren wir nach dieser Spalte und dann anwenden mutate()

Hinweis: Paketversionen sind dplyr 0.4.1 und stringi 0.4.1

library(dplyr) 
library(stringi) 

group_by(df, text) %>% 
    mutate(score = sum(dict$score[stri_detect_fixed(text, dict$word)])) 
# Source: local data frame [2 x 2] 
# Groups: text 
# 
#    text score 
# 1 I love pandas  2 
# 2 I hate monkeys -2 

ich die do() Methode entfernt I letzte Nacht gepostet, aber Sie können es in der Bearbeitungsgeschichte finden. Für mich scheint es unnötig, da die obige Methode genauso gut funktioniert und die dplyr Methode ist, es zu tun.

Darüber hinaus, wenn Sie für eine non dplyr Antwort offen sind, sind hier zwei Basisfunktionen.

total <- with(dict, { 
    vapply(df$text, function(X) { 
     sum(score[vapply(word, grepl, logical(1L), x = X, fixed = TRUE)]) 
    }, 1) 
}) 
cbind(df, total) 
#    text total 
# 1 I love pandas  2 
# 2 I hate monkeys -2 

Oder ein alternatives strsplit() mit dem gleichen Ergebnis

s <- strsplit(df$text, " ") 
total <- vapply(s, function(x) sum(with(dict, score[match(x, word, 0L)])), 1) 
cbind(df, total) 
2

Ein bisschen Doppel Looping über sapply und gregexpr:

res <- sapply(dict$word, function(x) { 
    sapply(gregexpr(x,df$text),function(y) length(y[y!=-1])) 
}) 
rowSums(res * dict$score) 
#[1] 2 -2 

Dies gilt auch für Konten, wenn es in mehrere Übereinstimmungen eine einzelne Zeichenfolge:

df <- data.frame(text = c("I love love pandas", "I hate monkeys")) 
# run same code as above 
#[1] 3 -2