Python数据清洗的实用方法与步骤
数据清洗是机器学习前的重要步骤,涵盖缺失值、异常值及不必要数据的处理。基于Kaggle房地产数据集,介绍了缺失数据的检测(热图、百分比列表、直方图)与处理方法(丢弃观察值或特征、中位数或众数填充、特殊值替换),异常值通过直方图、箱形图及描述统计识别,不必要数据包括低信息特征。
在机器学习或统计建模之前,数据清洗几乎是绕不开的一步。一堆杂乱无章的数据扔进模型,指望它输出有意义的结果,基本是天方夜谭。
所谓数据清洗,就是从记录集、表或数据库中检测并修正(或删除)受损、不准确记录的过程。它要把数据中不完善、不准确或不相关的部分揪出来,然后替换、修改或直接清除这些“脏”数据。定义听上去就不短,实际执行起来更是既枯燥又耗时。
为了让这个流程简单点,这里整理了一份在Python中执行数据清洗的完整分步指南。读完你就能掌握如何识别并处理以下几类问题:
- 缺失数据
- 不规则数据(异常值)
- 不必要数据:重复数据、复制数据等
- 不一致数据:大小写、地址等
我们选用的数据集来自Kaggle竞赛——Sberbank俄罗斯房地产价值预测项目(目标是预测俄罗斯房价波动)。当然,没有用全部数据,只抽取了其中一部分样本。在正式进入清洗流程之前,先看看数据长什么样。
# import packages
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
import matplotlib
plt.style.use('ggplot')
from matplotlib.pyplot import figure
%matplotlib inline
matplotlib.rcParams['figure.figsize'] = (12,8)
pd.options.mode.chained_assignment = None
# read the data
df = pd.read_csv('sberbank.csv')
# shape and data types of the data
print(df.shape)
print(df.dtypes)
# select numeric columns
df_numeric = df.select_dtypes(include=[np.number])
numeric_cols = df_numeric.columns.values
print(numeric_cols)
# select non numeric columns
df_non_numeric = df.select_dtypes(exclude=[np.number])
non_numeric_cols = df_non_numeric.columns.values
print(non_numeric_cols)
从输出可以看到,数据集共有30,471行、292列,同时能分辨出哪些特征是数值型、哪些是分类型——都是之后要考虑的信息。
接下来,我们对照这份“脏数据”检查清单,逐个击破。开始吧。
缺失数据
处理缺失值可以说是数据清洗里最棘手也最常见的环节。很多模型能容忍其他类型的数据问题,但面对缺失数据,绝大多数模型会直接“罢工”。
如何找出缺失数据?
这里介绍三种方法,帮你全面了解数据集中的缺失情况。
方法1:缺失数据热图
当特征数量不多时,热图是一种非常直观的可视化方式。
cols = df.columns[:30] # first 30 columns
colours = ['#000099', '#ffff00'] # specify the colours - yellow is missing. blue is not missing.
sns.heatmap(df[cols].isnull(), cmap=sns.color_palette(colours))
下图展示了前30个特征的缺失模式。横轴是特征名,纵轴是观察值/行数,黄色表示缺失,蓝色表示完整。比如,特征life_sq在多行中都有缺失值,而floor只在第7000行左右出现零星的缺失。

方法2:缺失数据百分比列表
当特征很多时,热图可能变得拥挤。这时可以列出每个特征的缺失百分比。
# if it's a larger dataset and the visualization takes too long can do this.
# % of missing.
for col in df.columns:
pct_missing = np.mean(df[col].isnull())
print('{} - {}%'.format(col, round(pct_missing*100)))
得到的结果能清晰看到每个特征的缺失比例:life_sq有21%缺失,而floor只有1%。这个列表可以说是热图的有效补充。

方法3:缺失数据直方图
当特征数量庞大时,还可以用直方图从观察值角度查看缺失模式。
# first create missing indicator for features with missing data
for col in df.columns:
missing = df[col].isnull()
num_missing = np.sum(missing)
if num_missing > 0:
print('created missing indicator for: {}'.format(col))
df['{}_ismissing'.format(col)] = missing
# then based on the indicator, plot the histogram of missing values
ismissing_cols = [col for col in df.columns if 'ismissing' in col]
df['num_missing'] = df[ismissing_cols].sum(axis=1)
df['num_missing'].value_counts().reset_index().sort_values(by='index').plot.bar(x='index', y='num_missing')
这张直方图能帮我们识别30,471行观察值中的缺失状况。例如,超过6000个观察值没有任何缺失,接近4000个观察值只有1个缺失值。

