Fractionner la colonne de la chaîne de trames de données en plusieurs colonnes

Je voudrais prendre des données du formulaire

before = data.frame(attr = c(1,30,4,6), type=c('foo_and_bar','foo_and_bar_2')) attr type 1 1 foo_and_bar 2 30 foo_and_bar_2 3 4 foo_and_bar 4 6 foo_and_bar_2 

et utilisez split() sur la colonne ” type ” ci-dessus pour obtenir quelque chose comme ceci:

  attr type_1 type_2 1 1 foo bar 2 30 foo bar_2 3 4 foo bar 4 6 foo bar_2 

Je suis arrivé à quelque chose d’incroyablement complexe impliquant une forme d’ apply qui a fonctionné, mais je l’ai depuis mal placé. Cela semblait trop compliqué pour être le meilleur moyen. Je peux utiliser strsplit comme ci-dessous, mais je ne strsplit pas comment le récupérer dans 2 colonnes du strsplit de données.

 > strsplit(as.character(before$type),'_and_') [[1]] [1] "foo" "bar" [[2]] [1] "foo" "bar_2" [[3]] [1] "foo" "bar" [[4]] [1] "foo" "bar_2" 

Merci pour tous les conseils. Je n’ai pas encore tout à fait excité les listes R.

Utilisez ssortingngr::str_split_fixed

 library(ssortingngr) str_split_fixed(before$type, "_and_", 2) 

Une autre option consiste à utiliser le nouveau package tidyr.

 library(dplyr) library(tidyr) before < - data.frame( attr = c(1, 30 ,4 ,6 ), type = c('foo_and_bar', 'foo_and_bar_2') ) before %>% separate(type, c("foo", "bar"), "_and_") ## attr foo bar ## 1 1 foo bar ## 2 30 foo bar_2 ## 3 4 foo bar ## 4 6 foo bar_2 

5 ans plus tard, ajout de la solution obligatoire data.table

 library(data.table) ## v 1.9.6+ setDT(before)[, paste0("type", 1:2) := tstrsplit(type, "_and_")] before # attr type type1 type2 # 1: 1 foo_and_bar foo bar # 2: 30 foo_and_bar_2 foo bar_2 # 3: 4 foo_and_bar foo bar # 4: 6 foo_and_bar_2 foo bar_2 

Nous pourrions aussi bien faire en sorte que les colonnes résultantes aient des types corrects et améliorer les performances en ajoutant fixed arguments type.convert et fixed (puisque "_and_" n’est pas vraiment une regex)

 setDT(before)[, paste0("type", 1:2) := tstrsplit(type, "_and_", type.convert = TRUE, fixed = TRUE)] 

Encore une autre approche: utilisez rbind on out :

 before < - data.frame(attr = c(1,30,4,6), type=c('foo_and_bar','foo_and_bar_2')) out <- strsplit(as.character(before$type),'_and_') do.call(rbind, out) [,1] [,2] [1,] "foo" "bar" [2,] "foo" "bar_2" [3,] "foo" "bar" [4,] "foo" "bar_2" 

Et pour combiner:

 data.frame(before$attr, do.call(rbind, out)) 

