數據清洗概念
之前已經講過,數據分析的過程是這樣的。
1.明確需求 2.數據采集 3.數據清洗 4.數據分析 5.數據報告+數據可視化
之前我們學習的一系列python模塊,比如BeautifulSoup、Xpath、selenium等模塊,都是屬於數據清洗的范疇;matplotlib模塊屬於數據可視化模塊。numpy和pandas模塊我們側重於學習了理論部分,接下來我們就要學習如何在實際工作中使用它們了。
干巴巴的理論不太好理解,我們還是打個比方來幫助記憶,如果把數據分析比作做菜:
數據分析過程 → 做菜過程
明確需求 明確做什么菜品
數據采集 去市場買菜
數據清洗 洗菜切菜配菜
數據分析 下鍋炒菜
數據報告+數據可視化 拍照發朋友圈+吃菜
什么是數據清洗?
數據清洗的定義:從記錄表、表格、數據庫中檢測、糾正或刪除損壞或不准確記錄的過程。
換個俏皮的說法,數據清洗就是把臟數據變為干凈的數據的過程。
臟數據:沒有經過處理的自身含有一定問題的數據(殘缺數據、錯誤數據、重復數據、不符合規則的數據)。
干凈數據:經過處理的完全符合規范要求的數據(可以直接帶入模型)。
數據清洗的常用方法
1.讀取外部數據
pd.read_csv
pd.read_excel
pd.read_sql
pd.read_html
2.數據概覽
index
columns
head
tail
shape
describe
info
dtype
除了info比較眼生,這些內容都是之前在pandas模塊的DataFrame數據類型中就學習過的參數。
3.簡單處理
移除首尾空格
大小寫轉換
4.重復值處理
duplicated() # 查看是否含有重復數據 drop_duplicats() # 刪除重復數據
MySQL中的操作
我們來回憶一下,MySQL中如何快速判斷某列是否含有重復的數據?
create database db1; create table t1(id int,name varchar(32),pwd int); insert into t1 values(1,'joe',123),(2,'simon',321),(3,'frank',222),(4,'jerry',111),(5,'eddie',222),(6,'eddie',333);
思路:
select count(name) from t1; # 統計表t1中name字段的數據量
如果只單純給name字段計數的話,它只會給出name字段所有數據的數量。
所以我們首先要進行去重操作,然后統計字段去重后的數據。將得到的計數和原先未去重的計數相一比較,立馬就能知道有沒有重復的數據了。
select count(distinct(name)) from t1; # 將name字段的數據去重之后再計數
若兩者數字相同,表示name列沒有重復的數據,不同則表示含有重復的數據。
5.缺失數值處理
刪除缺失值 填充缺失值 均值填充法 向前填充/向后填充(原理,視頻03) 模型填補法,如隨機森林 ……
向前填充即是指缺失值選用前面一個值進行填充,向后填充即是指缺失值選用后面一個值進行填充。
模型填補法這里暫且不展開講,混個眼熟即可。
6.異常值處理
刪除異常值
作為缺失值處理
修正異常值(同樣是當做缺失值處理)
平均值修正、蓋帽法修正
7.文本字符串處理
切割
篩選
……
8.時間格式處理
Y # 年 m # 月 d # 日 H # 時 M # 分 S # 秒
其中許多內容都是曾經學習過的知識點,具體用法可以參考過去的博客。
在整套數據清洗流程中,第3~第8步沒有固定的順序,可以根據需要隨意調整操作順序。前期不熟練時可以按序號來。
數據清洗實戰案例
接下來我們就通過一個實際案例來看看數據清洗應該怎么做,同時這也是對之前學的個方面內容的一個全面回顧。
旅游數據的清洗
首先啟動anaconda,等到載入完后運行jupyter notebook。新建文件夾並且重命名便於辨識。放入數據資料,新建python3文件。准備工作都完成后我們可以進行編程了。
首先還是雷打不動地載入需要用到的模塊。
# 導入三劍客 import numpy as np import pandas as pd import matplotlib.pyplot as plt
1.讀取數據
data = pd.read_csv(r'qunar_freetrip.csv')
2.數據概覽
data.index # 查看行數 data.columns # 查看列字段 data.shape # 查看行數和列數 data.head() # 查看頭幾行數據 data.tail() # 查看尾幾行數據 data.dtypes # 查看字段類型 data.describe() # 簡單的數據統計 data.describe(include='all') # include參數可以指定計算的類型,all表示不論數據類型所有字段都進行數據統計(一般不加) data.info() # 既可以查看每個列的數據類型,還可以查看缺失數據量
data.info是個挺有意思的語句,不光列出了字段的名稱,還列出了每個字段的總數、缺失數據數量、字段數據類型。
3.簡單處理
1.將Unnamed: 0列刪除
data.drop(columns=['Unnamed: 0'],inplace=True) # 既可以刪列數據也可以刪行數據
2.列字段處理
data['列字段'] # 獲取指定列字段的數據 eg: data['目的地'] # 報錯
報錯?但是語句沒問題啊。
其實這里是有個小陷阱,如果觀察不仔細很容易忽略。
data.columns # 查看列字段
有些列字段中帶有空格,所以單寫中文沒法獲取到數據。
所以應該先移除列字段空格。
new_columns = [] # 定義一個空列表 for col in data.columns: # 循環獲取列字段名 new_columns.append(col.strip()) # 將去除空格后的列字段名加入到空列表中 data.columns = new_columns # 修改原列字段
這里還可以做優化,用列表生成的方式就能用一行代碼解決。
data.columns = [col.strip() for col in data.columns] data.columns = new_columns
無論用哪種方式,改完后都可以正常索引獲取數據了。
data['目的地'] # 可以正常獲取
4.重復值處理
首先,查看是否含有重復數據。
data.duplicated().sum() # 查看重復數據的數量 data[data.duplicated()] # 查看重復數據
其中有100條重復數據。
# 直接刪除重復數據 data.drop_duplicates(inplace=True) # 驗證是否成功 data.duplicated().sum() # 0 data.shape # (5000, 13) # 刪除重復數據后將引發行標簽問題(行標簽並不會重新排序) data.tail()
針對行標簽是否需要重新排序,取決於業務需要,一般情況下可以不重排。
data.reset_index(inplace=True) # 重置行標簽,重置后會默認保留原來的行標簽 data.drop(columns=['index'],inplace=True) # 刪除原來的行標簽
重置行標簽另有一種更簡便的方法(支持自定義起始數字,推薦)
data.index = range(0,df.shape[0])
5.異常值處理
1.利用快速統計大致篩選出可能有異常值的列字段
data.describe()
節省這一列看起來很不正常,統計下來的總和居然比價格還要多出幾塊錢來。
可以利用公式求證我們的猜想。
公式計算:(列數據-列數據均值)/列數據標准差,如果值大於3就是有異常的。 sd = (((data['價格'] - data['價格'].mean()) / data['價格'].std())>3).sum() # 有可能是負數
得到的結果可能是負數,所以這里需要用到絕對值。
sd_abs = abs(((data['價格'] - data['價格'].mean()) / data['價格'].std())>3).sum() data[sd_abs]
2.同理驗證節省是否有異常
sd2_abs = abs(((data['節省'] - data['節省'].mean()) / data['節省'].std())>3).sum() # 按照公式計算確實存在異常,但可能是由於促銷、引流等因素導致的,針對這類數據可以保留
3.驗證節省大於價格的
data[data['節省'] > data['價格']]
4.刪除價格和節省都有異常的數據
pd.concat(data[]) # 合並表 data.drop(index=del_index,inplace=True) # 操作完后行標簽又變了,需要的話還是要調整
6.缺失數據處理
# 查看缺失數據的方式 data.isnull().sum() # 求每個列字段中缺失數據的數量 data.info() # 也可以驗證
1.修改出發地缺失數據
res = data[data['出發地'].isnull()] # 篩選出出發地為缺失值的數據
缺失數據項有時可能存在於其他列數據中。
接下來就是取值了。
res['路線名'].values # 從路線名字段中取值
針對本案例有多種獲取城市名的方式。
# 1.字符串切片(適用性太窄,局限性太大,不推薦) d1[0:2] # 2.字符串切割(可以) d1.split('-')[0] # 3.正則表達式 import re re.findall('(.*?)-',d2) # 可用列表表達式來完成 data.loc[data.出發地.isnull(),'出發地'] = [i.split('-')[0] for i in data.loc[data.出發地.isnull(),'路線名'].values] # 驗證是否還有缺失數據 data[data['出發地'].isnull()]
2.修改目的地缺失數據
# 篩選出目的地為缺失值的數據 res = data[data['目的地'].isnull()] res # 從路線名字段中取值 data.loc[data.目的地.isnull(),'路線名'].values d3 = '深圳-大連3天2晚 | 入住大連黃金山大酒店 + 南方航空/東海往返機票' # 正則篩選出需要的數據 re.findall('-(.*?)\d',d3) # 三元表達式整合 data.loc[data.目的地.isnull(),'目的地'] = [re.findalll('-(.*?)\d',i) for i in data.loc[data.目的地.isnull(),'路線名'].values] # 驗證是否還有缺失數據 data[data['目的地'].isnull()]
3.修改價格缺失數據
round(data['價格'].mean(),1) #求價格均值,保留一位小數 data['價格'].fillna(round(data['價格'].mean(),1),inplace=True) # 用價格平均值填充入缺失數據 data[data['價格'].isnull()] # 驗證是否還有缺失數據
4.修改節省缺失數據
round(data['節省'].mean(),1) #求節省均值,保留一位小數 data['節省'].fillna(round(data['節省'].mean(),1),inplace=True) # 用節省平均值填充入缺失數據 data[data['節省'].isnull()] # 驗證是否還有缺失數據
如此一來,例題的數據清洗步驟就完成了。