缺失数据和文本数据(尤其是混杂性文本)是数据预处理中比较麻烦的类型,本章节聚焦于缺失数据。
Nullable类型和String类型可能在未来成为Pandas的主流数据类型。
理论部分
练习部分
Pandas 1.0之前的版本:三种缺失符号
缺失符号 | np.nan | None | np.NaT |
---|---|---|---|
类型 | float64 | Nonetype | pandas._libs.tslibs.nattype.NaTType |
equal的时候是否会包括在内 | 不包括 | 包括 | 不包括 |
某列出现该值的类型 | 1.数值型,布尔型统一转换为float64;2.字符型统一转换为O(object类型) | 1.None传入数值型自动变换为np.nan;None传入时间型变量自动变换为np.NaT;2.None传入布尔类型,不改变原布尔列表;3.None传入字符型为NoneType | 时间型变量类型不改变 |
** Pandas 1.0之后的版本:Nullable类型与NA符号**
官方鼓励用户使用新的数据类型和缺失类型pd.NA
加号与乘号规则
groupby方法中的缺失值
问题一:如何删除缺失值占比超过25%的列?
//
import pandas as pd
import numpy as np
# 创建一个有不同缺失情况的dataframe对象
df_d = pd.DataFrame({'A':[np.nan,np.nan,np.nan,np.nan,np.nan,np.nan],'B':[np.nan,np.nan,np.nan,np.nan,np.nan,2],'C':[np.nan,np.nan,np.nan,np.nan,2,1],'D':[np.nan,np.nan,np.nan,3,2,1],'E':[np.nan,np.nan,4,3,2,1],'F':[np.nan,5,4,3,2,1],'G':[6,5,4,3,2,1]})
# 首先将其中所有全为缺失值的列直接删除
df_d_new_1 = df_d.dropna(axis = 1, how = 'all')
# 统计不同列缺失值数量,s是一个series对象
s = df_d_new_1.isna().sum()
# 计算整个对象的行数
row = df_d_new_1.shape[0]
# 通过索引的循环,分别计算每一列的缺失值比例,并和原dataframe行数的四分之一进行比较,大于四分之一的就会被删除
for index in s.index:
if (int(s['{}'.format(index)])) > (row/4):
df_d_new_1 = df_d_new_1.drop(columns = '{}'.format(index))
print('finished')
print(df_d_new_1)
问题二:什么是Nullable类型?请谈谈为什么要引入这个设计?
这是一种具有相对比较独立属性特征的缺失值类型
统一目前Pandas中比较混乱的缺失值处理符号,同时规范缺失值处理过程中对于原dataframe对象,series对象数据类型的影响
问题三:对于一份有缺失值的数据,可以采取哪些策略或方法深化对它的了解?
练习一
现有一份虚拟数据集,列类型分别为string/浮点/整型,请解决如下问题:
(a)请以列类型读入数据,并选出C为缺失值的行。
(b)现需要将A中的部分单元转为缺失值,单元格中的最小转换概率为25%,且概率大小与所在行B列单元的值成正比。
练习1解答
A | B | C | |
---|---|---|---|
0 | not_NaN | 0.922 | 4.0 |
1 | not_NaN | 0.700 | NaN |
2 | not_NaN | 0.503 | 8.0 |
3 | not_NaN | 0.938 | 4.0 |
4 | not_NaN | 0.952 | 10.0 |
//
pd.read_csv('data/Missing_data_one.csv').head()
import pandas as pd
import numpy as np
import random
# 导入数据
df = pd.read_csv('data/Missing_data_one.csv').convert_dtypes()
# 筛选出C列存在缺失值的行
df_1 = df[df['C'].isna()]
print(df_1.head())
# 将前面两列的名称存入变量
column_1 = df.columns.values[0]
column_2 = df.columns.values[1]
# 确定B列的最大/最小值,方便后面概率的计算
min = int(df['{}'.format(column_2)].idxmin())
max = int(df['{}'.format(column_2)].idxmax())
# 通过循环遍历整个A列
for index in range(df.shape[0]):
# 生成一个0-1之间的随机浮点数
rand_num = random.random()
# 通过与A列该元素同行的B列数字大小确定其被填充为缺失值的概率,最小为25%,最大为100%,概率大小随着B列数字的大小变化而变化
probability = 0.25 + 0.75 * (float(df.at[index,'{}'.format(column_2)])-min)/(max-min)
# 如果随机生成的浮点数rand_num没有落在概率范围内,则表示该次命中概率,A列相应元素被填充为缺失值
if rand_num < probability:
df.loc[index,'{}'.format(column_1)] = pd.NA
print(df.head())
print('==================')
练习二
现有一份缺失的数据集,记录了36个人来自的地区、身高、体重、年龄和工资,请解决如下问题:
(a)统计各列缺失的比例并选出在后三列中至少有两个非缺失值的行。¶
(b)请结合身高列和地区列中的数据,对体重进行合理插值。
练习2解答
编号 | 地区 | 身高 | 体重 | 年龄 | 工资 | |
---|---|---|---|---|---|---|
0 | 1 | A | 157.50 | NaN | 47.0 | 15905.0 |
1 | 2 | B | 202.00 | 91.80 | 25.0 | NaN |
2 | 3 | C | 169.09 | 62.18 | NaN | NaN |
3 | 4 | A | 166.61 | 59.95 | 77.0 | 5434.0 |
4 | 5 | B | 185.19 | NaN | 62.0 | 4242.0 |
//
pd.read_csv('data/Missing_data_two.csv').head()
import pandas as pd
import numpy as np
from scipy import interpolate
# 读取数据
df = pd.read_csv('data/Missing_data_two.csv').convert_dtypes()
# 将列名称存入一个列表
columns = df.columns.values.tolist()
# 定义一个空的字典
Pom_values = {}
# 计算每列缺失值比例,并存入一个字典,与列名相对应
for column in columns:
num = int(df.shape[0])
Pom_values['{}'.format(column)] = 1 - ((int(df['{}'.format(column)].count()))/num)
# 创建一个空的列表,用于存储后三列中至少有两个非缺失值的行索引
index_list = []
# 遍历每行,分别计算其后三列的缺失值数量(因为除了后三列,其他列都没有缺失值存在,就直接遍历了整行内容,没有考虑效率)
for index in range(df.shape[0]):
num = 0
for column in columns:
if pd.isnull(df.at[index,'{}'.format(column)]) :
num += 1
if num <= 1:
index_list.append(index)
print(index_list)
print('=====')
#print(df)
df_1 = df.sort_values(by = ['地区', '身高'])
print(df_1)
print('=====')
# 首先将“地区”列中的所有不同元素存入一个列表
List_type = df['地区'].unique()
# 根据不同的地区元素将原来的大dataframe拆分成三个小的dataframe,在小的对象中,只有一个地区属性,并按身高进行排序
data0 = df[df['地区'].isin([List_type[0]])].sort_values(by = ['身高'])
data1 = df[df['地区'].isin([List_type[1]])].sort_values(by = ['身高'])
data2 = df[df['地区'].isin([List_type[2]])].sort_values(by = ['身高'])
# 在每个地区中,分别进行线性插值
#data0['体重'] = data0['体重'].fillna(method = 'pad')
data0['体重'] = data0['体重'].interpolate()
data1['体重'] = data1['体重'].interpolate()
data2['体重'] = data2['体重'].interpolate()
#print(data0)
result = data0.append([data1, data2])
print(result)