Data.table sütunlarında metin dizesini bölme


87

Bir CSV dosyasındaki verileri a'ya okuyan data.tableve ardından bir sütundaki metni birkaç yeni sütuna bölen bir komut dosyam var. Bunu yapmak için şu anda lapplyve strsplitişlevlerini kullanıyorum . İşte bir örnek:

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 

Yukarıdaki örnekte sütun PREFIXiki yeni sütuna PXve PY"_" karakterine bölünmüştür .

Bu gayet iyi çalışsa da, bunu kullanarak yapmanın daha iyi (daha verimli) bir yolu olup olmadığını merak ediyordum data.table. Gerçek veri kümelerimin> = 10 milyondan fazla satırı var, bu nedenle zaman / bellek verimliliği gerçekten önemli hale geliyor.


GÜNCELLEME:

@ Frank'in önerisini takiben daha büyük bir test senaryosu oluşturdum ve önerilen komutları kullandım, ancak stringr::str_split_fixedorijinal yöntemden çok daha uzun sürüyor.

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 

Yani str_split_fixedyöntem yaklaşık 20X kat daha uzun sürer.


Bence işlemi önce data.table dışında yapmak daha iyi olabilir. Eğer kullanırsanız stringrpaketi, bu komut şöyledir: str_split_fixed(PREFIX,"_",2). Cevap vermiyorum çünkü hızlanmayı test etmedim ... Veya tek adımda:dt[,c("PX","PY"):=data.table(str_split_fixed(PREFIX,"_",2))]
Frank

Yanıtlar:


123

Güncelleme: 1.9.6 sürümünden (Eylül'15 itibarıyla CRAN'da), işlevi tstrsplit()doğrudan (ve çok daha verimli bir şekilde) sonuçları almak için kullanabiliriz :

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()temelde, fonksiyonun yakın zamanda uygulandığı, bir listenin transpoze transpose(strsplit())edildiği bir sarmalayıcıdır transpose(). Lütfen örnekler için ?tstrsplit()ve bakın ?transpose().

Eski cevaplar için geçmişe bakın.


Teşekkürler Arun. Önce listeyi, sonra indeksi ve sonra da "a_spl" de anlatılan sütunları yaratma yöntemini düşünmemiştim. Her şeyi tek bir satırda yapmanın en iyi yol olduğunu hep düşünmüşümdür. Merak ettiğim için indeks yolu neden bu kadar hızlı çalışıyor?
Derric Lewis

@Arun, bu soruyla ilgili olarak, burada yazdığım gibi bir işlevde göreceğiniz bazı tuzaklar nelerdir : gist.github.com/mrdwab/6873058 Temelde kullandım freadama bunu yapmak için, Bir argümana eşdeğer tempfilegibi görünmediği için a (bir darboğaz gibi görünüyor) kullanmak zorunda kaldım . Bu örnek verilerle test edilirse, performansı sizin ve yaklaşımlarınız arasındadır . freadtexta_spla_sub
A5C1D2H2I1M1N2O1R2T1

4
LHS'deki sütun sayısının nasıl tahmin edilebileceğini merak ediyordum: = ve grep tstrsplit oluşumlarına göre yeni sütunların adlarını dinamik olarak yaratın
amonk

Bu yaklaşımı kullanarak orijinal PREFIX sütununu bir kerede kaldırmanın etkili bir yolu var mı? Demek istediğim, bu daha hızlı olabilir veya işlem sırasında zincirleme veya ayrı bir işlem olarak yapmaktan daha az bellek kullanabilir.
Mark E.

15

data.table V1.9.5 kullanmayan ve ayrıca tek satırlık bir çözüm isteyen biri için cevap ekliyorum .

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

7

splitstackshapePaketi kullanarak :

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

Deneyebiliriz:

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

Tidyr ile çözüm şudur:

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

Soru özellikle data.table çözümleri istedi. Bu alanda çalışan kişiler, zorluklarına göre iyi bir nedenden ötürü, çözümleri düzenli hale getirmek yerine data.table çözümlerini zaten seçmişlerdir.
Michael Tuchman

Diğer kullanıcılar diğer kütüphanelerle de çözümler sağladılar, ben sadece geçerli bir alternatif verdim, kolay ve hızlı.
Skan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.