參考:https://datawhalechina.github.io/joyful-pandas/build/html/%E7%9B%AE%E5%BD%95/ch7.html
一、缺失值的統(tǒng)計(jì)和刪除
1. 缺失信息的統(tǒng)計(jì) isna 或 isnull
| 函數(shù) | 例子 | 備注 |
|---|---|---|
| isna或isnull | df.isna().mean() # 查看缺失的比例 |
isna 或 isnull (兩個(gè)函數(shù)沒(méi)有區(qū)別) |
isna或者notna |
df[df.Height.isna()] | 應(yīng)用于series,只能統(tǒng)計(jì)一列的缺失信息 |
isna,notna 和 any, all 的組合 |
sub_set = df[['Height', 'Weight', 'Transfer']]<br />df[sub_set.isna().all(1)]# 全部缺失<br />df[sub_set.isna().any(1)].head() # 至少有一個(gè)缺失<br />df[sub_set.notna().all(1)].head() # 沒(méi)有缺失 | 同時(shí)對(duì)幾個(gè)列,檢索出全部為缺失或者至少有一個(gè)缺失或者沒(méi)有缺失的行 |
2. 缺失信息的刪除
| 函數(shù) | 參數(shù) | 例子 | 備注 |
|---|---|---|---|
| dropna | axis(默認(rèn)為0,即刪除行) | ||
how(刪除方式)主要有 any 和 all 兩種參數(shù)可以選擇 |
df.dropna(how = 'any', subset = ['Height', 'Weight']) | 刪除身高體重至少有一個(gè)缺失的行 | |
| thresh(刪除的非缺失值個(gè)數(shù)閾值) | res = df.dropna(1, thresh=df.shape[0]-15) | 刪除超過(guò)15個(gè)缺失值的列 | |
| subset(備選的刪除子集) | res = df.loc[df[['Height', 'Weight']].notna().all(1)]<br />res = df.loc[:, ~(df.isna().sum()>15)] | 上述的兩個(gè)操作,也可以使用布爾索引來(lái)完成 |
二、缺失值的填充和插值
1. 利用fillna進(jìn)行填充
| 函數(shù) | 參數(shù) | 例子 |
|---|---|---|
| fillna | method:填充方法(ffill: 用缺失值前面的元素填充;bfill:用后面的元素填充) | s.fillna(method='ffill') # 用前面的值向后填充 |
| limit:連續(xù)缺失值的最大填充次數(shù) | s.fillna(method='ffill', limit=1) # 連續(xù)出現(xiàn)的缺失,最多填充一次 | |
| value:填充值,可以是標(biāo)量,也可以是索引到元素的字典映射 | s.fillna(s.mean()) # value為標(biāo)量<br />s.fillna({'a': 100, 'd': 200}) # 通過(guò)索引映射填充的值 |
練一練
對(duì)一個(gè)序列以如下規(guī)則填充缺失值:如果單獨(dú)出現(xiàn)的缺失值,就用前后均值填充,如果連續(xù)出現(xiàn)的缺失值就不填充,即序列[1, NaN, 3, NaN, NaN]填充后為[1, 2, 3, NaN, NaN],請(qǐng)利用
fillna函數(shù)實(shí)現(xiàn)。(提示:利用limit參數(shù))
s = pd.Series([1,np.nan,3, np.nan, np.nan])
s1 = s.fillna(method='ffill',limit=1)
s2 = s.fillna(method='bfill',limit=1)
s = pd.Series(list(map(lambda x,y: (x+y)/2 if not np.isnan(x) and not np.isnan(y) else np.nan, s1,s2)))
s
'''
0 1.0
1 2.0
2 3.0
3 NaN
4 NaN
'''
2. 插值函數(shù)
這里只討論比較常用且簡(jiǎn)單的三類(lèi)情況,即線(xiàn)性插值、最近鄰插值和索引插值。
| 函數(shù) | 插值類(lèi)型 | 參數(shù) | 例子 | 備注 |
|---|---|---|---|---|
| interpolate | 線(xiàn)性插值 | limit_direction:控制方向,默認(rèn)為 forward,還有backward或both(雙向限制插值) |
s.interpolate(limit_direction='backward', limit=1) | |
| limit:控制最大連續(xù)缺失值插值個(gè)數(shù) | ||||
| 最近鄰插補(bǔ) | method | s.interpolate('nearest').values | 缺失值的元素和離它最近的非缺失值元素一樣 | |
| 索引插值 | s.interpolate(method='index') | 根據(jù)索引大小進(jìn)行線(xiàn)性插值 |
三、Nullable類(lèi)型
1. 缺失記號(hào)及其缺陷
| 缺失記號(hào) | 說(shuō)明 |
|---|---|
| None | 在 python 中的缺失值用 None 表示,該元素除了等于自己本身之外,與其他任何元素不相等 |
| np.nan | 和所有元素都不相等,包括自己;但在兩個(gè)序列使用equals進(jìn)行比較時(shí),會(huì)跳過(guò)nan來(lái)比較 |
| pd.NaT | 時(shí)間序列對(duì)象的缺失值,作用和np.nan一樣 |
np.nan的缺陷:np.nan屬于浮點(diǎn)類(lèi)型,和任何其他類(lèi)型一列,這一列就會(huì)變成其他類(lèi)型:
- 和整數(shù)一列:整列變成float類(lèi)型
- 和bool一列:整列變成object類(lèi)型
- 和datetime一列:整列變成object類(lèi)型
所以出現(xiàn)了pd.NaT。1.0.0版本后,pandas設(shè)計(jì)了新的缺失類(lèi)型pd.NA以及3種Nullable序列類(lèi)型。
2. Nullable類(lèi)型的性質(zhì)
序列類(lèi)型不受nan影響,在聲明序列時(shí)指定序列的類(lèi)型為Int, boolean 和 string
- pd.Series([np.nan, 1], dtype = 'Int64') # "i"是大寫(xiě)的
- pd.Series([np.nan, True], dtype = 'boolean')
- pd.Series([np.nan, 'my_str'], dtype = 'string')
| 類(lèi)型 | 計(jì)算 | NA類(lèi)型返回 | 序列類(lèi)型 |
|---|---|---|---|
| Int | pd.Series([np.nan, 0], dtype = 'Int64') + 1 | NA | Int64 |
| pd.Series([np.nan, 0], dtype = 'Int64') == 0 | NA | boolean | |
| pd.Series([np.nan, 0], dtype = 'Int64') * 0.5 | NA | Float64 |
boolean和bool的區(qū)別:
s = pd.Series(['a', 'b'])
s_bool = pd.Series([True, np.nan])
s_boolean = pd.Series([True, np.nan]).astype('boolean')
| bool | bool結(jié)果 | boolean | boolean結(jié)果 |
|---|---|---|---|
| s[s_bool] | 報(bào)錯(cuò); nan不能作為索引器的選擇 | s[s_boolean] | 0 a <br />dtype: object 把nan當(dāng)成False |
| s_bool & True | bool類(lèi)型在缺失處返回的永遠(yuǎn)是False<br />0 True <br />1 False |
s_boolean & True |
boolean 會(huì)根據(jù)邏輯運(yùn)算是否能確定唯一結(jié)果來(lái)返回相應(yīng)的值<br />0 True <br />1 <NA> |
| s_bool | True | 0 True <br />1 False | s_boolean | True | 0 True <br />1 True |
| ~s_bool | 0 False <br />1 False | ~s_boolean | 0 False <br />1 <NA> |
一般在實(shí)際數(shù)據(jù)處理時(shí),可以在數(shù)據(jù)集讀入后,先通過(guò) convert_dtypes 轉(zhuǎn)為 Nullable 類(lèi)型:
df = df.convert_dtypes()
3. 缺失數(shù)據(jù)的計(jì)算和分組
sum, prob 使用加法和乘法的時(shí)候,缺失數(shù)據(jù)等價(jià)于被分別視作0和1,即不改變?cè)瓉?lái)的計(jì)算結(jié)果。當(dāng)使用累計(jì)函數(shù)時(shí),會(huì)自動(dòng)跳過(guò)缺失值所處的位置。
單個(gè)標(biāo)量運(yùn)算:
| 運(yùn)算 | 結(jié)果 |
|---|---|
| np.nan ** 0 | 1.0 |
| 1 ** np.nan | 1.0 |
| pd.NA ** 0 | 1 |
| 1 ** pd.NA | 1 |
| 其他所有涉及np.nan的運(yùn)算 | False |
| 其他所有涉及pd.NA的運(yùn)算 | NA |
對(duì)于一些函數(shù)而言,缺失可以作為一個(gè)類(lèi)別處理,例如在 groupby, get_dummies 中可以設(shè)置相應(yīng)的參數(shù)來(lái)進(jìn)行增加缺失類(lèi)別:
- df_nan.groupby('category', dropna=False)['value'].mean() # pandas版本大于1.1.0
- pd.get_dummies(df_nan.category, dummy_na=True)
四、練習(xí)
Ex1:缺失值與類(lèi)別的相關(guān)性檢驗(yàn)
統(tǒng)計(jì)學(xué)的概念不太記得了,題目不太讀得懂。。。Orz
答案:
df = pd.read_csv('../data/missing_chi.csv')
cat_1 = df.X_1.fillna('NaN').mask(df.X_1.notna()).fillna("NotNaN")
cat_2 = df.X_2.fillna('NaN').mask(df.X_2.notna()).fillna("NotNaN")
df_1 = pd.crosstab(cat_1, df.y, margins=True)
df_2 = pd.crosstab(cat_2, df.y, margins=True)
def compute_S(my_df):
S = []
for i in range(2):
for j in range(2):
E = my_df.iat[i, j]
F = my_df.iat[i, 2]*my_df.iat[2, j]/my_df.iat[2,2]
S.append((E-F)**2/F)
return sum(S)
res1 = compute_S(df_1)
res2 = compute_S(df_2)
from scipy.stats import chi2
chi2.sf(res1, 1) # X_1檢驗(yàn)的p值 # 不能認(rèn)為相關(guān),剔除
chi2.sf(res2, 1) # X_2檢驗(yàn)的p值 # 認(rèn)為相關(guān),保留
Ex2:用回歸模型解決分類(lèi)問(wèn)題
對(duì)KNN的類(lèi)不太了解,題目好難。。。Orz
- 對(duì)于回歸問(wèn)題而言,需要得到的是一個(gè)具體的數(shù)值,因此預(yù)測(cè)值由最近的 n 個(gè)樣本對(duì)應(yīng)的平均值獲得。請(qǐng)把上面的這個(gè)分類(lèi)問(wèn)題轉(zhuǎn)化為回歸問(wèn)題,僅使用
KNeighborsRegressor來(lái)完成上述的KNeighborsClassifier功能。
答案:
from sklearn.neighbors import KNeighborsRegressor
df = pd.read_excel('../data/color.xlsx')
df_dummies = pd.get_dummies(df.Color)
stack_list = []
for col in df_dummies.columns:
clf = KNeighborsRegressor(n_neighbors=6)
clf.fit(df.iloc[:,:2], df_dummies[col])
res = clf.predict([[0.8, -0.2]]).reshape(-1,1)
stack_list.append(res)
code_res = pd.Series(np.hstack(stack_list).argmax(1))
df_dummies.columns[code_res[0]]