Pandas进阶柒 缺失数据

Pandas进阶柒 缺失数据

pandas进阶系列根据datawhale远昊大佬的joyful pandas教程写一些自己的心得和补充,本文部分引用了原教程,并参考了《利用Python进行数据分析》、pandas官网
为了方便自己回顾和助教审阅,每一节先将原教程重要知识点罗列一下帮助自己回顾,Nullable章节没做更改,然后在其后写一些自己的心得以及我的习题计算过程

另注:本文是对joyful pandas教程的延伸,完整理解需先阅读joyful pandas教程第七章

一、缺失值的统计和删除

常用操作 功能
df.isna().mean() 查看各列缺失值的比例
df.isna().sum() 查看各列缺失样本数
df[df.col.isna()] 利用缺失值索引样本
df[sub_set.isna().all()] 多个列的缺失项可以用any,all

1. 缺失信息的统计

缺失数据可以使用isnaisnull(两个函数没有区别)来查看每个单元格是否缺失,结合mean可以计算出每列缺失值的比例:

import numpy as np
import pandas as pd
df = pd.read_csv('../data/learn_pandas.csv', usecols = ['Grade', 'Name', 'Gender', 'Height', 'Weight', 'Transfer'])
df.isna().head(2)
Grade Name Gender Height Weight Transfer
0 False False False False False False
1 False False False False False False

如果想要查看某一列缺失或者非缺失的行,可以利用Series上的isna或者notna进行布尔索引。例如,查看身高缺失的行:

df[df.Height.isna()].head(2)
Grade Name Gender Height Weight Transfer
3 Sophomore Xiaojuan Sun Female NaN 41.0 N
12 Senior Peng You Female NaN 48.0 NaN

如果想要同时对几个列,检索出全部为缺失或者至少有一个缺失或者没有缺失的行,可以使用isna, notnaany, all的组合。例如,对身高、体重和转系情况这3列分别进行这三种情况的检索:

sub_set = df[['Height', 'Weight', 'Transfer']]
df[sub_set.isna().all(1)] # 全部缺失
Grade Name Gender Height Weight Transfer
102 Junior Chengli Zhao Male NaN NaN NaN
df[sub_set.isna().any(1)].head(2) # 至少有一个缺失
Grade Name Gender Height Weight Transfer
3 Sophomore Xiaojuan Sun Female NaN 41.0 N
9 Junior Juan Xu Female 164.8 NaN N

2. 缺失信息的删除

数据处理中经常需要根据缺失值的大小、比例或其他特征来进行行样本或列特征的删除,pandas中提供了dropna函数来进行操作。

dropna的主要参数为轴方向axis(默认为0,即删除行)、删除方式how、删除的非缺失值个数阈值thresh 非 缺 失 值 \color{red}{非缺失值} 没有达到这个数量的相应维度会被删除)、备选的删除子集subset,其中how主要有anyall两种参数可以选择。

例如,删除身高体重至少有一个缺失的行:

res = df.dropna(how = 'any', subset = ['Height', 'Weight'])
res.shape
(174, 6)

例如,删除超过15个缺失值的列:

res = df.dropna(1, thresh=df.shape[0]-15) # 身高被删除
res.head(2)
Grade Name Gender Weight Transfer
0 Freshman Gaopeng Yang Female 46.0 N
1 Freshman Changqiang You Male 70.0 N

当然,不用dropna同样是可行的,例如上述的两个操作,也可以使用布尔索引来完成:

res = df.loc[df[['Height', 'Weight']].notna().all(1)]
res.shape
(174, 6)
res = df.loc[:, ~(df.isna().sum()>15)]
res.head()
Grade Name Gender Weight Transfer
0 Freshman Gaopeng Yang Female 46.0 N
1 Freshman Changqiang You Male 70.0 N
2 Senior Mei Sun Male 89.0 N
3 Sophomore Xiaojuan Sun Female 41.0 N
4 Sophomore Gaojuan You Male 74.0 N

