Pandas基础教程学习(六)

Pandas基础教程学习(六)

Pandas基础(六):缺失数据的类型与查找、运算与分组、填充与剔除、插值处理

DataWhale第十四期组队学习:Joyful-Pandas

文章目录

  • Pandas基础教程学习(六)
    • 一、缺失数据的观测与缺失数据类型
      • 1.1 了解缺失信息
      • 1.2 三种缺失符号
        • 1.2.1 np.nan
        • 1.2.2 None
        • 1.2.3 NaT
      • 1.3 Nullable类型与NA符号
        • 1.3.1 Nullable整型
        • 1.3.2 Nullable布尔
        • 1.3.3 string类型
      • 1.4 NA的特性
      • 1.5 convert_dtypes方法
    • 二、缺失数据的运算与分组
      • 2.1 加号与乘号规则
      • 2.2 groupby方法中的缺失值
    • 三、缺失值的填充与剔除
      • 3.1 fillna方法
      • 3.2 dropna方法
    • 四、缺失值的插值
      • 4.1 线性插值
        • 4.1.1 索引无关的线性插值
        • 4.1.2 与索引有关的插值
      • 4.2 高级插值方法
      • 4.3 interpolate中的限制参数
    • 五、问题与练习
      • 5.1 问题
        • 【问题一】 如何删除缺失值占比超过25%的列?
        • 【问题二】 什么是Nullable类型?请谈谈为什么要引入这个设计?
        • 【问题三】 对于一份有缺失值的数据,可以采取哪些策略或方法深化对它的了解?
      • 5.2 练习
        • 【练习一】现有一份虚拟数据集,列类型分别为string/浮点/整型,请解决如下问题:
        • 【练习二】 现有一份缺失的数据集,记录了36个人来自的地区、身高、体重、年龄和工资,解决如下问题:

Pandas缺失数据的内容包括

  • 缺失数据的类型,如何定位数据中的缺失
  • 缺失数据的运算如何处理,缺失数据的分组是怎么样的
  • 缺失数据如何填充值,如何从数据中心剔除缺失数据
  • 缺失数据如何进行例如线性插值等数值分析的处理方法

虚拟数据表

import pandas as pd
import numpy as np
df = pd.read_csv('data/table_missing.csv')
df.head()
School Class ID Gender Address Height Weight Math Physics
0 S_1 C_1 NaN M street_1 173 NaN 34.0 A+
1 S_1 C_1 NaN F street_2 192 NaN 32.5 B+
2 S_1 C_1 1103.0 M street_2 186 NaN 87.2 B+
3 S_1 NaN NaN F street_2 167 81.0 80.4 NaN
4 S_1 C_1 1105.0 NaN street_4 159 64.0 84.8 A-

一、缺失数据的观测与缺失数据类型

1.1 了解缺失信息

常用的方法有isna()notna()两种,分别代表缺失的是与否

# 对Series使用会返回布尔列表
df['Physics'].isna().head()
df['Physics'].notna().head()

# 对DataFrame使用会返回布尔表(用布尔值替换原有表数据)
df.isna().head()

# 对DataFrame每列缺失值数量进行统计
df.isna().sum()
df.info() # 基本信息中也包含缺失值信息

上面两个方法是整体上的,使用上可以更进一步

例如找出Physics为缺失值的条目(对行进行筛选)

df[df['Physics'].isna()]

例如找出不包含缺失值的条目(对行进行筛选)

# all可以理解为与操作,0为列,1为行
df[df.notna().all(1)]

1.2 三种缺失符号

在Pandas中,缺失有三种表达的符号

1.2.1 np.nan

np.nan不等与任何东西,甚至不等于自己

np.nan == np.nan  	# False
np.nan == 0			# False
np.nan == None   	# False

equals函数在比较时会自动略过两侧全是np.nan的单元格,所以不影响结果

df.equals(df)

特殊的地方是,np.nan在numpy中的类型为浮点

这就导致了,即时原来是整数的列,只要有缺失值就会变为浮点型

