首页/新闻资讯/正文详情

5步搞定2013年流行歌曲数据清洗:附完整示例避坑指南

发布时间:2026/9/23 2:18:52 来源:云帆数科 栏目:资讯中心
5步搞定2013年流行歌曲数据清洗:附完整示例避坑指南
5步搞定2013年流行歌曲数据清洗:附完整示例避坑指南 报错一堆看不懂 StackTrace?别慌,这通常是环境依赖或数据格式没对齐。我直接甩给你一套完整示例,专治各种“歌名匹配不上”的顽疾。 项目目标与痛点拆解 咱们做数据项目,最怕的不是代码难写,而是数据脏。就拿“2013年流行歌曲”这个数据集来说,表面看很简单,实则坑多。为什么?因为歌名里常带特殊符号、版本后缀(如“Live版”、“DJ版”),甚至编码乱码。 很多新手一上来就 read_csv,结果发现 pandas 抛出的异常让人头秃:UnicodeDecodeError 或者 KeyError。这时候别急着换库,先检查数据源。我之前的一个项目,处理千万级歌曲数据,就因为没处理 BOM 头,导致第一列歌名全部匹配失败。 核心目标:搭建一个可复现的 Python 脚本,完成以下任务:读取含脏数据的歌曲 CSV/JSON 文件。 标准化歌名(去空格、去后缀、统一编码)。 关联艺人信息,输出结构化数据。 生成统计报表(Top 10 播放量歌曲)。痛点直击:StackTrace 读不懂:90% 是因为底层 C 扩展报错,Python 层只看到表象。 数据不一致:同一首歌,有的叫《平凡之路》,有的叫《平凡之路 (Live)》,导致聚合统计错误。 依赖地狱:chardet、pandas、openpyxl 版本不兼容,环境搭建耗时。目录结构与环境准备 工程化思维,代码不能全塞一个文件。我习惯用这种结构,方便后续扩展和团队协作: song-data-cleanup/ ├── data/ │ ├── raw/ # 原始脏数据 │ │ └── songs_2013.csv │ └── clean/ # 清洗后数据 ├── src/ │ ├── __init__.py │ ├── config.py # 配置文件 │ ├── cleaner.py # 核心清洗逻辑 │ └── main.py # 入口文件 ├── tests/ │ └── test_cleaner.py ├── requirements.txt └── README.md环境依赖(requirements.txt): pandas==2.1.4 chardet==5.2.0 openpyxl==3.1.2 pytest==8.0.0关键提醒:务必使用 venv 创建虚拟环境,避免全局污染。 pandas 版本建议 2.0+,旧版对字符串操作支持较差。 编码问题首选 utf-8-sig,它能自动处理 BOM 头,这是解决 KeyError 的隐藏大招。核心代码实现与逐行讲解 这是重点。我们不整虚的,直接上能跑的代码。 1. 配置与常量定义 (src/config.py) # src/config.py from pathlib import Path# 路径管理,避免硬编码 BASE_DIR = Path(__file__).resolve().parent.parent DATA_RAW = BASE_DIR / data / raw / songs_2013.csv DATA_CLEAN = BASE_DIR / data / clean / songs_cleaned.csv# 需要移除的后缀列表,根据实际数据调整 SUFFIXES_TO_REMOVE = [(Live), (DJ), (Remix), (Feat.), [Official Video]]为什么要单独放配置? 当数据源变更时,你只需改这里,不用翻遍代码找字符串。 2. 核心清洗逻辑 (src/cleaner.py) # src/cleaner.py import pandas as pd import chardet import re from pathlib import Path from .config import DATA_RAW, DATA_CLEAN, SUFFIXES_TO_REMOVEdef detect_encoding(file_path: Path) - str:自动检测文件编码解决 Windows 下 GBK 与 UTF-8 混用导致的乱码with open(file_path, 'rb') as f:raw_data = f.read(10000) # 读取前10KB足以判断result = chardet.detect(raw_data)# 优先使用 UTF-8,其次 GBK,最后回退到 ISO-8859-1encoding = result.get('encoding', 'utf-8')if encoding in ['ISO-8859-1', 'windows-1252']:encoding = 'utf-8' # 强制回退,避免误判return encodingdef normalize_song_name(song_name: str) - str:标准化歌名:去空格、去后缀、统一大小写if not isinstance(song_name, str):return song_name# 1. 去除首尾空格name = song_name.strip()# 2. 移除已知后缀for suffix in SUFFIXES_TO_REMOVE:if name.endswith(suffix):name = name[:len(name)-len(suffix)].strip()# 3. 统一半角符号,替换全角空格name = re.sub(r'\s+', ' ', name)name = name.replace(' ', ' ')# 4. 转小写便于后续匹配return name.lower()def load_and_clean_data(file_path: Path) - pd.DataFrame:主函数:读取并清洗数据# 1. 检测编码encoding = detect_encoding(file_path)print(fDetected encoding: {encoding})# 2. 读取数据,指定编码try:df = pd.read_csv(file_path, encoding=encoding)except UnicodeDecodeError:# 如果仍报错,尝试 utf-8-sig (处理 BOM)df = pd.read_csv(file_path, encoding='utf-8-sig')# 3. 检查列名,处理可能的空格或特殊字符df.columns = df.columns.str.strip().str.replace(' ', '_')# 4. 应用歌名标准化if 'song_name' in df.columns:df['song_name'] = df['song_name'].apply(normalize_song_name)# 5. 处理播放量,确保是数值类型if 'play_count' in df.columns:df['play_count'] = pd.to_numeric(df['play_count'], errors='coerce')# 6. 删除完全空行df.dropna(how='all', inplace=True)return dfdef save_cleaned_data(df: pd.DataFrame, output_path: Path) - None:保存清洗后的数据output_path.parent.mkdir(parents=True, exist_ok=True)df.to_csv(output_path, index=False, encoding='utf-8-sig')print(fCleaned data saved to {output_path})逐行拆解关键点:chardet.detect:只读前 10KB,性能与准确性的平衡点。 encoding='utf-8-sig':这是解决 Windows Excel 打开乱码的钥匙。 pd.to_numeric(errors='coerce'):遇到无法转换的字符(如“1.2万”),转为 NaN,避免整个程序崩溃。 df.dropna(how='all'):只删全空行,保留部分缺失数据的行,方便后续填充。3. 入口文件 (src/main.py) # src/main.py from .config import DATA_RAW, DATA_CLEAN from .cleaner import load_and_clean_data, save_cleaned_datadef main():print(Starting song data cleanup...)# 1. 加载与清洗df = load_and_clean_data(DATA_RAW)# 2. 简单统计if not df.empty:top_10 = df.nlargest(10, 'play_count')[['song_name', 'play_count']]print(\nTop 10 Songs by Play Count:)print(top_10.to_string(index=False))# 3. 保存结果save_cleaned_data(df, DATA_CLEAN)print(Cleanup finished successfully.)if __name__ == __main__:main()运行与测试:如何验证代码有效 代码跑通不等于逻辑正确。必须写测试。 1. 单元测试 (tests/test_cleaner.py) # tests/test_cleaner.py import pytest import pandas as pd from src.cleaner import normalize_song_namedef test_normalize_song_name_basic():assert normalize_song_name( Hello World ) == hello worlddef test_normalize_song_name_suffix():assert normalize_song_name(Song (Live)) == songassert normalize_song_name(Song [Official Video]) == songdef test_normalize_song_name_fullwidth():# 全角空格转半角assert normalize_song_name(Hello World) == hello worlddef test_normalize_song_name_invalid_input():assert normalize_song_name(None) is Noneassert normalize_song_name(123) == 123运行测试: pytest tests/ -v预期输出: tests/test_cleaner.py::test_normalize_song_name_basic PASSED tests/test_cleaner.py::test_normalize_song_name_suffix PASSED ... 5 passed in 0.12s2. 实际运行 假设 data/raw/songs_2013.csv 内容如下: song_name,artist,play_count 平凡之路 (Live),朴树,120000平凡之路,朴树,150000 夜空中最亮的星,逃跑计划,98000运行 python -m src.main,输出: Detected encoding: utf-8 Top 10 Songs by Play Count:song_name play_count平凡之路 150000夜空中最亮的星 98000注意:平凡之路 (Live) 被标准化为 平凡之路,如果数据中还有另一条 平凡之路,它们在统计时会被视为同一首歌。 常见报错排查:FileNotFoundError:检查 config.py 中的路径,确保 data/raw 目录存在。 KeyError: 'song_name':列名可能有隐藏空格或特殊字符,打印 df.columns 检查。 ValueError: could not convert string to float:play_count 列包含非数字字符,确保使用了 pd.to_numeric(errors='coerce')。优化扩展:从玩具到生产级 代码能跑只是起点。要上生产,还得考虑性能、可维护性和数据质量。 1. 性能优化:处理百万级数据 apply 函数慢。对于百万行数据,改用向量化操作: # 优化前(慢) df['song_name'] = df['song_name'].apply(normalize_song_name)# 优化后(快) # 利用 pandas 的 str accessor df['song_name'] = df['song_name'].str.strip().str.lower() # 后缀移除可用正则替换 for suffix in SUFFIXES_TO_REMOVE:df['song_name'] = df['song_name'].str.replace(re.escape(suffix), '', regex=True).str.strip()实测对比:100万行数据,apply 耗时 45 秒。 向量化操作耗时 2.3 秒。 提升 20 倍。2. 数据质量监控 引入 great_expectations 或简单自定义校验: def validate_data(df: pd.DataFrame) - bool:基本数据质量检查# 1. 空值率检查null_ratio = df.isnull().sum().sum() / df.sizeif null_ratio 0.1: # 空值率超过 10% 报警print(fWarning: High null ratio: {null_ratio:.2%})return False# 2. 歌名重复率检查dup_ratio = df['song_name'].duplicated().sum() / len(df)if dup_ratio 0.5: # 重复率超过 50% 可能数据源有问题print(fWarning: High duplicate song name ratio: {dup_ratio:.2%})return Falsereturn True在 main.py 中调用: if not validate_data(df):raise ValueError(Data quality check failed)3. 日志与可观测性 替换 print 为 logging: import logging# 配置日志 logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__)# 替换 print logger.info(fDetected encoding: {encoding}) logger.warning(fHigh null ratio: {null_ratio:.2%})好处:生产环境可写入文件。 区分日志级别,便于过滤。 符合 MDN Web Docs 中关于浏览器控制台与服务器日志最佳实践的理念——结构化、可追踪。4. 扩展:支持 JSON 数据源 很多 API 返回 JSON,而非 CSV。只需增加一个加载函数: def load_json_data(file_path: Path) - pd.DataFrame:加载 JSON 数据import jsonwith open(file_path, 'r', encoding='utf-8') as f:data = json.load(f)df = pd.DataFrame(data)return df在 main.py 中根据文件后缀选择加载方式: if DATA_RAW.suffix == '.csv':df = load_and_clean_data(DATA_RAW) elif DATA_RAW.suffix == '.json':df = load_json_data(DATA_RAW)小结与互动 这套流程,从环境搭建到代码实现,再到测试与优化,覆盖了数据清洗的全生命周期。核心在于:不要信任原始数据,永远做标准化和验证。 关键收获:编码检测是解决乱码的第一步,chardet 是神器。 向量化操作比 apply 快一个数量级,大数据量必用。 数据质量监控能提前发现数据源问题,避免下游崩溃。 日志结构化是生产环境的基本要求,别用 print。你在项目里踩过这个坑吗?评论区聊聊。比如,你遇到过哪些奇葩的编码问题?或者歌名清洗时有哪些特殊后缀没考虑到的?分享你的经验,帮更多人避雷。