【应用场景】
下图是我之前做的贷款违约预测的比赛,里面的缺失值情况。可以看到存在多列同时缺失的情况,当时我的处理方法是统计缺失的列超过阈值的项,删除那些项,现在学习了缺失值处理之后发现可以直接用dropna确定阈值来删除,不过一定要注意dropna的thresh参数的含义是非缺失值没有达到这个阈值会被删除
Pandas进阶柒 缺失数据_第1张图片

二、缺失值的填充和插值

1. 利用fillna进行填充

填充方案:

  • value: 可以是标量,也可以是字典(Series也可以看做是一种字典)
  • method: ffill, bfill (用前面的或后面的元素填充)
  • limit: 连续缺失值的最大填充次数。

【练一练】

对一个序列以如下规则填充缺失值:如果单独出现的缺失值,就用前后均值填充,如果连续出现的缺失值就不填充,即序列[1, NaN, 3, NaN, NaN]填充后为[1, 2, 3, NaN, NaN],请利用fillna函数实现。(提示:利用limit参数)

【我的思路】

单独出现的缺失值可以用limit=1检索出来,要前后均值来填充所以考虑使用参数method,而填充方法里只有前项填充和后项填充,因此在两个方向都填充一次取平均值即可

s = pd.Series([None, 1, None, 3, None, None, 5, None, None])
s.values
array([nan,  1., nan,  3., nan, nan,  5., nan, nan])
res = (s.fillna(method='ffill', limit=1)+s.fillna(method='bfill', limit=1))/2
res.values
array([nan,  1.,  2.,  3., nan, nan,  5., nan, nan])

【END】

2. 插值函数interpolate

  • method
    • linear 利用空值两边最近的非缺失值的线性关系插值
    • nearest 缺失值的元素和离它最近的非缺失值元素一样
    • index 考虑索引进行插值
    • polynomial: 调用scipy.interpolate.interp1d函数,需要传递order参数,代表多项式的最高幂次
    • spline
  • limit_direction: 与fillna中类似,有forword, backword, both, 顾名思义
  • limit: 限制连续填充的最大数量
s = pd.Series([np.nan, np.nan, 1, np.nan, np.nan, np.nan, 2, np.nan, 3, np.nan, np.nan])
s.values
array([nan, nan,  1., nan, nan, nan,  2., nan,  3., nan, nan])

这里为了弄明白线性插值的原理,将原教程中的数据改造成上述数据,先不使用limit,并把所有值都填满(默认情况使用ffill不会全部填满)看一下插值结果。

s.interpolate(limit_direction='both').values
array([1.  , 1.  , 1.  , 1.25, 1.5 , 1.75, 2.  , 2.5 , 3.  , 3.  , 3.  ])

可以看出线性插值的方法有如下特点:

  1. 两端的空值(存在一个方向没有值)的填充值等于有值的一边最近的值
  2. 两个方向存在值的空值的填充是按照两边最近的两个值,与空值的距离关系填充值的,因此在上面的例子中1与2中填充了1.25,1.5,1.75,2与3中填充了2.5,并不是按照所有数据的线性关系考虑的,仅考虑空值两边最近的两个值

再加入limit参数看一下,与上面的例子对比,每个位置的空值的填充值不改变,limit只决定哪些值被填充

res = s.interpolate(limit_direction='backward', limit=1)
res.values
array([ nan, 1.  , 1.  ,  nan,  nan, 1.75, 2.  , 2.5 , 3.  ,  nan,  nan])

第二种常见的插值是最近邻插补,即缺失值的元素和离它最近的非缺失值元素一样:

s.interpolate('nearest').values
array([nan, nan,  1.,  1.,  1.,  2.,  2.,  2.,  3., nan, nan])

最后来介绍索引插值,即根据索引大小进行线性插值。例如,构造不等间距的索引进行演示:

s = pd.Series([0,np.nan,4],index=[0,1,2])
s
0    0.0
1    NaN
2    4.0
dtype: float64
s.interpolate() # 默认的线性插值,等价于计算中点的值
0    0.0
1    2.0
2    4.0
dtype: float64
s.interpolate(method='index') # 和索引有关的线性插值,计算相应索引大小对应的值
0    0.0
1    2.0
2    4.0
dtype: float64

