臟數據就是在物理上臨時存在過,但在邏輯上不存在的數據。

數據清洗是整個數據分析過程的第一步,就像做一道菜之前需要先擇菜洗菜一樣。 數據分析師經常需要花費大量的時間來清洗數據或者轉換格式,這個工作甚至會佔整個數據分析流程的80%左右的時間。

在這篇文章中,我嘗試簡單地歸納一下用Python來做數據清洗的7步過程,供大家參考。

一、數據預處理

一、數據預處理

  1. 部署環境,導入分析包和數據

#導入數據分析包
import pandas as pd
import numpy as np

#導入csv數據
#dtype = str,最好讀取的時候都以字元串的形式讀入,不然可能會使數據失真
#比如一個0010008的編號可能會讀取成10008

fileNameStr = ./Actual transactions from UK retailer.csv
DataDF = pd.read_csv(fileNameStr,encoding = "ISO-8859-1",dtype = str)

# encoding = "ISO-8859-1" -- 用什麼解碼,一般會默認系統的編碼,如果是中文就用 "utf-8"
DataDF = pd.read_csv(fileNameStr,encoding = "utf-8",dtype = str)

2. 嘗試去理解這份數據集

我們可以通過對數據集提問來判斷這份數據能不能滿足解答我們的問題,數據是否乾淨需不需要進一步處理,問題包括但不限於:

數據集多少數據?

包含了什麼欄位?欄位格式是什麼?欄位分別代表什麼意義欄位之間的關係是什麼?可以用做什麼分析?或者說能否滿足了對分析的要求?有沒有缺失值;如果有的話,缺失值多不多?現有數據裡面有沒有臟數據?尤其需要注意人工輸入的數據,經常會出現名稱寫錯,多輸入空格等等的情況

3. 下面我們就結合代碼來看一下數據

#1 從宏觀一點的角度去看數據:查看dataframe的信息
DataDF.info()

也可以用這兩條來看:

#1.1查看每一列的數據類型
DataDF.dtypes

#1.2有多少行,多少列
DataDF.shape

# 2.檢查缺失數據
# 如果你要檢查每列缺失數據的數量,使用下列代碼是最快的方法。
# 可以讓你更好地瞭解哪些列缺失的數據更多,從而確定怎麼進行下一步的數據清洗和分析操作。

DataDF.isnull().sum().sort_values(ascending=False)

# 3.是抽出一部分數據來,人工直觀地理解數據的意義,儘可能地發現一些問題
DataDF.head()

可以看到:

1)Country和UnitPrice都出現了NaN值,需要去掉

2)InvoiceDate的時間出現具體時分,可以刪去

3)Description大概率是人工填寫的數據,一般都會有比較多格式問題。

猜測會存在有標點符號摻雜/大小寫不一致等問題,所以進一步這些人工填寫數據的去重項拎出來研究一下

# 查看這個商品名稱的去重項
DataDF[Description].unique()

# 設置輸出全部的內容
# threshold就是設置超過了多少條,就會呈現省略
#(比如threshold=10的意思是超過10條就會省略)
np.set_printoptions(threshold=np.inf)

發現有很多空格的問題

根據第一步數據預處理後,整理一下該數據集有下列問題需要處理:

1)調整數據類型:由於一開始用到了str來導入,打算後期再更換格式,需要調整數據類型。

2)修改列名:該數據的名稱不易於理解,需要改列名

3)選擇部分子集:因為有部分列在數據分析中不需要用到

4)可能存在邏輯問題需要篩選:比如Unit Price為負

5)格式一致化:Description可能會存在有標點符號摻雜/大小寫不一致/空格重複出現等問題

6)消滅空值:CustomerID、Description、Country和UnitPrice都出現了NaN值,需要去掉

於是下面就開始後續的數據清洗6步


二、調整數據類型

數據類型調整前

