Suddividi la stringa di testo in colonne data.table


87

Ho uno script che legge i dati da un file CSV in un data.tablee quindi divide il testo in una colonna in diverse nuove colonne. Attualmente sto usando le funzioni lapplye strsplitper farlo. Ecco un esempio:

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 

Nell'esempio sopra la colonna PREFIXè divisa in due nuove colonne PXe PYsul carattere "_".

Anche se funziona bene, mi chiedevo se esiste un modo migliore (più efficiente) per farlo usando data.table. I miei set di dati reali hanno> = 10 milioni di righe, quindi l'efficienza di tempo / memoria diventa davvero importante.


AGGIORNARE:

Seguendo il suggerimento di @ Frank ho creato un test case più grande e ho utilizzato i comandi suggeriti, ma stringr::str_split_fixedrichiede molto più tempo rispetto al metodo originale.

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 

Quindi il str_split_fixedmetodo richiede circa 20 volte di più.


Penso che fare prima l'operazione al di fuori di data.table potrebbe essere meglio. Se si utilizza il stringrpacchetto, questo è il comando: str_split_fixed(PREFIX,"_",2). Non rispondo perché non ho testato l'accelerazione ... Oppure, in un unico passaggio:dt[,c("PX","PY"):=data.table(str_split_fixed(PREFIX,"_",2))]
Frank

Risposte:


123

Aggiornamento: dalla versione 1.9.6 (su CRAN a partire da settembre 15), possiamo utilizzare la funzione tstrsplit()per ottenere i risultati direttamente (e in modo molto più efficiente):

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()fondamentalmente è un wrapper per transpose(strsplit()), dove la transpose()funzione, anch'essa recentemente implementata, traspone una lista. Si prega di vedere ?tstrsplit()e ?transpose()per esempi.

Visualizza la cronologia per le vecchie risposte.


Grazie Arun. Non avevo pensato al metodo per creare prima la lista, poi l'indice e poi le colonne come descritto in "a_spl". Ho sempre pensato che fare tutto in una sola riga fosse il modo migliore. Solo per curiosità, perché il modo index funziona molto più velocemente?
Derric Lewis

@ Arun, relativo a questa domanda, quali sono alcune delle insidie ​​che vedresti in una funzione come quella che ho scritto qui: gist.github.com/mrdwab/6873058 Fondamentalmente, ne ho fatto uso fread, ma per farlo, Ho dovuto usare un tempfile(che sembrerebbe essere un collo di bottiglia) poiché non sembra freadavere un equivalente a un textargomento. Testando con questi dati di esempio, le sue prestazioni sono tra i tuoi approcci a_sple a_sub.
A5C1D2H2I1M1N2O1R2T1

4
Mi chiedevo come si potesse indovinare il numero di colonne sul LHS di: = e creare dinamicamente i nomi delle nuove colonne in base alle occorrenze grep tstrsplit
amonk

Esiste un modo efficiente per eliminare la colonna PREFIX originale tutta in una volta utilizzando questo approccio? Voglio dire che potrebbe essere più veloce o utilizzare meno memoria nel processo rispetto al concatenamento o eseguendolo come operazione separata.
Mark E.

15

Aggiungo la risposta per qualcuno che non usa la data.table v1.9.5 e desidera anche una soluzione di una riga.

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

7

Utilizzo del splitstackshapepacchetto:

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

Potremmo provare:

library(data.table)  
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

Con tidyr la soluzione è:

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

La domanda richiedeva specificamente soluzioni data.table. Le persone che lavorano in questo settore hanno già scelto le soluzioni data.table rispetto alle soluzioni tidyr per una buona ragione rispetto alle loro sfide.
Michael Tuchman

Altri utenti hanno fornito soluzioni anche con altre biblioteche, io ho solo dato una valida alternativa, facile e veloce.
skan
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.