studio - superponer graficas en r
Divida una columna de un marco de datos en varias columnas (15)
5 años más tarde agregando la solución obligatoria 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
También podríamos asegurarnos de que las columnas resultantes tengan los tipos correctos y mejoren el rendimiento agregando type.convert
y argumentos fixed
(ya que "_and_"
no es realmente una expresión regular)
setDT(before)[, paste0("type", 1:2) := tstrsplit(type, "_and_", type.convert = TRUE, fixed = TRUE)]
Me gustaría tomar datos del formulario
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
y use split()
en la columna " type
" de arriba para obtener algo como esto:
attr type_1 type_2
1 1 foo bar
2 30 foo bar_2
3 4 foo bar
4 6 foo bar_2
Se me ocurrió algo increíblemente complejo que implicaba alguna forma de apply
que funcionó, pero desde entonces he extraviado eso. Parecía demasiado complicado para ser la mejor manera. Puedo usar strsplit
como a continuación, pero luego no veo cómo volver a ponerlo en 2 columnas en el marco de datos.
> strsplit(as.character(before$type),''_and_'')
[[1]]
[1] "foo" "bar"
[[2]]
[1] "foo" "bar_2"
[[3]]
[1] "foo" "bar"
[[4]]
[1] "foo" "bar_2"
Gracias por cualquier puntero. Todavía no he comido completamente las listas de R.
Aquí hay un revestimiento base R one que se superpone a varias soluciones anteriores, pero devuelve un data.frame con los nombres propios.
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
Utiliza strsplit
para dividir la variable y data.frame
con do.call
/ rbind
para volver a poner los datos en un data.frame. La mejora incremental adicional es el uso de setNames
para agregar nombres de variables al data.frame.
Desde la versión R 3.4.0, puede usar strcapture()
desde el paquete utils (incluido con las instalaciones base R), vinculando el resultado a la (s) otra (s) columna (s).
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
El tema está casi agotado, pero me gustaría ofrecer una solución a una versión un poco más general en la que no se conoce el número de columnas de salida, a priori. Entonces, por ejemplo, tienes
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
No podemos usar dplyr separate()
porque no conocemos el número de columnas de resultados antes de la división, entonces he creado una función que usa stringr
para dividir una columna, dado el patrón y un prefijo de nombre para el generado columnas Espero que los patrones de codificación utilizados sean correctos.
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)
}
Entonces podemos usar split_into_multiple
en una tubería dplyr de la siguiente manera:
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>
Y luego podemos usar gather
para poner en orden ...
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
Esta pregunta es bastante antigua, pero añadiré que la solución que encontré es la más simple en la actualidad.
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
Otra opción es usar el nuevo paquete 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
Otro enfoque más: use 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"
Y para combinar:
data.frame(before$attr, do.call(rbind, out))
Otro enfoque si desea seguir con strsplit()
es usar el unlist()
. Aquí hay una solución en ese sentido.
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")
Para agregar a las opciones, también podría usar mi función splitstackshape::cSplit
como esta:
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
Tenga en cuenta que sapply con "[" se puede usar para extraer el primer elemento o el segundo elemento en esas listas de modo que:
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
Y aquí hay un método gsub:
before$type_1 <- gsub("_and_.+$", "", before$type)
before$type_2 <- gsub("^.+_and_", "", before$type)
before$type <- NULL
Una manera fácil es usar sapply()
y la [
función:
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_'')
Por ejemplo:
> data.frame(t(sapply(out, `[`)))
X1 X2
1 foo bar
2 foo bar_2
3 foo bar
4 foo bar_2
sapply()
es una matriz y necesita transposición y conversión a un marco de datos. Es entonces algunas manipulaciones simples que producen el resultado que deseaba:
after <- with(before, data.frame(attr = attr))
after <- cbind(after, data.frame(t(sapply(out, `[`))))
names(after)[2:3] <- paste("type", 1:2, sep = "_")
En este punto, after
es lo que querías
> after
attr type_1 type_2
1 1 foo bar
2 30 foo bar_2
3 4 foo bar
4 6 foo bar_2
Utilice stringr::str_split_fixed
library(stringr)
str_split_fixed(before$type, "_and_", 2)
aquí hay un trazador de líneas en la misma línea que la solución de aniko, pero utilizando el paquete stringr de hadley:
do.call(rbind, str_split(before$type, ''_and_''))
base pero probablemente lenta:
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))
}
}