第七章
内容
第七章¶
零、练一练¶
练一练
请检索出身高体重全为缺失值的行。
df = pd.read_csv('data/learn_pandas.csv',
usecols = ['Grade', 'Name', 'Gender',
'Height', 'Weight', 'Transfer'])
df.loc[df[["Weight", "Height"]].isna().all(1)]
Grade | Name | Gender | Height | Weight | Transfer | |
---|---|---|---|---|---|---|
91 | Sophomore | Yanfeng Han | Male | NaN | NaN | N |
102 | Junior | Chengli Zhao | Male | NaN | NaN | NaN |
练一练
将上述Series使用s.fillna(method="bfill")填充,并观察与ffill处理结果的差别。
s = pd.Series([np.nan, 1, np.nan, np.nan, 2, np.nan], list('aaabcd'))
s.fillna(method="bfill")
a 1.0
a 1.0
a 2.0
b 2.0
c 2.0
d NaN
dtype: float64
练一练
请构造1个缺失值比例为5%的序列,并用众数进行填充。
s = pd.Series(np.random.randint(0, 4, 20))
s[0] = np.nan
s.head()
0 NaN
1 3.0
2 1.0
3 0.0
4 3.0
dtype: float64
s.fillna(s.value_counts().index[0]).head()
0 3.0
1 3.0
2 1.0
3 0.0
4 3.0
dtype: float64
练一练
对1个序列以如下规则填充缺失值:如果单独出现的缺失值,就用前后均值填充,如果连续出现的缺失值就不填充,即序列[1, NaN, 3, NaN, NaN]填充后为[1, 2, 3, NaN, NaN],请利用fillna()函数实现。(提示:利用limit参数)
s = pd.Series([1, np.nan, 3, np.nan, np.nan])
forward = s.fillna(method="ffill", limit=1)
backward = s.fillna(method="bfill", limit=1)
res = (forward + backward) / 2
res
0 1.0
1 2.0
2 3.0
3 NaN
4 NaN
dtype: float64
练一练
请实现上述interpolate(method="index")的功能,即给定一个索引为整数的Series,返回其索引插值结果。
def index_interpolate(s):
s_former = pd.Series([np.nan]+s.iloc[:-1].values.tolist())
s_former.index = [np.nan]+s.index[:-1].tolist()
s_latter = pd.Series(s.iloc[1:].values.tolist()+[np.nan])
s_latter.index = s.index[1:].tolist()+[np.nan]
val = (s.index - s_former.index) * (s_latter.values - s_former.values) / (s_latter.index - s_former.index)
s_copy = s.copy()
s_copy.loc[s.isna()] = val[s.isna()]
return s_copy
s = pd.Series([0,np.nan,10],index=[0,1,10])
index_interpolate(s)
0 0.0
1 1.0
10 10.0
dtype: float64
练一练
请设计一个my_get_dummies()函数,其作用是仅对非缺失值对应行的类别进行独热编码,缺失值对应行的编码结果列全设为缺失值,例如df_nan.category的返回结果如下表所示:
a b
0 1 0
1 0 0
2 0 1
3 <NA> <NA>
4 <NA> <NA>
def my_get_dummies(s):
res = pd.get_dummies(s_nan, dummy_na=True)
res = res.loc[:, res.columns.notna()]
res.loc[(1-res).all(1)] = np.nan
return res
s_nan = pd.Series(['a','a','b',np.nan,np.nan])
my_get_dummies(s_nan)
a | b | |
---|---|---|
0 | 1.0 | 0.0 |
1 | 1.0 | 0.0 |
2 | 0.0 | 1.0 |
3 | NaN | NaN |
4 | NaN | NaN |
一、缺失数据筛选¶
在data/ch7/missing.csv中存放了1000列数据,请按照如下条件进行数据筛选:
选出缺失比例低于50%的列和缺失值个数超过520个的行
选出最大连续缺失值个数超过20的列
若某一列左右两侧的列满足行同时缺失的比例超过10%,则称此列满足缺失对称条件。表中是否存在满足缺失对称条件的列?若存在,请找出所有符合条件的列。
【解答】
1
df = pd.read_csv("data/ch7/missing.csv")
res = df.loc[df.isna().sum(1)>520, df.isna().mean()<0.5]
res.shape
(60, 498)
2
def missing_helper(s):
temp = s.isna().astype("int").rename("temp_col")
temp = pd.concat([s, (temp != temp.shift()).cumsum()], axis=1)
return temp[s.isna()].groupby("temp_col").size().max() > 20
res = df.loc[:, df.apply(missing_helper)]
res.shape
(1000, 246)
3
cols = []
for i in range(1, 999):
temp = df.iloc[:,[i-1,i+1]]
if temp.isna().all(1).mean() > 0.1:
cols.append("f%d"%i)
len(cols)
677
二、K近邻填充¶
K近邻是一种监督学习模型,对于分类变量,利用KNN分类模型可以实现其缺失值的插补,思路是度量缺失样本的特征与所有其他样本特征的距离,当给定了模型参数n_neighbors=n时,计算离该样本距离最近的n个样本点中最多的那个类别,并把这个类别作为该样本的缺失预测类别,具体如图7.1所示,未知的类别被预测为黄色:
图中有色点的特征数据提供如下:
df = pd.read_excel('data/ch7/color.xlsx')
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\),那么预测类别可以如下写出:
from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier(n_neighbors=6) # 定义分类器
clf.fit(df.iloc[:,:2].values, df.Color) # 拟合数据
clf.predict([[0.8, -0.2]]) # 获取未知点的类别
array(['Yellow'], dtype=object)
7.2.2节介绍的近邻插值和此处介绍的K近邻填充有什么联系?
对于数据集中的缺失特征而言,可以把已有的类别看做有颜色的点,缺失的类别看做需要预测的点,请根据上述方法对data/ch7/audit.csv中的Employment变量进行缺失值填充,字符串变量可用独热编码转为数值变量。
【解答】
1
近邻插值是一维情况下\(K=1\)的K近邻填充。
2
from sklearn.neighbors import KNeighborsClassifier
df = pd.read_csv('data/ch7/audit.csv')
df_num = df[['Age','Income','Hours']].apply(lambda x:(x-x.min())/(x.max()-x.min()))
df_str = pd.get_dummies(df[['Marital', 'Gender']])
new_df = pd.concat([df_num, df_str, df.Employment], axis=1)
X_train = new_df[new_df.Employment.notna()]
X_test = new_df[new_df.Employment.isna()]
clf = KNeighborsClassifier(n_neighbors=6)
clf.fit(X_train.iloc[:,:-1], X_train.Employment)
predict_res = clf.predict(X_test.iloc[:,:-1])
df.loc[df.Employment.isna(), 'Employment'] = predict_res
df.Employment.notna().all()
True
三、条件近邻插值¶
近邻插值使用最近的非缺失值进行填充,但有时候我们需要对最近的元素做一些限制,例如用另一列中和待填充元素相同类别的上一个最近值进行填充。假设现有如下的DataFrame:
df = pd.DataFrame({
"A": [1,2,3,4,np.nan],
"B": list("YXZXY")})
df
A | B | |
---|---|---|
0 | 1.0 | Y |
1 | 2.0 | X |
2 | 3.0 | Z |
3 | 4.0 | X |
4 | NaN | Y |
若现在需要按照B的类别对A进行近邻填充,那么首先找到缺失值df.iloc[4,0]对应的B列类别为Y,接着寻找距离其最近的上一个Y的所在位置(即第一行),此时使用df.iloc[0,0]的值1.0进行填充。如果A列中获取到的条件近邻值df.iloc[0,0]为也是缺失值,则不进行填充。此外,如果需要填充的值向前无法寻找到同类别的对应行,也不进行填充。
请按照上述规则,对data/ch7/near.csv中的A列进行填充。
df = pd.read_csv("data/ch7/near.csv")
df.head()
A | B | |
---|---|---|
0 | NaN | Q |
1 | 79.0 | T |
2 | -6.0 | S |
3 | NaN | T |
4 | NaN | T |
【解答】
res = df.groupby("B")["A"].fillna(method="ffill", limit=1)
res.head()
0 NaN
1 79.0
2 -6.0
3 79.0
4 NaN
Name: A, dtype: float64