Effiziente Überprüfung des Werts einer anderen Zeile in der Datentabelle

Hinweis: Dies ist eine Frage, die ich ursprünglich in der data.table-Hilfegruppe gepostet habe. Matt Dowle hat nach einem detaillierteren Beispiel gefragt und ich habe dieses gepostet, aber ich hatte Probleme mit der Formatierung in der E-Mail. Ich weiß bereits, wie man auf SO formatiert, also dachte ich, ich würde es stattdessen hier posten.

Grundsätzlich versuche ich, Zeilen aus einer data.table auf der Grundlage eines Werts in dieser Zeile zu unterteilenebenso gut wie ein Wert in einer vorhergehenden oder folgenden Zeile. Im Moment erstelle ich neue Spalten für zukünftige und vergangene Zeilen und tippe dann die data.table in diese Spalten, was jedoch ressourcenintensiv und aufwändig ist.

Das folgende Beispiel veranschaulicht den Ansatz, den ich jetzt verwende. Das Beispiel verwendet Wörter in Dokumenten (ich verwende numerische Indizes für beide). Ich möchte eine Teilmenge für ein bestimmtes Wort erstellen, jedoch nur, wenn diesem ein anderes Wort oder eine Reihe von Wörtern vorangestellt oder gefolgt wird:

Ich erstelle zunächst einen Dummy-Datensatz mit zehn Dokumenten, die eine Million Wörter enthalten. Es gibt drei eindeutige Wörter im Satz.

library(data.table)
set.seed(1000)
DT<-data.table(wordindex=sample(1:3,1000000,replace=T),docindex=sample(1:10,1000000,replace=T))
setkey(DT,docindex)
DT[,position:=seq.int(1:.N),by=docindex]


          wordindex docindex position
      1:         1        1        1
      2:         1        1        2
      3:         3        1        3
      4:         3        1        4
      5:         1        1        5
    ---                            
 999996:         2       10    99811
 999997:         2       10    99812
 999998:         3       10    99813
 999999:         1       10    99814
1000000:         3       10    99815

Beachten Sie, dass es einfach und schön ist, die Vorkommen des ersten eindeutigen Wortes in allen Dokumenten zu zählen.

setkey(DT,wordindex)
count<-DT[J(1),list(count.1=.N),by=docindex]
count

    docindex count.1
 1:        1   33533
 2:        2   33067
 3:        3   33538
 4:        4   33053
 5:        5   33231
 6:        6   33002
 7:        7   33369
 8:        8   33353
 9:        9   33485
10:       10   33225

Es wird unordentlicher, wenn man die vorausschauende Position berücksichtigt. Dies ist eine Abfrage, um die Vorkommen des ersten eindeutigen Wortes in allen Dokumenten zu zählenes sei denn es folgt das zweite eindeutige Wort. Zuerst erstelle ich eine neue Spalte mit dem Wort eine Stelle vor und tippe dann beide Wörter ein.

setkey(DT,docindex,position)
DT[,lead_wordindex:=DT[list(docindex,position+1)][,wordindex]]

         wordindex docindex position lead_wordindex
      1:         1        1        1              1
      2:         1        1        2              3
      3:         3        1        3              3
      4:         3        1        4              1
      5:         1        1        5              2
     ---                                           
 999996:         2       10    99811              2
 999997:         2       10    99812              3
 999998:         3       10    99813              1
 999999:         1       10    99814              3
1000000:         3       10    99815             NA

setkey(DT,wordindex,lead_wordindex)
countr2<-DT[J(c(1,1),c(1,3)),list(count.1=.N),by=docindex]
countr2

    docindex count.1
 1:        1   22301
 2:        2   21835
 3:        3   22490
 4:        4   21830
 5:        5   22218
 6:        6   21914
 7:        7   22370
 8:        8   22265
 9:        9   22211
10:       10   22190

Ich habe einen sehr großen Datensatz, für den die obige Abfrage zur Speicherzuweisung fehlschlägt. Alternativ können wir diese neue Spalte nur für die relevante Teilmenge von Daten erstellen, indem wir den ursprünglichen Datensatz filtern und ihn dann an der gewünschten Position wieder zusammenfügen:

