Diviser la colonne de chaîne de cadre de données en plusieurs colonnes
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
j'ai trouvé quelque chose d'incroyablement complexe impliquant une certaine forme de apply
qui a fonctionné, mais je l'ai depuis égaré. Il semble beaucoup trop compliqué pour être le meilleur moyen. Je peux utiliser strsplit
comme ci-dessous, mais ensuite pas clair comment obtenir ce retour en 2 colonnes dans le 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 conseils. Je n'ai pas encore tout à fait les listes R.
15 réponses
utiliser stringr::str_split_fixed
library(stringr)
str_split_fixed(before$type, "_and_", 2)
une autre option consiste à utiliser le nouveau paquet 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 ajoutant la solution obligatoire data.table
151970920"
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 la performance en ajoutant type.convert
et fixed
arguments (depuis "_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 à combiner:
data.frame(before$attr, do.call(rbind, out))
Avis que sapply avec "[" peut être utilisé pour extraire le premier ou le deuxième éléments de ces listes:
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 doublure 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
un moyen facile est d'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()
's résultat est une matrice et a besoin de transposer et de renvoyer à une base de données. Ce sont donc de simples manipulations 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 point, 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 qui chevauche un certain nombre de solutions précédentes, mais renvoie une donnée.cadre 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 remettre les données dans une donnée.cadre. L'amélioration supplémentaire est l'utilisation de setNames
pour ajouter des noms de variables aux données.cadre.
le sujet est presque épuisé, j'aimerais cependant offrir 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()
parce que nous ne connaissons pas le nombre des colonnes de résultat avant la séparation, donc j'ai créé une fonction qui utilise stringr
pour séparer une colonne, Étant donné le modèle et un préfixe de nom pour l'généré colonnes. J'espère que les codes 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)
}
nous pouvons 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
une autre approche si vous voulez vous en tenir à 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 3.4.0 de R, Vous pouvez utiliser strcapture()
du paquet utils (inclus avec la base R installe), liant la sortie sur la ou les 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 je vais ajouter la solution que j'ai trouvé le 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("a-c","d-e-f","g-h-i","m-n")
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))
}
}