在R中将多个不均匀嵌套列表转换为数据帧

在R中将多个不均匀嵌套列表转换为数据帧,r,R,我正在努力掌握R,作为一个实验,我想我会尝试利用一些板球数据。在其最原始的格式中,它是一个yaml文件,我使用yaml R包将其转换为一个R对象 但是,我现在有一些长度不均匀的嵌套列表,我想尝试将它们转换为R中的数据帧。我尝试了一些方法,例如编写一些循环来解析数据和tidyr包中的一些函数。然而,我似乎无法让它很好地工作 我想知道人们是否知道解决这个问题的最佳方法?复制数据结构在这里是很困难的,因为多个嵌套列表及其长度的不均匀性(这将导致非常长的代码块。但是,您可以在这里找到原始yaml数据:(

我正在努力掌握R,作为一个实验,我想我会尝试利用一些板球数据。在其最原始的格式中,它是一个yaml文件,我使用yaml R包将其转换为一个R对象

但是,我现在有一些长度不均匀的嵌套列表,我想尝试将它们转换为R中的数据帧。我尝试了一些方法,例如编写一些循环来解析数据和tidyr包中的一些函数。然而,我似乎无法让它很好地工作

我想知道人们是否知道解决这个问题的最佳方法?复制数据结构在这里是很困难的,因为多个嵌套列表及其长度的不均匀性(这将导致非常长的代码块。但是,您可以在这里找到原始yaml数据:(我使用的是ODI internationals)

提前谢谢

更新 我试过这个: 1) 使用tidyr-separate

d <- unnest(balls)
Name <- c("Batsman","Bowler","NonStriker","RunsBatsman","RunsExtras","RunsTotal","WicketFielder","WicketKind","PlayerOut")
a <- separate(d, x, Name, sep = ",",extra = "drop")

d警告:答案不完整;尝试安排局数数据

plyr::rbind.fill
可以提供一种解决方案,用不同的列数绑定行

我不使用
tidyr
,但下面是一些将局数数据转换为data.frame的粗略代码。然后,您可以在目录中的所有
yaml
文件中循环此操作

# Download and unzip data
download.file("http://cricsheet.org/downloads/odis.zip", temp<- tempfile())
tmp <- unzip(temp)

# Create lists - use first game
library(yaml)
raw_dat <- yaml.load_file(tmp[[2]])
#names(raw_dat)

# Function to process list into dataframe
p_fun <- function(X) {
          team = X[[1]][["team"]]

          # function to process each list subelement that represents each throw
          fn <- function(...) {
                    tmp = unlist(...)
                    tmp = data.frame(ball=gsub("[^0-9]", "", names(tmp))[1], t(tmp))
                    colnames(tmp) = gsub("[0-9]", "", colnames(tmp))
                    tmp
                    }
           # loop over all throws
           lst = lapply(X[[1]][["deliveries"]], fn )

           cbind(team, plyr:::rbind.fill(lst))
          }

# Loop over each innings
dat <- plyr::rbind.fill(lapply(raw_dat$innings, p_fun))
您可以截断它,使其更有用

str(raw_dat, 3)
length(raw_dat)
因此,有三个主要的列表元素-
meta
info
、和
innings
。你也可以看到这一点

names(raw_dat)
要访问元数据,可以使用

raw_dat$meta
#or using `[[1]]` to access the first element of the list (see ?'[[')
raw_dat[[1]]
#and get sub-elements by either
raw_dat$meta$data_version
raw_dat[[1]][[1]] # you can also use the names of the list elements eg [[`data_version`]]
主要数据在
局数
元素中

str(raw_dat$innings, 3)
查看列表元素中的名称

lapply(raw_dat$innings, names)
lapply(raw_dat$innings[[1]], names)
有两个列表元素,每个元素都有子元素。您可以通过以下方式访问这些

raw_dat$innings[[1]][[1]][["team"]] # raw_dat$innings[[1]][["1st innings"]][["team"]]
raw_dat$innings[[2]][[1]][["team"]] # raw_dat$innings[[2]][["2nd innings"]][["team"]]

上述函数解析了
原始数据$innings
中的交付数据。要了解它的功能,请从内部进行操作

使用一条记录查看其工作原理 (注意
lappy
,带
p\u fun
,在
raw\u dat$局[[1]]
raw\u dat$局[[2]]]
上循环;因此这是外循环,
lappy
,带
fn
,在一局内循环传递;内环)

所以我们第一局的第一次投球

tmp = data.frame(ball=gsub("[^0-9]", "", names(tmp))[1], t(tmp))
colnames(tmp) = gsub("[0-9]", "", colnames(tmp))
tmp
#   ball X..batsman X..bowler X..non_striker X..runs.batsman X..runs.extras X..runs.total
# 1   01    IR Bell  DW Steyn       MJ Prior               0              0             0
要查看
lappy
的工作方式,请使用前三次交付(您需要在工作区中运行函数
fn

因此,对于一局内的每一次投球,我们都会得到一个
列表
元素。然后我们使用
rbind.fill
创建一个data.frame


如果我要尝试解析每个yaml文件,我会使用一个循环

以前三条记录为例,并添加匹配日期

tmp <- unzip(temp)[2:4]

all_raw_dat <- vector("list", length=length(tmp))

for(i in seq_along(tmp)) {
      d = yaml.load_file(tmp[i])
      all_raw_dat[[i]] <- cbind(date=d$info$date, plyr::rbind.fill(lapply(d$innings, p_fun)))
}

rbind.fill
不返回并在需要的地方用额外的列添加/更新行(
a
仍然没有列
z
),将其视为创建一个空数据框,列数等于在数据框列表中找到的唯一列数-
unique(c(名称(a),名称(b))
。然后,在可能的情况下,在每一行中填充值,并保留缺失值(NA)。否则。

警告:答案不完整;尝试安排局数数据

plyr::rbind.fill
可以提供一种解决方案,用不同的列数绑定行

我不使用
tidyr
,但下面是一些将局数数据转换为data.frame的粗略代码。然后,您可以在目录中的所有
yaml
文件中循环此操作

# Download and unzip data
download.file("http://cricsheet.org/downloads/odis.zip", temp<- tempfile())
tmp <- unzip(temp)

# Create lists - use first game
library(yaml)
raw_dat <- yaml.load_file(tmp[[2]])
#names(raw_dat)

# Function to process list into dataframe
p_fun <- function(X) {
          team = X[[1]][["team"]]

          # function to process each list subelement that represents each throw
          fn <- function(...) {
                    tmp = unlist(...)
                    tmp = data.frame(ball=gsub("[^0-9]", "", names(tmp))[1], t(tmp))
                    colnames(tmp) = gsub("[0-9]", "", colnames(tmp))
                    tmp
                    }
           # loop over all throws
           lst = lapply(X[[1]][["deliveries"]], fn )

           cbind(team, plyr:::rbind.fill(lst))
          }

# Loop over each innings
dat <- plyr::rbind.fill(lapply(raw_dat$innings, p_fun))
您可以截断它,使其更有用

str(raw_dat, 3)
length(raw_dat)
因此,有三个主要的列表元素-
meta
info
、和
innings
。你也可以看到这一点

names(raw_dat)
要访问元数据,可以使用

raw_dat$meta
#or using `[[1]]` to access the first element of the list (see ?'[[')
raw_dat[[1]]
#and get sub-elements by either
raw_dat$meta$data_version
raw_dat[[1]][[1]] # you can also use the names of the list elements eg [[`data_version`]]
主要数据在
局数
元素中

str(raw_dat$innings, 3)
查看列表元素中的名称

lapply(raw_dat$innings, names)
lapply(raw_dat$innings[[1]], names)
有两个列表元素,每个元素都有子元素。您可以通过以下方式访问这些

raw_dat$innings[[1]][[1]][["team"]] # raw_dat$innings[[1]][["1st innings"]][["team"]]
raw_dat$innings[[2]][[1]][["team"]] # raw_dat$innings[[2]][["2nd innings"]][["team"]]

上述函数解析了
原始数据$innings
中的交付数据。要了解它的功能,请从内部进行操作

使用一条记录查看其工作原理 (注意
lappy
,带
p\u fun
,在
raw\u dat$局[[1]]
raw\u dat$局[[2]]]
上循环;因此这是外循环,
lappy
,带
fn
,在一局内循环传递;内环)

所以我们第一局的第一次投球

tmp = data.frame(ball=gsub("[^0-9]", "", names(tmp))[1], t(tmp))
colnames(tmp) = gsub("[0-9]", "", colnames(tmp))
tmp
#   ball X..batsman X..bowler X..non_striker X..runs.batsman X..runs.extras X..runs.total
# 1   01    IR Bell  DW Steyn       MJ Prior               0              0             0
要查看
lappy
的工作方式,请使用前三次交付(您需要在工作区中运行函数
fn

因此,对于一局内的每一次投球,我们都会得到一个
列表
元素。然后我们使用
rbind.fill
创建一个data.frame


如果我要尝试解析每个yaml文件,我会使用一个循环

以前三条记录为例,并添加匹配日期

tmp <- unzip(temp)[2:4]

all_raw_dat <- vector("list", length=length(tmp))

for(i in seq_along(tmp)) {
      d = yaml.load_file(tmp[i])
      all_raw_dat[[i]] <- cbind(date=d$info$date, plyr::rbind.fill(lapply(d$innings, p_fun)))
}

rbind.fill
不返回并在需要的地方用额外的列添加/更新行(
a
仍然没有列
z
),将其视为创建一个空数据框,列数等于在数据框列表中找到的唯一列数-
unique(c(名称(a),名称(b))
。然后尽可能在每一行中填充这些值,否则将丢失(NA)..

Hi!谢谢你,我已经试过了,它看起来很有效。虽然我将不得不花一点时间理解代码:)再次感谢!所以我花了一点时间在脚本上,认为我基本上理解了它背后的概念。但是有一些我不太明白。我已经把问题分成了几个单独的评论!再次感谢您将这些放在一起,它帮助我掌握了R中的数据操作,尽管有