好多数据集都含缺失数据。缺失数据有多重表现形式
- 数据库中,缺失数据表示为
NULL
- 在某些编程语言中用
NA
或None
表示- 缺失值也可能是空字符串
''
或数值0
- 在Pandas中使用
NaN
表示缺失值
- Pandas中的NaN值来自NumPy库,NumPy中缺失值有几种表示形式:NaN,NAN,nan,他们都一样
from numpy import NaN,NAN,nan
print(NaN==True)
print(NaN==False)
print(NaN==0)
print(NaN=='')
print(NaN==None)
# 输出结果如下
False
False
False
False
False
print(NaN==NaN)
print(NaN==nan)
print(NaN==NAN)
print(nan==NAN)
# 输出结果如下
False
False
False
False
isnull/isna
方法,用于测试某个值是否为缺失值import pandas as pd
print(pd.isnull(NaN))
print(pd.isnull(nan))
print(pd.isnull(NAN))
# 输出结果如下
True
True
True
notnull/notna
方法也可以用于判断某个值是否为缺失值print(pd.notnull(NaN))
print(pd.notnull(42))
# 输出结果如下
False
True
加载数据时可以通过
keep_default_na
与na_values
指定加载数据时的缺失值
print(pd.read_csv('data/survey_visited.csv'))
# 输出结果如下
ident site dated
0 619 DR-1 1927-02-08
1 622 DR-1 1927-02-10
2 734 DR-3 1939-01-07
3 735 DR-3 1930-01-12
4 751 DR-3 1930-02-26
5 752 DR-3 NaN
6 837 MSK-4 1932-01-14
7 844 DR-1 1932-03-22
keep_default_na=False
参数加载数据,不显示默认缺失值print(pd.read_csv('data/survey_visited.csv', keep_default_na=False))
# 输出结果如下
ident site dated
0 619 DR-1 1927-02-08
1 622 DR-1 1927-02-10
2 734 DR-3 1939-01-07
3 735 DR-3 1930-01-12
4 751 DR-3 1930-02-26
5 752 DR-3
6 837 MSK-4 1932-01-14
7 844 DR-1 1932-03-22
na_values
参数加载数据,通过该参数指定我们认为的缺失值# 在这里我们通过na_values参数,指鹿为马
print(pd.read_csv('data/survey_visited.csv', na_values=["1927-02-08"], keep_default_na=False))
# 输出结果如下
ident site dated
0 619 DR-1 NaN
1 622 DR-1 1927-02-10
2 734 DR-3 1939-01-07
3 735 DR-3 1930-01-12
4 751 DR-3 1930-02-26
5 752 DR-3
6 837 MSK-4 1932-01-14
7 844 DR-1 1932-03-22
merge
、join
等操作时,也会产生缺失值,参数用法一致,这里不再累述此数据为泰坦尼克生存预测数据,Survived字段,代表该名乘客是否获救
train=pd.read_csv('data/titanic_train.csv')
print(train.shape)
train.head()
# 输出结果如下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5zZrnnSJ-1630413451390)(./img/空值处理-1.png)]
字段说明
比如此时我们想查看泰坦尼克号上获救与否的人数统计
# 对Survived列的值的数量分别求和
train['Survived'].value_counts()
# 输出结果如下
0 549
1 342
Name: Survived, dtype: int64
手写通用的缺失值统计函数
# 构造一个函数,传入dataframe,返回计算每一列数据缺失值数量和比例的df
def missing_values_table(df):
# 计算所有列的缺失值,返回每一列缺失值数量构成的series
mis_val = df.isnull().sum()
# 计算所有列缺失值比例,返回每一列缺失值数量占比的series
mis_val_percent = df.isnull().sum() / len(df) * 100
# 将结果拼接成dataframe
mis_val_table = pd.concat([mis_val, mis_val_percent], axis=1)
# 将列重命名
mis_val_table_ren_columns = mis_val_table.rename(
columns={0 :'缺失值', 1:'占比(%'}
)
# 排除缺失值为0的数据
# df的缺失值列(第1列)的值不为0
mis_val_table_ren_columns = mis_val_table_ren_columns[mis_val_table_ren_columns.iloc[:,0] != 0]
# 按照缺失值降序排列
mis_val_table_ren_columns = mis_val_table_ren_columns.sort_values('缺失值', ascending=False)
# 打印信息
print_info = '传入的数据集中共有{}列。\n其中{}列含有缺失值。'.format(
df.shape[1],
mis_val_table_ren_columns.shape[0]
)
print(print_info)
# 返回缺失值信息的dataframe
return mis_val_table_ren_columns
train_missing = missing_values_table(train)
train_missing
# 输出结果如下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VoHdnww4-1630413451392)(./img/缺失值处理-2.png)]
我们可以使用Missingno来对缺失值进行可视化
jupyter notebook
pip install missingno
jupyter notebook
,并运行全部代码missingno.bar(df)
函数查看数据集数据完整性import missingno as msno
msno.bar(train)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QoSYsLnX-1630413451393)(./img/缺失值处理-3.png)]
missingno.matrix
函数 提供了快速直观的查看缺失值的分布情况msno.matrix(train)
# 输出结果如下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0LGKDzAi-1630413451396)(./img/缺失值处理-4.png)]
# df.sample(n) # 随机取出n行数据,返回新的df
msno.matrix(train.sample(100))
# 输出结果如下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jn2euluv-1630413451398)(./img/缺失值处理-5.png)]
msno.heatmap(train)
# 输出结果如下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c5xLlgzW-1630413451399)(./img/缺失值处理-6.png)]
sorted = train.sort_values('Age')
msno.matrix(sorted)
# 输出结果如下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1W4hQ0IB-1630413451400)(./img/缺失值处理-7.png)]
删除缺失值:删除缺失值会损失信息,并不推荐删除,当缺失数据占比较低的时候,可以尝试使用删除缺失值
# 复制一份数据
train_1 = train.copy()
# 对Age列进行处理,空值就删除整行数据
train_1.dropna(subset=['Age'], how='any', inplace=True)
# 输出Age列缺失值的总数
print(train_1['Age'].isnull().sum())
# 图形化缺失值情况
msno.matrix(train_1)
# 输出结果如下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hMgOSekI-1630413451401)(./img/缺失值处理-8.png)]
# 函数用法
df.dropna(axis=0, how='any', subset=['列名',...], inplace=True, thresh=n)
# 参数解释
#可选参数subset,不与thresh参数一起使用
接收一个列表,列表中的元素为列名: 对特定的列进行缺失值删除处理
#可选参数thresh=n
参数值为int类型,按行去除NaN值,去除NaN值后该行剩余数值的数量(列数)大于等于n,便保留这一行
#可选参数 axis=0
0, or 'index':删除包含丢失值的行
1, or 'columns':删除包含丢失值的列
默认为0
#可选参数 how='any',与inplace=True参数一起使用
'any': 如果存在NA值,则删除该行或列
'all': 如果所有值都是NA,则删除该行或列
默认为'any'
#可选参数inplace=False
默认False,不在源文件上删除
inplce=True,在源文件上删除
df.drop(['列名',..], axis=1)
函数将指定列删除,但最好不要删除数据msno.matrix(train_1.drop(['Age'], axis=1))
# 输出结果如下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P8jRDGsJ-1630413451402)(./img/缺失值处理-9.png)]
填充缺失值(非时间序列数据):填充缺失值是指用一个估算的值来去替代缺失数
# 复制一个df
train_constant = train.copy()
# 将空值都填为0,inplace=True为必要参数
train_constant.fillna(0, inplace=True)
# 计算各列空值总数
print(train_constant.isnull().sum())
print('='*10)
#查看Cabin列值为0的总数
print(train_constant[train_constant['Cabin']==0].shape[0])
# 输出结果如下
PassengerId 0
Survived 0
Pclass 0
Name 0
Sex 0
Age 0
SibSp 0
Parch 0
Ticket 0
Fare 0
Cabin 0
Embarked 0
dtype: int64
==========
687
# 复制一个df
train_mean = train.copy()
# 对Age列的空值,用mean平均值来填充
train_mean['Age'].fillna(train_mean['Age'].mean(), inplace=True)
# 计算各列空值总数
print(train_constant.isnull().sum())
# 输出结果如下
PassengerId 0
Survived 0
Pclass 0
Name 0
Sex 0
Age 0
SibSp 0
Parch 0
Ticket 0
Fare 0
Cabin 687
Embarked 2
dtype: int64
city_day = pd.read_csv('data/city_day.csv',parse_dates=True,index_col='Date')
city_day_2 = city_day.copy()
msno.matrix(city_day_2)
# 输出结果如下图所示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V1dB3etz-1630413451402)(./img/缺失值处理-10.png)]
city_day2_missing = missing_values_table(city_day_2)
city_day2_missing
# 输出结果如下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-orUb1mXR-1630413451403)(./img/缺失值处理-11.png)]
# 抽样查看Xylene字段的数据缺失情况
city_day_2['Xylene'].sample(20)
# 输出结果如下
Date
2015-08-25 0.75
2019-02-04 NaN
2018-12-09 NaN
2020-03-18 NaN
2016-02-28 0.00
2016-07-17 0.19
2019-07-22 NaN
2018-01-19 NaN
2015-08-21 NaN
2017-10-18 NaN
2017-03-07 NaN
2019-12-29 NaN
2018-05-03 NaN
2019-11-26 NaN
2019-06-28 NaN
2020-04-12 NaN
2015-10-04 0.64
2016-07-07 4.74
2019-07-08 NaN
2019-03-11 NaN
Name: Xylene, dtype: float64
fillna
函数中的ffill
参数,用时间序列中空值的上一个非空值填充city_day_2.fillna(method='ffill', inplace=True)
msno.matrix(city_day_2)
# 输出结果如下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DjU89VQY-1630413451404)(./img/缺失值处理-12.png)]
fillna
函数中的bfill
参数,用时间序列中空值的下一个非空值填充city_day_2.fillna(method='bfill',inplace=True)
msno.matrix(city_day_2)
# 输出结果如下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ehqdSerb-1630413451405)(./img/缺失值处理-13.png)]
df.interpolate(limit_direction="both", inplace=True)
对缺失数据进行线性填充
# 为了演示效果,查看指定范围的Xylene列的数据
print(city_day['Xylene'][50:65])
print('='*10)
# 复制数据集df
city_day_3 = city_day.copy()
# 使用df.interpolate(limit_direction="both", inplace=True) 对缺失数据进行线性填充
city_day_3.interpolate(limit_direction="both", inplace=True)
# 再次查看
print(city_day_3['Xylene'][50:65])
# 输出结果如下
Date
2015-02-20 7.48
2015-02-21 15.44
2015-02-22 8.47
2015-02-23 28.46
2015-02-24 6.05
2015-02-25 0.81
2015-02-26 NaN
2015-02-27 NaN
2015-02-28 NaN
2015-03-01 1.32
2015-03-02 0.22
2015-03-03 2.25
2015-03-04 1.55
2015-03-05 4.13
2015-03-06 NaN
Name: Xylene, dtype: float64
==========
Date
2015-02-20 7.4800
2015-02-21 15.4400
2015-02-22 8.4700
2015-02-23 28.4600
2015-02-24 6.0500
2015-02-25 0.8100
2015-02-26 0.9375
2015-02-27 1.0650
2015-02-28 1.1925
2015-03-01 1.3200
2015-03-02 0.2200
2015-03-03 2.2500
2015-03-04 1.5500
2015-03-05 4.1300
2015-03-06 2.2600
Name: Xylene, dtype: float64
# 画图看的清楚
city_day_3['Xylene'][50:65].plot()
city_day['Xylene'][50:65].plot()
# 输出结果如下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VPz3aeZC-1630413451406)(./img/缺失值处理-14.png)]
数据中包含缺失值是很常见的情况,缺失值可能在很多环节产生(用户没填,程序错误,数据合并…)
pandas中用np.NaN 表示缺失值,通过pd.isnull()或者pd.notnull()来判断是否是缺失值
常用的缺失值处理方式包括