我的目标是将我的代码生成的嵌套列表转换为一个嵌套框架.我有下面的代码,它在一个循环中从几个网址中提取一些数据,并将它们存储在列表中.

library(rvest)
library(XML)
library(purrr)
library(stringr)
library(dplyr)

# declare variables
month = c('07','09')
year = c('2022','2023')
day = c('040','050')

# initialize the empty list
final = list()

# perform the loop
for (i in year) {
  for (j in month) {
    for (k in day) {
    
    skip_to_next <- FALSE
    
    url <- paste0('https://www.baseball-reference.com/boxes/ARI/ARI', i, j, k, '.shtml')
    
    Sys.sleep(5)
    game_path <- tryCatch(url |>
                            read_html() |>
                            html_nodes(xpath = '//div[contains(@id, "batting")]') |> 
                            map(\(x) x |> 
                                  as.character() |> 
                                  str_remove_all("<!--|-->") |> 
                                  read_html() |> 
                                  html_table()) |> 
                            unlist(recursive = FALSE), error = function(e) {skip_to_next <<- TRUE} )
    
    if(skip_to_next) {next}
    
    url <- read_html(url)
    
    list_url <- url %>%
      html_nodes(xpath = "//td/a") %>% 
      html_text() 
    
    List_2_letters = as.list(list_url[nchar(list_url) > 5])
    
    game_path <- mapply(cbind, game_path, "Date" = paste(gsub('.{1}$', '', k), j, i, sep = '-'), SIMPLIFY=F)
    
    game_path <- Map(cbind, game_path, "Team" = List_2_letters)
    
    final[[i]][[j]][[k]] <- game_path
    
    }
  }
}

我得到了一堆如下所示的列表:

enter image description here

我想做的是把所有有data.frame个值的列表组合起来.

我try 了所有这些:

final_2 = map_dfr(final, ~ bind_rows(.x))
final_2 <- as.data.frame(do.call(cbind, final))
final_2 <- do.call("rbind", final)

但它们都只是同时生成两个列表.我真的很困惑如何才能解决这个问题?

推荐答案

通过不创建嵌套列表,您可以更轻松地实现所需结果.我重构了您的代码,首先将主要的抓取代码放在一个函数中,以便更容易地进行调试和测试.在此函数中,我已经使用dplyr::bind_rows将团队表绑定到一个数据帧中.该函数也应该更高效一些,因为它避免了像在代码中那样两次读取HTML.

对于循环部分,我切换到了purrr::map.为此,创建一个包含日期和相应URL的数据帧.这样,您就可以直接在URL上循环,而不需要嵌套的for循环.结果,您将获得一个数据帧列表,您最终可以按行将它们绑定在一起.

最后,请注意,我go 掉了tryCatch,而使用purrr::safely进行错误处理.

library(rvest)
library(purrr)
library(stringr)
library(dplyr)

make_url <- function(year, month, day) {
  paste0(
    "https://www.baseball-reference.com/boxes/ARI/ARI",
    year, month, day, ".shtml"
  )
}

scrape_table <- function(url) {
  html <- read_html(url)

  nodes <- html |>
    html_elements(xpath = '//div[starts-with(@id, "all_") and contains(@id, "batting")]')

  teams <- html %>%
    html_elements(xpath = "//td/a") %>%
    html_text()

  nodes |>
    purrr::set_names(teams) |>
    purrr::map(\(x) {
      x |>
        as.character() |>
        str_remove_all("<!--|-->") |>
        read_html() |>
        html_table()
    }) |>
    unlist(recursive = FALSE) |>
    dplyr::bind_rows(.id = "Team")
}


# declare variables
month <- c("07", "09")
year <- c("2022")
day <- c("040")

dates <- expand.grid(
  year = year, month = month, day = day
)

urls <- dates |>
  mutate(
    url = make_url(year, month, day),
    date = paste(year, month, day, sep = "-"),
    .keep = "unused"
  )

safe_scrape_table <- purrr::safely(scrape_table)

final <- purrr::map(urls$url, \(url) {
  Sys.sleep(5)
  safe_scrape_table(url)
}) |>
  set_names(urls$date)

final <- final |>
  purrr::transpose() |>
  pluck("result") |>
  bind_rows(.id = "Date")

head(final)
#> # A tibble: 6 × 26
#>   Date       Team  Batting    AB     R     H   RBI    BB    SO    PA    BA   OBP
#>   <chr>      <chr> <chr>   <int> <int> <int> <int> <int> <int> <int> <dbl> <dbl>
#> 1 2022-07-0… San … Austin…     2     0     0     0     1     1     3 0.243 0.367
#> 2 2022-07-0… San … Mike Y…     2     0     0     0     0     1     2 0.236 0.338
#> 3 2022-07-0… San … Wilmer…     3     1     0     0     0     2     4 0.242 0.331
#> 4 2022-07-0… San … Darin …     2     1     0     0     1     2     4 0.22  0.335
#> 5 2022-07-0… San … Evan L…     3     1     1     0     1     0     4 0.248 0.333
#> 6 2022-07-0… San … LaMont…     4     0     1     2     0     0     4 0.22  0.313
#> # ℹ 14 more variables: SLG <dbl>, OPS <dbl>, Pit <int>, Str <int>, WPA <dbl>,
#> #   aLI <dbl>, `WPA+` <dbl>, `WPA-` <chr>, cWPA <chr>, acLI <dbl>, RE24 <dbl>,
#> #   PO <int>, A <int>, Details <chr>

EDIT问题是列的数据类型因日期不同而不同,即对于2022-07-04,WPA-列被读取为character,因为值包括%符号,而对于2023-07-04,它只包含数字,因此被读取为numeric.要解决这个问题,我们需要在绑定表之前进行一些数据清理.下面的代码将这些步骤拆分成几个部分.首先,提取包含结果的表.然后循环遍历数据帧列表,并使用例如readr::parse_number将应为数字的字符列转换为数字.在这样做之后,约束原则上应该会奏效.我测试了2022年和2023年.但当然,也有可能出现其他问题.更安全的 Select 是将所有列转换为字符.然后Bundle .然后进行数据清理,最后将列转换为数字.

final_result <- final |>
  purrr::transpose() |>
  pluck("result")

# Data Cleaning
final <- map(
  final_result,
  \(x) {
    x |> 
      mutate(across(!c(Team, Batting, Details) & where(is.character), readr::parse_number))
  }
)

# Bind
final <- final |>
  bind_rows(.id = "Date")

R相关问答推荐

编码变量a、b、c以匹配来自另一个数据点的变量x

更改Heatmap Annotation对象的名称

R Highcharts与两个位置关联的注释

更改默认系列1以更改名称

如何通过Docker部署我的shiny 应用程序(多个文件)

使用tidy—select创建一个新的带有mutate的摘要变量

IMF IFS数据以R表示

根据文本字符串中的值粘贴新列

使用列/行匹配将两个不同维度的矩阵相加

合并DFS列表并将索引提取为新列

使用较长的查询提取具有部分匹配的列表中的较短目标,

以相同的方式对每个表进行排序

方法::slotName如何处理非类、非字符的参数?

如何在科学记数法中显示因子

在带有`R`中的`ggmosaic`的马赛克图中使用图案而不是 colored颜色

根据r中另一个文本列中给定的范围对各列求和

如何构建一个for循环来循环处理动物ID?

基于R中的辅助向量中的值有条件地连接向量中的字符串

如何将图例文本添加到图例符号中

R没有按顺序显示我的有序系数?