新闻详情

新闻详情

首页 / 资讯中心 / 详情

Steam游戏数据集CSV清洗与EDA实战:pandas处理65k条记录

发布时间:2026/9/26 23:01:57来源:尧图网络
Steam游戏数据集CSV清洗与EDA实战:pandas处理65k条记录
简介面向游戏市场分析与数据挖掘场景的Steam游戏数据集收录2021至2025年间超过6.5万款游戏包含10个特征字段。数据源自官方Steam网页API覆盖应用ID、名称、发行日期、价格、类型、类别、开发者、出版商及用户推荐数等维度其中类型与类别为逗号分隔的多值字段便于按题材或功能做多标签分析。资源适合观察近年来数字游戏行业的增长轨迹可用于研究市场趋势、类型热度变化、定价策略以及独立游戏的兴起也可作为数据清洗、特征工程、可视化和趋势预测的练手素材。压缩包采用7z格式共2个文件一个Python数据采集脚本和一份结构化CSV文件整体仅1.93MB下载与处理都很轻量。CSV可直接导入Excel、Pandas或BI工具开展探索性分析Python脚本可复现数据采集过程便于按需扩展新字段或更新数据。目前已有349人浏览学习无论是数据科学初学者、游戏行业分析师还是Steam平台研究者都能从中获得可靠的基础数据支撑。1. 拿到2021-2025 Steam游戏数据集后我首先做的是这四件事这份2021-2025 Steam游戏数据集10特征65k个独立条目CSV第一眼看上去就是一份标准的表格文件一行一个游戏10列带了从发行日期到好评率的核心信息。但真正动手之后会发现65k这个规模隐含了不少陷阱。它既不是Steam全量游戏库也不是评论明细而是某个时间窗内的商店条目快照。适合用它做价格分布、类型趋势、好评率对比这类中粒度分析也适合作为推荐系统或市场观察项目的起步数据。我拿到这份CSV后没有直接跑描述统计而是先做了四件事确认10个特征各自是什么单位、确认65k到底按什么维度计数、按2021-2025时间窗清洗数据、把价格和评测数里混入的字符统一转成数值。前三件事决定结论是否可信第四件事决定代码会不会中途报错。这篇记录会把每一步拆开讲包含可复现的pandas代码、参数选择理由以及五个我在类似数据集上踩过的坑。2. 数据集结构与10个特征先读懂每一列再动手在导入CSV之前先看数据字典。这一步看起来没技术含量但绝大多数后续翻车都发生在“没搞清字段口径”上。10个特征听起来不多实际处理时会发现类型、单位、缺失值规则都藏在列名背后。2.1 数据字典10个特征各自是什么、数值格式与边界下面这份字段结构是处理Steam快照类CSV的常见口径字段名可能略有不同但核心维度基本一致先按这张表对齐。字段名类型示例值说明appidint1091500Steam商店内部编号全局唯一namestring黑神话悟空游戏名称release_datedate2023-08-20正式发行日期缺失代表未发售或取消developersstringGame Science开发方多家时用分号分隔publishersstringGame Science发行方多家时用分号分隔pricefloat29.99采集快照时的当前价格0代表免费original_pricefloat29.99发行定价或未打折时的价格positive_ratioint96好评率百分比范围0-100review_countint32000评测总数代表热度genresstringAction, RPG类型标签逗号分隔appid是整个数据集唯一可依赖的主键。name只是给人看的理论上存在同名游戏或重新发行版本后面去重时会单独说明。release_date在pandas里建议解析成datetime类型否则按年份过滤会变成字符串比较容易出现“2021-1-1”这类被误判的边界问题。price和original_price这对字段是一组很有用的组合。两者相等表示当前没有折扣original_price大于price说明采集时处于促销期price为0且original_price大于0说明这款游戏正在做限时免费。如果整体数据里打折比例异常高要留意采集时间点是否接近大型促销活动不能把它当成全年价格规律。positive_ratio是整数百分比表示Steam评测中推荐的比例。review_count是分母两个字段必须放在一起看。只有5个评测、好评率100%的游戏和50000个评测、好评率96%的游戏可信度完全不同。后面做排序或建模时我会用review_count做加权避免冷门小游戏占据榜单头部。2.2 “65k个独立条目”是什么意思行数、appid数与时间窗标题里的65k个独立条目最合理的解释是按appid去重后有超过65000个游戏条目。这不是65万条评论也不代表Steam全平台数量而是在2021-2025这个时间范围内被采集到的商店条目快照。把65k摊到五年里平均每年约1.3万个条目。这个数字量级和Steam近年每年新增游戏数量的量级是接近的。如果你研究的目标是“近五年Steam新游发行节奏”这份数据的位置很合适但如果你想要的是“Steam全站热门榜”那还要确认里面是否包含评论区刷出来的冷门长尾游戏。理解“独立条目”的另一个关键在于一个appid就是一款独立的游戏但同一款游戏可能因为地区版本、捆绑包拆出项、重新上架而产生多条记录。所以拿到数据第一件事就是检查有没有重复appid。如果重复率超过2%基本能断定数据源在抓取时有分页重叠这时候必须先按appid去重再进入分析流程。2.3 10个特征够不够字段粒度背后的取舍10个特征属于中粒度快照不是Steam爬虫全字段采集。优点很明显行数可观、文件体积小、没有嵌套JSON可以直接交给pandas处理新手也能在十几分钟内完成导入、清洗、画图。缺点同样明确没有标签、语言、操作系统支持、最低配置、评论正文这些字段做游戏推荐或内容分析时不够用。这个粒度值不值得用来启动项目我一般会先跑一轮EDA也就是探索性数据分析。因为10个特征已经覆盖了价格、时间、评价、类型四个最关键维度足够回答“Steam 2021-2025年哪个类型的中位数价格更高”“免费游戏好评率是否真的更高”这类问题。如果后续发现标签或配置字段是刚需再通过最后一章的方式回补不必一开始就追求全量字段。3. 用pandas导入CSV文件并清洗65k条记录的最小可复现流程这个数据集从拿到到能进入分析最短路径是三步读取、过滤、清洗。每一步都有参数会踩坑先说读取。3.1 pandas读取CSV文件时决定成败的三个参数打开这份CSV的第一步千万不要直接双击Excel然后另存。最稳妥的方式是先写一段pandas读取代码把编码、列类型、日期解析一次定下来。import pandas as pd # 读取CSVutf-8-sig同时兼容普通UTF-8和Excel导出的BOM头 # usecols只保留10个特征避免混入未知列 df pd.read_csv( steam_games_2021_2025.csv, encodingutf-8-sig, dtype{appid: int64, price: float64, original_price: float64}, parse_dates[release_date], usecols[ appid, name, release_date, developers, publishers, price, original_price, positive_ratio, review_count, genres ] ) print(df.shape) print(df.dtypes)这段代码看起来简单但三个参数每个都有自己的作用。encodingutf-8-sig是为了兼容Excel另存为时自动加上的BOM头如果不加读出来的第一列列名会是“\ufeffappid”后面对appid做筛选时会直接报KeyError。dtype指定appid为int64避免它被误读成object同时指定价格为float64防止“0.99”这类字符串被当成文本。parse_dates则把release_date从字符串解析成pandas的datetime类型后面按2021-2025时间窗过滤才不会出边界问题。usecols参数在遇到来源不明的CSV时格外重要。如果数据源后来追加了列你的代码不会因为多出字段而报错但也可能因此悄悄漏掉关键列。我一般在读取后打印shape确认行数确实在65k这个量级再进入下一步。3.2 按2021-2025时间窗过滤与appid去重读取完成后第一件事不是算均值而是把数据范围限定在2021-2025。这个标题已经写明了时间窗但CSV里很可能还混着2020年甚至更早的条目。# 先转成datetime统一缺失值 df[release_date] pd.to_datetime(df[release_date], errorscoerce) # 按appid去重保留第一次出现的记录 df df.drop_duplicates(subsetappid, keepfirst).copy() # 时间窗过滤保留2021-01-01到2025-12-31之间发行的游戏 mask (df[release_date] 2021-01-01) (df[release_date] 2026-01-01) df df[mask].copy() print(去重后总数, len(df)) print(独立appid数量, df[appid].nunique())这里的顺序我建议固定为先to_datetime再去重最后过滤。先转日期是为了让缺失值统一变成NaT否则后面比较可能会抛出含糊错误。先drop_duplicates再过滤时间窗是为了避免出现“同一游戏的旧版本在2020年新版本在2022年按时间过滤后留下两条不同appid”的情况。keepfirst意味着保留重复条目中的第一行如果你的数据源里后出现的行字段更完整可以改成keeplast也可以用sort_values先排序再决定保留哪条。过滤后len和nunique应该完全一致。如果这两个数不相等说明重复id没清干净后面所有统计分析都会把某些游戏重复计数。3.3 缺失值、价格与单位字符的处理CSV数据不可避免会混入脏数据尤其是从steam爬虫或导出工具生成的版本。下面是针对这份数据最常见的三类问题处理方式。# 关键字段不能缺失直接删除 df df.dropna(subset[name, price, release_date]).copy() # 开发方和发行方缺失时填Unknown保留统计口径 df[developers] df[developers].fillna(Unknown) df[publishers] df[publishers].fillna(Unknown) # review_count可能被压缩成10k这类单位字符强制转数值 df[review_count] pd.to_numeric(df[review_count], errorscoerce) df[review_count] df[review_count].fillna(0).astype(int) # 价格守卫负数归零当前价格不应高于原价 df.loc[df[price] 0, price] 0 df[price] df[[price, original_price]].min(axis1)dropna这一步要看业务目标。如果你研究的就是未发售游戏或已下架游戏release_date缺失恰恰是有效信号不能简单删除。但如果目标是分析已发行游戏的价格和评价release_date缺失就没办法参与时间维度分析删掉更干净。review_count转数值这步容易出问题。如果数据源里存的是“32k”“10k”这类缩写pd.to_numeric会把这些值全部转成NaNfillna(0)又会让大量冷门游戏变成0评测。这里面没有后悔药被转换丢弃的精度无法恢复。我建议先运行df[review_count].astype(str).str.contains(r[a-zA-Z]).sum()看有多少行带了单位字符再决定要不要回头找原始数据。如果只是少数几行fillna(0)能接受如果超过5%就要考虑用正则把缩写还原成真实数值。清洗完成后建议导出干净副本后续分析基于副本而不是原始文件。df.to_csv(steam_2021_2025_clean.csv, indexFalse, encodingutf-8-sig)导出时再次使用utf-8-sig能保证你下次用Excel打开这个CSV时中文不乱码。这个习惯对任何后续接手文件的同事都很友好。4. 从这份CSV能得出什么结论价格、好评率与标签扩展清洗完成后下一步就是回答业务问题。我选了三个最常被问到的方向每个都配一段可以直接复现的代码。4.1 价格分布可视化免费游戏与付费游戏的分界线Steam玩家对价格极其敏感分析师最关心的问题是“这批游戏里免费占多少付费集中在哪个价位”。代码会告诉我们答案。import matplotlib.pyplot as plt free_mask df[price] 0 print(免费游戏占比{:.1%}.format(free_mask.mean())) paid df.loc[~free_mask, price] print(付费游戏价格分位数) print(paid.quantile([0.25, 0.5, 0.75, 0.9])) # 价格长尾严重画直方图时用log变换更直观 fig, axes plt.subplots(1, 2, figsize(12, 4)) axes[0].hist(paid, bins60, edgecolorwhite) axes[0].set_title(paid price distribution) axes[1].hist(paid, bins60, edgecolorwhite, logTrue) axes[1].set_title(paid price distribution (log y)) plt.show()第一张图通常会把所有付费游戏挤在0到5美元区间因为Steam小体量游戏实在太多右侧的3A游戏以几十到几百美元的价格拖出一条长尾。第二张图把y轴改成对数坐标后才能真正看清尾部结构。这里有个值得记录的输出免费游戏占比通常会显著高于直觉预期接近20%-30%这反映的是Steam近年免费游玩模式的普及而不是数据采集错误。价格列如果做过促销折扣处理直接用清洗后的price即可。如果原始CSV里price和original_price都非空可以额外增加一个discount_rate字段线上游戏环比增长时用得上。4.2 好评率口径与review_count联合使用从小众高分里捞真实口碑positive_ratio单独排序会有一个明显的坑评测数量极少的游戏往往占据榜首。真实口碑必须结合review_count做加权或筛选。import numpy as np # 方法一用评测数做加权平均 df[weighted_score] ( df[positive_ratio] * df[review_count] ).div(df[review_count].clip(lower1)) # 方法二过滤掉低热度样本后再看分布 valid df[df[review_count] 50] print(评测数50的样本量, len(valid)) print(valid[positive_ratio].describe())weighted_score这个字段不是标准的贝叶斯平均但它能大致解决“5个评测100%好评”压过“5万评测95%好评”的问题。clip(lower1)是为了避免review_count为0时出现除零但代价是0评测游戏的加权分变成0这在排序时反而不干扰。方法二的阈值取50还是100要看数据分布。可以先打印review_count的分位数再决定df[review_count].quantile([0.5, 0.75, 0.9])。如果50这个阈值筛掉了大半数据就降到10如果筛完还是很大用100更稳。这一步没有绝对标准只有适合当前问题的口径。4.3 把10个特征扩展成可建模变量标签拆分与时间特征原始CSV只有10个特征但genres字段是逗号分隔的多值标签一个特征能拆出十几个类型维度。# 将genres拆成多列Action, RPG - Action1, RPG1 genres_dummies df[genres].str.get_dummies(sep,) df_ext pd.concat([df, genres_dummies], axis1) print(genres_dummies.sum().sort_values(ascendingFalse).head(8)) # 时间特征发行年份、季度、到2025年末的上市天数 df_ext[release_year] df_ext[release_date].dt.year df_ext[release_quarter] df_ext[release_date].dt.quarter df_ext[days_on_market] ( pd.Timestamp(2025-12-31) - df_ext[release_date] ).dt.days yearly df_ext.groupby(release_year).size() print(各年份发行数量) print(yearly)str.get_dummies是处理CSV里逗号分隔多值字段最偷懒也最稳定的方法不需要手动split多列再merge。拆分后前几个类型通常是Indie、Action、Adventure、Casual这类高频标签。各年份发行数量的输出会呈现2021到2025的发行节奏变化。如果你发现2023年是峰值2024、2025有所回落这不一定是Steam政策影响更可能是采集截至时间点在年末导致当年新游没完全覆盖。判断时要结合数据采集时间而不是直接把曲线当结论。时间特征里days_on_market适合做“上市越久评价越多”这类相关性检验也可以作为推荐模型的数值特征。release_quarter则能反映季节效应比如年底促销前发行的策略类游戏更多。这些扩展后的变量让原本10列的CSV有了进入机器学习流程的资格。5. 五个常见坑从CSV编码到语义去重的血泪经验这部分是实操里最值得记录的地方。以下五个问题几乎每次处理Steam类CSV都会遇到按频率排序。5.1 坑1UTF-8 BOM让首列字段名多出一个\ufeff现象读取CSV后df.columns打印出来第一列是“\ufeffappid”用df[appid]筛选时报KeyError。原因这份CSV可能被Excel或者Windows下的文本编辑器另存过文件头加了BOM。pandas默认按UTF-8解析时BOM被当成普通字符塞进第一个列名。解决读取时直接带encodingutf-8-sig这个编码方式会自动去掉BOM。如果已经读进来了用df.columns df.columns.str.replace(\ufeff, )救急。这一步我在第3章代码里已经提前规避但如果你的数据来自同事手工处理过的版本一定要检查列名对象。5.2 坑2发行日期缺失导致按年过滤直接清空数据现象release_date列里有大量NaT执行df[df[release_date].dt.year 2023]时报错或者过滤后明明应该有几万行却只剩几十行。原因未发售项目、已下架项目、页面上没有显示发行日期的条目在爬虫阶段生成空值。直接用dt方法会撞上缺失值字符串比较又会造成边界误判。解决先pd.to_datetime(df[release_date], errorscoerce)把非法值统一成NaT再用布尔条件同时剔除NaT和范围外数据。如果你研究的是“未发售游戏”就不要用dropna而是单独建一个未发售子集保留NaT这个信息本身。5.3 坑3评测数出现“10k”导致整列变成字符串现象review_count这一列dtype是object排序时“9999”出现在“10k”后面画图时报TypeError。原因生成CSV的脚本为了压缩存储空间把大数缩写成了“10k”“3.2k”这类可读格式。这在steam爬虫场景里很常见但CSV分析最怕这种半格式化字符串。解决先统计非数字行占比df[review_count].astype(str).str.contains(r[a-zA-Z]).sum()。如果占比小用pd.to_numeric(errorscoerce)转成数值后fillna(0)如果占比大需要find原始数字用正则把“10k”解析成10000。不要直接fillna(0)否则一款几万评测的游戏会被当成0后面加权评分完全失真。5.4 坑4低价位区域与价格归一化的真实性矛盾现象统计价格分位数时发现大量游戏价格集中在0.99美元附近原价却动辄9.99美元或者markdown后价格普遍低于原价的一半造成“全球游戏都在打三折”的假象。原因采集到的价格可能是区域性定价也可能采集时点正好赶上促销季。CSV里只有一个price字段没有币种信息无法判断是美元还是其他货币。解决不要用price直接做跨国比较先用分组统计确认价格整体水平再看price和original_price的比值。如果超过一半条目都处于打折状态说明快照时点接近促销活动这份数据只代表“采集日的价格”不代表全年价格水平。在结论里注明这一点比硬着头皮继续建模要重要得多。5.5 坑5同名游戏不是重复条目appid才是唯一键现象某款游戏在2021年和2024年各出现一次名称相同但价格和好评率完全不同。如果误用drop_duplicates(subsetname)会把两个合法版本删掉一个。原因原版、重制版、年度版、捆绑包拆出项经常共用相似的名称但Steam为它们分配了不同appid。名称是给人看的appid才是系统主键。解决去重时始终用subsetappid需要同时保留多个版本时干脆不去重。要记住“65k独立条目”的独立一词指的就是appid维度不是名称维度。如果非要用名称去重至少先检查df.groupby(name)[appid].nunique().max()看最大名称下挂了几个appid再决定策略。6. 进阶用法用Steam Web API回补CSV之外的特征10个特征能完成中粒度分析但推荐系统或内容分析往往还需要语言、标签、操作系统支持、最低配置甚至评论正文。常见做法是拿现有CSV里的appid当作输入列表用Steam Web API逐个回补详情。这个方向有一个前提你手上的这65k个appid就是唯一的查询字典。回补脚本的基本结构是读取清洗后的CSV取出appid列表对每个id请求一次详情接口把返回的JSON里的关键字段存成新行最后合并回原始DataFrame。注意Steam的接口对高频请求有限流机制并发太高会返回空结果或者错误码。我一般会循环里加0.5到1秒的间隔并设置一个最大重试次数避免一次性请求65k次直接把会话打挂。import time import requests # 从清洗后的CSV里取appid作为增量抓取的输入 ids df[appid].tolist() details [] for appid in ids[:10]: # 先跑前10个做连通性验证 url https://store.steampowered.com/api/appdetails?appids str(appid) try: r requests.get(url, timeout10) data r.json() details.append({ appid: appid, is_free: data.get(str(appid), {}).get(data, {}).get(is_free, False), languages: data.get(str(appid), {}).get(data, {}).get(languages, None), }) except requests.exceptions.RequestException: details.append({appid: appid, is_free: None, languages: None}) time.sleep(0.5) print(回补完成有效行数, sum(1 for d in details if d[is_free] is not None))这段代码不追求完整入库只演示连接模式和错误处理。真正跑65k条时还要加断点续传每处理1000个appid就保存一次局部结果避免中途网络波动导致全量重跑。我习惯把局部结果存成CSV文件命名带上批次号后面万一连接失败可以从最后一批继续不用重复请求。验证清洗结果这步也值得养成习惯。每次跑完脚本用assert把必须成立的条件写死assert df[appid].is_unique, appid存在重复 assert (df[price] 0).all(), 价格出现负值 assert df[release_date].notna().all(), 存在缺失发行日期 assert df[positive_ratio].between(0, 100).all(), 好评率越界这四个断言覆盖了这份CSV最常见的四个破坏点能防止你在后续特征工程中带着脏数据跑完整条链路。最后说一个我的个人习惯无论数据来自哪里先抽出20行人工看一遍再写任何统计逻辑。机器能处理规模和重复但只有人能发现“价格里混进了其他币种”或“好评率反了”这类语义错误。跑通脚本不是终点愿意回头检查数据语义才是让这份CSV真正产生价值的地方。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