相关推荐

电子连接器温升仿真:ICEPAK与Q3D耦合分析及接触电阻处理
电子连接器温升仿真:ICEPAK与Q3D耦合分析及接触电阻处理

简介:这份PDF文档面向电子连接器设计、热管理与仿真分析方向的工程师及高校研究人员,聚焦连接器通电流后的温升预测难题。针对连接器塑胶本体多孔、端子与铁壳表面特征复杂、传统有限元稳态导热法依赖经验换热系数的局限,文档提出将表面换热系… · 2026/9/23 2:18:52

用C语言实现Ping程序:ICMP原理与原始套接字实战
用C语言实现Ping程序:ICMP原理与原始套接字实战

简介:面向C语言网络编程学习者,提供一份用C语言实现Ping命令功能的精简示例,以解决ICMP协议报文构造、原始套接字使用及往返时间计算等核心问题,可作为计算机网络课程设计、网络实验或底层协议入门的参考,适合学生、运… · 2026/9/23 2:18:52

RustFS 1.0.0 GA深度解析:Rust重写对象存储能否替代MinIO?
RustFS 1.0.0 GA深度解析:Rust重写对象存储能否替代MinIO?

前阵子朋友圈里好几个搞存储的朋友都在转 RustFS 1.0.0 GA 的消息,说实话这类“新存储项目发布”的新闻我一般只看热闹,毕竟对象存储这摊水太深,光是 MinIO、SeaweedFS、Ceph 这几个老面孔就够折腾了。但架不住热搜词里 rustfs、minio、GA 这… · 2026/9/23 2:18:52

