4. 数据采集与清洗:从CSV/Excel导入材料数据,处理缺失值、重复值、异常值,数据类型转换
做材料数据管理,有个绕不开的坎——数据清洗。我见过太多人,花80%的时间在整理数据,只有20%的时间在做分析。说白了,数据科学家不是搞科研的,是搞卫生的。今天咱们就来聊聊,怎么把那些乱七八糟的CSV和Excel文件,变成能用的干净数据。
核心观点:数据清洗不是苦力活,是技术活。掌握好方法,能省下大把时间。
4.1 从CSV/Excel导入材料数据
先说说导入。Python里最常用的就是pandas库。我个人习惯用pd.read_csv()和pd.read_excel()。但这里有个坑——编码问题。
我在项目中遇到过,从某材料数据库导出的CSV文件,用UTF-8打开全是乱码。后来发现是GBK编码。所以导入时,我建议先试试encoding='utf-8',不行就换encoding='gbk'或encoding='latin1'。
import pandas as pd
# 导入CSV文件
df_csv = pd.read_csv('material_data.csv', encoding='utf-8')
# 导入Excel文件
df_excel = pd.read_excel('material_data.xlsx', sheet_name='Sheet1')
# 查看前5行
print(df_csv.head())
你想想看,如果文件里有特殊字符,比如材料名称带希腊字母(α、β),那就要注意encoding参数了。我一般会先打印df.columns看看列名对不对。
4.2 处理缺失值
缺失值,说白了就是数据里空着的地方。材料数据里常见的情况:某个样品的密度没测、某个成分的含量没记录。嗯,这里要注意,缺失值不能直接忽略,得处理。
常用的方法有三种:
- 删除法:直接删掉有缺失的行或列。适合缺失比例很小的情况。
- 填充法:用均值、中位数、众数填充。我习惯用中位数,因为它不受极端值影响。
- 插值法:用前后值进行线性插值。适合时间序列数据。
# 查看缺失值情况
print(df.isnull().sum())
# 删除含有缺失值的行
df_clean = df.dropna()
# 用中位数填充缺失值
df['density'].fillna(df['density'].median(), inplace=True)
# 向前填充(用上一个值填充)
df['temperature'].fillna(method='ffill', inplace=True)
小技巧:我曾经处理过一批合金数据,缺失值集中在某个批次。后来发现是实验员忘记记录了。这种情况下,用均值填充反而会引入偏差。我建议先查查缺失值是不是有规律。
4.3 处理重复值
重复值,就是一模一样的数据出现了多次。材料数据里常见的情况:同一个样品被录入了两次,或者不同来源的数据合并时产生了重复。
为什么会这样?我遇到过,某课题组把实验数据手动录入Excel,结果有人复制粘贴了两次。嗯,这种情况很常见。
# 查看重复行
print(df.duplicated().sum())
# 删除重复行(保留第一个)
df_unique = df.drop_duplicates()
# 按指定列判断重复
df_unique = df.drop_duplicates(subset=['sample_id', 'batch'])
我个人习惯,在删除重复值之前,先看看重复的数据长什么样。有时候重复数据里藏着信息——比如同一个样品在不同时间测了两次,那就不该删,而是保留。
4.4 处理异常值
异常值,就是那些明显不合理的数据。比如材料的密度是负数,或者温度达到了10000°C。说白了,就是数据出错了。
我常用的方法:
- 3σ原则:数据在均值±3个标准差之外,视为异常。
- 箱线图法:用四分位数判断,超出1.5倍IQR的视为异常。
- 业务规则:根据材料常识判断。比如钢铁的熔点不会超过2000°C。
# 用3σ原则检测异常值
mean = df['density'].mean()
std = df['density'].std()
df_outlier = df[(df['density'] > mean - 3*std) & (df['density'] < mean + 3*std)]
# 用箱线图法
Q1 = df['density'].quantile(0.25)
Q3 = df['density'].quantile(0.75)
IQR = Q3 - Q1
df_clean = df[(df['density'] > Q1 - 1.5*IQR) & (df['density'] < Q3 + 1.5*IQR)]
注意:我曾经处理过一批高温合金数据,用3σ原则删掉了一批数据,结果发现那些正是性能最优异的样品。所以,异常值不一定是错误,也可能是新发现。处理前一定要结合业务知识判断。
4.5 数据类型转换
数据类型转换,就是把数据变成正确的格式。材料数据里常见的问题:
- 数字被存成了字符串(比如'123.45')
- 日期格式不统一(比如'2023-01-01'和'01/01/2023'混在一起)
- 分类数据需要变成数值(比如'FCC'、'BCC'转成0、1)
# 查看数据类型
print(df.dtypes)
# 字符串转数值
df['density'] = pd.to_numeric(df['density'], errors='coerce')
# 日期格式统一
df['date'] = pd.to_datetime(df['date'], format='%Y-%m-%d')
# 分类数据编码
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
df['crystal_structure'] = le.fit_transform(df['crystal_structure'])
你想想看,如果密度列里混入了字母,pd.to_numeric会把它变成NaN。这时候就要回头看看原始数据,是不是录入错了。
4.6 数据清洗流程总结
说了这么多,咱们来画个流程图,把整个清洗流程串起来。
这张图把整个流程串起来了。从导入数据开始,依次处理缺失值、重复值、异常值,最后做数据类型转换。每一步都有对应的处理方法,我建议你按照这个顺序来,不容易漏掉。
4.7 实战案例:处理合金数据
最后,咱们来个完整的例子。假设有一份合金数据,包含密度、硬度、成分等信息。
import pandas as pd
import numpy as np
# 1. 导入数据
df = pd.read_csv('alloy_data.csv')
# 2. 查看基本信息
print('数据形状:', df.shape)
print('缺失值统计:\n', df.isnull().sum())
print('数据类型:\n', df.dtypes)
# 3. 处理缺失值
df['density'].fillna(df['density'].median(), inplace=True)
df['hardness'].fillna(df['hardness'].mean(), inplace=True)
# 4. 处理重复值
df.drop_duplicates(subset=['sample_id'], inplace=True)
# 5. 处理异常值(用箱线图法)
Q1 = df['density'].quantile(0.25)
Q3 = df['density'].quantile(0.75)
IQR = Q3 - Q1
df = df[(df['density'] > Q1 - 1.5*IQR) & (df['density'] < Q3 + 1.5*IQR)]
# 6. 数据类型转换
df['composition'] = df['composition'].astype(str)
df['date'] = pd.to_datetime(df['date'])
print('清洗完成!最终数据形状:', df.shape)
避坑指南:我曾经在处理一批数据时,直接用了dropna()删掉了所有缺失行,结果发现数据量少了三分之一。后来才意识到,有些缺失值是可以填充的。所以,我建议你先看看缺失比例,再决定用哪种方法。
好了,数据清洗这部分就聊到这儿。记住,干净的数据是分析的基础。你花在清洗上的时间,会在后续分析中加倍回报给你。