從今天開始,我們再一起來學習數據分析,共同進步!
首先先來進行一個數據清洗的實戰,使用比較經典的數據集,鐵達尼號生存預測數據。
數據集下載地址
https://github.com/zhouwei713/DataAnalyse/tree/master/Titanic_dataset
導入數據import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
df = pd.read_csv('titanic_data.csv')
df
數據集信息如下:
各欄位含義
pclass:船票等級
sibsp:一同登船的兄弟姐妹或配偶數量
parch:一同登船的父母或子女數量
ticket:船票號碼
fare:船票價格
cabin:船艙
embarked:登船地點
數據整體查看拿到數據之後,我們先整體查看下數據信息
df.describe()
還有些列是非數值的,所以沒有展示。
這裡得到的各項指標,我們先保存不動,在後面處理缺失值時會有用到。
處理缺失值首先查看缺失值
df.isnull().sum()
>>>
pclass 1
survived 1
name 1
sex 1
age 264
sibsp 1
parch 1
ticket 1
fare 2
cabin 1015
embarked 3
dtype: int64
可以看到,缺失值比較多的是 cabin 和 age
年齡處理查看缺失佔比
print('缺失佔比 %.2f%%' %((df['age'].isnull().sum()/df.shape[0])*100))
>>>
缺失佔比 20.15%
下面查看下年齡的分布情況
age = df['age'].hist(bins=15, color='teal', alpha=0.6)
age.set(xlabel='age')
plt.xlim(-10,85)
plt.show()
從圖中我們可以看出,整體數據是向左偏的,即大多數數據是小於平均值的,故而我們可以採用中位數來填補空值,而不是平均數。
從上面的 describe 函數的輸出值也可以看出,平均值是 29.88,中位數是 28,顯然中位數更加接近於大部分數據所在的區域。
使用中位數填充空缺的年齡值
data = df.copy()
data['age'].fillna(df['age'].median(skipna=True), inplace=True)
查看缺失百分比
print('缺失百分比 %.2f%%' %((df['cabin'].isnull().sum()/df.shape[0])*100))
>>>
缺失百分比 77.48%
由於倉位信息已經缺失了大部分,所以這裡選擇直接刪除處理
data.drop(columns=['cabin'], inplace=True)
我們先來查看下登船地點這列數據的形式
print(df['embarked'].value_counts())
sns.countplot(x='embarked', data=df, palette='Set2')
plt.show()
>>>
S 914
C 270
Q 123
Name: embarked, dtype: int64
可以看到,登船地點總共包含三類數據,S、C 和 Q,他們出現的次數分別為 914、270 和 123。
又因為該列數據總共缺失 3 個,缺失率很低,使用眾數來填充這三個缺失值應該是沒問題的。
使用眾數填充
data['embarked'].fillna(df['embarked'].value_counts().idxmax(), inplace=True)
對於其他列,只是缺失了一到兩個,可以採用眾數的方式來填充缺失值,也可以選擇直接刪除掉缺失的部分,不影響整體數據分布
data.dropna(axis=0, how='any', inplace=True)
最後,再查看確認下是否不存在缺失值了
data.isnull().sum()
>>>
pclass 0
survived 0
name 0
sex 0
age 0
sibsp 0
parch 0
ticket 0
fare 0
embarked 0
dtype: int64
對於 sibsp 和 parch 兩列,我們可以抽象成是否是獨自登船,這樣就能夠把兩列合併為一列,並用 0,1 來表示是否獨自登船。
我們新增一列 alone,把兩列都是 0 的數據添加到新列中並設置為 0,把兩列相加不為 0 的數據添加到新列中,並設置數值為 1。那麼原來的兩列就可以刪除了。
data['alone']=np.where((data["sibsp"]+data["parch"])>0, 0, 1)
data.drop('sibsp', axis=1, inplace=True)
data.drop('parch', axis=1, inplace=True)
data.head()
對於 embarked 和 sex 這兩列,都是字符串類型的數據,需要轉化為數字才能被算法模型分析處理。
這裡可以採用獨熱編碼的方式,來轉換數據
data =pd.get_dummies(data, columns=["embarked","sex"])
data.head()
獨熱編碼(one-hot encoding),是一種常用的數據轉換方式,對於每一個特徵,如果它有 m 個可能值,那麼經過獨熱編碼後,就變成了 m 個二元特徵,這些特徵互斥,每次只有一個激活。
對於 name 和 ticket 兩列,由於他們的存在,對於我們的數據分析沒有任何意義,往往會直接刪除掉這樣無意義的數據
data.drop('name', axis=1, inplace=True)
data.drop('ticket', axis=1, inplace=True)
至此,我們就把一份原始的數據,處理成了比較標準的,易於數據分析的數據。
透視表分析在處理數據之後,我們還可以使用透視表,整體分析下數據
這裡主要查看下各個特徵(船票等級,性別,倉位等)對於存活率的影響
注意數據集 df 與 data 的區別
性別透視表首先來看下,不同性別,存活率的情況
sex_sur_table = pd.pivot_table(df, index=['sex'], values='survived')
print(sex_sur_table)
>>>
survived
sex
female 0.727468
male 0.190985
女性存活率是遠遠高於男性的,ladies first。
船票等級與存活率pclass_sur_table = pd.pivot_table(df, index=['sex'], columns=['pclass'], values='survived')
print(pclass_sur_table)
>>>
pclass 1.0 2.0 3.0
sex
female 0.965278 0.886792 0.490741
male 0.340782 0.146199 0.152130
可以看到,一等船票的女性存活率是非常高的,同時船票等級越高,無論男女,存活率都越高
不同年齡存活率將年齡離散化處理
data['age_cut'] = pd.cut(data['age'], [0, 18, 90])
data['sex'] = df['sex']
print(data.head())
>>>
pclass survived age fare alone embarked_C embarked_Q \
0 1.0 1.0 29.0000 211.3375 1 0 0
1 1.0 1.0 0.9167 151.5500 0 0 0
2 1.0 0.0 2.0000 151.5500 0 0 0
3 1.0 0.0 30.0000 151.5500 0 0 0
4 1.0 0.0 25.0000 151.5500 0 0 0 embarked_S sex_female sex_male age_cut sex
0 1 1 0 (18, 90] female
1 1 0 1 (0, 18] male
2 1 1 0 (0, 18] female
3 1 0 1 (18, 90] male
4 1 1 0 (18, 90] female
年齡段與存活率
age_cut_sur_table = pd.pivot_table(data, index=['sex'], columns=['pclass', 'age_cut'], values='survived')
print(age_cut_sur_table)
>>>
pclass 1.0 2.0 3.0
age_cut (0, 18] (18, 90] (0, 18] (18, 90] (0, 18] (18, 90]
sex
female 0.923077 0.969466 0.952381 0.870588 0.534483 0.474684
male 0.750000 0.321637 0.523810 0.093333 0.208333 0.142857
當然,透視表還有很多強大的功能,你可以試著探索更多。
數據清洗的重要性要知道,一個好的數據分析師必定是一名數據清洗高手。在數據分析的過程中,數據清洗是最佔用時間與精力的步驟。數據質量的高低,直接影響我們最後分析的結果,千萬馬虎不得。
數據質量的準則那麼既然數據清洗這麼重要,我需要把原始數據處理到什麼程度,才算是合格的待分析數據呢?如下我總結了一些業界的標準,可以供你參考。
完整性:數據集中是否存在空值,統計的欄位是否完善。
全面性:某列數據,是否能夠全面的反應真實的情況,是否只包含一部分情況。
合法性:數據的類型,內容,大小等是否合理。比如:是否有年齡超過 150 的,是否有成績超過 1 萬的,數據單位是否統一等等。
唯一性:數據是否存在重複記錄。
在進行數據清洗的時候,一定要先耐心的觀察數據,充分的理解每列數據的意義,從真實的情況出發分析數據是否有真實的含義,再根據生活工作中的經驗,來逐一處理數據。
我們再用一個小例子來理解下
姓名身高體重年齡年齡張飛180把數據轉化成 Pandas 數據結構
mydata = pd.read_csv('mydata.csv', index_col='name')
print(mydata)
>>>
height weight age age.1
name
張飛 180.00 NaN 500 500
關羽 181.00 100.0 28 28
劉備 1.78 160.0 30K 30K
趙雲 175.00 140.0 23 23
曹操 180.00 150.0 37 37
趙雲 175.00 140.0 23 23
典韋 NaN NaN NaN NaN
查看缺失值
mydata1 = mydata.copy() # copy
mydata1.isnull().sum() # 查看總體缺失值
>>>
height 1
weight 2
age 1
age.1 1
dtype: int64
一般是處理空值,空行等
mydata1['weight'].fillna(mydata1['weight'].mean(), inplace=True) # 使用平均值填充
#mydata1['height'].fillna(mydata['height'].value_counts().index[0], inplace=True) # 使用眾數填充
mydata1.dropna(how='any', inplace=True) # 刪除空行
一定要先執行空值填充,再執行刪除空行的代碼,否則含有空值的行都會被刪除。
全面性劉備的身高是「米」的單位,我們需要轉換為「釐米」
mydata1.loc[mydata1['height']<100, 'height'] = mydata1[mydata1['height']<100]['height']*100
張飛的年齡是 500,顯然不合理,需要處理。因為張飛是三弟,年齡需要比劉備和關羽小,就設置為 27 吧
mydata1.loc['張飛', 'age'] = 27
同時劉備的年齡還存在一個 K 字符,需要去掉
mydata1['age'].replace({r'[K]': ''}, regex=True, inplace=True)
數據中還存在重複的行和列,也需要刪除,保證數據的唯一性
mydata1.drop_duplicates(inplace=True) # 刪除重複行
mydata1.drop('age.1', axis=1, inplace=True) # 刪除不需要的列
最終我們的數據為
print(mydata1)
>>>
height weight age
name
張飛 180.0 138.0 27
關羽 181.0 100.0 28
劉備 178.0 160.0 30
趙雲 175.0 140.0 23
曹操 180.0 150.0 37
本節我們共同完成了一個數據清洗的實戰和一個練習小例子。對於缺失值,需要根據其缺失的百分比及數據分布情況,來決定如何填充缺失值。對於一些非數字類型的數據,可以選擇獨熱編碼等方式轉換數據。還總結了數據清洗的準則,只要你遵循這些準則來處理數據,那麼得到的數據基本就是「好」的數據了。
招聘 or 求職,歡迎加數據分析工作交流群(新)
發求職介紹,聯繫小編!
發內推職位,聯繫小編!
直播分享經驗,聯繫小編!
加群交流(禁廣告),聯繫小編!
加群,掃小編微信