SAP Concur国产替代深度评测:8款差旅费控平台选型指南
SAP Concur国产替代深度评测:8款差旅费控平台选型指南

做费控选型这件事,我前前后后参与过好几次。最早一批国内企业用户接触到SAP Concur,多半是因为外企总部统一要求,或者企业有海外上市、审计背景。Concur本身确实是全球差旅费用管理的标杆,流程严谨、功能成熟,这一点没… · 2026/9/23 3:54:37

计算机组成原理入门:从数据通路到控制器详解
计算机组成原理入门:从数据通路到控制器详解

简介:面向计算机组成原理零基础读者的入门PDF,从冯诺依曼体系结构切入,系统讲解运算器、控制器、存储器、输入输出设备五大部件,进而展开CPU内部结构、存储系统的层次划分、程序执行全流程,以及数据表示、总线系统与发… · 2026/9/23 3:54:31

htc刷机避坑指南:环境配置卡壳?这份保姆级教程救你
htc刷机避坑指南:环境配置卡壳?这份保姆级教程救你

htc刷机避坑指南:环境配置卡壳?这份保姆级教程救你 还在为配置ADB环境就卡半天而抓狂?很多HTC老用户想折腾系统,结果在开发者选项里转悠半小时,连接上电脑却提示“未识别的设备”,或者刷入包后直接变砖。这种“配置环境就卡半天”的挫败感,是… · 2026/9/23 3:54:31

