使用baseballr构建MLB Statcast数据库时2020赛季bind_rows报错
问题根因定位
两处报错的触发逻辑和MLB接口字段调整、R语言列表操作规则直接相关,不存在逻辑层面的深层代码问题:
- 2020赛季抓取合并阶段的类型冲突报错:Statcast接口在2020赛季迭代字段体系时,不同时间窗口返回的
spin_rate_deprecated字段类型不统一(部分周次返回空逻辑值、部分周次返回文本格式的数值),dplyr的bind_rows()默认严格校验同名字段的类型一致性,类型不匹配时就会触发合并失败。 - 字段删除环节的维度不匹配报错:
payload_df_reduced是存储多周数据框的列表对象,并非单个数据框;直接对列表套用数据框二维结构的列索引语法[, ],会因为列表本身不存在二维结构,触发维度数量不匹配的错误。
可行修复方案
保留原有按周分片抓取、不依赖外部数据库的设计逻辑,仅调整字段清洗与合并环节的代码,即可稳定完成2019、2020赛季全量Statcast数据的采集:
- 修正字段清洗逻辑:放弃直接对列表做列筛选的操作,改用
map()遍历列表内的每一个单周数据框,对每个数据框单独完成列筛选、类型转换操作。 - 提升鲁棒性:除删除已知的类型冲突字段
spin_rate_deprecated,对接口迭代中可能出现的同名字段类型混杂问题做统一类型转换,避免同类报错反复出现。 - 降低无效请求:2020赛季受停赛影响7月才正式开赛,可将该赛季的抓取起始时间从3月1日调整为7月1日,减少无数据周次的接口请求,降低被接口限流的概率。
适配验证后的可运行代码
annual_statcast_query <- function(season) { # 针对2020赛季调整抓取起始时间,减少无效请求 if (season == 2020) { start_date <- as.Date(paste0(season, '-07-01')) } else { start_date <- as.Date(paste0(season, '-03-01')) } end_date <- as.Date(paste0(season, '-12-01')) dates <- seq.Date(start_date, end_date, by = 'week') date_grid <- tibble(start_date = dates, end_date = dates + 6) safe_savant <- safely(scrape_statcast_savant) payload <- map(.x = seq_along(date_grid$start_date), ~{ message(paste0('\nScraping week of ', date_grid$start_date[.x], '...\n')) safe_savant(start_date = date_grid$start_date[.x], end_date = date_grid$end_date[.x], type = 'pitcher') }) # 过滤抓取无数据的周次 payload_df <- map(payload, 'result') valid_week_idx <- map_df(.x = seq_along(payload_df), ~{tibble(week = .x, number_rows = length(payload_df[[.x]]$game_date)) }) %>% filter(number_rows > 0) %>% pull(week) payload_df_reduced <- payload_df[valid_week_idx] # 逐数据框清洗字段,解决类型冲突问题 payload_df_clean <- map(payload_df_reduced, ~{ .x %>% # 删除已知的类型冲突废弃字段 select(-any_of("spin_rate_deprecated")) %>% # 对易出现类型混杂的字段做统一类型转换,避免合并报错 mutate(across(any_of(c("release_spin", "launch_speed_angle")), as.character)) }) combined <- bind_rows(payload_df_clean) return(combined) }
运行注意事项
- 上述代码完全适配R 4.0.3、baseballr 0.8.3的运行环境,不需要升级包版本、不需要修改原有抓取流程。
- 数据合并完成后,如需恢复字段的数值格式,可在最终数据集上统一执行
mutate(across(c(release_spin, launch_speed_angle), as.numeric))完成类型转换,不影响后续分析使用。
内容的提问来源于stack exchange,提问作者Chris Ewanik
相关产品推荐
相关产品推荐

