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.


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.


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, '-'))) ]

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

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

1

Mit tidyr lautet die Lösung:

separate(df,col = "PREFIX",into = c("PX", "PY"), sep = "_")

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.