You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在data.table中高效展开嵌套JSON数据?

用data.table高效展开嵌套JSON数据

我有一个JSON格式的数据文件,想要将其展开(unnest)为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"
      }
    ]
  }
]

我已经能通过分步处理得到正确结果,代码如下:

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的j表达式实现更简洁的写法,比如:

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参数,仍然会报相同错误,求解决方法。


解决方案

方法1:简洁的data.table链式写法

可以直接结合rbindlist和分组操作,一步完成展开:

library(data.table)
library(purrr)
library(jsonlite)

reviews <- read_json("reviews.json")
dt <- data.table(
  version_id = map_chr(reviews, "version_id"),
  review_data = map(reviews, "data")
)

# 按version_id分组,将每组的review_data转换为data.table后合并
result <- dt[, rbindlist(map(review_data, as.data.table)), by = version_id]

方法2:利用jsonlite直接转换后展开

jsonlite读取数据后直接转成data.table,再通过unnest(需data.table 1.14.0及以上版本)快速展开:

library(data.table)
library(jsonlite)

dt <- as.data.table(read_json("reviews.json"))
result <- dt[, unnest(data)]

方法3:修正你尝试的写法逻辑

你之前的写法错误在于i的使用逻辑和.SD的调用方式,调整为按行处理嵌套数据即可:

library(data.table)
library(purrr)

reviews <- read_json("reviews.json")
dt <- data.table(
  version_id = map_chr(reviews, "version_id"),
  review_data = map(reviews, "data")
)

result <- dt[, 
  .(
    review_id = map_chr(review_data[[1]], "review_id"),
    rating = map_int(review_data[[1]], "rating"),
    review = map_chr(review_data[[1]], "review"),
    date = map_chr(review_data[[1]], "date")
  ),
  by = version_id
]

以上三种方法都能得到你需要的展开后data.table结果,其中方法1和方法2的代码最简洁高效。


内容的提问来源于stack exchange,提问作者Hamed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 20:05:21