Tengo un conjunto de datos (dt) como este en R:

n       id      val
1       1&&2    10
2       3       20
3       4&&5    30

Y lo que quiero conseguir es

n       id      val
1       1       10
2       2       10
3       3       20
4       4       30
5       5       30

Sé que para dividir identificadores necesito hacer algo como esto: id_split <- strsplit(dt$id,"&&")

Pero, ¿cómo creo nuevas filas con el mismo val para identificadores que inicialmente estaban juntos en una fila?

1
Anya Pilipentseva 8 may. 2020 a las 15:12

5 respuestas

La mejor respuesta

Puede cbind las divisiones para obtener una columna que cbind nuevamente a val (reciclaje).

res <- do.call(rbind, Map(data.frame, id=lapply(strsplit(dat$id, "&&"), cbind), 
                          val=dat$val))
res <- cbind(n=1:nrow(res), res)
res
#   n id val
# 1 1  1  10
# 2 2  2  10
# 3 3  3  20
# 4 4  4  30
# 5 5  5  30
2
jay.sf 8 may. 2020 a las 12:26

Una solución data.table.

library(data.table)
DT <- fread('n       id      val
1       1&&2    10
2       3       20
3       4&&5    30')


DT[,.(id=unlist(strsplit(id,split ="&&"))),by=.(n,val)][,n:=.I][]
#>    n val id
#> 1: 1  10  1
#> 2: 2  10  2
#> 3: 3  20  3
#> 4: 4  30  4
#> 5: 5  30  5

Creado en 2020-05-08 por el paquete reprex (v0.3.0)

Nota:

Una solución más rebosut es by = 1:nrow(DT). Pero debes jugar alrededor de tus otras columnas.

1
Frank Zhang 8 may. 2020 a las 12:39

Si alguien busca una solución ordenada,

dt %>%
  separate(id, into = paste0("id", 1:2),sep = "&&") %>% 
  pivot_longer(cols = c(id1,id2), names_to = "id_name", values_to = "id") %>% 
  drop_na(id) %>% 
  select(n, id, val)

Salida como

# A tibble: 5 x 3
      n id      val
  <dbl> <chr> <dbl>
1     1 1        10
2     1 2        10
3     2 3        20
4     3 4        30
5     3 5        30

Editar: Según lo sugerido por @sotos, y completamente extrañado por mí. una solución de revestimiento

d %>% separate_rows(id, ,sep = "&&")

Da la misma salida que

# A tibble: 5 x 3
      n id      val
  <dbl> <chr> <dbl>
1     1 1        10
2     1 2        10
3     2 3        20
4     3 4        30
5     3 5        30
1
Neel 8 may. 2020 a las 12:44

tstrplit por id de data.table puede hacer el trabajo

library(data.table)
df <- setDT(df)[,.('id' = tstrsplit(id, "&&")), by = c('n','val')]
df[,'n' := seq(.N)]

df
   n val id
1: 1  10  1
2: 2  10  2
3: 3  20  3
4: 4  30  4
5: 5  30  5
1
linog 8 may. 2020 a las 13:13

Puede usar lengths de la división de id y expandir sus filas. Luego configure n para que sea la secuencia de la longitud de su marco de datos, es decir

l1 <- strsplit(as.character(df$id), '&&')
res_df <- transform(df[rep(seq_len(nrow(df)), lengths(l1)),], 
                    id = unlist(l1), 
                    n = seq_along(unlist(l1)))

Lo que da,

    n id val
1   1  1  10
1.1 2  2  10
2   3  3  20
3   4  4  30
3.1 5  5  30

Puede eliminar los nombres de fila con rownames(res_df) <- NULL

1
Sotos 8 may. 2020 a las 12:30