同时,这种方法对于时间戳索引也是可以使用的,有关时间序列的其他话题会在第十章进行讨论,这里举一个简单的例子:

s = pd.Series([0,np.nan,10], index=pd.to_datetime(['20200101', '20200102', '20200111']))
s
2020-01-01     0.0
2020-01-02     NaN
2020-01-11    10.0
dtype: float64
s.interpolate()
2020-01-01     0.0
2020-01-02     5.0
2020-01-11    10.0
dtype: float64
s.interpolate(method='index')
2020-01-01     0.0
2020-01-02     1.0
2020-01-11    10.0
dtype: float64

试下多项式插值,这两种原理还是没太弄懂,看了下scipy的包他们是需要fit的,不过不知道在pandas里是怎么fit的,order=1的时候等同于线性插值

s.interpolate(method='polynomial', order=1)
2020-01-01     0.0
2020-01-02     1.0
2020-01-11    10.0
dtype: float64
s.interpolate(method='spline', order=1)
2020-01-01     0.0
2020-01-02     1.0
2020-01-11    10.0
dtype: float64

三、Nullable类型

1. 缺失记号及其缺陷

  • python中None==None 结果为True, 与其他元素均不相等
  • numpy中np.nan 和任何元素都不相等
  • 使用Series的equals检验相同性时会跳过两个表都是nan值的地方

注:这部分相较于原教程没有新加的内容,由于Nullable类型第一次见识到,所以这块内容我没有删除,随时遇到问题回来回顾

在时间序列的对象中,pandas利用pd.NaT来指代缺失值,它的作用和np.nan是一致的(时间序列的对象和构造将在第十章讨论):

pd.to_timedelta(['30s', np.nan]) # Timedelta中的NaT
TimedeltaIndex(['0 days 00:00:30', NaT], dtype='timedelta64[ns]', freq=None)
pd.to_datetime(['20200101', np.nan]) # Datetime中的NaT
DatetimeIndex(['2020-01-01', 'NaT'], dtype='datetime64[ns]', freq=None)

那么为什么要引入pd.NaT来表示时间对象中的缺失呢?仍然以np.nan的形式存放会有什么问题?在pandas中可以看到object类型的对象,而object是一种混杂对象类型,如果出现了多个类型的元素同时存储在Series中,它的类型就会变成object。例如,同时存放整数和字符串的列表:

pd.Series([1, 'two'])
0      1
1    two
dtype: object

NaT问题的根源来自于np.nan的本身是一种浮点类型,而如果浮点和时间类型混合存储,如果不设计新的内置缺失类型来处理,就会变成含糊不清的object类型,这显然是不希望看到的。

type(np.nan)
float

同时,由于np.nan的浮点性质,如果在一个整数的Series中出现缺失,那么其类型会转变为float64;而如果在一个布尔类型的序列中出现缺失,那么其类型就会转为object而不是bool

pd.Series([1, np.nan]).dtype
dtype('float64')
pd.Series([True, False, np.nan]).dtype
dtype('O')

因此,在进入1.0.0版本后,pandas尝试设计了一种新的缺失类型pd.NA以及三种Nullable序列类型来应对这些缺陷,它们分别是Int, booleanstring

2. Nullable类型的性质

从字面意义上看Nullable就是可空的,言下之意就是序列类型不受缺失值的影响。例如,在上述三个Nullable类型中存储缺失值,都会转为pandas内置的pd.NA

pd.Series([np.nan, 1], dtype = 'Int64') # "i"是大写的
0    
1       1
dtype: Int64
pd.Series([np.nan, True], dtype = 'boolean')
0    
1    True
dtype: boolean
pd.Series([np.nan, 'my_str'], dtype = 'string')
0      
1    my_str
dtype: string

Int的序列中,返回的结果会尽可能地成为Nullable的类型:

pd.Series([np.nan, 0], dtype = 'Int64') + 1
0    
1       1
dtype: Int64
pd.Series([np.nan, 0], dtype = 'Int64') == 0
0    
1    True
dtype: boolean
pd.Series([np.nan, 0], dtype = 'Int64') * 0.5 # 只能是浮点
0    NaN
1    0.0
dtype: float64