如何处理缺失数据?
这个问题没有统一的答案。必须针对具体的特征和数据集来决定最佳方式。下面是四种最常用的方法;如果情况复杂,可能需要创造性地使用更复杂的思路,比如对缺失数据本身进行建模。
解决方案1:丢弃观察值
统计学中这叫成列删除(listwise deletion),直接删除包含缺失值的整行观察值。只有当你能确信缺失数据不提供任何信息时,才建议使用。否则,考虑其他方案。
举例:从缺失直方图看到,只有少量观察值的缺失数量超过35。那么可以创建一个新数据集df_less_missing_rows,把缺失数多于35的行删掉。
# drop rows with a lot of missing values.
ind_missing = df[df['num_missing'] > 35].index
df_less_missing_rows = df.drop(ind_missing, axis=0)
解决方案2:丢弃特征
和上面类似,只有确认某个特征真的无法提供有用信息时才丢掉它。比如hospital_beds_raion缺失比例高达47%,可能直接扔掉更合适。
# hospital_beds_raion has a lot of missing.
# If we want to drop.
cols_to_drop = ['hospital_beds_raion']
df_less_hos_beds_raion = df.drop(cols_to_drop, axis=1)
解决方案3:填充缺失数据
对于数值特征,可以用同一特征其他非缺失值的平均值或中位数来替换缺失值。对于分类特征,常用众数(最频值)来填充。
以life_sq为例,用中位数填充:
# replace missing values with the median.
med = df['life_sq'].median()
print(med)
df['life_sq'] = df['life_sq'].fillna(med)
也可以一次性对所有数值特征应用同样的策略:
# impute the missing values and create the missing value indicator variables for each numeric column.
df_numeric = df.select_dtypes(include=[np.number])
numeric_cols = df_numeric.columns.values
for col in numeric_cols:
missing = df[col].isnull()
num_missing = np.sum(missing)
if num_missing > 0: # only do the imputation for the columns that ha ve missing values.
print('imputing missing values for: {}'.format(col))
df['{}_ismissing'.format(col)] = missing
med = df[col].median()
df[col] = df[col].fillna(med)

这个数据集里的分类特征很幸运没有缺失值。如果遇到,也可以统一用众数填充:
# impute the missing values and create the missing value indicator variables for each non-numeric column.
df_non_numeric = df.select_dtypes(exclude=[np.number])
non_numeric_cols = df_non_numeric.columns.values
for col in non_numeric_cols:
missing = df[col].isnull()
num_missing = np.sum(missing)
if num_missing > 0: # only do the imputation for the columns that ha ve missing values.
print('imputing missing values for: {}'.format(col))
df['{}_ismissing'.format(col)] = missing
top = df[col].describe()['top'] # impute with the most frequent value.
df[col] = df[col].fillna(top)
解决方案4:替换缺失值
对于分类特征,可以新增一个类别如_MISSING_。对于数值特征,可以用一个特殊值(比如-999)来替换。这样保留了缺失信息,让缺失本身也可能成为有价值的信号。
# categorical
df['sub_area'] = df['sub_area'].fillna('_MISSING_')
# numeric
df['life_sq'] = df['life_sq'].fillna(-999)
不规则数据(异常值)
异常值是指与其他观察值差异显著的数据点,可能是真正的极端值,也可能是录入错误。
如何找出异常值?
根据特征的类型(数值或分类),有不同的检测方法。
方法1:直方图/箱形图
对于数值特征,直方图和箱形图非常直接。下图是life_sq的直方图:
# histogram of life_sq.
df['life_sq'].hist(bins=100)
数据高度偏斜,很可能存在异常值。

再来看箱形图:
# box plot.
df.boxplot(column=['life_sq'])
从图上看,有一个大于7000的数值明显是异常值。

方法2:描述统计学
有时异常值太极端,箱形图可能显示不全。查看特征的描述性统计就能发现:life_sq的最大值是7478,而上四分位数只有43——7478无疑是个异常点。
df['life_sq'].describe()

方法3:条形图
对于分类特征,可以用条形图观察类别分布。比如ecology的分布看起来还算合理;但如果某个类别(比如“other”)只有一个值,那它就可能是异常值。
# bar chart - distribution of a categorical variable
df['ecology'].value_counts().plot.bar()