type(np.nan)	# float
pd.Series([1,2,3]).dtype	# dtype('int64')
pd.Series([1,np.nan,3]).dtype	# dtype('float64')

对于布尔类型的列表,如果是np.nan填充,那么np.nan的值会自动变为True而不是False

pd.Series([1,np.nan,3],dtype='bool')
# True True True dtype:bool

但是在修改一个布尔列表的时候,会改变列表类型,而不是赋值为True

s = pd.Series([True,False],dtype='bool')
s[1]=np.nan # 这里不会把False变为True
s
'''
0    1.0
1    NaN
dtype: float64
'''

总的来看,在我们读取一个表格后,无论是什么类型的数据,默认的缺失值都是np.nan类型

这就导致了整型列变为浮点,但是字符不能转化为浮点,所以变为object类型(‘O’),若原来是浮点型的则类型不变

df['ID'].dtype  # dtype('float64')
df['Math'].dtype  # dtype('float64')
df['Class'].dtype  # dtype('O')

1.2.2 None

None是等于自身的,且布尔值为False,且修改布尔列表的时候不会改变数据类型

None == None  # True

pd.Series([None],dtype='bool')  # None变为False

s = pd.Series([True,False],dtype='bool')
s[0]=None
s
'''
0    False
1    False
dtype: bool
'''

s = pd.Series([1,0],dtype='bool')
s[0]=None
s
'''
0    False
1    False
dtype: bool
'''

但是若是传入数值类型之后,None会自动变为np.nan

因而除非是人工命名为None,否则不会自动出现在Pandas中

type(pd.Series([1,None])[1])  # numpy.float64

另外None在使用equals函数时不会被略过,因而下面的情况会返回False

pd.Series([None]).equals(pd.Series([np.nan]))

1.2.3 NaT

可以把NaT看作是时序版本的np.nan,和自己不相等,且使用equals函数时也会自动跳过

# 构造一个时序的序列
s_time = pd.Series([pd.Timestamp('20120101')]*5)
'''
0   2012-01-01
1   2012-01-01
2   2012-01-01
3   2012-01-01
4   2012-01-01
dtype: datetime64[ns]
'''

以下三种操作都会将原序列中的下标为2的一项替换为NaT

s_time[2] = None
s_time[2] = np.nan
s_time[2] = pd.NaT
'''
0   2012-01-01
1   2012-01-01
2          NaT
3   2012-01-01
4   2012-01-01
dtype: datetime64[ns]
'''

1.3 Nullable类型与NA符号

可以感觉到之前的缺失值很混乱,所以在1.0版本之后引入了统一缺失值处理办法

在之后的版本中鼓励用户使用新的数据类型和确实类型pd.NA

1.3.1 Nullable整型

对于该种类型而言,它与原来标记int上的符号区别在于首字母大写:‘Int’

s_original = pd.Series([1, 2], dtype="int64")
'''
0    1
1    2
dtype: int64
'''

s_new = pd.Series([1, 2], dtype="Int64")
'''
0    1
1    2
dtype: Int64
'''

在这种类型下,之前的三种缺失值都会被替换为NA符号,且不会改变类型

s_new[1] = np.nan
'''
0     1
1    
dtype: Int64
'''

1.3.2 Nullable布尔

s_original = pd.Series([1, 0], dtype="bool")
'''
0     True
1    False
dtype: bool
'''

s_new = pd.Series([0, 1], dtype="boolean")
'''
0    False
1     True
dtype: boolean
'''

s_new[0] = np.nan
s_new
'''
0    
1    True
dtype: boolean
'''

1.3.3 string类型

该类型是1.0的一大创新,目的之一就是为了区分开原本含糊不清的object类型

它本质上也属于Nullable类型,因为并不会因为含有缺失而改变类型

s = pd.Series(['dog','cat'],dtype='string')
'''
0    dog
1    cat
dtype: string
'''

s[0] = np.nan
# s[0] = None
# s[0] = pd.NaT
'''
0    
1     cat
dtype: string
'''

此外,和object类型的一点重要区别就在于,在调用字符方法后

  • string类型返回的是Nullable类型

  • object则会根据缺失类型和数据类型而改变