3个避坑要点:seo团队管理系统报价全拆解 2026/9/27 0:36:15

3个避坑要点:seo团队管理系统报价全拆解

3个避坑要点:seo团队管理系统报价全拆解 备案流程一头雾水,卡在工信部ICP备案系统那一步,项目进度直接停摆?这种场景我见得太多了。很多老板找外包做seo团队管理系统,前期聊得火热,一谈到费用就变脸,要么报价低得离谱,要么后期增项多到让你…

阅读更多 →
wordpress建站百度网盘一文搞懂 2026/9/27 0:36:03

wordpress建站百度网盘一文搞懂

5步搞定WordPress建站资源,揭秘真实建站报价单 网站做好了没人访问?这确实是很多老板和开发者踩过的最大坑。我见过太多花大价钱做的精美官网,上线三个月流量还是个位数,根本带不来询盘。这时候大家往往只盯着 建站报价…

阅读更多 →
ASP做登入网站一文搞懂从0到1实战指南 2026/9/27 0:35:36

ASP做登入网站一文搞懂从0到1实战指南

ASP做登入网站一文搞懂从0到1实战指南 自己不会代码想做网站,是不是觉得登录模块就是填个框输个密码?别被表象骗了。很多初学者以为 ASP 登录就是写个…

阅读更多 →
wordpress+后门检查常见报错与解决 2026/9/27 0:35:24