对于boolean类型的序列而言,其和bool序列的行为主要有两点区别:

第一点是带有缺失的布尔列表无法进行索引器中的选择,而boolean会把缺失值看作False

s = pd.Series(['a', 'b'])
s_bool = pd.Series([True, np.nan])
s_boolean = pd.Series([True, np.nan]).astype('boolean')
# s[s_bool] # 报错
s[s_boolean]
0    a
dtype: object

第二点是在进行逻辑运算时,bool类型在缺失处返回的永远是False,而boolean会根据逻辑运算是否能确定唯一结果来返回相应的值。那什么叫能否确定唯一结果呢?举个简单例子:True | pd.NA中无论缺失值为什么值,必然返回TrueFalse | pd.NA中的结果会根据缺失值取值的不同而变化,此时返回pd.NAFalse & pd.NA中无论缺失值为什么值,必然返回False

s_boolean & True
0    True
1    
dtype: boolean
s_boolean | True
0    True
1    True
dtype: boolean
~s_boolean # 取反操作同样是无法唯一地判断缺失结果
0    False
1     
dtype: boolean

关于string类型的具体性质将在下一章文本数据中进行讨论。

一般在实际数据处理时,可以在数据集读入后,先通过convert_dtypes转为Nullable类型:

df = pd.read_csv('../data/learn_pandas.csv')
df = df.convert_dtypes()
df.dtypes
School          string
Grade           string
Name            string
Gender          string
Height         float64
Weight           Int64
Transfer        string
Test_Number      Int64
Test_Date       string
Time_Record     string
dtype: object

3. 缺失数据的计算和分组

当调用函数sum, prob使用加法和乘法的时候,缺失数据等价于被分别视作0和1,即不改变原来的计算结果:

s = pd.Series([2,3,np.nan,4,5])
s.sum()
14.0
s.prod()
120.0

当使用累计函数时,会自动跳过缺失值所处的位置:

s.cumsum()
0     2.0
1     5.0
2     NaN
3     9.0
4    14.0
dtype: float64

当进行单个标量运算的时候,除了np.nan ** 01 ** np.nan这两种情况为确定的值之外,所有运算结果全为缺失(pd.NA的行为与此一致 ),并且np.nan在比较操作时一定返回False,而pd.NA返回pd.NA

np.nan == 0
False
pd.NA == 0

np.nan > 0
False
pd.NA > 0

np.nan + 1
nan
np.log(np.nan)
nan
np.add(np.nan, 1)
nan
np.nan ** 0
1.0
pd.NA ** 0
1
1 ** np.nan
1.0
1 ** pd.NA
1

另外需要注意的是,diff, pct_change这两个函数虽然功能相似,但是对于缺失的处理不同,前者凡是参与缺失计算的部分全部设为了缺失值,而后者缺失值位置会被设为 0% 的变化率:

s.diff()
0    NaN
1    1.0
2    NaN
3    NaN
4    1.0
dtype: float64
s.pct_change()
0         NaN
1    0.500000
2    0.000000
3    0.333333
4    0.250000
dtype: float64

对于一些函数而言,缺失可以作为一个类别处理,例如在groupby, get_dummies中可以设置相应的参数来进行增加缺失类别:

df_nan = pd.DataFrame({
     'category':['a','a','b',np.nan,np.nan], 'value':[1,3,5,7,9]})
df_nan
category value
0 a 1
1 a 3
2 b 5
3 NaN 7
4 NaN 9
df_nan.groupby('category', dropna=False)['value'].mean() # pandas版本大于1.1.0
category
a      2
b      5
NaN    8
Name: value, dtype: int64
pd.get_dummies(df_nan.category, dummy_na=True)
a b NaN
0 1 0 0
1 1 0 0
2 0 1 0
3 0 0 1
4 0 0 1

四、练习

Ex1:缺失值与类别的相关性检验

