python資料預處理: 使用pandas 進行資料清洗
問題:
介紹資料清洗方法。。
解答:
所謂資料清洗主要處理的是資料中的缺失值、異常值和重複值:
缺失值處理
資料缺失值指由於各種原因導致資料中存在的空缺值:資料庫中的null,python返回物件none,pandas或numpy中的nan;另空字串是有實體的不算是缺失值,缺失值沒有實體。
處理資料缺失值一般有4中方法:
- 丟棄
- 補全
- 真值轉化
- 不處理
丟棄
如果缺失的資料量不大,且對其丟棄對資料特徵的影響有限,可以選著丟棄,畢竟這樣容易操作。某一列丟失的資料過多,為了避免增加噪聲,可以丟棄。
補全
相對於丟棄補全更常用,畢竟資料的完整性很重要,但是補全的方式也很重要,會影響到後續的資料處理。
對全的方法:
- 用平均值、中值、分位數、眾數、隨機值等替代。效果一般,因為等於人為增加了噪聲。
- 建立一個模型來“預測”缺失的資料。
- 對於重要的資料,可以專家不足,或是呼叫資料來源頭補全,缺點是成本高
轉化法
我們承認缺失值的存在,並且把資料缺失也作為資料分佈規律的一部分,這將變數的實際值和缺失值都作為輸入維度參與後續資料處理和模型計算。但是變數的實際值可以作為變數值參與模型計算,而缺失值通常無法參與運算,因此需要對缺失值進行真值轉換。
以使用者性別欄位為例,很多資料庫集都無法對會員的性別進行補足,但又捨不得將其丟棄,那麼我們將選擇將其中的值,包括男、女、未知從一個變數的多個值分佈狀態轉換為多個變數的真值分佈狀態。
不處理
一些模型可以自己處理缺失值,所以可以不對缺失值進行處理。常見的能夠自動處理缺失值的模型包括:KNN、決策樹和隨機森林、神經網路和樸素貝葉斯、DBSCAN(基於密度的帶有噪聲的空間聚類)等
資料:
# 資料缺失值處理
import pandas as pd
import numpy as np
from sklearn.preprocessing import Imputer
import random
# 獲取資料
df1 = pd.read_csv('https://raw.githubusercontent.com/ffzs/dataset/master/boston/train.csv' )
df = df0.iloc[:10,:7]
# 檢視是否有缺失值
df.isna().values.any()
# False 沒有缺失值
# 資料行列
df.shape
# (10, 7) 10行 7列
# 製造10個缺失值
for i in range(10):
df.iloc[random.choice(range(10)), random.choice(range(3,7))] = np.nan
看下資料:
# 檢視缺失值分佈
df.isna().sum()
ID 0
crim 0
zn 0
indus 1
chas 2
nox 3
rm 3
dtype: int64
# 丟棄缺失值
df_droped = df.dropna()
df_droped
ID crim zn indus chas nox rm
2 4 0.03237 0.0 2.18 0.0 0.458 6.998
3 5 0.06905 0.0 2.18 0.0 0.458 7.147
9 15 0.63796 0.0 8.14 0.0 0.538 6.096
# 將缺失值替代為特定值
# 用後面的值替代缺失值
df.fillna(method='backfill')
ID crim zn indus chas nox rm
0 1 0.00632 18.0 2.31 0.0 0.469 6.998
1 2 0.02731 0.0 7.07 0.0 0.469 6.998
2 4 0.03237 0.0 2.18 0.0 0.458 6.998
3 5 0.06905 0.0 2.18 0.0 0.458 7.147
4 7 0.08829 12.5 7.87 0.0 0.524 6.012
5 11 0.22489 12.5 7.87 0.0 0.524 6.377
6 12 0.11747 12.5 7.87 0.0 0.524 5.889
7 13 0.09378 12.5 7.87 0.0 0.524 5.889
8 14 0.62976 0.0 8.14 0.0 0.538 5.949
9 15 0.63796 0.0 8.14 0.0 0.538 6.096
# 限制每一個後值只能替換一個缺失值
df.fillna(method='bfill', limit=1)
ID crim zn indus chas nox rm
0 1 0.00632 18.0 2.31 0.0 0.469 NaN
1 2 0.02731 0.0 7.07 0.0 0.469 6.998
2 4 0.03237 0.0 2.18 0.0 0.458 6.998
3 5 0.06905 0.0 2.18 0.0 0.458 7.147
4 7 0.08829 12.5 7.87 0.0 NaN 6.012
5 11 0.22489 12.5 7.87 0.0 0.524 6.377
6 12 0.11747 12.5 7.87 0.0 0.524 5.889
7 13 0.09378 12.5 7.87 0.0 0.524 5.889
8 14 0.62976 0.0 8.14 0.0 0.538 5.949
9 15 0.63796 0.0 8.14 0.0 0.538 6.096
# 用前值替換缺失值,如果沒有前值則沒法替換
df.fillna(method='pad')
ID crim zn indus chas nox rm
0 1 0.00632 18.0 2.31 NaN NaN NaN
1 2 0.02731 0.0 7.07 0.0 0.469 NaN
2 4 0.03237 0.0 2.18 0.0 0.458 6.998
3 5 0.06905 0.0 2.18 0.0 0.458 7.147
4 7 0.08829 12.5 7.87 0.0 0.458 6.012
5 11 0.22489 12.5 7.87 0.0 0.458 6.377
6 12 0.11747 12.5 7.87 0.0 0.524 6.377
7 13 0.09378 12.5 7.87 0.0 0.524 5.889
8 14 0.62976 0.0 7.87 0.0 0.538 5.949
9 15 0.63796 0.0 8.14 0.0 0.538 6.096
# 插值法就是通過兩點(x0,y0),(x1,y1)估計中間點的值 必須有前後值
df.interpolate()
ID crim zn indus chas nox rm
0 1 0.00632 18.0 2.310 NaN NaN NaN
1 2 0.02731 0.0 7.070 0.0 0.469 NaN
2 4 0.03237 0.0 2.180 0.0 0.458 6.998
3 5 0.06905 0.0 2.180 0.0 0.458 7.147
4 7 0.08829 12.5 7.870 0.0 0.480 6.012
5 11 0.22489 12.5 7.870 0.0 0.502 6.377
6 12 0.11747 12.5 7.870 0.0 0.524 6.133
7 13 0.09378 12.5 7.870 0.0 0.524 5.889
8 14 0.62976 0.0 8.005 0.0 0.538 5.949
9 15 0.63796 0.0 8.140 0.0 0.538 6.096
# 用0值替換缺失值
df.fillna(0)
ID crim zn indus chas nox rm
0 1 0.00632 18.0 2.31 0.0 0.000 0.000
1 2 0.02731 0.0 7.07 0.0 0.469 0.000
2 4 0.03237 0.0 2.18 0.0 0.458 6.998
3 5 0.06905 0.0 2.18 0.0 0.458 7.147
4 7 0.08829 12.5 7.87 0.0 0.000 6.012
5 11 0.22489 12.5 7.87 0.0 0.000 6.377
6 12 0.11747 12.5 7.87 0.0 0.524 0.000
7 13 0.09378 12.5 7.87 0.0 0.524 5.889
8 14 0.62976 0.0 0.00 0.0 0.538 5.949
9 15 0.63796 0.0 8.14 0.0 0.538 6.096
# 用不同值替換
df.fillna(dict(nox=0.5, rm=6.0))
ID crim zn indus chas nox rm
0 1 0.00632 18.0 2.31 NaN 0.500 6.000
1 2 0.02731 0.0 7.07 0.0 0.469 6.000
2 4 0.03237 0.0 2.18 0.0 0.458 6.998
3 5 0.06905 0.0 2.18 0.0 0.458 7.147
4 7 0.08829 12.5 7.87 0.0 0.500 6.012
5 11 0.22489 12.5 7.87 0.0 0.500 6.377
6 12 0.11747 12.5 7.87 0.0 0.524 6.000
7 13 0.09378 12.5 7.87 NaN 0.524 5.889
8 14 0.62976 0.0 NaN 0.0 0.538 5.949
9 15 0.63796 0.0 8.14 0.0 0.538 6.096
# 用平均數替代,選擇各自列的均值
df.fillna(df.mean()['chas':'rm'])
ID crim zn indus chas nox rm
0 1 0.00632 18.0 2.31 0.0 0.501286 6.352571
1 2 0.02731 0.0 7.07 0.0 0.469000 6.352571
2 4 0.03237 0.0 2.18 0.0 0.458000 6.998000
3 5 0.06905 0.0 2.18 0.0 0.458000 7.147000
4 7 0.08829 12.5 7.87 0.0 0.501286 6.012000
5 11 0.22489 12.5 7.87 0.0 0.501286 6.377000
6 12 0.11747 12.5 7.87 0.0 0.524000 6.352571
7 13 0.09378 12.5 7.87 0.0 0.524000 5.889000
8 14 0.62976 0.0 NaN 0.0 0.538000 5.949000
9 15 0.63796 0.0 8.14 0.0 0.538000 6.096000
異常值處理
異常資料,是脫離資料正常分佈範圍的資料,即噪聲,對異常處理需要先辨別異常資料是否為真正需要去除掉的資料
一下幾個情況無需對異常資料進行處理:
異常資料來源於業務運營
由於業務部門特定的動作導致資料分佈異常:如雙十一銷量猛增,如果資料被剔除的話就無法真實對業務做反饋
異常檢測模型
模型本身就是為了檢測出資料中的異常資料,如果剔除掉了這些資料,本末倒置,如信用卡欺詐、藥物識別、疾病預測、網路攻擊等。
非異常敏感模型
如果資料演算法和模型對異常值不敏感,不對異常值進行處理也不會對模型造成負面影響,如決策樹
# 製造異常值
df = df0.iloc[:10,:7]
df.iloc[4, 5] = 100
df
ID crim zn indus chas nox rm
0 1 0.00632 18.0 2.31 0 0.538 6.575
1 2 0.02731 0.0 7.07 0 0.469 6.421
2 4 0.03237 0.0 2.18 0 0.458 6.998
3 5 0.06905 0.0 2.18 0 0.458 7.147
4 7 0.08829 12.5 7.87 0 100.000 6.012
5 11 0.22489 12.5 7.87 0 0.524 6.377
6 12 0.11747 12.5 7.87 0 0.524 6.009
7 13 0.09378 12.5 7.87 0 0.524 5.889
8 14 0.62976 0.0 8.14 0 0.538 5.949
9 15 0.63796 0.0 8.14 0 0.538 6.096
# 用過Z-Scores方法判斷異常值
# 複製一個dataframe來儲存score
df_zscore = df.copy()
for col in df.columns:
df_col = df[col]
# 計算每個值的zscore
z_score = (df_col - df_col.mean()) / df_col.std()
# 判斷Z-Score得分是否大於2.2
df_zscore[col] = z_score.abs() > 2.2
df_zscore
ID crim zn indus chas nox rm
0 False False False False False False False
1 False False False False False False False
2 False False False False False False False
3 False False False False False False False
4 False False False False False True False
5 False False False False False False False
6 False False False False False False False
7 False False False False False False False
8 False False False False False False False
9 False False False False False False False
通過箱型圖判斷:
# 通過箱型圖
%matplotlib inline
import matplotlib.pyplot as plt
df.boxplot()
對於有固定業務規則的可直接套用業務規則,而對於沒有固定業務規則的,可以採用常見的數學模型進行判斷,即基於概率分佈的模型(例如正態分佈的標準差範圍)、基於聚類的方法(例如KMeans)、基於密度的方法(例如LOF)、基於分類的方法(例如KNN)、基於統計的方法(例如分位數法)等,此時異常值的定義帶有較強的主觀判斷色彩,具體需要根據實際情況選擇。
重複值處理
重複值包括以下兩種情況:資料完全相同的多條資料;資料主體相同但是匹配到的唯一屬性不同,多見於資料倉庫的變化維度表,同一個事實表的主體匹配到多個屬性。
以下情況需慎重去重:
重複計入用於分析演變規律
商品類別的維度表中,每個商品對應了同一類別的值是唯一的,但是在所有商品的類別的值重構或升級時,原有的商品可能被分配了類別中的不同值,對於這種情況需要跟業務進行協商再決定做去重處理。
為解決樣本不均衡產生的重複值
處理分類資料建模過程中樣本不均衡的問題,對資料量較少的樣本類別偶簡單過取樣,這樣會產生一些重複值也不能去重。
運營過程產生的重複記錄
在運營過程中重複的資料可能意味著重大的運營規則問題,特別是這些重複值出現在企業經營中金錢相關的場景,如重複的訂單,重複充值
這些資料一般產生於誤操作,重複點選生成訂單,如果這樣的資料被視為重複資料去除掉的話,運營人員就無法發現出現的問題,無法及時對誤操作產生的後果做補救。
# 創造重複值
df = df0.iloc[:5,:7]
df0 = pd.concat([df, df])
# 判斷重複資料
df0.duplicated()
0 False
1 False
2 False
3 False
4 False
0 True
1 True
2 True
3 True
4 True
dtype: bool
# 刪除重複值
df0.drop_duplicates()
ID crim zn indus chas nox rm
0 1 0.00632 18.0 2.31 0 0.538 6.575
1 2 0.02731 0.0 7.07 0 0.469 6.421
2 4 0.03237 0.0 2.18 0 0.458 6.998
3 5 0.06905 0.0 2.18 0 0.458 7.147
4 7 0.08829 12.5 7.87 0 0.524 6.012
# 根據特定列進行刪除重複值
df0.drop_duplicates(['zn', 'chas'])
ID crim zn indus chas nox rm
0 1 0.00632 18.0 2.31 0 0.538 6.575
1 2 0.02731 0.0 7.07 0 0.469 6.421
4 7 0.08829 12.5 7.87 0 0.524 6.012
# inplace 為 true 改變dataframe
df0.drop_duplicates(['zn', 'chas'], inplace=True)
df0
ID crim zn indus chas nox rm
0 1 0.00632 18.0 2.31 0 0.538 6.575
1 2 0.02731 0.0 7.07 0 0.469 6.421
4 7 0.08829 12.5 7.87 0 0.524 6.012
參考:
《python資料分析與資料化運營》 宋天龍
【Python資料分析基礎】: 異常值檢測和處理
機器學習中如何處理缺失資料?