pandas缺失数据isna,notna,np.nan,None,NaT,Nullable,NA,convert_dtypes,fillna,dropna,interpolate

快速浏览

    • 缺失数据的统计与转换
      • isna和notna方法
      • np.nan与None与NaT
      • Nullable类型与NA符号
      • convert_dtypes
    • 缺失数据的运算与分组
    • 缺失数据的填充与剔除
      • fillna方法与dropna方法
      • 插值方法(线性插值interpolate等)
    • 问题与练习
      • 问题
      • 练习
    • Reference

缺失数据的统计与转换

#从清华镜像拉装1.0.5版本的Pandas
!pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pandas==1.0.5
import pandas as pd
import numpy as np
df = pd.read_csv('data/table_missing.csv')
print(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-

isna和notna方法

对Series使用isna和notna方法可以返回是否缺失的布尔列表,对DataFrame使用可以返回是否缺失的返回布尔表

print(df['Physics'].isna().head())
print(df['Physics'].notna().head())
print(df.isna().head())

一般情况下,拿到手的数据还是要先整体观测是否有缺失情况

print(df.isna().sum())
print(df.info())
School      0
Class       4
ID          6
Gender      7
Address     0
Height      0
Weight     13
Math        5
Physics     4
dtype: int64


RangeIndex: 35 entries, 0 to 34
Data columns (total 9 columns):
 #   Column   Non-Null Count  Dtype  
---  ------   --------------  -----  
 0   School   35 non-null     object 
 1   Class    31 non-null     object 
 2   ID       29 non-null     float64
 3   Gender   28 non-null     object 
 4   Address  35 non-null     object 
 5   Height   35 non-null     int64  
 6   Weight   22 non-null     float64
 7   Math     30 non-null     float64
 8   Physics  31 non-null     object 
dtypes: float64(3), int64(1), object(5)
memory usage: 2.6+ KB
None

挑选出所有无缺失值的行:使用all就是全部非缺失值df[df.notna().all(1)],如果是any就是至少有一个不是缺失值df[df.notna().any(1)]
查看有缺失值的行:以Physics列为例,挑出Physics列缺失值的行df[df['Physics'].isna()],挑出Physics列不缺失值的行df[df['Physics'].notna()]

np.nan与None与NaT

np.nan是常见的缺失数据表示,它不等于0也不等于None,甚至不等于自己。不过在用equals函数比较时,df.equals(df)equals函数会自动略过两侧全是np.nan的单元格,因此结果不会影响True

print(np.nan == np.nan,np.nan == 0,np.nan == None)
False False False

np.nan在numpy中的类型为浮点,由此导致数据集读入时,即使原来是整数的列,只要有缺失值就会变为浮点型。在所有的表格读取后,无论列是存放什么类型的数据,默认的缺失值全为np.nan类型,故而整型列转为浮点;而字符由于无法转化为浮点,因此只能归并为object类型(‘O’);原来是浮点型的则类型不变

print(type(np.nan),pd.Series([1,2,3]).dtype,pd.Series([1,np.nan,3]).dtype)
print(df['ID'].dtype,df['Math'].dtype,df['Class'].dtype)
 int64 float64
float64 float64 object

None会等于自身,其布尔值为False。在传入数值类型后,会自动变为np.nan。只有当传入object类型是保持不动,几乎可以认为,除非人工命名None,它基本不会自动出现在Pandas中。在使用equals函数时不会被略过,因此pd.Series([None]).equals(pd.Series([np.nan]))情况下返回False

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

NaT是针对时间序列的缺失值,是Pandas的内置类型,可以完全看做时序版本的np.nan,与自己不等,且使用equals是也会被跳过。

s_time = pd.Series([pd.Timestamp('20120101')]*5)
print(s_time)
s_time[2] = None
s_time[3] = np.nan
s_time[4] = pd.NaT
print('\n',s_time,type(s_time[2]),type(s_time[3]),type(s_time[4]))
print(s_time[2] == s_time[2],s_time.equals(s_time))
0   2012-01-01
1   2012-01-01
2   2012-01-01
3   2012-01-01
4   2012-01-01
dtype: datetime64[ns]

0   2012-01-01
1   2012-01-01
2          NaT
3          NaT
4          NaT
dtype: datetime64[ns]   
False True

Nullable类型与NA符号

这是Pandas在1.0新版本中引入的重大改变,其目的就是为了(在若干版本后)解决之前出现的混乱局面,统一缺失值处理方法

“The goal of pd.NA is provide a “missing” indicator that can be used consistently across data types (instead of np.nan, None or pd.NaT depending on the data type).”——User Guide for Pandas v-1.0

官方鼓励用户使用新的数据类型和缺失类型pd.NA
Nullable整形与原来标记int上的符号区别在于首字母大写:‘Int’。它的好处就在于,前面提到的三种缺失值np.nan、None、NaT都会被替换为统一的NA符号,且不改变数据类型。

s_original = pd.Series([1, 2, 3, 4], dtype="int64")
s_new = pd.Series([1, 2, 3, 4], dtype="Int64")
s_original[1] = np.nan
print(s_original)
s_new[1] = np.nan
s_new[2] = None
s_new[3] = pd.NaT
print(s_new)
0    1.0
1    NaN
2    3.0
3    4.0
dtype: float64
0       1
1    
2    
3    
dtype: Int64

Nullable布尔作用与上面的类似,记号为boolean。需要注意的是,含有pd.NA的布尔列表在1.0.2之前的版本作为索引时会报错,这是一个之前的bug,现已经修复。

s_original = pd.Series([1, 1, 0, 1], dtype="bool")
s_new = pd.Series([1, 0, 0, 1], dtype="boolean")
s_original[1] = None
print(s_original)
s_new[1] = np.nan
s_new[2] = None
s_new[3] = pd.NaT
print(s_new)
0     True
1    False
2    False
3     True
dtype: bool
0    True
1    
2    
3    
dtype: boolean
s = pd.Series(['dog','cat','fish','mouse'])
s[s_new]
0    dog
dtype: object

string类型是1.0的一大创新,目的之一就是为了区分开原本含糊不清的object类型,这里将简要地提及string,因为它是第7章的主题内容它本质上也属于Nullable类型,因为并不会因为含有缺失而改变类型。此外,和object类型的一点重要区别就在于,在调用字符方法后,string类型返回的是Nullable类型,object则会根据缺失类型和数据类型而改变。

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

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

print(True | pd.NA, False | pd.NA, False & pd.NA, True & pd.NA)
True  False 

NA参与算术运算和比较运算时只需记住除了两类情况,其他结果都是NA。

print(pd.NA ** 0, 1 ** pd.NA)
print(pd.NA + 1, "a" * pd.NA, pd.NA == pd.NA, pd.NA < 2.5, np.log(pd.NA), np.add(pd.NA, 1))
1 1
     

convert_dtypes

convert_dtypes函数的功能往往就是在读取数据时,就把数据列转为Nullable类型,是1.0的新函数。

print(pd.read_csv('data/table_missing.csv').dtypes)
print(pd.read_csv('data/table_missing.csv').convert_dtypes().dtypes)
School      object
Class       object
ID         float64
Gender      object
Address     object
Height       int64
Weight     float64
Math       float64
Physics     object
dtype: object
School      string
Class       string
ID           Int64
Gender      string
Address     string
Height       Int64
Weight       Int64
Math       float64
Physics     string
dtype: object

缺失数据的运算与分组

使用加法时,缺失值为0;使用乘法时,缺失值为1;使用累计函数时,缺失值自动略过。

s = pd.Series([2,3,np.nan,4])
print(s.sum(),s.prod(),s.cumsum(),s.cumprod(),s.pct_change())
9.0 24.0 0    2.0
1    5.0
2    NaN
3    9.0
dtype: float64 0     2.0
1     6.0
2     NaN
3    24.0
dtype: float64 0         NaN
1    0.500000
2    0.000000
3    0.333333
dtype: float64

groupby方法中自动忽略为缺失值的组。

df_g = pd.DataFrame({'one':['A','B','C','D',np.nan],'two':np.random.randn(5)})
print(df_g,'\n',df_g.groupby('one').groups)
   one       two
0    A -0.180930
1    B -0.454848
2    C  1.324243
3    D  0.994247
4  NaN  1.246457 
 {'A': Int64Index([0], dtype='int64'), 'B': Int64Index([1], dtype='int64'), 'C': Int64Index([2], dtype='int64'), 'D': Int64Index([3], dtype='int64')}

缺失数据的填充与剔除

fillna方法与dropna方法

fillna方直接进行值填充,也可以分别使用ffill方法和bfill方法前后向填充。

print(df['Physics'].fillna('missing').head())
print(df['Physics'].fillna(method='ffill').head())
print(df['Physics'].fillna(method='backfill').head())
0         A+
1         B+
2         B+
3    missing
4         A-
Name: Physics, dtype: object
0    A+
1    B+
2    B+
3    B+
4    A-
Name: Physics, dtype: object
0    A+
1    B+
2    B+
3    A-
4    A-
Name: Physics, dtype: object

填充中的对齐特性具体例子是返回的结果中没有C,根据对齐特点不会被填充。

df_f = pd.DataFrame({'A':[1,3,np.nan],'B':[2,4,np.nan],'C':[3,5,np.nan]})
print(df_f.fillna(df_f.mean()))
print(df_f.fillna(df_f.mean()[['A','B']]))
     A    B    C
0  1.0  2.0  3.0
1  3.0  4.0  5.0
2  2.0  3.0  4.0
     A    B    C
0  1.0  2.0  3.0
1  3.0  4.0  5.0
2  2.0  3.0  NaN

dropna方法中axis参数选择行(0)列(1),how参数(可以选all或者any,表示去除全为缺失的和去除存在缺失的),subset参数(即在某一组列范围中搜索缺失值)。

df_d = pd.DataFrame({'A':[np.nan,np.nan,np.nan],'B':[np.nan,3,2],'C':[3,2,1]})
print(df_d.dropna(axis=0))
print('\n',df_d.dropna(axis=1))
print(df_d.dropna(axis=1,how='all'))
print(df_d.dropna(axis=0,subset=['B','C']))
Empty DataFrame
Columns: [A, B, C]
Index: []

   C
0  3
1  2
2  1
     B  C
0  NaN  3
1  3.0  2
2  2.0  1
    A    B  C
1 NaN  3.0  2
2 NaN  2.0  1

插值方法(线性插值interpolate等)

默认状态下,interpolate会对缺失的值进行线性插值(与索引无关)。

s = pd.Series([1,10,15,-5,-2,np.nan,np.nan,28])
print(s.interpolate())
print(s.interpolate().plot())
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

pandas缺失数据isna,notna,np.nan,None,NaT,Nullable,NA,convert_dtypes,fillna,dropna,interpolate_第1张图片
method中的index和time选项可以使插值线性地依赖索引,即插值为索引的线性函数。比如s.interpolate(method='index')。如果索引是时间,那么可以(如下)按照时间长短插值.

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

interpolate中有一些限制参数,limit表示最多插入多少个。limit_direction表示插值方向,可选forward,backward,both,默认前向。imit_area表示插值区域,可选inside,outside,默认None。

s = pd.Series([np.nan,np.nan,1,np.nan,np.nan,np.nan,5,np.nan,np.nan,])
print(s.interpolate(limit=2))
print(s.interpolate(limit_direction='backward'))
print(s.interpolate(limit_area='inside'))
print(s.interpolate(limit_area='outside'))
0    NaN
1    NaN
2    1.0
3    2.0
4    3.0
5    NaN
6    5.0
7    5.0
8    5.0
dtype: float64
0    1.0
1    1.0
2    1.0
3    2.0
4    3.0
5    4.0
6    5.0
7    NaN
8    NaN
dtype: float64
0    NaN
1    NaN
2    1.0
3    2.0
4    3.0
5    4.0
6    5.0
7    NaN
8    NaN
dtype: float64
0    NaN
1    NaN
2    1.0
3    NaN
4    NaN
5    NaN
6    5.0
7    5.0
8    5.0
dtype: float64

例如样条插值、多项式插值等(需要安装Scipy),方法详情请看此处
在这里插入图片描述

问题与练习

问题

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

s = pd.DataFrame([[0,1,2,3],[0,None,2,3],[0,None,2,3],[0,None,2,3],[0,1,2,3],[0,1,2,3]])
print(s)
print(s.drop(s.columns[s.isna().sum()/s.shape[0]>0.25],axis=1))
   0    1  2  3
0  0  1.0  2  3
1  0  NaN  2  3
2  0  NaN  2  3
3  0  NaN  2  3
4  0  1.0  2  3
5  0  1.0  2  3
   0  2  3
0  0  2  3
1  0  2  3
2  0  2  3
3  0  2  3
4  0  2  3
5  0  2  3

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

这是Pandas在1.0新版本中引入的重大改变,其目的就是为了(在若干版本后)解决之前出现的混乱局面,统一缺失值处理方法。

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


练习

练习一
现有一份虚拟数据集Missing_data_one.csv,列类型分别为string/浮点/整型,请解决如下问题:
(a)请以列类型读入数据,并选出C为缺失值的行。
(b)现需要将A中的部分单元转为缺失值,单元格中的最小转换概率为25%,且概率大小与所在行B列单元的值成正比。

ex1 = pd.read_csv('data/Missing_data_one.csv').convert_dtypes()
print('(a)以列类型读入数据,并选出C为缺失值的行')
print(ex1[ex1['C'].isna()])
print('(b)将A中的部分单元转为缺失值,单元格中的最小转换概率为25%,且概率大小与所在行B列单元的值成正比')
ex1['A'] = pd.Series(list(zip(ex1['A'].values,ex1['B'].values))).apply(lambda x:x[0] if 0.25*x[1]/ex1['B'].min()<np.random.rand() else np.nan)
print(ex1.head())
(a)以列类型读入数据,并选出C为缺失值的行
          A      B     C
1   not_NaN  0.700  
5   not_NaN  0.972  
11  not_NaN  0.736  
19  not_NaN  0.684  
21  not_NaN  0.913  
(b)将A中的部分单元转为缺失值,单元格中的最小转换概率为25%,且概率大小与所在行B列单元的值成正比
         A      B     C
0  not_NaN  0.922     4
1  not_NaN  0.700  
2  not_NaN  0.503     8
3      NaN  0.938     4
4  not_NaN  0.952    10

练习二
现有一份缺失的数据集Missing_data_two.csv,记录了36个人来自的地区、身高、体重、年龄和工资,请解决如下问题:
(a)统计各列缺失的比例并选出在后三列中至少有两个非缺失值的行。
(b)请结合身高列和地区列中的数据,对体重进行合理插值。

ex2 = pd.read_csv('data/Missing_data_two.csv').convert_dtypes()
print('(a)统计各列缺失的比例并选出在后三列中至少有两个非缺失值的行')
print(ex2.isna().sum()/ex2.shape[0])
print(ex2[ex2[['体重','年龄','工资']].isna().sum(1)<=1])
print('(b)结合身高列和地区列中的数据,对体重进行合理插值')
ex2_1 = ex2.copy()
for part,group in ex2_1.groupby('地区'):
    ex2_1.loc[group.index,'体重'] = group[['身高','体重']].sort_values(by='身高').interpolate()['体重']
print(ex2_1)
(a)统计各列缺失的比例并选出在后三列中至少有两个非缺失值的行
编号    0.000000
地区    0.000000
身高    0.000000
体重    0.222222
年龄    0.250000
工资    0.222222
dtype: float64
    编号 地区      身高     体重    年龄     工资
0    1  A  157.50    NaN    47  15905
1    2  B  202.00  91.80    25   
3    4  A  166.61  59.95    77   5434
4    5  B  185.19    NaN    62   4242
5    6  A  187.13  78.42    55  13959
6    7  C  163.81  57.43    43   6533
7    8  A  183.80  75.42    48  19779
8    9  B  179.67  71.70    65   8608
9   10  C  186.08  77.47    65  12433
10  11  B  163.41  57.07     6495
13  14  B  175.99  68.39    13130
15  16  A  165.68    NaN    46  13683
16  17  B  166.48  59.83    31  17673
17  18  C  191.62  82.46    12447
18  19  A  172.83  65.55    23  13768
19  20  B  156.99  51.29    62   3054
20  21  C  200.22  90.20    41   
21  22  A  154.63  49.17    35  14559
22  23  B  157.87  52.08    67   7398
23  24  A  165.55    NaN    66  19890
24  25  C  181.78  73.60    63  11383
25  26  A  164.43  57.99    34  19899
27  28  C  172.39  65.15    43  10362
28  29  B  162.12  55.91    13362
29  30  A  183.73  75.36    58   8270
30  31  C  181.19    NaN    41  12616
31  32  B  167.28  60.55    64  18317
34  35  B  170.12  63.11    77   7398
35  36  C  180.47  72.42    78   9554
(b)结合身高列和地区列中的数据,对体重进行合理插值
    编号 地区      身高         体重    年龄     工资
0    1  A  157.50  53.580000    47  15905
1    2  B  202.00  91.800000    25   
2    3  C  169.09  62.180000     
3    4  A  166.61  59.950000    77   5434
4    5  B  185.19  81.750000    62   4242
5    6  A  187.13  78.420000    55  13959
6    7  C  163.81  57.430000    43   6533
7    8  A  183.80  75.420000    48  19779
8    9  B  179.67  71.700000    65   8608
9   10  C  186.08  77.470000    65  12433
10  11  B  163.41  57.070000     6495
11  12  A  202.56  92.300000     
12  13  C  177.37  68.785000    79   
13  14  B  175.99  68.390000    13130
14  15  C  199.11  89.200000     
15  16  A  165.68  59.296667    46  13683
16  17  B  166.48  59.830000    31  17673
17  18  C  191.62  82.460000    12447
18  19  A  172.83  65.550000    23  13768
19  20  B  156.99  51.290000    62   3054
20  21  C  200.22  90.200000    41   
21  22  A  154.63  49.170000    35  14559
22  23  B  157.87  52.080000    67   7398
23  24  A  165.55  58.643333    66  19890
24  25  C  181.78  73.600000    63  11383
25  26  A  164.43  57.990000    34  19899
26  27  B  158.28  53.995000    51   
27  28  C  172.39  65.150000    43  10362
28  29  B  162.12  55.910000    13362
29  30  A  183.73  75.360000    58   8270
30  31  C  181.19  73.206667    41  12616
31  32  B  167.28  60.550000    64  18317
32  33  C  181.01  72.813333    13021
33  34  A  196.67  87.000000     
34  35  B  170.12  63.110000    77   7398
35  36  C  180.47  72.420000    78   9554

Reference

  1. pandas官网
  2. Joyful-Pandas

你可能感兴趣的:(Pandas教程)