#字元串轉換為數值(整型)
DataDF[Quantity] = DataDF[Quantity].astype(int)
#字元串轉換為數值(浮點型)
DataDF[UnitPrice] = DataDF[UnitPrice].astype(float)

日期調整前(為求簡便這裡用已經剔除分秒,剔除的辦法後面在格式一致化的空格分割再詳細說)

#數據類型轉換:字元串轉換為日期
#errors=coerce 如果原始數據不符合日期的格式,轉換後的值為空值NaT

DataDF.loc[:,InvoiceDate]=pd.to_datetime(DataDF.loc[:,InvoiceDate],
format=%d/%m/%Y,
errors=coerce)

#!!?? format 是你[原始數據]中日期的格式

%y 兩位數的年份表示(00-99
%Y 四位數的年份表示(000-9999
%m 月份(01-12
%d 月內中的一天(0-31
%H 24小時制小時數(0-23
%I 12小時制小時數(01-12
%M 分鐘數(00-59
%S 秒(00-59

日期類型調整後

數據類型調整完畢

三、修改列名

修改前

#建立字典字典:舊列名和新列名對應關係
colNameDict = {InvolceDate:SaleDate,StockCode:StockNo}

#!! ??一定要舊列名放在冒號前
#每組對應關係以[逗號]隔開

salesDf.rename(columns = colNameDict,inplace=True)

修改後

四、選擇部分子集

這是一個8列*541909行的數據集。

#選擇子集,選擇其中一列
subDataDF1=DataDF["InvoiceDate"]

#選擇子集,選擇其中兩列
subDataDF1=DataDF[["InvoiceDate","UnitPrice"]]

利用切片篩選數據功能 df.loc

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.loc.html#pandas.DataFrame.loc?

pandas.pydata.org

loc這個代碼有點像Excel裡面的滑鼠左鍵,可以隨意拉動你需要的數據進行切片。

以逗號作為隔開的界限,左邊為index,右邊為column

subDataDF1=DataDF.loc[:,"InvoiceDate"]
subDataDF1
#單一個冒號意味著不作限制的全選

subDataDF2=DataDF.loc[0:9,:]
subDataDF2

subDataDF3=DataDF.loc[1:9,"StockCode":"CustomerID"]
subDataDF3

五、邏輯問題需要篩選

還是Dataframe.loc這個函數的知識點。

由於loc還可以判斷條件是否為True

DataDF.loc[:,UnitPrice]>0

一般來說價格不能為負,所以從邏輯上來說如果價格是小於0的數據應該予以篩出

#刪除異常值:通過條件判斷篩選出數據
#查詢條件
querySer=DataDF.loc[:,Quantity]>0
#應用查詢條件
print(刪除異常值前:,DataDF.shape)
DataDF=DataDF.loc[querySer,:]
print(刪除異常值後:,DataDF.shape)

六、格式一致化

  1. 大小寫/去除空格

將我們數據中所有的Descrption改成大寫:

DataDF[Description]= DataDF[Description].str.upper()

類似的代碼還有 字元串修改方法:

str().
upper()
lower()
title()
lstrip()
strip()

DataDF[Description]= DataDF[Description].str.strip()

2. 去除字元串符號 去亂碼

3. 空格分割

#定義函數:分割InvoiceDate,獲取InvoiceDate
#輸入:timeColSer InvoiceDate這一列,是個Series數據類型
#輸出:分割後的時間,返回也是個Series數據類型

def splitSaletime(timeColSer):
timeList=[]
for value in timeColSer:
#例如2018/01/01 12:50,分割後為:2018-01-01
dateStr=value.split( )[0]
timeList.append(dateStr)
#將列錶轉行為一維數據Series類型
timeSer=pd.Series(timeList)
return timeSer

最後再賦值回去

DataDF.loc[:,InvoiceDate]=splitSaletime(DataDF.loc[:,InvoiceDate])

七、處理缺失值

python缺失值有3種:

1)Python內置的None值

2)在pandas中,將缺失值表示為NA,表示不可用not available。

3)對於數值數據,pandas使用浮點值NaN(Not a Number)表示缺失數據。 後面出來數據,如果遇到錯誤:說什麼float錯誤,那就是有缺失值,需要處理掉

所以,缺失值有3種:None,NA,NaN

那None和NaN有什麼區別呢:

None是Python的一種數據類型,

NaN是浮點類型

兩個都用作空值

1、去除缺失值

# 再一次提醒檢查缺失數據

DataDF.isnull().sum().sort_values(ascending=False)

去除缺失值的知識點:

DataFrame.dropna

DataFrame.dropna(axis=0, how=any, thresh=None, subset=None, inplace=False)

pandas.DataFrame.dropna - pandas 0.24.2 documentation?

pandas.pydata.org

# 默認(axis=0)是逢空值剔除整行,設置關鍵字參數axis=1表示逢空值去掉整列
# any如果一行(或一列)裏任何一個數據有任何出現Nan就去掉整行,
『all』一行(或列)每一個數據都是Nan纔去掉這整行

DataDF.dropna(how=any)
DataDF.dropna(how=all)

# 更精細的thresh參數,它表示留下此行(或列)時,要求有多少[非缺失值]
DataDF.dropna(thresh = 6 )

2、填充缺失內容:某些缺失值可以進行填充,方法有以下四種:

1) 以業務知識或經驗推測(默認值)填充缺失值

2) 以同一指標的計算結果(均值、中位數、眾數等)填充缺失值

3) 用相鄰值填充缺失值

4) 以不同指標的計算結果填充缺失值

去除缺失值的知識點:

DataFrame.fillna

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.fillna.html#pandas.DataFrame.fillna?

pandas.pydata.org

1) 用默認值填充- df.fillna( )

