写在前面的话
你导出的CSV文件,是不是经常这样:
- 有的行只有5列,有的行有8列
- 姓名列里混着空格、emoji、特殊符号
- 日期格式乱七八糟——“2024-01-01”、“2024/01/01”、“2024年1月1日"全都有
- 价格列混着人民币符号”¥“和单位"元”
我遇到过一次,一个同事从Excel导出的CSV文件,有3列数据被塞进了一个单元格,用逗号分隔,但逗号本身也是数据的一部分——CSV被CSV了。
这种数据叫脏数据。不清洗,直接拿去分析,结果全是错的。
这篇文章不讲理论,直接上代码。5个步骤,帮你从一团糟的CSV变成干净可用的数据。
准备工作:你需要什么
我用 Python + pandas,因为:
- pandas 是数据分析的事实标准
- 数据清洗的80%操作都能在pandas里完成
- 代码可读性强,改改就能复用
如果你还没装:
pip install pandas openpyxl
第一步:读取CSV并了解数据全貌
很多新手直接pd.read_csv()就完了。别这样。先看看数据长什么样。
import pandas as pd
# 读取CSV,先只看前10行
df = pd.read_csv("data.csv", nrows=10)
# 快速诊断
print(f"数据形状: {df.shape}")
print(f"列名: {list(df.columns)}")
print(f"列类型:\n{df.dtypes}")
print(f"缺失值统计:\n{df.isnull().sum()}")
输出长这样:
数据形状: (10, 6)
列名: ['id', 'name', 'email', 'date', 'price', 'category']
列类型:
id int64
name object
email object
date object
price object
category object
缺失值统计:
id 0
name 1
email 2
date 0
price 3
category 0
看出来了:email有2个空值,price有3个空值,name有1个空值。
第二步:处理缺失值
缺失值不能直接删——除非缺的比例很低。一般超过5%的缺失,建议填充而不是删除。
# 先算一下缺失比例
missing_pct = df.isnull().sum() / len(df) * 100
print(missing_pct[missing_pct > 0])
处理方式按数据类型分:
| 数据类型 | 处理方式 | 适用场景 |
|---|---|---|
| 数值型 | 中位数填充 | 有极端值时用中位数,不用均值 |
| 分类列 | 众数填充 | 选出现频率最高的类别 |
| 文本列 | “Unknown"填充 | 或者留空字符串”" |
# 数值列用中位数填充
df['price'] = df['price'].fillna(df['price'].median())
# 分类列用众数填充
df['category'] = df['category'].fillna(df['category'].mode()[0])
# 文本列留空
df['name'] = df['name'].fillna('')
注意: 如果你的price列里有非数字字符(比如"¥100元"),需要先清洗再填充,否则会报错。
第三步:处理重复行
重复是CSV导出最常见的bug。特别是从多个系统导数据合并的时候。
# 看有多少重复
print(f"总行数: {len(df)}")
print(f"去重后: {df.drop_duplicates().shape[0]}")
print(f"重复行数: {len(df) - df.drop_duplicates().shape[0]}")
输出:
总行数: 5000
去重后: 4872
重复行数: 128
128条重复!如果拿去算月度报表,收入会虚高2.6%。
# 去重——保留第一条出现的数据
df = df.drop_duplicates()
# 如果只需要某些列作为去重依据
df = df.drop_duplicates(subset=['email', 'date'])
第四步:清洗文本列
这是最折腾的一步。CSV里的文本数据往往千奇百怪。
4.1 去除首尾空格
# 给所有字符串列去空格
for col in df.select_dtypes(include='object').columns:
df[col] = df[col].str.strip()
4.2 统一日期格式
# pandas的to_datetime能自动识别多种格式
df['date'] = pd.to_datetime(df['date'], errors='coerce')
# errors='coerce' 会把无法解析的变成 NaT(Not a Time)
print(df['date'].isnull().sum(), "行日期无法解析")
4.3 清洗价格列
# 去掉"¥"、"元"、"人民币"等非数字字符
import re
df['price'] = df['price'].astype(str).apply(
lambda x: re.sub(r'[^\d.]', '', x)
)
# 转成浮点数
df['price'] = pd.to_numeric(df['price'], errors='coerce')
4.4 处理大小写不一致
# 分类列统一小写
df['category'] = df['category'].str.lower().str.strip()
# 或者首字母大写
df['category'] = df['category'].str.title()
第五步:数据验证和导出
清洗完了别急着用。先验证一下。
# 检查还有没有null值
print("剩余缺失值:")
print(df.isnull().sum())
# 检查数值列范围
print("\n价格统计:")
print(df['price'].describe())
# 检查日期范围
print("\n日期范围:")
print(df['date'].min(), "至", df['date'].max())
验证通过,导出干净的CSV:
# 导出
df.to_csv("cleaned_data.csv", index=False)
# 或者导出为Excel(需要openpyxl)
df.to_excel("cleaned_data.xlsx", index=False)
完整代码模板
把上面所有步骤串起来,一份可以直接复用的模板:
import pandas as pd
import re
# 1. 读取
df = pd.read_csv("raw_data.csv")
# 2. 去重
df = df.drop_duplicates()
# 3. 填充缺失值
for col in df.select_dtypes(include='number').columns:
df[col] = df[col].fillna(df[col].median())
df = df.fillna('')
# 4. 清洗文本
for col in df.select_dtypes(include='object').columns:
df[col] = df[col].astype(str).str.strip()
# 5. 日期格式化
if 'date' in df.columns:
df['date'] = pd.to_datetime(df['date'], errors='coerce')
# 6. 导出
df.to_csv("cleaned_data.csv", index=False)
print("✅ 清洗完成,共", len(df), "行")
把这个脚本保存成clean_csv.py,以后有脏数据就跑一次。
进阶:用我站的在线工具配合
如果你的数据量不大(10万行以下),其实不需要写代码。
我站提供了几个可以直接在线使用的工具:
- CSV Viewer —— 粘贴或上传CSV文件,在线查看、排序、筛选
- JSON Formatter —— 如果CSV导出的是JSON格式,格式化一下再看
- Text Replacer —— 批量替换特定字符,比如去掉所有"¥“符号
- Text Deduplicator —— 快速去重
代码适合批量、自动化、大规模处理。在线工具适合快速看一眼、小批量处理、不想装环境的时候用。
总结
CSV数据清洗的核心思路就四句话:
- 先读后洗——先看数据全貌,再动手改
- 先数量后质量——去重和填缺失是优先级最高的
- 先整体后局部——统一格式化(日期、大小写)优于逐个字段改
- 洗了要验——导出前检查一遍缺失值和范围
按这个顺序走,90%的CSV脏数据都能搞定。
剩下的10%,通常是数据源本身就有问题——那种情况建议跟数据提供方聊,别自己在代码里疯狂打补丁。