107 votes

Fractionner une chaîne de texte dans les colonnes d'un tableau de données.

J'ai un script qui lit des données à partir d'un fichier CSV dans un data.table et divise ensuite le texte d'une colonne en plusieurs nouvelles colonnes. J'utilise actuellement la fonction lapply y strsplit pour ce faire. Voici un exemple :

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 

Dans l'exemple ci-dessus, la colonne PREFIX est divisé en deux nouvelles colonnes PX y PY sur le caractère "_".

Même si cela fonctionne très bien, je me demandais s'il n'y avait pas une meilleure façon (plus efficace) de le faire en utilisant data.table . Mes ensembles de données réels comportent plus de 10 millions de lignes, de sorte que l'efficacité en termes de temps et de mémoire devient très importante.


UPDATE :

En suivant la suggestion de @Frank, j'ai créé un cas de test plus important et utilisé les commandes suggérées, mais le fichier stringr::str_split_fixed prend beaucoup plus de temps que la méthode 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 

Ainsi, le str_split_fixed prend environ 20 fois plus de temps.

163voto

Arun Points 41689

Mise à jour : A partir de la version 1.9.6 (sur CRAN à partir de septembre 15), nous pouvons utiliser la fonction tstrsplit() pour obtenir les résultats directement (et de manière beaucoup plus efficace) :

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() est essentiellement une enveloppe pour transpose(strsplit()) donde transpose() également récemment implémentée, transpose une liste. Voir ?tstrsplit() y ?transpose() par exemple.

Voir l'historique pour les anciennes réponses.

16voto

Minh Ha Pham Points 2506

J'ajoute la réponse pour quelqu'un qui n'utilise pas data.table v1.9.5 et veulent aussi une solution en une seule ligne.

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

8voto

zx8754 Points 13573

Utilisation de splitstackshape paquet :

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

7voto

user2657469 Points 119

On pourrait essayer :

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

4voto

skan Points 690

Avec tidyr, la solution est :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X