在数据处理中,含有过多缺失值的列往往会被删除,除非缺失情况与标签强相关。下面有一份关于二分类问题的数据集,其中X_1, X_2为特征变量,y为二分类标签。
事实上,有时缺失值出现或者不出现本身就是一种特征,并且在一些场合下可能与标签的正负是相关的。关于缺失出现与否和标签的正负性,在统计学中可以利用卡方检验来断言它们是否存在相关性。按照特征缺失的正例、特征缺失的负例、特征不缺失的正例、特征不缺失的负例,可以分为四种情况,设它们分别对应的样例数为 n 11 , n 10 , n 01 , n 00 n_{11}, n_{10}, n_{01}, n_{00} n11,n10,n01,n00。假若它们是不相关的,那么特征缺失中正例的理论值,就应该接近于特征缺失总数 × \times ×总体正例的比例,即:

E 11 = n 11 ≈ ( n 11 + n 10 ) × n 11 + n 01 n 11 + n 10 + n 01 + n 00 = F 11 E_{11} = n_{11} \approx (n_{11}+n_{10})\times\frac{n_{11}+n_{01}}{n_{11}+n_{10}+n_{01}+n_{00}} = F_{11} E11=n11(n11+n10)×n11+n10+n01+n00n11+n01=F11

其他的三种情况同理。现将实际值和理论值分别记作 E i j , F i j E_{ij}, F_{ij} Eij,Fij,那么希望下面的统计量越小越好,即代表实际值接近不相关情况的理论值:

S = ∑ i ∈ { 0 , 1 } ∑ j ∈ { 0 , 1 } ( E i j − F i j ) 2 F i j S = \sum_{i\in \{0,1\}}\sum_{j\in \{0,1\}} \frac{(E_{ij}-F_{ij})^2}{F_{ij}} S=i{ 0,1}j{ 0,1}Fij(EijFij)2

可以证明上面的统计量近似服从自由度为 1 1 1的卡方分布,即 S ∼ ⋅ χ 2 ( 1 ) S\overset{\cdot}{\sim} \chi^2(1) Sχ2(1)。因此,可通过计算 P ( χ 2 ( 1 ) > S ) P(\chi^2(1)>S) P(χ2(1)>S)的概率来进行相关性的判别,一般认为当此概率小于 0.05 0.05 0.05时缺失情况与标签正负存在相关关系,即不相关条件下的理论值与实际值相差较大。

上面所说的概率即为统计学上关于 2 × 2 2\times2 2×2列联表检验问题的 p p p值, 它可以通过scipy.stats.chi2(S, 1)得到。请根据上面的材料,分别对X_1, X_2列进行检验。

【我的思路】

根据题目要求,应该要求出X_1列的缺失例数,非缺失例数,标签列的正例、负例数来求解2*2的列联表,然后根据列联表的值求公式中的S,代入S求自由度为1的卡方分布的p值。
这里查了一下,可以使用scipy.stats.chi2_contingency来计算两个数组的卡方,其返回值分别是卡方值、p值、自由度和独立时矩阵
X_2列同理

import scipy.stats as st
df = pd.read_csv('../data/missing_chi.csv')
def f(col):
    df[col].where(df[col].isna(), 'not nan', inplace=True)
    df[col].fillna('is nan', inplace=True)
    t = pd.crosstab(df[col], df['y'])
    print(t)
    return t
st.chi2_contingency(f('X_1'))
y          0   1
X_1             
is nan   785  70
not nan  133  12





(0.016298159494174517,
 0.8984146867233628,
 1,
 array([[784.89,  70.11],
        [133.11,  11.89]]))
st.chi2_contingency(f('X_2'))
y          0   1
X_2             
is nan   894   0
not nan   24  82





(743.1188624676491,
 1.2578178884243594e-163,
 1,
 array([[820.692,  73.308],
        [ 97.308,   8.692]]))

上面的数据分别展示了X_1、X_2与标签的列联表、卡方统计值、p值、自由度和假设独立时的矩阵,可以看出,X_1与标签值分布的p值接近0.9,大于0.05,因此不能拒绝X_1与标签独立的假设;X_2与标签值分布的p值为1.25e-163,小于0.05,可以拒绝原假设,因此X_2与标签存在相关关系。

