Отмена вложения данных JSON в `j` data.table

У меня есть данные в файле JSON, которые я хочу вложить в таблицу data.table. JSON структурирован следующим образом:

[
  {
    "version_id": "123456",
    "data": [
      {
        "review_id": "1",
        "rating": 5,
        "review": "This app is great",
        "date": "2024-09-01"
      },
      {
        "review_id": "2",
        "rating": 1,
        "review": "This app is terrible",
        "date": "2024-09-01"
      }
    ]
  },
  {
    "version_id": "789101",
    "data": [
      {
        "review_id": "3",
        "rating": 3,
        "review": "This app is OK",
        "date": "2024-09-01"
      }
    ]
  }
]

Я могу преобразовать это в data.table, выполнив кучу операций, а затем связав результаты вместе, но я хочу знать, можно ли сделать это проще и/или эффективнее с помощью j в data.table.

Что я делаю сейчас:

reviews <- jsonlite::read_json("reviews.json")
version_ids <- purrr::map_chr(reviews, "version_id")
review_data <- purrr::map(reviews, "data")
cbind(
  data.table::data.table(
    version_id = rep(version_ids, lengths(review_data))
  ),
  lapply(
    review_data,
    function(d) {
      data.table::data.table(
        review_id = purrr::map_chr(d, "review_id"),
        rating = purrr::map_int(d, "rating"),
        review = purrr::map_chr(d, "review"),
        date = purrr::map_chr(d, "date")
      )
    }
  ) |> 
    data.table::rbindlist()
)
#>    version_id review_id rating               review       date
#> 1:     123456         1      5    This app is great 2024-09-01
#> 2:     123456         2      1 This app is terrible 2024-09-01
#> 3:     789101         3      3       This app is OK 2024-09-01

Это правильный результат, но я надеюсь сделать что-то вроде:

data.table::data.table(
  version_id = version_ids,
  review_data = review_data
)[
  rep(version_id, lengths(review_data)),
  .(
    review_id = purrr::map_chr(.SD["review_data"], "review_id"),
    rating = purrr::map_int(.SD["review_data"], "rating"),
    review = purrr::map_chr(.SD["review_data"], "review"),
    date = purrr::map_chr(.SD["review_data"], "date")
  ),
  by = version_id
]

Но это дает мне ошибку:

Error in `[.data.table`(data.table::data.table(version_id = version_ids,  : 
  When i is a data.table (or character vector), the columns to join by must be specified using 'on=' argument (see ?data.table), by keying x (i.e. sorted, and, marked as sorted, see ?setkey), or by sharing column names between x and i (i.e., a natural join). Keyed joins might have further speed benefits on very large data due to x being sorted in RAM.

Я получаю ту же ошибку, если полностью опустить i. Может ли кто-нибудь помочь заставить это работать?

Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
52
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

РЕДАКТИРОВАТЬ с помощью data.table: вместо того, чтобы пытаться заставить таблицу данных рекурсивно проходить через себя, изначально настройте таблицу данных как длинную, а затем вы можете использовать функцию rbindlist() для разделения столбцов:

data.table::data.table(
    version_id = rep(version_ids, map(review_data, length)),
    data.table::rbindlist(unlist(review_data, recursive = FALSE))
)

   version_id review_id rating               review       date
1:     123456         1      5    This app is great 2024-09-01
2:     123456         2      1 This app is terrible 2024-09-01
3:     789101         3      3       This app is OK 2024-09-01

Оригинальный ответ с использованием tidyr, если он будет полезен другим: Если кто-то использовал tidyr, а не data.table, следующее должно работать путем поэтапного разделения списка: сначала для получения индекса version_id с помощью unnest_wider(), затем для создания строки для каждого обзора (с использованием unnest()), а затем путем создания столбцов для характеристики каждого отзыва (используя unnest_wider()):

library(tidyverse)
reviews <- jsonlite::read_json("reviews.json")

tibble(reviews) %>% 
  unnest_wider(col = reviews) %>% 
  unnest(col = data) %>% 
  unnest_wider(col = data) 
# A tibble: 3 × 5
  version_id review_id rating review               date      
  <chr>      <chr>      <int> <chr>                <chr>     
1 123456     1              5 This app is great    2024-09-01
2 123456     2              1 This app is terrible 2024-09-01
3 789101     3              3 This app is OK       2024-09-01

При необходимости тиббл можно преобразовать в data.table.

Спасибо, но я пытаюсь найти решение конкретно в data.table.

Hamed 05.09.2024 13:57

Отредактировано, чтобы включить решение data.table.

mikebader 05.09.2024 20:08

Быстрое и грязное решение data.table:

reviews <- jsonlite::read_json("reviews.json")
n <- sapply(reviews, \(x) length(x$data))
DT <- data.table(
  version_id = rep(sapply(reviews, \(x) x$version_id), n),
  lapply(reviews, \(x) rbindlist(lapply(x$data, as.data.table))) |>
    rbindlist()
)

#    version_id review_id rating               review       date
#        <char>    <char>  <int>               <char>     <char>
# 1:     123456         1      5    This app is great 2024-09-01
# 2:     123456         2      1 This app is terrible 2024-09-01
# 3:     789101         3      3       This app is OK 2024-09-01

Спасибо вам за это. Я принял ответ от mikebader, потому что он проще и выполняется примерно в трети времени, но этот ответ дал совершенно правильные результаты для моих игрушечных и реальных данных.

Hamed 06.09.2024 15:55

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