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

用Tidyverse转嵌套列表为DataFrame:优化慢速的unnest_wider

高效转换游戏日志嵌套列表为矩形格式的需求

我读取存储游戏日志的JSON文件后得到一个嵌套列表,其中time为简单向量,inputManagerStates和syncedProperties为可包含0或多个元素的列表。此前用tidyr的unnest_wider将数据转为矩形格式,但处理大量此类JSON文件时,unnest_wider运行速度极慢。

测试列表

test_list <- 
  list(list(time = 9.92405605316162, inputManagerStates = list(), 
syncedProperties = list()), list(time = 9.9399995803833, 
inputManagerStates = list(list(inputId = "InputY", buttonState = FALSE, 
    axisValue = 0), list(inputId = "InputX", buttonState = FALSE, 
    axisValue = 0.0501395985484123), list(inputId = "xPos", 
    buttonState = FALSE, axisValue = 5), list(inputId = "yPos", 
    buttonState = FALSE, axisValue = 0.0799999982118607), 
    list(inputId = "zPos", buttonState = FALSE, axisValue = 0), 
    list(inputId = "xRot", buttonState = FALSE, axisValue = 0), 
    list(inputId = "yRot", buttonState = FALSE, axisValue = -0.70664256811142), 
    list(inputId = "zRot", buttonState = FALSE, axisValue = 0), 
    list(inputId = "wRot", buttonState = FALSE, axisValue = 0.707570731639862)), 
syncedProperties = list(list(name = "timeStamp", value = "97,2"))), 
list(time = 9.95659446716309, inputManagerStates = list(list(
    inputId = "InputY", buttonState = FALSE, axisValue = 0), 
    list(inputId = "InputX", buttonState = FALSE, axisValue = 0.0993990004062653), 
    list(inputId = "xPos", buttonState = FALSE, axisValue = 5), 
    list(inputId = "yPos", buttonState = FALSE, axisValue = 0.0799999982118607), 
    list(inputId = "zPos", buttonState = FALSE, axisValue = 0), 
    list(inputId = "xRot", buttonState = FALSE, axisValue = 0), 
    list(inputId = "yRot", buttonState = FALSE, axisValue = -0.705721318721771), 
    list(inputId = "zRot", buttonState = FALSE, axisValue = 0), 
    list(inputId = "wRot", buttonState = FALSE, axisValue = 0.708489596843719)), 
    syncedProperties = list(list(name = "timeStamp", value = "97,21667"))), 
list(time = 20.0626411437988, inputManagerStates = list(list(
    inputId = "InputY", buttonState = FALSE, axisValue = 0.601816594600677), 
    list(inputId = "InputX", buttonState = FALSE, axisValue = 0), 
    list(inputId = "xPos", buttonState = FALSE, axisValue = -1.31777036190033), 
    list(inputId = "yPos", buttonState = FALSE, axisValue = 0.0800001174211502), 
    list(inputId = "zPos", buttonState = FALSE, axisValue = 6.08214092254639), 
    list(inputId = "xRot", buttonState = FALSE, axisValue = 0), 
    list(inputId = "yRot", buttonState = FALSE, axisValue = -0.391442984342575), 
    list(inputId = "zRot", buttonState = FALSE, axisValue = 0), 
    list(inputId = "wRot", buttonState = FALSE, axisValue = 0.920202374458313)), 
    syncedProperties = list(list(name = "timeStamp", value = "107,3167"), 
        list(name = "previousGameState", value = "1"), list(
            name = "newGameState", value = "2"))))

当前转换代码

library(tidyverse)  

output_df <- 
  test_list %>% 
  tibble::enframe(name = "frame", value = "value") %>% 
  tidyr::unnest_wider(value) %>%
  tidyr::unnest(inputManagerStates, keep_empty = TRUE) %>%
  tidyr::unnest(syncedProperties, keep_empty = TRUE) %>%
  tidyr::unnest_wider(syncedProperties) %>%
  tidyr::unnest_wider(inputManagerStates)

output_df
#> # A tibble: 46 x 7
#>    frame  time inputId buttonState axisValue name      value
#>    <int> <dbl> <chr>   <lgl>           <dbl> <chr>     <chr>
#>  1     1  9.92 <NA>    NA            NA      <NA>      <NA> 
#>  2     2  9.94 InputY  FALSE          0      timeStamp 97,2 
#>  3     2  9.94 InputX  FALSE          0.0501 timeStamp 97,2 
#>  4     2  9.94 xPos    FALSE          5      timeStamp 97,2 
#>  5     2  9.94 yPos    FALSE          0.0800 timeStamp 97,2 
#>  6     2  9.94 zPos    FALSE          0      timeStamp 97,2 
#>  7     2  9.94 xRot    FALSE          0      timeStamp 97,2 
#>  8     2  9.94 yRot    FALSE         -0.707  timeStamp 97,2 
#>  9     2  9.94 zRot    FALSE          0      timeStamp 97,2 
#> 10     2  9.94 wRot    FALSE          0.708  timeStamp 97,2 
#> # ... with 36 more rows

性能对比

对我的数据而言,unnest速度尚可,但unnest_wider速度极慢。第一个unnest_wider(value)可通过base R代码替代,且速度显著提升:

microbenchmark::microbenchmark(
  unnest_wider =  
    test_list %>% 
    tibble::enframe(name = "frame", value = "value") %>% 
    tidyr::unnest_wider(value), 
  baser_r =  
    test_list %>% 
    tibble::enframe(name = "frame", value = "value") %>% 
    cbind(., do.call("rbind", .$value)) %>%
    select(-value)
)
#> Unit: milliseconds
#>          expr    min      lq     mean  median      uq     max neval cld
#>  unnest_wider 3.1446 3.34645 4.031113 3.63625 4.22770 10.5289   100   b
#>       baser_r 1.4005 1.48225 1.770210 1.63475 1.86465  5.0407   100  a

需求

我正在寻求替代%>% tidyr::unnest_wider(syncedProperties) %>% tidyr::unnest_wider(inputManagerStates)的更快代码,但因行数量不一致,cbind方案无法适用。尝试用unnest::unnest()未得到预期结构,且tidytable::unnest_wider目前仅支持向量,恳请提供高效解决方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 19:04:06