0
本文作者: skura | 2019-11-23 20:26 |
根據(jù) Businessbroadway 的一項分析,數(shù)據(jù)專業(yè)人員將會花高達 60% 的時間用于收集、清理和可視化數(shù)據(jù)。
資料來源:Businessbroadway
清理和可視化數(shù)據(jù)的一個關鍵方面是如何處理丟失的數(shù)據(jù)。Pandas 以 fillna 方法的形式提供了一些基本功能。雖然 fillna 在最簡單的情況下工作得很好,但只要數(shù)據(jù)中的組或數(shù)據(jù)順序變得相關,它就會出現(xiàn)問題。本文將討論解決這些更復雜情況的技術。
這些情況通常是發(fā)生在由不同的區(qū)域(時間序列)、組甚至子組組成的數(shù)據(jù)集上。不同區(qū)域情況的例子有月、季(通常是時間范圍)或一段時間的大雨。性別也是數(shù)據(jù)中群體的一個例子,子組的例子有年齡和種族。
這篇文章附帶了代碼。所以你可以隨意啟動一個 Notebook,直接開始。
文章結(jié)構:
Pandas fillna 概述
當排序不相關時,處理丟失的數(shù)據(jù)
當排序相關時,處理丟失的數(shù)據(jù)
Pandas fillna 概述
圖片來自 Pixabay
Pandas 有三種通過調(diào)用 fillna()處理丟失數(shù)據(jù)的模式:
method='ffill':ffill 或 forward fill 向前查找非空值,直到遇到另一個非空值
method='bfill':bfill 或 backward fill 將第一個觀察到的非空值向后傳播,直到遇到另一個非空值
顯式值:也可以設置一個精確的值來替換所有的缺失值。例如,這個替換值可以是 -999,以表示缺少該值。
例子:
當排序不相關時,處理丟失的數(shù)據(jù)
來自 Pixabay 公共領域的圖片
通常,在處理丟失的數(shù)據(jù)時,排序并不重要,因此,用于替換丟失值的值可以基于可用數(shù)據(jù)的整體來決定。在這種情況下,你通常會用你猜測的最佳值(即,可用數(shù)據(jù)的平均值或中等值)替換丟失的值。
讓我們快速回顧一下為什么應該小心使用此方法。假設你調(diào)查了 1000 個男孩和 1000 個女孩的體重。不幸的是,在收集數(shù)據(jù)的過程中,有些數(shù)據(jù)丟失了。
# imports
import numpy as np
# sample 1000 boys and 1000 girls
boys = np.random.normal(70,5,1000)
girls = np.random.normal(50,3,1000)
# unfortunately, the intern running the survey on the girls got distracted and lost 100 samples
for i in range(100):
girls[np.random.randint(0,1000)] = np.nan
# build DataFrame
boys = pd.DataFrame(boys, columns=['weight'])
boys['gender'] = 'boy'
girls = pd.DataFrame(girls, columns=['weight'])
girls['gender'] = 'girl'
df = pd.concat([girls,boys],axis=0)
df['weight'] = df['weight'].astype(float)
子組
如果不是很在意缺失值填充什么,我們可以用整個樣本的平均值填充缺失的值。不過,結(jié)果看起來有些奇怪。女孩的 KDE 有兩個駝峰。有人可能會得出結(jié)論,在我們的樣本中有一個子組的女孩體重較重。因為我們預先構建了分布,所以我們知道情況并非如此。但如果這是真實的數(shù)據(jù),我們可能會從中得出錯誤的結(jié)論。
男孩和女孩的體重 KDE,我們用樣本均值替換缺失的數(shù)據(jù)(下附代碼)
# PLOT CODE:
sns.set_style('white')
fig, ax = plt.subplots(figsize=(16, 7))
mean = df['weight'].mean()
sns.distplot(
df[df['gender'] == 'girl']['weight'].fillna(mean),
kde=True,
hist=False,
ax=ax,
label='girls'
)
sns.distplot(
df[df['gender'] == 'boy']['weight'],
kde=True,
hist=False,
ax=ax,
label='boys'
)
plt.title('Kernel density estimation of weight for boys and girls')
sns.despine()
用組的平均值填充缺失值
在這種情況下,Pandas 的轉(zhuǎn)換函數(shù)就派上了用場,它使用變換提供了一種簡潔的方法來解決這個問題:
df['filled_weight'] = df.groupby('gender')['weight'].transform(
lambda grp: grp.fillna(np.mean(grp))
)
運行上述命令并繪制填充的權重值的 KDE 將得到:
男孩和女孩權重的 KDE,我們用組平均值替換缺失值(下面附代碼)
# PLOT CODE:
sns.set_style('white')
fig, ax = plt.subplots(figsize=(16, 7))
sns.distplot(
df[df['gender'] == 'girl']['filled_weight'],
kde=True,
hist=False,
ax=ax,
label='girls'
)
sns.distplot(
df[df['gender'] == 'boy']['filled_weight'],
kde=True,
hist=False,
ax=ax,
label='boys'
)
plt.title('Kernel density estimation of weight for boys and girls')
sns.despine()
多個子組
讓我們使用前面的例子,但是這次,我們進一步將數(shù)據(jù)細分為年齡組。我們先創(chuàng)建一些模擬數(shù)據(jù):
# paramter for the weight distribution (mean, std)
param_map = {
'boy':{
'<10':(40,4),
'<20':(60,4),
'20+':(70,5),
},
'girl':{
'<10':(30,2),
'<20':(40,3),
'20+':(50,3),
}
}
# generate 10k records
df = pd.DataFrame({
'gender':np.random.choice(['girl','boy'],10000),
'age_cohort':np.random.choice(['<10','<20','20+'],10000)
})
# set random weight based on parameters
df['weight'] = df.apply(
lambda x: np.random.normal(
loc=param_map[x['gender']][x['age_cohort']][0],
scale=param_map[x['gender']][x['age_cohort']][1]
),axis=1
)
# set 500 values missing
for i in range(500):
df.loc[np.random.randint(0,len(df)),'weight'] = np.nan
繪制數(shù)據(jù)圖,會出現(xiàn)一些奇怪的雙峰分布(后面有代碼)。
用樣本平均值代替缺失值
# PLOT CODE
df['filled_weight'] = df['weight'].fillna(
df['weight'].mean()
)
g = sns.FacetGrid(
df,
col='age_cohort',
row='gender',
col_order=['<10','<20','20+']
)
g.map(sns.kdeplot,'filled_weight')
現(xiàn)在,如果我們只用性別的平均值來代替缺失的值,就遠遠不夠,因為男孩和女孩不僅體重不同,而且不同年齡組的體重也大不相同。
幸運的是,可以像前面一樣使用轉(zhuǎn)換。我們將對兩列進行分組,代碼如下:
df['filled_weight'] = df.groupby(['gender','age_cohort'])
['weight'].transform(
lambda grp: grp.fillna(np.mean(grp))
)
運行上述代碼片段將生成更清晰的曲線:
按年齡、性別分組的體重 KDE 用各組的平均值代替缺失值
當順序相關時,處理丟失的數(shù)據(jù)
Jake Hills 在 Unsplash 上的照片
在處理時間序列數(shù)據(jù)時,經(jīng)常會出現(xiàn)兩種情況:
調(diào)整日期范圍:假設你有一份關于各國的 GDP、教育水平和人口年增長率的數(shù)據(jù)。對一些國家來說,你缺失了最初幾年、最后幾年或者中間幾年的數(shù)據(jù)。當然,你可以忽略它們。不過,為了可視化,你可能想要填充這些數(shù)據(jù)。
插值:看時間序列數(shù)據(jù)插值,你會發(fā)現(xiàn)排序變得非常相關。如果用基于截至 2019 年的數(shù)據(jù)計算出的平均值來替換 2012 年丟失的股票數(shù)據(jù),勢必會產(chǎn)生一些古怪的結(jié)果。
我們將以《2019 年世界幸福報告》(World Happiness Report 2019)中的數(shù)據(jù)為基礎來看一個例子,在這個例子中,我們將處理這兩種情況?!妒澜缧腋蟾妗吩噲D回答影響全世界幸福的因素。該報告調(diào)查了 2005 年至 2018 年的數(shù)據(jù)。
載入數(shù)據(jù)
# Load the data
df = pd.read_csv('https://raw.githubusercontent.com/FBosler/you- datascientist/master/happiness_with_continent.csv')
樣本檢驗
與 df.head(5)相反,df.sample(5) 選擇五個隨機行,從而使你有一個偏差更小的數(shù)據(jù)可視化圖。
下載數(shù)據(jù)幀中的數(shù)據(jù)示例
讓我們看看我們每年有多少國家的數(shù)據(jù)。
每年有數(shù)據(jù)的國家數(shù)量
# PLOT CODE:
df.groupby(['Year']).size().plot(
kind='bar',
title='Number of countries with data',
figsize=(10,5)
)
我們可以看到,特別是在早些年,我們沒有多少國家的數(shù)據(jù),而且整個樣本周期都有一些波動。為了減輕丟失數(shù)據(jù)的影響,我們將執(zhí)行以下操作:
按國家分組并重新索引到整個日期范圍
在對每個國家分組的范圍之外的年份內(nèi)插和外推
1.按國家分組并重新索引日期范圍
# Define helper function
def add_missing_years(grp):
_ = grp.set_index('Year')
_ = _.reindex(list(range(2005,2019)))
del _['Country name']
return _
# Group by country name and extend
df = df.groupby('Country name').apply(add_missing_years)
df = df.reset_index()
我們現(xiàn)在大約有 600 行數(shù)據(jù)。然而,這些觀察結(jié)果現(xiàn)在是無效的。
擴展數(shù)據(jù)幀,所有國家在 2005 年到 2018 年間都有數(shù)據(jù)
2.在對每個國家分組的范圍之外的年份內(nèi)插和外推
# Define helper function
def fill_missing(grp):
res = grp.set_index('Year')\
.interpolate(method='linear',limit=5)\
.fillna(method='ffill')\
.fillna(method='bfill')
del res['Country name']
return res
# Group by country name and fill missing
df = df.groupby(['Country name']).apply(
lambda grp: fill_missing(grp)
)
df = df.reset_index()
fill_missing 函數(shù)在末尾和開頭進行插值和外推,結(jié)果是:
很完美!現(xiàn)在我們有樣本中所有國家 2005 年至 2018 年的數(shù)據(jù)。當我寫這篇關于可視化的文章時,上面的方法對我來說很有意義。如果你想了解更多關于這篇報告的信息,可以查看:https://towardsdatascience.com/plotting-with-python-c2561b8c0f1f?source=post_page-----cb6ccf060531---------------------- 。
雷鋒網(wǎng)雷鋒網(wǎng)雷鋒網(wǎng)
雷峰網(wǎng)版權文章,未經(jīng)授權禁止轉(zhuǎn)載。詳情見轉(zhuǎn)載須知。