setkey(DT,wordindex)
filter<-DT[J(1),list(wordindex,docindex,position)]
filter[,lead_position:=position+1]

        wordindex wordindex docindex position lead_position
     1:         1         1        2    99717         99718
     2:         1         1        3    99807         99808
     3:         1         1        4   100243        100244
     4:         1         1        1        1             2
     5:         1         1        1       42            43
    ---                                                    
332852:         1         1       10    99785         99786
332853:         1         1       10    99787         99788
332854:         1         1       10    99798         99799
332855:         1         1       10    99804         99805
332856:         1         1       10    99814         99815

setkey(DT,docindex,position)
filter[,lead_wordindex:=DT[J(filter[,list(docindex,lead_position)])][,wordindex]]

        wordindex wordindex docindex position lead_position lead_wordindex
     1:         1         1        2    99717         99718             NA
     2:         1         1        3    99807         99808             NA
     3:         1         1        4   100243        100244             NA
     4:         1         1        1        1             2              1
     5:         1         1        1       42            43              1
    ---                                                                   
332852:         1         1       10    99785         99786              3
332853:         1         1       10    99787         99788              3
332854:         1         1       10    99798         99799              3
332855:         1         1       10    99804         99805              3
332856:         1         1       10    99814         99815              3

setkey(filter,wordindex,lead_wordindex)
countr2.1<-filter[J(c(1,1),c(1,3)),list(count.1=.N),by=docindex]
countr2.1

    docindex count.1
 1:        1   22301
 2:        2   21835
 3:        3   22490
 4:        4   21830
 5:        5   22218
 6:        6   21914
 7:        7   22370
 8:        8   22265
 9:        9   22211
10:       10   22190

Ziemlich hässlich, finde ich. Außerdem möchte ich vielleicht mehr als ein Wort vorausschauen, was die Erstellung einer weiteren Spalte erforderlich macht. Der einfache, aber kostenintensive Weg ist:

setkey(DT,docindex,position)
DT[,lead_lead_wordindex:=DT[list(docindex,position+2)][,wordindex]]

         wordindex docindex position lead_wordindex lead_lead_wordindex
      1:         1        1        1              1                   3
      2:         1        1        2              3                   3
      3:         3        1        3              3                   1
      4:         3        1        4              1                   2
      5:         1        1        5              2                   3
     ---                                                               
 999996:         2       10    99811              2                   3
 999997:         2       10    99812              3                   1
 999998:         3       10    99813              1                   3
 999999:         1       10    99814              3                  NA
1000000:         3       10    99815             NA                  NA

setkey(DT,wordindex,lead_wordindex,lead_lead_wordindex)
countr23<-DT[J(1,2,3),list(count.1=.N),by=docindex]
countr23

    docindex count.1
 1:        1    3684
 2:        2    3746
 3:        3    3717
 4:        4    3727
 5:        5    3700
 6:        6    3779
 7:        7    3702
 8:        8    3756
 9:        9    3702
10:       10    3744

Allerdings muss ich derzeit wegen der Größe die hässliche Filter-and-Join-Methode verwenden.

Die Frage ist also, gibt es einen einfacheren und schöneren Weg?

AKTUALISIEREN:

Vielen Dank an Arun und eddi für den sauberen und einfachen Code, der das Problem löst. Bei meinen ~ 200M Zeilendaten funktioniert diese Lösung mit einer einfachen Kombination von Wörtern in ungefähr 10 Sekunden, was ziemlich gut ist!

Ich habe jedoch ein zusätzliches Problem, das den Vektor-Scan-Ansatz weniger als optimal macht. Obwohl ich in diesem Beispiel nur nach einer Wortkombination suche, kann es in der Praxis vorkommen, dass an jeder Position ein Vektor von Wörtern gesucht wird. Wenn ich zu diesem Zweck die "==" - Anweisungen in "% in%" ändere (Vektoren mit 100 Wörtern oder mehr), dauert die Ausführung der Abfrage viel länger. Ich wäre also immer noch an einer binären Suchlösung interessiert, falls es eine gibt. Wenn Arun jedoch keine kennt, könnte dies nicht der Fall sein, und ich würde seine Antwort gerne annehmen.

Antworten auf die Frage(3)

Ihre Antwort auf die Frage