Notez que sapply avec “[” peut être utilisé pour extraire le premier ou le second élément de ces listes afin:

 before$type_1 < - sapply(strsplit(as.character(before$type),'_and_'), "[", 1) before$type_2 <- sapply(strsplit(as.character(before$type),'_and_'), "[", 2) before$type <- NULL 

Et voici une méthode gsub:

 before$type_1 < - gsub("_and_.+$", "", before$type) before$type_2 <- gsub("^.+_and_", "", before$type) before$type <- NULL 

voici un seul liner dans le même sens que la solution d’aniko, mais en utilisant le paquet ssortingngr de hadley:

 do.call(rbind, str_split(before$type, '_and_')) 

Pour append aux options, vous pouvez également utiliser ma fonction splitstackshape::cSplit comme ceci:

 library(splitstackshape) cSplit(before, "type", "_and_") # attr type_1 type_2 # 1: 1 foo bar # 2: 30 foo bar_2 # 3: 4 foo bar # 4: 6 foo bar_2 

Un moyen simple consiste à utiliser sapply() et la [ fonction:

 before < - data.frame(attr = c(1,30,4,6), type=c('foo_and_bar','foo_and_bar_2')) out <- strsplit(as.character(before$type),'_and_') 

Par exemple:

 > data.frame(t(sapply(out, `[`))) X1 X2 1 foo bar 2 foo bar_2 3 foo bar 4 foo bar_2 

sapply() est une masortingce qui doit être transposée et renvoyée dans un sapply() données. Ce sont alors des manipulations simples qui donnent le résultat que vous vouliez:

 after < - with(before, data.frame(attr = attr)) after <- cbind(after, data.frame(t(sapply(out, `[`)))) names(after)[2:3] <- paste("type", 1:2, sep = "_") 

À ce stade, after est ce que vous vouliez

 > after attr type_1 type_2 1 1 foo bar 2 30 foo bar_2 3 4 foo bar 4 6 foo bar_2 

Voici un liner de base R qui recouvre plusieurs solutions précédentes, mais renvoie un data.frame avec les noms propres.

 out < - setNames(data.frame(before$attr, do.call(rbind, strsplit(as.character(before$type), split="_and_"))), c("attr", paste0("type_", 1:2))) out attr type_1 type_2 1 1 foo bar 2 30 foo bar_2 3 4 foo bar 4 6 foo bar_2 

Il utilise strsplit pour décomposer la variable, et data.frame avec do.call / rbind pour replacer les données dans un data.frame. L'amélioration incrémentielle supplémentaire est l'utilisation de setNames pour append des noms de variables au data.frame.

Une autre approche si vous voulez restr avec strsplit() est d’utiliser la commande unlist() . Voici une solution dans ce sens.

 tmp < - matrix(unlist(strsplit(as.character(before$type), '_and_')), ncol=2, byrow=TRUE) after <- cbind(before$attr, as.data.frame(tmp)) names(after) <- c("attr", "type_1", "type_2") 

Depuis la version R 3.4.0, vous pouvez utiliser strcapture() partir du package utils (inclus avec les installations de base R), en liant la sortie aux autres colonnes.

 out < - strcapture( "(.*)_and_(.*)", as.character(before$type), data.frame(type_1 = character(), type_2 = character()) ) cbind(before["attr"], out) # attr type_1 type_2 # 1 1 foo bar # 2 30 foo bar_2 # 3 4 foo bar # 4 6 foo bar_2 

Le sujet est presque épuisé, je voudrais cependant proposer une solution à une version légèrement plus générale où vous ne connaissez pas le nombre de colonnes de sortie, a priori. Ainsi, par exemple, vous avez

 before = data.frame(attr = c(1,30,4,6), type=c('foo_and_bar','foo_and_bar_2', 'foo_and_bar_2_and_bar_3', 'foo_and_bar')) attr type 1 1 foo_and_bar 2 30 foo_and_bar_2 3 4 foo_and_bar_2_and_bar_3 4 6 foo_and_bar 

Nous ne pouvons pas utiliser dplyr separate() car nous ne connaissons pas le nombre de colonnes de résultats avant le fractionnement. J’ai donc créé une fonction qui utilise ssortingngr pour diviser une colonne, en fonction du modèle et du préfixe de nom généré. colonnes. J’espère que les schémas de codage utilisés sont corrects.

 split_into_multiple < - function(column, pattern = ", ", into_prefix){ cols <- str_split_fixed(column, pattern, n = Inf) # Sub out the ""'s returned by filling the matrix to the right, with NAs which are useful cols[which(cols == "")] <- NA cols <- as.tibble(cols) # name the 'cols' tibble as 'into_prefix_1', 'into_prefix_2', ..., 'into_prefix_m' # where m = # columns of 'cols' m <- dim(cols)[2] names(cols) <- paste(into_prefix, 1:m, sep = "_") return(cols) } 

On peut alors utiliser split_into_multiple dans un tube dplyr comme suit:

 after < - before %>% bind_cols(split_into_multiple(.$type, "_and_", "type")) %>% # selecting those that start with 'type_' will remove the original 'type' column select(attr, starts_with("type_")) >after attr type_1 type_2 type_3 1 1 foo bar  2 30 foo bar_2  3 4 foo bar_2 bar_3 4 6 foo bar  

Et puis nous pouvons utiliser gather pour ranger ...

 after %>% gather(key, val, -attr, na.rm = T) attr key val 1 1 type_1 foo 2 30 type_1 foo 3 4 type_1 foo 4 6 type_1 foo 5 1 type_2 bar 6 30 type_2 bar_2 7 4 type_2 bar_2 8 6 type_2 bar 11 4 type_3 bar_3 

Cette question est assez ancienne mais j’appendai la solution que j’ai trouvée la plus simple à l’heure actuelle.

 library(reshape2) before = data.frame(attr = c(1,30,4,6), type=c('foo_and_bar','foo_and_bar_2')) newColNames < - c("type1", "type2") newCols <- colsplit(before$type, "_and_", newColNames) after <- cbind(before, newCols) after$type <- NULL after 

base mais probablement lente:

 n < - 1 for(i in strsplit(as.character(before$type),'_and_')){ before[n, 'type_1'] <- i[[1]] before[n, 'type_2'] <- i[[2]] n <- n + 1 } ## attr type type_1 type_2 ## 1 1 foo_and_bar foo bar ## 2 30 foo_and_bar_2 foo bar_2 ## 3 4 foo_and_bar foo bar ## 4 6 foo_and_bar_2 foo bar_2 
 tp < - c("ac","def","ghi","mn") temp = strsplit(as.character(tp),'-') x=c(); y=c(); z=c(); #tab=data.frame() #tab= cbind(tab,c(x,y,z)) for(i in 1:length(temp) ) { l = length(temp[[i]]); if(l==2) { x=c(x,temp[[i]][1]); y=c(y,"NA") z=c(z,temp[[i]][2]); df= as.data.frame(cbind(x,y,z)) }else { x=c(x,temp[[i]][1]); y=c(y,temp[[i]][2]); z=c(z,temp[[i]][3]); df= as.data.frame(cbind(x,y,z)) } }