缺失值處理
數據缺失主要包括記錄缺失和字段信息缺失等情況,其對數據分析會有較大影響,導致結果不確定性更加顯著
缺失值的處理:刪除記錄 / 數據插補 / 不處理
1.判斷是否有缺失數據
import numpy as np import pandas as pd import matplotlib.pyplot as plt from scipy import stats % matplotlib inline
s = pd.Series([12,33,45,23,np.nan,np.nan,66,54,np.nan,99]) df = pd.DataFrame({'value1':[12,33,45,23,np.nan,np.nan,66,54,np.nan,99,190], 'value2':['a','b','c','d','e',np.nan,np.nan,'f','g',np.nan,'g']})
df # 創建數據
判斷是否有缺失值數據 - isnull,notnull
isnull:缺失值為True,非缺失值為False
notnull:缺失值為False,非缺失值為True
s.isnull() df.notnull()適用於Series和DataFrame;
s[s.isnull() == False] #把不是缺失值的給找出來,只針對Series ;
print(s.isnull()) #布爾型的一個結果,只要有一個是NaN就是True; Series直接判斷是否是缺失值,返回一個Series print(df.notnull())#判斷不是缺失值的有哪些,可以加索引判斷如print(df['value1'].notnull()) ;直接判斷是否是缺失值,返回一個Series
0 False 1 False 2 False 3 False 4 True 5 True 6 False 7 False 8 True 9 False dtype: bool value1 value2 0 True True 1 True True 2 True True 3 True True 4 False True 5 False False 6 True False 7 True True 8 False True 9 True False 10 True True
print(s[s.isnull() == False]) #把不是缺失值的給找出來。 <<<===>>> s[s.notnull()] 布爾型的索引 print(df[df['value2'].notnull()]) # 注意和 df2 = df[df['value2'].notnull()] ['value1'] 的區別
##篩選非缺失值
2. 刪除缺失值 - dropna
.dropna(inplace = True)
# 刪除缺失值 - dropna s = pd.Series([12,33,45,23,np.nan,np.nan,66,54,np.nan,99]) df = pd.DataFrame({'value1':[12,33,45,23,np.nan,np.nan,66,54,np.nan,99,190], 'value2':['a','b','c','d','e',np.nan,np.nan,'f','g',np.nan,'g']}) # 創建數據 s.dropna(inplace = True) s
df.dropna(inplace = True) df
3.填充/替換缺失值
s.fillna(0,inplace = True) 缺失值用0來填充;
df['value1'].fillna(method = 'pad',inplace = True) 缺失值的用pad/ffill之前前一個數據填充;
s.replace(np.nan,'缺失數據',inplace = True) 把np.nan的數據用 ‘缺失數據’來代替;
s.replace([1,2,3],np.nan,inplace = True) 也可以替換多值。
# 填充/替換缺失數據 - fillna、replace s = pd.Series([12,33,45,23,np.nan,np.nan,66,54,np.nan,99]) df = pd.DataFrame({'value1':[12,33,45,23,np.nan,np.nan,66,54,np.nan,99,190], 'value2':['a','b','c','d','e',np.nan,np.nan,'f','g',np.nan,'g']}) # 創建數據 s.fillna(0,inplace = True) print(s) print('------') # s.fillna(value=None, method=None, axis=None, inplace=False, limit=None, downcast=None, **kwargs) # value:填充值 # 注意inplace參數
df['value1'].fillna(method = 'pad',inplace = True) print(df) print('------') # method參數: # pad / ffill → 用之前的數據填充 # backfill / bfill → 用之后的數據填充
s = pd.Series([1,1,1,1,2,2,2,3,4,5,np.nan,np.nan,66,54,np.nan,99]) s.replace(np.nan,'缺失數據',inplace = True) print(s) print('------') # df.replace(to_replace=None, value=None, inplace=False, limit=None, regex=False, method='pad', axis=None) # to_replace → 被替換的值 # value → 替換值 s.replace([1,2,3],np.nan,inplace = True) print(s) # 多值用np.nan代替
4.缺失值插補
(1)均值/中位數/眾數補插
s.fillna(u,inplace = True) u為均值;
# 缺失值插補 # 幾種思路:均值/中位數/眾數插補、臨近值插補、插值法 # (1)均值/中位數/眾數插補 s = pd.Series([1,2,3,np.nan,3,4,5,5,5,5,np.nan,np.nan,6,6,7,12,2,np.nan,3,4]) #print(s) print('------') # 創建數據 u = s.mean() # 均值 me = s.median() # 中位數 mod = s.mode() # 眾數 print('均值為:%.2f, 中位數為:%.2f' % (u,me)) print('眾數為:', mod.tolist()) print('------') # 分別求出均值/中位數/眾數 s.fillna(u,inplace = True) print(s) # 用均值填補
(2)臨近值插補
s.fillna(method = 'ffill',inplace = True) ffill用前值插入; bfill用后值插補;
# 缺失值插補 # 幾種思路:均值/中位數/眾數插補、臨近值插補、插值法 # (2)臨近值插補 s = pd.Series([1,2,3,np.nan,3,4,5,5,5,5,np.nan,np.nan,6,6,7,12,2,np.nan,3,4]) #print(s) print('------') # 創建數據 s.fillna(method = 'ffill',inplace = True) print(s) # 用前值插補
(3)插值法---拉格朗日插值法
lagrange(x, y) --->> y = a0 * x**2 + a1 * x + a2 → y = -0.11111111 * x**2 + 0.33333333 * x + 10
# 缺失值插補 # 幾種思路:均值/中位數/眾數插補、臨近值插補、插值法 # (3)插值法 —— 拉格朗日插值法 from scipy.interpolate import lagrange x = [3, 6, 9] y = [10, 8, 4] plt.scatter(x, y) print(lagrange(x, y)) #直接輸出多項式的方程 # 的輸出值為的是多項式的n個系數 # 這里輸出3個值,分別為a0,a1,a2 # y = a0 * x**2 + a1 * x + a2 → y = -0.11111111 * x**2 + 0.33333333 * x + 10 print('插值10為:%.2f' % lagrange(x,y)(10)) print('------') # -0.11111111*100 + 0.33333333*10 + 10 = -11.11111111 + 3.33333333 +10 = 2.22222222
用少數身邊的臨近值去推測這個值的本身,
df = pd.DataFrame({'x':np.arange(15)}) #創建一個數組 df['y'] = lagrange(x, y)(df['x']) #加一個y的標簽 df
plt.plot(df['x'], df['y'], linestyle = '--', color = 'k')
data_na = data[data.isnull()] 缺失值數量; print('缺失值數據量:%i' % len(data_na))
# 缺失值插補 # 幾種思路:均值/中位數/眾數插補、臨近值插補、插值法 # (3)插值法 —— 拉格朗日插值法,實際運用 data = pd.Series(np.random.rand(100)*100) data[3,6,33,56,45,66,67,80,90] = np.nan print(data.head()) print('總數據量:%i' % len(data)) print('------') # 創建數據 data_na = data[data.isnull()] print('缺失值數據量:%i' % len(data_na)) print('缺失數據占比:%.2f%%' % (len(data_na) / len(data) * 100)) # 缺失值的數量 data_c = data.fillna(data.median()) # 中位數填充缺失值 fig,axes = plt.subplots(1,4,figsize = (20,5)) data.plot.box(ax = axes[0],grid = True,title = '數據分布') #直接生成圖,做一個密度圖會直接排除缺失值。 data.plot(kind = 'kde',style = '--r',ax = axes[1],grid = True,title ='刪除缺失值',xlim = [-50,150]) data_c.plot(kind = 'kde',style = '--b',ax = axes[2],grid = True,title ='缺失值填充中位數',xlim = [-50,150]) # 密度圖查看缺失值情況
def na_c(s,n,k=5): #5為位置 y = s[list(range(n-k,n+1+k))] # 取數 y = y[y.notnull()] # 剔除空值,把缺失值去掉,非缺失值篩選出來 return(lagrange(y.index,list(y))(n)) # 創建函數,做插值,由於數據量原因,以空值前后5個數據(共10個數據)為例做插值 na_re = [] for i in range(len(data)): if data.isnull()[i]: #判斷data里邊的缺失值 data[i] = na_c(data,i) print(na_c(data,i)) na_re.append(data[i]) data.dropna(inplace=True) # 清除插值后仍存在的缺失值 data.plot(kind = 'kde',style = '--k',ax = axes[3],grid = True,title = '拉格朗日插值后',xlim = [-50,150]) print('finished!') # 缺失值插值