Предположим, у нас есть data.table
с вложенным столбцом val
dt <- data.table(
grp = c(1, 2, 1, 3, 4),
val = list("a", c("b", "c"), c("d", "e", "f"), "g", c("h", "i"))
)
который показывает как
> dt
grp val
1: 1 a
2: 2 b,c
3: 1 d,e,f
4: 3 g
5: 4 h,i
Я хотел бы unnest
столбец val
, где возможный вариант использует tidyr::unnest
, т.е.
> dt %>%
+ unnest(val)
# A tibble: 9 × 2
grp val
<dbl> <chr>
1 1 a
2 2 b
3 2 c
4 1 d
5 1 e
6 1 f
7 3 g
8 4 h
9 4 i
Мне интересно, можем ли мы реализовать это, используя только data.table
.
Следите за порядком значений в столбце grp
, я бы хотел сохранить такой порядок, как 1,2,1,3,4
, а не 1,1,2,3,4
.
data.table
попыткаМоя попытка, как показано ниже
> dt[, id := .I][, lapply(.SD, unlist), id][, id := NULL][]
grp val
1: 1 a
2: 2 b
3: 2 c
4: 1 d
5: 1 e
6: 1 f
7: 3 g
8: 4 h
9: 4 i
или же
> dt[,.(grp = rep(grp,lengths(val)), val = unlist(val))]
grp val
1: 1 a
2: 2 b
3: 2 c
4: 1 d
5: 1 e
6: 1 f
7: 3 g
8: 4 h
9: 4 i
но я думаю, что может быть более краткий и элегантный способ сделать это, например, без создания вспомогательного столбца id
или использования rep
+ lengths
.
Есть идеи? Очень признателен!
Например. Как «исключить» столбец из data.table, с предложениями, подобными приведенным здесь.
Мне очень нравится второе решение (с повторениями и длинами).
Я думаю, это работает:
dt[, lapply(.SD, unlist), by = .(grp)]
grp val
1: 1 a
2: 1 d
3: 1 e
4: 1 f
5: 2 b
6: 2 c
7: 3 g
8: 4 h
9: 4 i
Это хорошая попытка, но значения grp
должны сохранять тот же порядок, что и в исходном dt
, например, 1,2,1,3,4
, а не 1,1,2,3,4
.
Еще один вариант:
dt[, .(grp, val = unlist(val)), by = .I][, !"I"]
# grp val
# <num> <char>
# 1: 1 a
# 2: 2 b
# 3: 2 c
# 4: 1 d
# 5: 1 e
# 6: 1 f
# 7: 3 g
# 8: 4 h
# 9: 4 i
PS. Если вы используете data.table 1.4.2
или старше by = .I
, это не сработает. Вместо этого вы можете использовать:
dt[, .(grp, val = unlist(val)), by = 1:nrow(dt)][, !"nrow"]
PS2. Я думаю, что ваш dt[,.(grp = rep(grp,lengths(val)), val = unlist(val))]
аккуратнее.
Почему-то не могу воспроизвести ваш вывод
Столбец val правильный, но группа grp перепутана: ` grp val 1: 1 a 2: 2 b 3: 1 c 4: 3 d 5: 4 e 6: 1 f 7: 2 g 8: 1 h 9 : 3 я
Не могли бы вы добавить каждый шаг? может это только я
У меня 1.14.2. Давайте посмотрим, работает ли это для других людей; в конце концов, это не мой вопрос; ничего страшного, если не работает только на моей сессии
Думаю нашел разницу и добавил примечание.
Хорошее решение с использованием by = .I
!
то же, что и @Maël, с data.table 1.14.2 я не нахожу правильный порядок групп и получаю предупреждающее сообщение Item 1 has 5 rows but longest item has 9; recycled with remainder
Другой вариант без группы by
вот так:
library(data.table)
dt[rep(1:.N, lengths(val))][, val:=unlist(dt$val)][]
#> grp val
#> 1: 1 a
#> 2: 2 b
#> 3: 2 c
#> 4: 1 d
#> 5: 1 e
#> 6: 1 f
#> 7: 3 g
#> 8: 4 h
#> 9: 4 i
Created on 2022-09-16 with reprex v2.0.2
Это должно работать:
dt <- dt[, .(grp, unlist(val)), by=.(rleid(grp))][, rleid := NULL]
Ссылки на несколько постов SO здесь: Создайте эффективную функцию распаковки. Может какие актуальные?