1.缺失值處理
1)概念
數據缺失主要包括記錄缺失和字段信息缺失等情況,其對數據分析會有較大影響,導致結果不確定性更加顯著
2)處理方法
缺失值的處理:刪除記錄 / 數據插補 / 不處理
3)缺失值分析及處理
A:缺失值判斷
# 判斷是否有缺失值數據 - isnull,notnull
# isnull:缺失值為True,非缺失值為False
# notnull:缺失值為False,非缺失值為True
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']}) print(s.isnull()) # Series直接判斷是否是缺失值,返回一個Series
print(df.notnull()) # Dataframe直接判斷是否是缺失值,返回一個Series
print(df['value1'].notnull()) # 通過索引判斷
print('------') # 篩選非缺失值
s2 = s[s.isnull() == False] df2 = df[df['value2'].notnull()] # 注意和 df2 = df[df['value2'].notnull()] ['value1'] 的區別
print(s2) print(df2)
B:缺失值刪除
# 刪除缺失值 - 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) df2 = df['value1'].dropna() print(s) print(df2) # drop方法:可直接用於Series,Dataframe # 注意inplace參數,默認False → 生成新的值
C:缺失值替換
import numpy as np import pandas as pd import matplotlib.pyplot as plt from scipy import stats % matplotlib inline # 填充/替換缺失數據 - 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代替
D:缺失值插補(上)
# 缺失值插補 # 幾種思路:均值/中位數/眾數插補、臨近值插補、插值法 # (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) # 用均值填補
E:缺失值插補(中)
# 缺失值插補 # 幾種思路:均值/中位數/眾數插補、臨近值插補、插值法 # (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) # 用前值插補
F:缺失值插補(下)
# 缺失值插補 # 幾種思路:均值/中位數/眾數插補、臨近值插補、插值法 # (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): 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[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!') # 缺失值插值
2.異常值處理
1)概念
異常值是指樣本中的個別值,其數值明顯偏離其余的觀測值。
異常值也稱離群點,異常值的分析也稱為離群點的分析
2)處理方法
異常值分析 → 3σ原則 / 箱型圖分析
異常值處理方法 → 刪除 / 修正填補
3)異常值分析及處理
A:3σ原則
如果數據服從正態分布,異常值被定義為一組測定值中與平均值的偏差超過3倍的值 → p(|x - μ| > 3σ) ≤ 0.003
import numpy as np import pandas as pd import matplotlib.pyplot as plt from scipy import stats % matplotlib inline # 創建數據
data = pd.Series(np.random.randn(10000)*100) # 計算均值、標准差
u = data.mean() std = data.std() # 正態性檢驗
stats.kstest(data, 'norm', (u, std)) print('均值為:%.3f,標准差為:%.3f' % (u,std)) # 繪制數據密度曲線
fig = plt.figure(figsize = (10,6)) ax1 = fig.add_subplot(2,1,1) data.plot(kind = 'kde',grid = True,style = '-k',title = '密度曲線') plt.axvline(3*std,hold=None,color='r',linestyle="--",alpha=0.8) plt.axvline(-3*std,hold=None,color='r',linestyle="--",alpha=0.8) # 篩選出異常值error、剔除異常值之后的數據data_c
ax2 = fig.add_subplot(2,1,2) error = data[np.abs(data - u) > 3*std] data_c = data[np.abs(data - u) <= 3*std] print('異常值共%i條' % len(error)) # 圖表表達
plt.scatter(data_c.index,data_c,color = 'k',marker='.',alpha = 0.3) plt.scatter(error.index,error,color = 'r',marker='.',alpha = 0.5) plt.xlim([-10,10010]) plt.grid()
B:箱型圖分析
import numpy as np import pandas as pd import matplotlib.pyplot as plt from scipy import stats % matplotlib inline # 箱型圖看數據分布情況 # 以內限為界
fig = plt.figure(figsize = (10,6)) ax1 = fig.add_subplot(2,1,1) color = dict(boxes='DarkGreen', whiskers='DarkOrange', medians='DarkBlue', caps='Gray') data.plot.box(vert=False, grid = True,color = color,ax = ax1,label = '樣本數據') # 基本統計量
s = data.describe() print(s) # 計算分位差
q1 = s['25%'] q3 = s['75%'] iqr = q3 - q1 mi = q1 - 1.5*iqr ma = q3 + 1.5*iqr print('分位差為:%.3f,下限為:%.3f,上限為:%.3f' % (iqr,mi,ma)) # 篩選出異常值error、剔除異常值之后的數據data_c
ax2 = fig.add_subplot(2,1,2) error = data[(data < mi) | (data > ma)] data_c = data[(data >= mi) & (data <= ma)] print('異常值共%i條' % len(error)) # 圖表表達
plt.scatter(data_c.index,data_c,color = 'k',marker='.',alpha = 0.3) plt.scatter(error.index,error,color = 'r',marker='.',alpha = 0.5) plt.xlim([-10,10010]) plt.grid()
3.數據歸一化
1)概念
數據的標准化(normalization)是將數據按比例縮放,使之落入一個小的特定區間。
在某些比較和評價的指標處理中經常會用到,去除數據的單位限制,將其轉化為無量綱的純數值,便於不同單位或量級的指標能夠進行比較和加權
2)處理方法
最典型的就是數據的歸一化處理,即將數據統一映射到[0,1]區間上
0-1標准化 / Z-score標准化
3)歸一化分析及處理
A:0-1標准化
# 將數據的最大最小值記錄下來,並通過Max-Min作為基數(即Min=0,Max=1)進行數據的歸一化處理
# x = (x - Min) / (Max - Min)
import numpy as np import pandas as pd import matplotlib.pyplot as plt from scipy import stats % matplotlib inline # 創建數據
df = pd.DataFrame({"value1":np.random.rand(10)*20, 'value2':np.random.rand(10)*100}) print(df.head()) # 創建函數,標准化數據
def data_norm(df,*cols): df_n = df.copy() for col in cols: ma = df_n[col].max() mi = df_n[col].min() df_n[col + '_n'] = (df_n[col] - mi) / (ma - mi) return(df_n) # 標准化數據
df_n = data_norm(df,'value1','value2') print(df_n.head())
B:Z-score標准化
# Z分數(z-score),是一個分數與平均數的差再除以標准差的過程 → z=(x-μ)/σ,其中x為某一具體分數,μ為平均數,σ為標准差
# Z值的量代表着原始分數和母體平均值之間的距離,是以標准差為單位計算。在原始分數低於平均值時Z則為負數,反之則為正數
# 數學意義:一個給定分數距離平均數多少個標准差?
import numpy as np import pandas as pd import matplotlib.pyplot as plt from scipy import stats % matplotlib inline # 創建數據
df = pd.DataFrame({"value1":np.random.rand(10) * 100, 'value2':np.random.rand(10) * 100}) print(df.head()) print('------') # 創建函數,標准化數據
def data_Znorm(df, *cols): df_n = df.copy() for col in cols: u = df_n[col].mean() std = df_n[col].std() df_n[col + '_Zn'] = (df_n[col] - u) / std return(df_n) # 標准化數據 # 經過處理的數據符合標准正態分布,即均值為0,標准差為1
df_z = data_Znorm(df,'value1','value2') u_z = df_z['value1_Zn'].mean() std_z = df_z['value1_Zn'].std() print(df_z) print('標准化后value1的均值為:%.2f, 標准差為:%.2f' % (u_z, std_z)) # 什么情況用Z-score標准化: # 在分類、聚類算法中,需要使用距離來度量相似性的時候,Z-score表現更好
4.數據連續屬性離散化
1)概念
數據連續屬性離散化
連續屬性變換成分類屬性,即連續屬性離散化
在數值的取值范圍內設定若干個離散划分點,將取值范圍划分為一些離散化的區間,最后用不同的符號或整數值代表每個子區間中的數據值
2)處理方法
等寬法 / 等頻法
3)數據連續離散化分析及處理
A:等寬法
# 將數據均勻划分成n等份,每份的間距相等
# cut方法
import numpy as np import pandas as pd import matplotlib.pyplot as plt from scipy import stats % matplotlib inline # 有一組人員年齡數據,希望將這些數據划分為“18到25”,“26到35”,“36到60”,“60以上”幾個面元
ages=[20,22,25,27,21,23,37,31,61,45,41,32] # 返回的是一個特殊的Categorical對象 → 一組表示面元名稱的字符串
bins = [18,25,35,60,100] cats = pd.cut(ages,bins) print(cats) print(type(cats)) # cut結果含有一個表示不同分類名稱的層級數組以及一個年齡數據進行標號的代號屬性
print(cats.codes, type(cats.codes)) # 0-3對應分組后的四個區間,用代號來注釋數據對應區間,結果為ndarray
print(cats.categories, type(cats.categories)) # 四個區間,結果為index
print(pd.value_counts(cats)) # 按照區間計數
# 通過right函數修改閉端,默認為True
print(pd.cut(ages,[18,26,36,61,100],right=False)) print('-------') # 可以設置自己的區間名稱,用labels參數
group_names=['Youth','YoungAdult','MiddleAged','Senior'] print(pd.cut(ages,bins,labels=group_names)) print('-------') # 對一個Dataframe數據進行離散化,並計算各個區間的數據計數
df = pd.DataFrame({'ages':ages}) group_names=['Youth','YoungAdult','MiddleAged','Senior'] s = pd.cut(df['ages'],bins) # 也可以 pd.cut(df['ages'],5),將數據等分為5份
df['label'] = s cut_counts = s.value_counts(sort=False) print(df) print(cut_counts) # 用散點圖表示,其中顏色按照codes分類 # 注意codes是來自於Categorical對象
plt.scatter(df.index,df['ages'],cmap = 'Reds',c = cats.codes) plt.grid()
B:等頻法
# 以相同數量的記錄放進每個區間
# qcut方法
import numpy as np import pandas as pd import matplotlib.pyplot as plt from scipy import stats % matplotlib inline # qcut → 根據樣本分位數對數據進行面元划分,得到大小基本相等的面元,但並不能保證每個面元含有相同數據個數 # 也可以設置自定義的分位數(0到1之間的數值,包含端點) → pd.qcut(data1,[0,0.1,0.5,0.9,1])
data = np.random.randn(1000) s = pd.Series(data) cats = pd.qcut(s,4) # 按四分位數進行切割,可以試試 pd.qcut(data,10)
print(cats.head()) print(pd.value_counts(cats)) # 用散點圖表示,其中顏色按照codes分類 # 注意codes是來自於Categorical對象
plt.scatter(s.index,s,cmap = 'Greens',c = pd.qcut(data,4).codes) plt.xlim([0,1000]) plt.grid()
# 判斷是否有缺失值數據 - isnull,notnull# isnull:缺失值為True,非缺失值為False# notnull:缺失值為False,非缺失值為True
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']})# 創建數據
print(s.isnull()) # Series直接判斷是否是缺失值,返回一個Seriesprint(df.notnull()) # Dataframe直接判斷是否是缺失值,返回一個Seriesprint(df['value1'].notnull()) # 通過索引判斷print('------')
s2 = s[s.isnull() == False] df2 = df[df['value2'].notnull()] # 注意和 df2 = df[df['value2'].notnull()] ['value1'] 的區別print(s2)print(df2)# 篩選非缺失值