Je voudrais prendre des données de la forme
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
" d'en haut 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 certaine forme de apply
qui a fonctionné, mais je me suis égaré depuis. Cela semblait trop compliqué pour être le meilleur moyen. Je peux utiliser strsplit
comme ci-dessous, mais je ne vois pas comment le récupérer dans deux colonnes du bloc 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 pointeurs. Je n'ai pas encore tout à fait grillé les listes R.
Utilisez stringr::str_split_fixed
library(stringr)
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 également nous assurer que les colonnes résultantes auront les types corrects et améliorer les performances en ajoutant type.convert
et fixed
arguments (puisque "_and_"
n'est pas vraiment un regex)
setDT(before)[, paste0("type", 1:2) := tstrsplit(type, "_and_", type.convert = TRUE, fixed = TRUE)]
Encore une autre approche: utilisez rbind
sur 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 deuxième élément de ces listes, ainsi:
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 une ligne qui va dans le même sens que la solution d’Aniko, mais en utilisant le paquet stringr de hadley:
do.call(rbind, str_split(before$type, '_and_'))
Pour ajouter 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
Une méthode 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
Le résultat de sapply()
est une matrice et doit être transposé et retranscrit dans une trame de données. Ce sont ensuite quelques manipulations simples qui donnent le résultat souhaité:
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 tu voulais
> 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 une ligne de base R qui chevauche un certain nombre de solutions précédentes, mais renvoie un nom de données (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 nom data.frame. L’amélioration incrémentale supplémentaire consiste à utiliser setNames
pour ajouter des noms de variables à data.frame.
Le sujet est presque épuisé. J'aimerais cependant proposer une solution à une version légèrement plus générale dans laquelle vous ne connaissez pas le nombre de colonnes de sortie, a priori. Donc, 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 la scission. J'ai donc créé une fonction qui utilise stringr
pour scinder une colonne, en fonction du modèle et un préfixe de nom pour les colonnes générées. J'espère que les modèles 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 tuyau 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 <NA>
2 30 foo bar_2 <NA>
3 4 foo bar_2 bar_3
4 6 foo bar <NA>
Et ensuite, 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
Depuis la version 3.4.0 de R, vous pouvez utiliser strcapture()
à partir du paquet tils (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
Cette question est assez ancienne mais j'ajouterai 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
Une autre approche si vous voulez rester avec strsplit()
consiste à 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")
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