其他方法还包括散点图、z分数、聚类等,这里不一一展开。
如何处理异常值?
检测异常值不难,但如何处理高度依赖数据集和项目目标。和缺失值类似,无非是丢弃、修改或保留三种策略(可参考上一节的解决方案)。
不必要数据
处理完缺失值和异常值,接下来是不必要数据。这部分相对更直接——送入模型的所有数据都应该服务于项目目标,无法增加价值的数据就是不必要的。
这里介绍三种主要类型。
不必要数据类型1:信息不足/重复
有时候,一个特征不提供信息是因为它的大部分行值都相同。怎么找出来?可以创建一个列表,列出那些超过95%行都是相同值的特征。
num_rows = len(df.index)
low_information_cols = []
for col in df.columns:
cnts = df[col].value_counts(dropna=False)
top_pct = (cnts/num_rows).iloc[0]
if top_pct > 0.95:
low_information_cols.append(col)
print('{0}: {1:.5f}%'.format(col, top_pct*100))
print(cnts)
print()

对于这些特征,逐个确认它们是否真的有用,没用的就丢弃。
不必要数据类型2:不相关
再次强调:数据必须与问题相关。如果一个特征和项目目标毫无关系,就是不相关数据。
如何找出?手动浏览特征列表。比如“多伦多气温”这个特征,对俄罗斯房价预测项目毫无帮助。
如何处理?直接删除。
不必要数据类型3:复制
复制数据指存在完全重复或部分重复的观察值。主要有两种子类型。
基于所有特征的复制
这种复制很容易找——所有特征值完全相同。先暂时去掉唯一标识符id,然后用drop_duplicates()去重,对比前后行数即可知道有多少重复。
# we know that column 'id' is unique, but what if we drop it?
df_dedupped = df.drop('id', axis=1).drop_duplicates()
# there were duplicate rows
print(df.shape)
print(df_dedupped.shape)
结果显示有10行是完全重复的观察值。
处理方法:直接删除。
基于关键特征的复制
有时更好的做法是基于一组唯一标识符来检查。比如,两次房产交易在同一时间、相同使用面积、相同楼层、相同建造年份、相同房间数和相同价格,几乎不可能同时发生。我们可以用timestamp、full_sq、life_sq、floor、build_year、num_room、price_doc作为关键特征进行分组。
key = ['timestamp', 'full_sq', 'life_sq', 'floor', 'build_year', 'num_room', 'price_doc']
df.fillna(-999).groupby(key)['id'].count().sort_values(ascending=False).head(20)
基于这组关键特征,找到了16条复制数据。

处理方法:同样删除。
# drop duplicates based on an subset of variables.
key = ['timestamp', 'full_sq', 'life_sq', 'floor', 'build_year', 'num_room', 'price_doc']
df_dedupped2 = df.drop_duplicates(subset=key)
print(df.shape)
print(df_dedupped2.shape)
删除后得到新数据集df_dedupped2。

不一致数据
模型对数据的一致性也很敏感。需要用不同的方式去发现不一致,很大程度上依赖观察和经验——不存在一行代码就能跑出所有不一致问题的万能脚本。
下面介绍四种常见的不一致类型。
不一致数据类型1:大写
类别值中混用大小写是常见错误。Python对大小写敏感,这可能导致麻烦。
如何发现?看特征sub_area:
df['sub_area'].value_counts(dropna=False)
看起来地区名称挺标准化的。

但同一特征内可能有不一致的大小写——例如“Poselenie Sosenskoe”和“pOseleNie sosenskeo”实际指同一个地区。
如何处理?将所有字母统一转为小写(或大写):
# make everything lower case.
df['sub_area_lower'] = df['sub_area'].str.lower()
df['sub_area_lower'].value_counts(dropna=False)

不一致数据类型2:格式
另一个需要标准化的地方是数据格式,比如把字符串格式的日期转为DateTime格式。
如何发现?特征timestamp目前是字符串:
df

如何转换?使用pd.to_datetime,然后提取年份、月份等信息:
df['timestamp_dt'] = pd.to_datetime(df['timestamp'], format='%Y-%m-%d')
df['year'] = df['timestamp_dt'].dt.year
df['month'] = df['timestamp_dt'].dt.month
df['weekday'] = df['timestamp_dt'].dt.weekday
print(df['year'].value_counts(dropna=False))
print()
print(df['month'].value_counts(dropna=False))