我們應該去掉那些不友好的 NaN 值。但是,我們應該用什麼值替換呢?這個時候可能要結合你對這個數據集的理解,看填充什麼數據纔是比較合適,以下是一下常用的方法。

在這個數據集中,我們大致判斷CustomerID如果是不太重要的,就我們可以用使用""空字元串或其他默認值。

DataDF.Country= DataDF.Country.fillna(Not Given)

上面,我們就將「country」整個列使用「」空字元串替換了,或者,我們也可以輕易地使用「Not Given」這樣的默認值進行替換。

如果想了解更多 fillna() 的詳細信息參考 pandas.DataFrame.fillna

pandas.DataFrame.fillna - pandas 0.24.2 documentation?

pandas.pydata.org

2) 以同一指標的計算結果(均值、中位數、眾數等)填充缺失值

平均值- df.fillna(df.mean())

使用數字類型的數據有可能可以通過這樣的方法來去減少錯誤。

比如,這個案例裡面的價格。如果用0或者"Not Given"等來去填充都不太合適,但這個大概的價格是可以根據其他數據估算出來的。

DataDF.UnitPrice = DataDF.UnitPrice.fillna(DataDF.UnitPrice.mean())

3)除此,還有一種常見的方法,就是用相鄰的值進行填充,

這在時間序列分析中相當常見,用前面相鄰的值向後填充,也可以用後面相鄰的值向前填充。

print(DataDF)
print(DataDF.UnitPrice.fillna(method=ffill)) # 前向後填充
print(DataDF.UnitPrice.fillna(method=bfill)) # 後向前填充

填充後

4) 以不同指標的計算結果填充缺失值

關於這種方法年齡欄位缺失,但是有屏蔽後六位的身份證號可以推算具體的年齡是多少。

參考來源:

張雨萌-醬油哥:給妹子講python-S02E14Pandas缺失值的處理?

zhuanlan.zhihu.com
圖標
陳丹奕:數據清洗的一些梳理?

zhuanlan.zhihu.com
圖標
量子位:8個數據清洗Python代碼,複製可用,最長11行 | 資源?

zhuanlan.zhihu.com
圖標

推薦閱讀:
相關文章