Есть ли более краткий способ `data.table` для отделения вложенного столбца в data.table?

Предположим, у нас есть 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.

Есть идеи? Очень признателен!

Ссылки на несколько постов SO здесь: Создайте эффективную функцию распаковки. Может какие актуальные?

Henrik 13.09.2022 22:17

Например. Как «исключить» столбец из data.table, с предложениями, подобными приведенным здесь.

Henrik 17.09.2022 13:37

Мне очень нравится второе решение (с повторениями и длинами).

nicola 21.09.2022 15:37
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
10
3
287
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Я думаю, это работает:

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.

ThomasIsCoding 13.09.2022 15:05
Ответ принят как подходящий

Еще один вариант:

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))] аккуратнее.

Почему-то не могу воспроизвести ваш вывод

Maël 13.09.2022 15:51

Столбец 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 я

Maël 13.09.2022 15:53

Не могли бы вы добавить каждый шаг? может это только я

Maël 13.09.2022 15:53

У меня 1.14.2. Давайте посмотрим, работает ли это для других людей; в конце концов, это не мой вопрос; ничего страшного, если не работает только на моей сессии

Maël 13.09.2022 15:57

Думаю нашел разницу и добавил примечание.

sindri_baldur 13.09.2022 16:00

Хорошее решение с использованием by = .I!

ThomasIsCoding 13.09.2022 21:18

то же, что и @Maël, с data.table 1.14.2 я не нахожу правильный порядок групп и получаю предупреждающее сообщение Item 1 has 5 rows but longest item has 9; recycled with remainder

denis 20.09.2022 09:01

Другой вариант без группы 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]

Другие вопросы по теме