У меня есть данные в файле 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
. Может ли кто-нибудь помочь заставить это работать?
РЕДАКТИРОВАТЬ с помощью 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.
Быстрое и грязное решение 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, потому что он проще и выполняется примерно в трети времени, но этот ответ дал совершенно правильные результаты для моих игрушечных и реальных данных.
Спасибо, но я пытаюсь найти решение конкретно в data.table.