Textzeichenfolge in eine data.table-Spalte teilen

86

Ich habe ein Skript, das Daten aus einer CSV-Datei in eine einliest data.tableund dann den Text in einer Spalte in mehrere neue Spalten aufteilt. Ich benutze derzeit die lapplyund strsplitFunktionen, um dies zu tun. Hier ist ein Beispiel:

library("data.table")
df = data.table(PREFIX = c("A_B","A_C","A_D","B_A","B_C","B_D"),
                VALUE  = 1:6)
dt = as.data.table(df)

# split PREFIX into new columns
dt$PX = as.character(lapply(strsplit(as.character(dt$PREFIX), split="_"), "[", 1))
dt$PY = as.character(lapply(strsplit(as.character(dt$PREFIX), split="_"), "[", 2))

dt 
#    PREFIX VALUE PX PY
# 1:    A_B     1  A  B
# 2:    A_C     2  A  C
# 3:    A_D     3  A  D
# 4:    B_A     4  B  A
# 5:    B_C     5  B  C
# 6:    B_D     6  B  D 

Im obigen Beispiel wird die Spalte PREFIXin zwei neue Spalten PXund PYmit dem Zeichen "_" aufgeteilt.

Obwohl dies gut funktioniert, habe ich mich gefragt, ob es einen besseren (effizienteren) Weg gibt, dies mit zu tun data.table. Meine realen Datensätze haben> = 10M + Zeilen, daher wird die Zeit- / Speichereffizienz wirklich wichtig.


AKTUALISIEREN:

Auf @ Franks Vorschlag hin habe ich einen größeren Testfall erstellt und die vorgeschlagenen Befehle verwendet, aber das stringr::str_split_fixeddauert viel länger als die ursprüngliche Methode.

library("data.table")
library("stringr")
system.time ({
    df = data.table(PREFIX = rep(c("A_B","A_C","A_D","B_A","B_C","B_D"), 1000000),
                    VALUE  = rep(1:6, 1000000))
    dt = data.table(df)
})
#   user  system elapsed 
#  0.682   0.075   0.758 

system.time({ dt[, c("PX","PY") := data.table(str_split_fixed(PREFIX,"_",2))] })
#    user  system elapsed 
# 738.283   3.103 741.674 

rm(dt)
system.time ( {
    df = data.table(PREFIX = rep(c("A_B","A_C","A_D","B_A","B_C","B_D"), 1000000),
                     VALUE = rep(1:6, 1000000) )
    dt = as.data.table(df)
})
#    user  system elapsed 
#   0.123   0.000   0.123 

# split PREFIX into new columns
system.time ({
    dt$PX = as.character(lapply(strsplit(as.character(dt$PREFIX), split="_"), "[", 1))
    dt$PY = as.character(lapply(strsplit(as.character(dt$PREFIX), split="_"), "[", 2))
})
#    user  system elapsed 
#  33.185   0.000  33.191 

Die str_split_fixedMethode dauert also etwa 20-mal länger.

Derric Lewis
quelle
Ich denke, die Operation außerhalb der data.table zuerst durchzuführen, könnte besser sein. Wenn Sie das stringrPaket verwenden, ist dies der Befehl : str_split_fixed(PREFIX,"_",2). Ich antworte nicht, weil ich die Beschleunigung nicht getestet habe ... Oder in einem Schritt:dt[,c("PX","PY"):=data.table(str_split_fixed(PREFIX,"_",2))]
Frank

Antworten:

122

Update: Ab Version 1.9.6 (auf CRAN ab 15. September) können wir die Funktion verwenden tstrsplit(), um die Ergebnisse direkt (und auf viel effizientere Weise) abzurufen :

require(data.table) ## v1.9.6+
dt[, c("PX", "PY") := tstrsplit(PREFIX, "_", fixed=TRUE)]
#    PREFIX VALUE PX PY
# 1:    A_B     1  A  B
# 2:    A_C     2  A  C
# 3:    A_D     3  A  D
# 4:    B_A     4  B  A
# 5:    B_C     5  B  C
# 6:    B_D     6  B  D

tstrsplit()Im Grunde ist es ein Wrapper für transpose(strsplit()), bei dem die transpose()ebenfalls kürzlich implementierte Funktion eine Liste transponiert. Bitte sehen Sie ?tstrsplit()und ?transpose()für Beispiele.

In der Geschichte finden Sie alte Antworten.

Arun
quelle
Danke Arun. Ich hatte nicht an die Methode gedacht, zuerst die Liste, dann den Index und dann die Spalten zu erstellen, wie in "a_spl" beschrieben. Ich habe immer gedacht, dass es am besten ist, alles in einer einzigen Zeile zu tun. Nur aus Neugier, warum funktioniert der Index so viel schneller?
Derric Lewis
@Arun, im Zusammenhang mit dieser Frage, was sind einige der Fallstricke, die Sie in einer Funktion sehen würden, wie ich sie hier geschrieben habe: gist.github.com/mrdwab/6873058 Grundsätzlich habe ich davon Gebrauch gemacht fread, aber um dies zu tun, Ich musste ein verwenden tempfile(was wie ein Engpass erscheinen würde), da es nicht so aussieht, als hätte freades ein Äquivalent zu einem textArgument. Beim Testen mit diesen Beispieldaten liegt die Leistung zwischen Ihren a_splund Ihren a_subAnsätzen.
A5C1D2H2I1M1N2O1R2T1
4
Ich habe mich gefragt, wie man die Anzahl der Spalten auf der LHS von: = erraten und die Namen der neuen Spalten basierend auf den Vorkommen von grep tstrsplit dynamisch erstellen kann
amonk
15

Ich füge eine Antwort für jemanden hinzu, der data.table v1.9.5 nicht verwendet und auch eine einzeilige Lösung möchte.

dt[, c('PX','PY') := do.call(Map, c(f = c, strsplit(PREFIX, '-'))) ]
Ha Pham
quelle
7

Mit splitstackshapePaket:

library(splitstackshape)
cSplit(df, splitCols = "PREFIX", sep = "_", direction = "wide", drop = FALSE)
#    PREFIX VALUE PREFIX_1 PREFIX_2
# 1:    A_B     1        A        B
# 2:    A_C     2        A        C
# 3:    A_D     3        A        D
# 4:    B_A     4        B        A
# 5:    B_C     5        B        C
# 6:    B_D     6        B        D
zx8754
quelle
4

Wir könnten es versuchen:

cbind(dt, fread(text = dt$PREFIX, sep = "_", header = FALSE))
#    PREFIX VALUE V1 V2
# 1:    A_B     1  A  B
# 2:    A_C     2  A  C
# 3:    A_D     3  A  D
# 4:    B_A     4  B  A
# 5:    B_C     5  B  C
# 6:    B_D     6  B  D
user2657469
quelle
1

Mit tidyr lautet die Lösung:

separate(df,col = "PREFIX",into = c("PX", "PY"), sep = "_")
Skan
quelle
Die Frage wurde speziell nach data.table-Lösungen gestellt. Menschen, die in diesem Bereich arbeiten, haben bereits aus gutem Grund für ihre Herausforderungen data.table-Lösungen gegenüber tidyr-Lösungen ausgewählt.
Michael Tuchman
Andere Benutzer haben auch Lösungen mit anderen Bibliotheken bereitgestellt. Ich habe gerade eine gültige Alternative angegeben, einfach und schnell.
Skan