s = pd.Series(["a", None, "b"], dtype="string")
s.str.count('a')
'''
0       1
1    
2       0
dtype: Int64
'''
s2 = pd.Series(["a", None, "b"], dtype="object")
s2.str.count("a")
'''
0    1.0
1    NaN
2    0.0
dtype: float64
'''
s.str.isdigit()
'''
0    False
1     
2    False
dtype: boolean
'''
s2.str.isdigit()
'''
0    False
1     None
2    False
dtype: object
'''

1.4 NA的特性

逻辑运算

只需看该逻辑运算的结果是否依赖pd.NA的取值,如果依赖,则结果还是NA,如果不依赖,则直接计算结果

True | pd.NA  # True
pd.NA | True  # True
False | pd.NA  # 
False & pd.NA  # False
True & pd.NA  # 

算术运算和比较运算

除了以下两种其他的该类运算都是NA

pd.NA ** 0 # 1
1 ** pd.NA  # 1

1.5 convert_dtypes方法

这个函数的功能就是在读取数据的时候,就把数据列转化Nullable类型

pd.read_csv('data/table_missing.csv').convert_dtypes().dtypes

二、缺失数据的运算与分组

2.1 加号与乘号规则

使用加法的时候,缺失值为0

使用乘法的时候,缺失值为1

使用累计函数的时候(累计和、积、百分比),缺失值自动略过

s = pd.Series([2,3,np.nan,4])
s.sum() # 9.0
s.prod() # 24.0
s.cumsum()
'''
0    2.0
1    5.0
2    NaN
3    9.0
dtype: float64
'''

2.2 groupby方法中的缺失值

df_g = pd.DataFrame({
     'one':['A','B','C','D',np.nan],'two':np.random.randn(5)})
df_g.groupby('one').groups

'''
{'A': Int64Index([0], dtype='int64'),
 'B': Int64Index([1], dtype='int64'),
 'C': Int64Index([2], dtype='int64'),
 'D': Int64Index([3], dtype='int64')}
'''

三、缺失值的填充与剔除

3.1 fillna方法

df['Physics'].fillna('missing').head()  # 值填充
df['Physics'].fillna(method='ffill').head()  # 前向填充
df['Physics'].fillna(method='backfill').head()  # 后向填充

3.2 dropna方法

df_d = pd.DataFrame({
     'A':[np.nan,np.nan,np.nan],
                     'B':[np.nan,3,2],
                     'C':[3,2,1]})
df_d.dropna(axis=0) # 去除有缺失值的行
df_d.dropna(axis=1) # 去除有缺失值的列

# how参数:全为缺失去除all、存在缺失去除any
df_d.dropna(axis=1,how='all') # 去除了第一列

# subset参数(即在某一组列范围中搜索缺失值)
# 去除了第一行,因为B中含有缺失值
df_d.dropna(axis=0,subset=['B','C'])

四、缺失值的插值

4.1 线性插值

4.1.1 索引无关的线性插值

默认状态下,interpolate会对缺失的值进行线性插值,此时插值与索引数值无关

s = pd.Series([1,10,15,-5,-2,np.nan,np.nan,28])
s.interpolate()
'''
0     1.0
1    10.0
2    15.0
3    -5.0
4    -2.0
5     8.0
6    18.0
7    28.0
dtype: float64
'''
s.interpolate().plot()

Pandas基础教程学习(六)_第1张图片

4.1.2 与索引有关的插值

method中的index和time选项可以使插值线性地依赖索引,即插值为索引的线性函数

# 主要与上式的差别
s.interpolate(method='index').plot()

Pandas基础教程学习(六)_第2张图片

如果索引是时间,那么可以按照时间长短插值

s_t = pd.Series([0,np.nan,10],index=[pd.Timestamp('2012-05-01'),
                                     pd.Timestamp('2012-05-07'),
                                     pd.Timestamp('2012-06-03')])
'''
2012-05-01     0.0
2012-05-07     NaN
2012-06-03    10.0
dtype: float64
'''
s_t.interpolate(method='time').plot()

