在嵌套列表中提取列表并在 R 中创建数据帧

Extract lists within a nested list and create dataframe in R

提问人:Michael 提问时间:10/16/2023 最后编辑:Michael 更新时间:10/16/2023 访问量:99

问:

这是我之前在这里发布的问题的后续。

我使用 nhlapi 包来抓取一些 boxscores,这会产生一个大的嵌套列表。

install.packages("nhlapi")
library(nhlapi)

boxscores <- nhl_games_boxscore(gameIds = 2023020001)

目标是从多场比赛(而不仅仅是一场比赛)中获取 boxscores,并将所有统计数据以及玩家信息合并到一个数据帧中。

我调整了我的代码,如下所示:

gameIds = c(2023020001, 2023020002) #multiple gameIds

boxscores <- nhl_games_boxscore(gameIds = gameIds)

根据之前的答案,我使用了:

away_players <- map(boxscores, \(x) boxscores[[1]][["teams"]][["away"]][["players"]])

之前建议使用:

away_players <- map(boxscores, \(x) x[[1]][["teams"]][["away"]][["players"]])

但这会导致一个我不确定如何解决的错误。subscript out of bounds

我可以看到我有 2 份 22 人的名单,它们是两场比赛的客队:

str(away_players, list.len=2)
List of 2
 $ :List of 22
  ..$ ID8482062:List of 4
  .. ..$ person      :List of 22

使用前面建议的代码:

df_away <- away_players %>% 
  map(unlist) %>% 
  map(enframe) %>% 
  map(\(x) pivot_wider(x,
                       names_from = name,
                       values_from = value)) %>% 
  bind_rows()

给出 1060 个变量的 2 个 obs。df

我确信我可能需要在列表中降低一个级别,这可能会起作用,但我被困在如何到达那里。

我还想将 添加为一列,但我不确定如何做到这一点,因为 没有出现在任何地方的初始列表中。gameIdgameId

我也尝试过循环,但我必须覆盖数据,因为我两次得到一个具有相同 22 名玩家的 df:

comb_away = data.frame()
for (game_id in gameIds) {
  boxscore = nhl_games_boxscore(gameIds = gameIds)
  away_players = boxscores[[1]][["teams"]][["away"]][["players"]]
  res = away_players %>% 
    map(unlist) %>% 
    map(enframe) %>% 
    map(\(x) pivot_wider(x,
                         names_from = name,
                         values_from = value)) %>% 
    bind_rows()
  comb_away = rbind(res, comb_away)
}
r dplyr 咕噜

评论

1赞 IRTFM 10/16/2023
与 map 或 lapply 相比,for 循环的一个优点是,可以通过检查循环索引来立即调查此类错误。我承认令人困惑的是,您在映射ping方面所做的努力都没有与您用作形式上的“x”有任何作用。

答:

1赞 margusl 10/16/2023 #1
map(boxscores, \(x) x[[1]][["teams"]][["away"]][["players"]])

抛出错误,因为在 map 中的第一次迭代函数期间,它的计算结果为:subscript out of bounds[[1]]

boxscore[[1]][[1]][["teams"]][["away"]][["players"]]

并且恰好指的是第一个游戏的一个项目。boxscores[[1]][[1]]copyright

当遍历输入列表的项目时,它会将每个项目传递给其函数,在函数的上下文中,列表深度已经减少了一个级别,这意味着 和 已经是顶级列表项。和你一起穿越到其中的第一个。map()copyrightteamsofficialsx[[1]]

我会选择这样的东西:

library(nhlapi)
library(purrr)
library(tibble)
library(tidyr)

gameIds = c(2023020001, 2023020002) 
boxscores <- nhl_games_boxscore(gameIds = gameIds)

boxscores |>
  # assume (kmh.. ) that list from nhl_games_boxscore is in the same order as 
  # gameIds input list
  set_names(gameIds) |>
  # use map with pluck() shorthand to extract nested list element, check ?pluck
  map(list("teams", "away", "players")) |>
  # use map with a named function, `name` and `value` are passed to enframe as args 
  map(enframe, name = "id", value = "player") |>
  # bind resulting dataframes, list names to gameId column
  list_rbind(names_to = "gameId") |>
  unnest_wider(player) |>
  # we don't need to unnest the whole nested structure, 
  # hoisting a single item from person lists:
  hoist(person, "fullName")
#> # A tibble: 44 × 7
#>    gameId     id    fullName person       jerseyNumber position     stats       
#>    <chr>      <chr> <chr>    <list>       <chr>        <list>       <list>      
#>  1 2023020001 ID84… Cole Sm… <named list> 36           <named list> <named list>
#>  2 2023020001 ID84… Yakov T… <named list> 13           <named list> <named list>
#>  3 2023020001 ID84… Gustav … <named list> 14           <named list> <named list>
#>  4 2023020001 ID84… Colton … <named list> 10           <named list> <named list>
#>  5 2023020001 ID84… Tommy N… <named list> 82           <named list> <named list>
#>  6 2023020001 ID84… Filip F… <named list> 9            <named list> <named list>
#>  7 2023020001 ID84… Roman J… <named list> 59           <named list> <named list>
#>  8 2023020001 ID84… Luke Sc… <named list> 2            <named list> <named list>
#>  9 2023020001 ID84… Samuel … <named list> 11           <named list> <NULL>      
#> 10 2023020001 ID84… Alexand… <named list> 45           <named list> <named list>
#> # ℹ 34 more rows

创建于 2023-10-16 with reprex v2.0.2

评论

0赞 Michael 10/16/2023
这太棒了!我们将如何取消所有列表的嵌套?我需要查看所有人员/职位/统计数据详细信息。你还会为主队球员重复这一点吗,也许 rbind 因为我也需要这些?
0赞 margusl 10/16/2023
1- 你很可能需要经历一系列的unnest() / unnest_wider() / unnest_longer()操作来塑造这个框架,以满足你的需要,这样它才有意义(当你在错误的方向上未经测试时,你会肯定知道)。进程有时被称为矩形,有这个漂亮的小插图 - tidyr.tidyverse.org/articles/rectangle.html 。2 - 您可以在第一次调用中替换为 。awayhomemap()