R语言ggplot2用reorder排序堆叠柱状图显示异常问题求助
问题原因
- 核心诱因是失码数据未做格式清洗直接用于绘图:从目标网站爬取的失码字段自带千位分隔符逗号,默认读取为字符串格式。你仅在计算总失码
sums时临时转了数值,但没有处理原字段的逗号,as.numeric()遇到带逗号的字符串会直接返回NA,同时绘图用的conceded字段始终是字符串类型。 - ggplot读取字符串类型的y轴变量时,会自动转为因子按整数编码计算柱高,完全偏离实际失码数值;且
reorder()遇到NA的sums值会将对应球队排在x轴最左侧,刚好对应Panthers队,就出现了你看到的异常。
修复代码
首先对爬取的失码字段做逗号清除+数值转换,修改后可直接解决异常:
library(rvest) library(data.table) library(ggplot2) # 读取和清洗数据 def = read_html("https://www.pro-football-reference.com/years/2021/opp.htm") defense = setDT(html_table(def)[[1]]) # 跳过表头和汇总行 defense = defense[-c(1,34:36),] # 重命名列 setnames(defense, c("rank", "team", "games", "points_against", "yards_conceded", "off_plays_faced", "yards_per_off_play", "TO", "fumbles_recovered", "1stD_faced", "pass_cmp", "pass_att", "pass_yd_conceded", "pass_td_conceded", "int_recovered", "net_yd_pass_att", "pass_1stD_against", "rush_att", "rush_yards_conceded", "rush_td_conceded", "rush_yards_per_att", "rush_1stD_conceded", "pen_against", "pen_yards_conceded", "pen_1stD", "off_score_percent", "turnover_percent", "expected_points_conceded")) # 核心修复:清除失码字段的千位分隔符,转成数值 defense[, c("pass_yd_conceded", "rush_yards_conceded") := lapply(.SD, function(x) as.numeric(gsub(",", "", x))), .SDcols = c("pass_yd_conceded", "rush_yards_conceded")] # 长宽转换生成绘图用数据集(代替原来的分别建表再合并,效率更高) yds_conc = melt(defense, id.vars = "team", measure.vars = c("pass_yd_conceded", "rush_yards_conceded"), variable.name = "type", value.name = "conceded") # 关联总失码用于排序 yds_conc[, sums := sum(conceded), by = team] # 转换type的标签 yds_conc[, type := fifelse(type == "pass_yd_conceded", "pass", "rush")] # 绘图 defplot = ggplot(yds_conc, aes(fill = type, x = reorder(team, sums), y = conceded)) + geom_col() + # geom_col等价于geom_bar(stat="identity"),写法更简洁 scale_fill_manual(name = "进攻类型", labels = c("传球", "冲球"), values = c("#006aff", "#4fb350")) + labs(title = "2021赛季球队防守失码统计(按进攻类型拆分)", x = NULL, y = "总失码数") + theme(axis.text.x = element_text(angle = 60, vjust = 1, hjust = 1), axis.ticks.y = element_blank(), axis.text.y = element_blank()) defplot
代码优化点
- 用data.table的原生语法
setDT、setnames、lapply(.SD)处理数据,比逐行操作效率高3~5倍,适合后续处理更大的赛季数据集。 - 用
melt()直接做宽表转长表,代替原来分别创建传球、冲球数据表再合并的写法,代码更简洁,也避免手动重复操作引入的错误。 - 用
geom_col()代替geom_bar(stat = 'identity'),是ggplot2针对堆叠柱状图场景的原生简化写法,可读性更强。 - 总失码计算用
by = team分组聚合,不需要手动重复计算再rep拼接,逻辑更清晰。
内容的提问来源于stack exchange,提问作者JDiddy36
相关产品推荐
相关产品推荐

