R语言for循环仅输出末次迭代结果问题排查(足球传球网络分析)
原代码的问题
你的循环跑不出预期结果,是5个明确的逻辑错误导致的,和缺索引没有直接关系:
- 结果存储逻辑完全错误:你初始化
network_metrics时长度设为比赛总场次,和你需要的307个换人结果长度不匹配;且循环内直接执行network_metrics <- density,每次都会把整个结果变量覆盖成单个数值,循环跑完自然只剩最后一次计算的单值。 - 未处理单场多换人场景:单场比赛存在多次换人,你用
which(match_i$type.name == "Substitution")会一次性返回本场所有换人行号,直接拿来切片会把所有换人对应的窗口数据混在一起,根本没有逐个为每个换人单独构建传球网络,必然漏算大量换人事件。 - 窗口提取逻辑错误:直接从换人行号往前数50行,没有排除行序列中夹杂的其他换人事件,会导致窗口内实际传球数不足50,甚至把换人事件的空字段当成传球边,建图结果完全错误。
- 存在冗余重复代码:单场数据提取的代码重复写了两次,虽不影响结果但属于无效代码。
- 未做边界处理:如果某次换人发生时场上累计传球不足50次,直接减50会生成负数行索引,提取到完全无关的行。
修正后可运行代码
核心调整是加一层内层循环遍历单场所有换人事件,单独提取每个换人前的50次有效传球,按索引存储结果,同时处理边界异常:
library(igraph) # 读取所有比赛ID match_ids <- unique(pass.sub_dataframe$match_id) # 初始化结果向量,长度匹配总换人次数 total_subs <- sum(pass.sub_dataframe$type.name == "Substitution") network_metrics <- numeric(total_subs) # 结果存储位置计数器 res_pos <- 1 for (i in seq_along(match_ids)) { # 提取单场全量数据 match_data <- pass.sub_dataframe[pass.sub_dataframe$match_id == match_ids[i], ] # 单独标记本场所有传球、换人事件的行位置 all_pass_rows <- which(match_data$type.name == "Pass") all_sub_rows <- which(match_data$type.name == "Substitution") # 内层遍历本场每一次换人 for (j in seq_along(all_sub_rows)) { current_sub_loc <- all_sub_rows[j] # 筛选当前换人发生前的所有传球 prior_passes <- all_pass_rows[all_pass_rows < current_sub_loc] # 取最近50次传球,不足50次则取全部可用传球 if (length(prior_passes) >= 50) { window_rows <- tail(prior_passes, 50) } else { window_rows <- prior_passes warning(paste0("比赛ID", match_ids[i], "第", j, "次换人前方累计传球不足50次,实际使用", length(window_rows), "条传球计算")) } # 提取传球边表,剔除缺失值 edge_df <- match_data[window_rows, c("player.name", "pass.recipient.name")] edge_df <- na.omit(edge_df) # 构建有向图计算密度,直接用igraph内置函数避免手写公式出错 pass_graph <- graph_from_data_frame(edge_df, directed = TRUE) network_metrics[res_pos] <- edge_density(pass_graph, loops = FALSE) # 存储位置后移 res_pos <- res_pos + 1 } }
补充说明
- 最终
network_metrics会按换人事件在原始数据中出现的顺序,返回对应307个密度值。如果需要匹配每次换人的具体信息(比赛、上场/离场球员、换人时间),可以额外建个数据框同步存储这些字段和对应密度,方便后续分析。 - 代码用igraph内置的
edge_density()计算密度,默认适配有向图计算逻辑,自动排除自环影响,比手写公式容错率更高。 - 加了传球不足50次的兼容逻辑,不会出现负数索引取错数据的问题,警告信息可以帮你定位哪些换人场景的传球窗口长度不足50,按需调整即可。
内容的提问来源于stack exchange,提问作者Jasfr
相关产品推荐
相关产品推荐