不一致数据类型3:类别值
分类变量的取值数量有限,但拼写错误可能导致意外的值出现。
如何发现?需要人工观察。这个房地产数据集不存在这个问题,所以我们创建一个示例数据集来说明。假设城市变量应该有四个值:toronto、vancouver、montreal、calgary,但实际数据中间出现了“torontoo”、“tronto”、“vancover”等错误拼写。
一种简单的识别方式是模糊逻辑(编辑距离),衡量一个词变成另一个词需要修改的字母数。与正确词距离越小,越可能是拼写错误。
from nltk.metrics import edit_distance
df_city_ex = pd.DataFrame(data={'city': ['torontoo', 'toronto', 'tronto', 'vancouver', 'vancover', 'vancouvr', 'montreal', 'calgary']})
df_city_ex['city_distance_toronto'] = df_city_ex['city'].map(lambda x: edit_distance(x, 'toronto'))
df_city_ex['city_distance_vancouver'] = df_city_ex['city'].map(lambda x: edit_distance(x, 'vancouver'))
df_city_ex

如何处理?设定一个阈值,比如距离≤2的视为拼写错误,统一修正:
msk = df_city_ex['city_distance_toronto'] <= 2
df_city_ex.loc[msk, 'city'] = 'toronto'
msk = df_city_ex['city_distance_vancouver'] <= 2
df_city_ex.loc[msk, 'city'] = 'vancouver'
df_city_ex

不一致数据类型4:地址
地址数据是很多人头疼的老问题。通常人们输入地址时不会遵循统一格式。
如何发现?直接浏览就能看出混乱。即便看不出,也可以运行标准化代码。这个房地产数据集不含地址列,所以创建一个示例:
# no address column in the housing dataset. So create one to show the code.
df_add_ex = pd.DataFrame(['123 MAIN St Apartment 15', '123 Main Street Apt 12 ', '543 FirSt A v', ' 876 FIRst A ve.'], columns=['address'])
df_add_ex
地址看起来相当混乱。

如何处理?把字母转为小写,去除首尾空格,去掉句点,并将常用词标准化:
df_add_ex['address_std'] = df_add_ex['address'].str.lower()
df_add_ex['address_std'] = df_add_ex['address_std'].str.strip() # remove leading and trailing whitespace.
df_add_ex['address_std'] = df_add_ex['address_std'].str.replace('.', '') # remove period.
df_add_ex['address_std'] = df_add_ex['address_std'].str.replace('bstreetb', 'st') # replace street with st.
df_add_ex['address_std'] = df_add_ex['address_std'].str.replace('bapartmentb', 'apt') # replace apartment with apt.
df_add_ex['address_std'] = df_add_ex['address_std'].str.replace('ba vb', 'a ve') # replace apartment with apt.
df_add_ex
现在看起来干净多了。

好了,数据清洗的旅程到此结束。现在你可以用文章里介绍的方法,清洗掉所有阻碍模型训练的“脏”数据了。
你是一名 AI 行业编辑,请围绕下面这条热点输出一份资讯解读:
热点:Python数据清洗的实用方法与步骤要求:
1. 先用一句话解释这条热点在讲什么
2. 再总结它为什么重要
3. 说明会影响哪些 AI 产品或内容方向
4. 最后给出 3 个适合资讯站使用的标题
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
相关热点WordPress生态里从来不缺各种各样的插件和主题,但真正把焦点放在低成本营销工具上的,其实不算多。wptao就是专门做这件事的——它专注WordPress插件与主题开发,提供的工具包括WordPress连接微博、微信机器人、淘宝客插件等。对于需要轻量级获客方案的用户来说,这类产品倒是很对胃口。
360站长平台助力网站运营者监控360搜索抓取记录、收录数量及问题页面,同时提供搜索优化知识与实用建议,便于边用边学,提升站点表现。
JobGenie面向求职者与招聘方,智能生成基于职位描述的个性化面试问题,并能快速创建全面职位描述,旨在通过AI自动化解放人力,让招聘者聚焦核心判断,大幅提升招聘与面试效率。
百度站长社区是官方为站长搭建的学习交流平台,提供权威运营技巧、算法解读与行业经验分享,内容涵盖收录、优化等日常问题,信息实用接地气,旨在帮助站长解决站点优化难题,提升网站表现,获取官方一手资讯。
- 日榜
- 周榜
- 月榜
热点快看