Ex2:用回归模型解决分类问题

KNN是一种监督式学习模型,既可以解决回归问题,又可以解决分类问题。对于分类变量,利用KNN分类模型可以实现其缺失值的插补,思路是度量缺失样本的特征与所有其他样本特征的距离,当给定了模型参数n_neighbors=n时,计算离该样本距离最近的 n n n个样本点中最多的那个类别,并把这个类别作为该样本的缺失预测类别,具体如下图所示,未知的类别被预测为黄色:

Pandas进阶柒 缺失数据_第2张图片

上面有色点的特征数据提供如下:

df = pd.read_excel('../data/color.xlsx', engine='openpyxl')
df.head(3)
X1 X2 Color
0 -2.5 2.8 Blue
1 -1.5 1.8 Blue
2 -0.8 2.8 Blue

已知待预测的样本点为 X 1 = 0.8 , X 2 = − 0.2 X_1=0.8, X_2=-0.2 X1=0.8,X2=0.2,那么预测类别可以如下写出:

from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier(n_neighbors=6)
clf.fit(df.iloc[:,:2], df.Color)
clf.predict([[0.8, -0.2]])
array(['Yellow'], dtype=object)
  1. 对于回归问题而言,需要得到的是一个具体的数值,因此预测值由最近的 n n n个样本对应的平均值获得。请把上面的这个分类问题转化为回归问题,仅使用KNeighborsRegressor来完成上述的KNeighborsClassifier功能。
  2. 请根据第1问中的方法,对audit数据集中的Employment变量进行缺失值插补。

【我的思路-第一问】

先看一下color的值,有3个,数量较少且没有序数关系,因此可以用one-hot编码来做

df.Color.value_counts()
Green     8
Yellow    8
Blue      7
Name: Color, dtype: int64
from sklearn.neighbors import KNeighborsRegressor
y_train = pd.get_dummies(df.Color)
nn = KNeighborsRegressor(n_neighbors=6)
nn.fit(df.iloc[:,:2], y_train)
nn.predict([[0.8, -0.2]])
array([[0.16666667, 0.33333333, 0.5       ]])
y_train.head(0)
Blue Green Yellow

看一下顺序,可以看出预测是蓝、绿、黄的概率分别为0.17, 0.33, 0.5
因此预测结果应该是Yellow

【我的思路-第二问】

先吐槽一下,竟然还有这样的数据集??!
先查看一下各列缺失值情况,看来只有Employment列存在缺失,另外有几列的值不是数字,需要转换成数字再使用回归

df2 = pd.read_csv('../data/audit.csv')
df2.head(3)
ID Age Employment Marital Income Gender Hours
0 1004641 38 Private Unmarried 81838.00 Female 72
1 1010229 35 Private Absent 72099.00 Male 30
2 1024587 32 Private Divorced 154676.74 Male 40
df2.isna().sum()
ID              0
Age             0
Employment    100
Marital         0
Income          0
Gender          0
Hours           0
dtype: int64
df2.Employment.value_counts()
Private       1411
Consultant     148
PSLocal        119
SelfEmp         79
PSState         72
PSFederal       69
Volunteer        1
Unemployed       1
Name: Employment, dtype: int64
df2.Marital.value_counts()
Married                  917
Absent                   669
Divorced                 266
Unmarried                 67
Widowed                   59
Married-spouse-absent     22
Name: Marital, dtype: int64

可以看出,Employment列和Marital列都是类别,,而且类别数量较多,我选择用sklearn.LabelEncoder,这样其实也不是很好因为会产生自定义的序数关系,不过做起来简单一些
另外Gender列可以换做isMale表达,因此可以直接转换成01序列
ID列应该与结果无关,选择删除
其他列由于值的范围不一样需要归一化处理
以下开始按照上面的分析一个一个处理

data = df2.copy()
data['isMale'] =  data['Gender'].map({
     'Male':1, 'Female':0})