Pandas基础教程学习(六)_第3张图片

4.2 高级插值方法

高级指的是与线性插值相比较,例如样条插值、多项式插值、阿基玛插值等

ser = pd.Series(np.arange(1, 10.1, .25) ** 2 + np.random.randn(37))
missing = np.array([4, 13, 14, 15, 16, 17, 18, 20, 29])
ser[missing] = np.nan
methods = ['linear', 'quadratic', 'cubic']
df = pd.DataFrame({
     m: ser.interpolate(method=m) for m in methods})
df.plot()

Pandas基础教程学习(六)_第4张图片

4.3 interpolate中的限制参数

# 限制只能插值2个,第三个还是缺失值
s = pd.Series([1,np.nan,np.nan,np.nan,5])
s.interpolate(limit=2)
# limit_direction表示插值方向,可选forward,backward,both,默认前向
# 最后两个还是缺失值
s = pd.Series([np.nan,np.nan,1,np.nan,np.nan,np.nan,5,np.nan,np.nan,])
s.interpolate(limit_direction='backward')
# limit_area表示插值区域,可选inside,outside,默认None

# 只有夹在中间的插值了
s = pd.Series([np.nan,np.nan,1,np.nan,np.nan,np.nan,5,np.nan,np.nan,])
s.interpolate(limit_area='inside')

# 只有最后面的几个插值了
s = pd.Series([np.nan,np.nan,1,np.nan,np.nan,np.nan,5,np.nan,np.nan,])
s.interpolate(limit_area='outside')

五、问题与练习

5.1 问题

【问题一】 如何删除缺失值占比超过25%的列?

第一步,计算单列缺失值的数量,计算单列总样本数

第二步,算出比例,得到一个列的布尔列表

第三步,利用这个布尔列表进行列索引或列删除

df.loc[:,(df.isna().sum()/df.isna().count()<0.25).values]

【问题二】 什么是Nullable类型?请谈谈为什么要引入这个设计?

我的理解是Nullable类型是一种为了统一NaN,Null,NaT三类缺失值而诞生的新的类型

是在原来的数值、布尔、字符等类型的基础上进行小改,优化了当出现缺失值情况时的应对

引入这个设计时为了更好的处理缺失值,统一缺失值处理方法

【问题三】 对于一份有缺失值的数据,可以采取哪些策略或方法深化对它的了解?

可以查看缺失值出现的比例;查看缺失值之间的关联性;查看总体的缺失信息;根据缺失信息判断是否为有效数据;根据缺失信息清洗数据等等

5.2 练习

【练习一】现有一份虚拟数据集,列类型分别为string/浮点/整型,请解决如下问题:

q1 = pd.read_csv('data/Missing_data_one.csv')
q1.head()
	 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

(a)请以列类型读入数据,并选出C为缺失值的行。

q1[q1['C'].isna()]

(b)现需要将A中的部分单元转为缺失值,单元格中的最小转换概率为25%,且概率大小与所在行B列单元的值成正比

q1['A'] = pd.Series(list(zip(q1['A'].values,q1['B'].values))).apply(lambda x:x[0] if np.random.rand()>0.25*x[1]/q1['B'].min() else np.nan)

【练习二】 现有一份缺失的数据集,记录了36个人来自的地区、身高、体重、年龄和工资,解决如下问题:

pd.read_csv('data/Missing_data_two.csv').head()
   编号 地区 身高	体重	年龄	工资
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

(a)统计各列缺失的比例并选出在后三列中至少有两个非缺失值的行

q2.isna().sum()/q2.shape[0]
q2[q2.iloc[:,-3:].isna().sum(1)<=1].head()

(b)请结合身高列和地区列中的数据,对体重进行合理插值

q2_new = q2.copy()
for area,group in q2.groupby('地区'):
    q2_new.loc[group.index,'体重'] = group[['身高','体重']].sort_values(by='身高').interpolate()['体重'] 
q2_new = q2_new.round(decimals=2)
q2_new.head()

你可能感兴趣的:(Pandas,数据分析)