2020年我刚入门数据科学时,Pandas 就是最热门的工具之一。尽管如今涌现出许多新型工具,专门弥补 Pandas 在超大数据集处理上的短板,但在数据清洗、数据处理、数据分析等常规工作中,我依然首选 Pandas。诚然,面对数十亿行的海量数据时,Pandas 会显得吃力,但对于其余所有场景,它的性能完全绰绰有余。
Pandas 的应用场景早已不局限于探索性数据分析(EDA)或交互式笔记开发,如今也广泛落地于各类生产系统中。
本文将通过一套完整的数据清洗与处理实战案例,带大家直观感受 Pandas 强大的功能能力。
本次实战所用数据集包含库存单元(SKU)数据,以及对应 SKU 的搜索接口返回结果。
import pandas as pd
search_results = pd.read_csv("search_results.csv")
search_results.head()
数据集内的搜索结果看似是字典列表格式,实际存储为纯字符串,单条数据示例如下:
search_results.loc[0, "search_result"]
输出结果:
"\[\{'my\_id': 'HBCV00007F5Y2B', 'distance': 1\.0, 'entity': \{\}\},
\{'my\_id': 'HBCV00007UPQBM', 'distance': 1\.0, 'entity': \{\}\},
\{'my\_id': 'HBCV00008I29IH', 'distance': 1\.0, 'entity': \{\}\},
\{'my\_id': 'HBCV00006U3ZYB', 'distance': 0\.8961254358291626, 'entity': \{\}\},
\{'my\_id': 'HBCV0000AFA4H6', 'distance': 0\.8702399730682373, 'entity': \{\}\},
\{'my\_id': 'HBCV00009CDGD4', 'distance': 0\.86175537109375, 'entity': \{\}\},
\{'my\_id': 'HBCV000046336T', 'distance': 0\.8594968318939209, 'entity': \{\}\},
\{'my\_id': 'HBCV00009QDZRT', 'distance': 0\.8572311997413635, 'entity': \{\}\},
\{'my\_id': 'HBCV00008E11P3', 'distance': 0\.8553324937820435, 'entity': \{\}\},
\{'my\_id': 'HBV00000C4IY6', 'distance': 0\.8539167642593384, 'entity': \{\}\}\]
\.\.\. and 5 entities remaining"
可以看到,数据存在明显问题:末尾带有 ... and 5 entities remaining 多余后缀,且整体是纯字符串格式,无法直接用于数据分析。
为了正常使用数据,需要将其转换为标准的字典列表格式。首先通过字符串分割,截取省略号前的有效内容:
search_results.loc[0, "search_result"].split("...")[0].strip()
执行后虽然剔除了多余后缀,但结果依旧是字符串。此时可借助 Python 内置的 ast 模块,将字符串转为标准列表对象:
import ast
res = ast.literal_eval(search_results.loc[0, "search_result"].split("...")[0].strip())
res
转换后标准字典列表结果:
[{'my_id': 'HBCV00007F5Y2B', 'distance': 1.0, 'entity': {}},
{'my_id': 'HBCV00007UPQBM', 'distance': 1.0, 'entity': {}},
{'my_id': 'HBCV00008I29IH', 'distance': 1.0, 'entity': {}},
{'my_id': 'HBCV00006U3ZYB', 'distance': 0.8961254358291626, 'entity': {}},
{'my_id': 'HBCV0000AFA4H6', 'distance': 0.8702399730682373, 'entity': {}},
{'my_id': 'HBCV00009CDGD4', 'distance': 0.86175537109375, 'entity': {}},
{'my_id': 'HBCV000046336T', 'distance': 0.8594968318939209, 'entity': {}},
{'my_id': 'HBCV00009QDZRT', 'distance': 0.8572311997413635, 'entity': {}},
{'my_id': 'HBCV00008E11P3', 'distance': 0.8553324937820435, 'entity': {}},
{'my_id': 'HBV00000C4IY6', 'distance': 0.8539167642593384, 'entity': {}}]
至此,单行数据已完成标准化转换。接下来需要将该逻辑批量应用到整张数据表的所有 SKU 行。
最基础的实现方式是通过 for 循环遍历所有行,但这种方式效率极低。Pandas 最优实践是向量化操作,即一次性对所有行执行批量运算,大幅提升处理效率。
单行使用的字符串分割法无法适配向量化批量处理,因此采用兼容性更强的正则表达式批量清洗多余后缀:
search_results.loc[:, 'search_result'] = search_results['search_result'].str.replace(r"\.\.\..*", "", regex=True).str.strip()
该代码会批量清除所有行中 ... 及其后面的冗余文本,让整列数据统一为干净的字典列表字符串格式。查看任意行数据验证效果:
search_results.loc[10, "search_result"]
清洗后输出:
"\[\{'my\_id': 'HBCV00007F5Y2B', 'distance': 1\.0, 'entity': \{\}\},
\{'my\_id': 'HBCV00007UPQBM', 'distance': 1\.0, 'entity': \{\}\},
\{'my\_id': 'HBCV00008I29IH', 'distance': 1\.0, 'entity': \{\}\},
\{'my\_id': 'HBCV00006U3ZYB', 'distance': 0\.8961254358291626, 'entity': \{\}\},
\{'my\_id': 'HBCV0000AFA4H6', 'distance': 0\.8702399730682373, 'entity': \{\}\},
\{'my\_id': 'HBCV00009CDGD4', 'distance': 0\.86175537109375, 'entity': \{\}\},
\{'my\_id': 'HBCV000046336T', 'distance': 0\.8594968318939209, 'entity': \{\}\},
\{'my\_id': 'HBCV00009QDZRT', 'distance': 0\.8572311997413635, 'entity': \{\}\},
\{'my\_id': 'HBCV00008E11P3', 'distance': 0\.8553324937820435, 'entity': \{\}\},
\{'my\_id': 'HBV00000C4IY6', 'distance': 0\.8539167642593384, 'entity': \{\}\}\]"
此时数据仍为字符串类型,后续将通过 ast 模块批量转换为列表格式。
本次分析的核心目标是提取搜索结果中的 SKU 编码,对应字典中的 my_id 字段。整体提取逻辑分为三步:
通过 literal_eval 将字符串转为字典列表;
遍历列表,提取每个字典的 my_id 字段值(即 SKU);
通过 lambda 函数批量应用上述逻辑,生成 result_skus 新列:
search_results.loc[:, "result_skus"] = \
search_results["search_result"].apply(lambda x: [item['my_id'] for item in ast.literal_eval(x)])
search_results.head()
执行后,result_skus 列的每一行均包含10个搜索匹配的 SKU 编码。
接下来进行数据维度转换:将单行内的10个 SKU 列表,拆分为独立的10行数据。Pandas 提供极简的explode 函数实现该行拆分需求:
data = search_results[["sku", "result_skus"]].explode("result_skus", ignore_index=True)
data.head()
执行后即可实现「一个原始SKU对应多条匹配结果」的长表结构。
对应反向操作:若需将拆分后的多行数据,重新聚合为单行存储所有匹配 SKU,可通过 groupby 分组聚合实现:
new_data = data.groupby("sku", as_index=False)["result_skus"].apply(list)
new_data.head()
该操作可还原为拆分前的列表存储结构。
除了拆分为多行,日常分析中还常需要将列表数据拆分为多列(10个 SKU 对应10个独立字段)。
常规实现方式(非向量化,效率较低):
new_cols = new_data["result_skus"].apply(pd.Series)
new_data = pd.concat([new_data, new_cols], axis=1)
new_data.head()
上述 apply 方式不属于向量化操作,数据量大时运行缓慢。推荐高性能向量化方案,运行效率大幅提升:
new_cols = pd.Datafr ame(new_data["result_skus"].tolist())
new_data = pd.concat([new_data, new_cols], axis=1)
new_data.head()
两种代码的输出结果完全一致,但第二种向量化写法的执行速度远优于前者。
以上案例完整演示了数据分析师、数据科学家日常高频的清洗与处理流程。从业5年多以来,除了数十亿行级别的超大数据集场景,Pandas 完全可以满足绝大多数数据处理、分析需求。
即便在超大数据场景下,替代工具的语法也与 Pandas 高度相通:PySpark 融合了 Pandas 与 SQL 语法,Polars 的语法几乎与 Pandas 完全一致。因此,熟练掌握 Pandas,依旧是数据科学与 AI 领域从业者极具价值的核心技能。
感谢阅读!
推荐学习书籍 《CDA一级教材》适合CDA一级考生备考,也适合业务及数据分析岗位的从业者提升自我。完整电子版已上线CDA网校,累计已有10万+在读~ !