import sklearn.preprocessing as preprocessing        
def scale_it(cols):
    for col in cols:
        data[col] = preprocessing.MinMaxScaler().fit_transform(np.array(data[col]).reshape(-1,1))

对训练集中的类别变量编码

data['Marital'] = preprocessing.LabelEncoder().fit_transform(data['Marital'])
data.drop(columns=['ID', 'Gender', 'Employment'], inplace=True)
data.head()
Age Marital Income Hours isMale
0 38 4 81838.00 72 0
1 35 0 72099.00 30 1
2 32 1 154676.74 40 1
3 45 2 27743.82 55 1
4 60 2 7568.23 40 1

归一化处理

scale_cols = data.columns
scale_it(scale_cols)
data['Employment'] = df2['Employment']
na_index = data.Employment.isna()

划分测试集、训练集

test = data[na_index].drop(columns=['Employment'])
train = data[data['Employment'].notna()]
y = train['Employment']
encoder = preprocessing.LabelEncoder()
y = encoder.fit_transform(y)
train.drop(columns = ['Employment'], inplace=True)
train.head()
Age Marital Income Hours isMale
0 0.287671 0.8 0.168997 0.724490 0.0
1 0.246575 0.0 0.148735 0.295918 1.0
2 0.205479 0.2 0.320539 0.397959 1.0
3 0.383562 0.4 0.056453 0.551020 1.0
4 0.589041 0.4 0.014477 0.397959 1.0
from sklearn.neighbors import KNeighborsRegressor
nn = KNeighborsRegressor(n_neighbors=6)
nn.fit(train, y)
KNeighborsRegressor(n_neighbors=6)
y_pred = nn.predict(test)
res = test.copy()
res['Employment'] = encoder.inverse_transform(np.round(y_pred).astype('uint8'))
res.head()
Age Marital Income Hours isMale Employment
60 0.356164 0.0 0.126044 0.397959 0.0 PSState
222 0.753425 0.4 0.031294 0.071429 1.0 PSLocal
226 0.068493 0.2 0.167540 0.397959 0.0 Private
260 0.013699 0.0 0.535839 0.153061 1.0 PSState
278 0.068493 0.0 0.190168 0.397959 1.0 Private

这里我测试了一下,发现了fillna的一个新用法,之前只知道fillna可以填个常数或者按method填,这里我自己测试了下用Python list填值,然后报错了,提示我要使用常量或者字典,然后我一想Series类本身就是一种index和value的对应,是不是可以当做字典,然后试了一下惊喜地发现真的可以直接把Series代入来填空值
我这里的res变量存的是加入了预测的label值的测试集

df2.Employment.fillna(res['Employment'], inplace=True)
df2.head()
ID Age Employment Marital Income Gender Hours
0 1004641 38 Private Unmarried 81838.00 Female 72
1 1010229 35 Private Absent 72099.00 Male 30
2 1024587 32 Private Divorced 154676.74 Male 40
3 1038288 45 Private Married 27743.82 Male 55
4 1044221 60 Private Married 7568.23 Male 40

看一下最新的缺失情况,表明已经都填上了

df2.isna().sum()
ID            0
Age           0
Employment    0
Marital       0
Income        0
Gender        0
Hours         0
dtype: int64

看一下最终填充了缺失值之后的表

df2[na_index].head()
ID Age Employment Marital Income Gender Hours
60 1264939 43 PSState Absent 61192.65 Female 40
222 2044175 72 PSLocal Married 15651.39 Male 8
226 2065595 22 Private Divorced 81137.85 Female 40
260 2298185 18 PSState Absent 258160.48 Male 16
278 2383312 22 Private Absent 92014.13 Male 40

题目搞定啦!之前正好有个项目,是天池上的贷款违约预测,涉及到空值处理,之前的做法只是简单地删除了一下,觉得用预测填值太麻烦了,看了今天教程的代码发现预测填值的代码可以那么简短!不过这里对一个分类问题用回归算法感觉效果比较差,不过让我有信心回到贷款违约项目上继续做空值处理的优化了!

你可能感兴趣的:(pandas,datawhale,python,数据分析,pandas,大数据)