wordpress+后门检查常见报错与解决

2026最新wordpress后门检查实战:3步揪出隐形木马 网站突然被挂马,首页变成博彩广告,后台密码改不了?别慌,这是很多站长最头疼的噩梦。尤其是使用 WordPress…

阅读更多 →
手机qq插件wordpress怎么装不卡顿?实测3个方案看多少钱 2026/9/27 0:35:04

手机qq插件wordpress怎么装不卡顿?实测3个方案看多少钱

手机qq插件wordpress怎么装不卡顿?实测3个方案看多少钱 改个需求建站公司拖一周,这种憋屈事儿谁没遇见过?很多站长朋友为了省事,想着装个“手机QQ插件”就能自动回复、引流或者做点自动化操作,结果一搜发现,要么插件老旧报错,要么被Wo…

阅读更多 →
JSP+Servlet商城系统全解析:从数据库设计到部署避坑指南 2026/9/27 0:34:19

JSP+Servlet商城系统全解析:从数据库设计到部署避坑指南

简介:面向毕业设计场景的Java Web家用电器购物商城系统,基于JSP、Servlet、JDBC搭建,配套MySQL数据库,适合需要快速掌握传统Java Web开发全流程的本科生或开发者。系统围绕管理员和用户双角色设计:管理员可维护商品信息…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