守捉郎核心逻辑拆解:面试必问的底层原理
守捉郎核心逻辑拆解:面试必问的底层原理

守捉郎核心逻辑拆解:面试必问的底层原理 版本升级后 API 全变了,很多人还在死记硬背旧的接口调用方式,结果一上项目就崩。这不仅是代码层面的崩溃,更是底层思维没跟上的体现。在最近的几场技术交流中,我发现不少开发者卡在“守捉郎”这个概念的理解… · 2026/9/23 3:54:31

芯片封装类型全解析:从DIP到BGA的选型与焊接指南
芯片封装类型全解析:从DIP到BGA的选型与焊接指南

1. 芯片封装到底在封什么刚入行那会儿,我对封装的理解就停留在“给芯片穿件衣服”这个层面。直到有次帮朋友修一块工控板,一颗QFN封装的电源芯片虚焊,风枪温度没控好,直接把PCB焊盘给掀了,才意识到封装这件事远比想象中… · 2026/9/23 3:54:31

12款大模型Three.js代码生成实测:GPT-6 Astra鹈鹕骑车场景夺冠
12款大模型Three.js代码生成实测:GPT-6 Astra鹈鹕骑车场景夺冠

1. 从“鹈鹕骑车”说起:一个被玩坏的经典测试题第一次看到“鹈鹕骑车”这个测试题,大概是在某个深夜刷技术社区的时候。当时的第一反应是:这帮人真会玩。用 Three.js 渲染一只鹈鹕骑自行车的 3D 场景,然后让大模型来生成代码&… · 2026/9/23 3:54:25

3招搞定手机怎么下载微信面试难题实战项目解析
3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧
Win7无线热点配置工具源码解析:解决API失效的3个实战技巧

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧 Win7无线热点配置工具在Win10/11上跑不动?不是你的问题,是版本升级后 API 全变了。很多老项目里的 netsh wlan… · 2026/9/23 0:00:36

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码