編者按:Mail.Ru數(shù)據(jù)科學(xué)家Yury Kashnitsky和Segmento數(shù)據(jù)科學(xué)家Katya Demidova合作開(kāi)設(shè)了機(jī)器學(xué)習(xí)開(kāi)發(fā)課程。第一課介紹了如何使用Pandas進(jìn)行數(shù)據(jù)分析。
這一篇文章意味著,我們OpenDataScience的開(kāi)放機(jī)器學(xué)習(xí)課程開(kāi)始了。這一課程的目標(biāo)并不是開(kāi)發(fā)另一個(gè)全面的機(jī)器學(xué)習(xí)或數(shù)據(jù)分析的引導(dǎo)課程(所以這并不能代替基礎(chǔ)性的教育,在線和線下課程,培訓(xùn),以及書(shū)籍)。本系列文章的目的是幫你快速溫習(xí)知識(shí),同時(shí)幫你找到進(jìn)一步學(xué)習(xí)的主題。我們的角度和Deep Learning book的作者們類(lèi)似,從回顧數(shù)學(xué)和機(jī)器學(xué)習(xí)基礎(chǔ)開(kāi)始——簡(jiǎn)短扼要,包含很多指向其他資源的鏈接。
這一課程的設(shè)計(jì)考慮到了理論和實(shí)踐的平衡;因此,每個(gè)主題附帶了作業(yè)。你可以參加Kaggle上相應(yīng)的競(jìng)賽。
我們?cè)贠penDataScience的Slack小組中討論這一課程。請(qǐng)?zhí)顚?xiě)這個(gè)表單申請(qǐng)加入。
概覽
關(guān)于本課程
作業(yè)說(shuō)明
主要Pandas方法
預(yù)測(cè)電信運(yùn)營(yíng)商的客戶(hù)離網(wǎng)率
作業(yè)一
參考資源
1. 關(guān)于本課程
大綱
使用Pandas探索數(shù)據(jù)分析
使用Python可視化數(shù)據(jù)
分類(lèi)、決策樹(shù)、K近鄰
線性分類(lèi)、線性回歸
Bagging、隨機(jī)森林
特征工程、特征選取
無(wú)監(jiān)督學(xué)習(xí):主成分分析、聚類(lèi)
Vowpal Wabbit:學(xué)習(xí)GB級(jí)數(shù)據(jù)
使用Python進(jìn)行時(shí)序分析
梯度提升
社區(qū)
我們的課程的最突出的優(yōu)勢(shì)之一就是活躍的社區(qū)。如果你加入OpenDataScience的Slack小組,你會(huì)發(fā)現(xiàn)文章和作業(yè)的作者們?cè)?eng_mlcourse_open頻道熱心答疑。對(duì)初學(xué)者而言,這非常有用。請(qǐng)?zhí)顚?xiě)這個(gè)表單申請(qǐng)加入。表單會(huì)詢(xún)問(wèn)一些關(guān)于你的背景和技能的問(wèn)題,包括幾道簡(jiǎn)單的數(shù)學(xué)題。
我們的聊天比較隨意,喜歡開(kāi)玩笑,經(jīng)常使用表情。不是所有的MOOC敢夸口說(shuō)自己有這樣一個(gè)活躍的社區(qū)。另外,Reddit上也有這一課程的subreddit。
預(yù)備知識(shí)
預(yù)備知識(shí):
微積分的基本概念
線性代數(shù)
概率論和統(tǒng)計(jì)
Python編程技能
如果你需要補(bǔ)習(xí)一下,可以參考“Deep Learning”一書(shū)的第一部分,以及眾多數(shù)學(xué)和Python的線上課程(例如,CodeAcademy的Python課程)。以后我們的wiki頁(yè)面將補(bǔ)充更多信息。
軟件
目前而言,你只需要Anaconda(基于Python 3.6),就可以重現(xiàn)當(dāng)前課程中的代碼。在之后的課程中,你需要安裝其他庫(kù),例如Xgboost和Vowpal Wabbit。
你也可以使用這個(gè)Docker容器,其中已經(jīng)預(yù)裝了所有需要用到的軟件。更多信息請(qǐng)看相應(yīng)的wiki頁(yè)面。
2. 作業(yè)
每篇文章以Jupyter notebook的形式給出作業(yè)。作業(yè)包括補(bǔ)全代碼片段,以及通過(guò)Google表單回答一些問(wèn)題。
每個(gè)作業(yè)必須在一周之內(nèi)完成。
請(qǐng)?jiān)贠penDataScience的Slack小組#eng_mlcourse_open頻道或直接評(píng)論留言討論課程內(nèi)容。
作業(yè)的答案會(huì)發(fā)給提交了相應(yīng)的Google表單的用戶(hù)。
Pandas的主要方法
好吧……其實(shí)已經(jīng)有很多關(guān)于Pandas和可視化數(shù)據(jù)分析的教程了。如果你很熟悉這些主題,請(qǐng)等待本系列的第3篇文章,正式開(kāi)始機(jī)器學(xué)習(xí)的內(nèi)容。
下面的材料以Jupyter notebook的形式查看效果最佳。如果你克隆了本教程的代碼倉(cāng)庫(kù),你也可以在本地運(yùn)行。
Pandas是一個(gè)Python庫(kù),提供了大量數(shù)據(jù)分析的方法。數(shù)據(jù)科學(xué)家經(jīng)常和表格形式的數(shù)據(jù)(比如.csv、.tsv、.xlsx)打交道。Pandas可以使用類(lèi)似SQL的方式非常方便地加載、處理、分析這些表格形式的數(shù)據(jù)。搭配Matplotlib和Seaborn效果更好。
Pandas的主要數(shù)據(jù)結(jié)構(gòu)是Series和DataFrame類(lèi)。前者是一個(gè)包含某種固定類(lèi)型數(shù)據(jù)的一維數(shù)組,后者是一個(gè)二維數(shù)據(jù)結(jié)構(gòu)——一張表格——其中每列包含相同類(lèi)型的數(shù)據(jù)。你可以把它看成Series實(shí)例構(gòu)成的字典。DataFrame很適合表示真實(shí)數(shù)據(jù):行代表實(shí)例(對(duì)象、觀測(cè),等等),列代表每個(gè)實(shí)例的相應(yīng)特征。
我們將通過(guò)分析電信運(yùn)營(yíng)商的客戶(hù)離網(wǎng)率數(shù)據(jù)集來(lái)展示Pandas的主要方法。我們首先通過(guò)read_csv讀取數(shù)據(jù),然后使用head方法查看前5行數(shù)據(jù):
import pandas as pd
import numpy as np
df = pd.read_csv('../../data/telecom_churn.csv')
df.head()
每行對(duì)應(yīng)一位客戶(hù),我們的研究對(duì)象,列則是對(duì)象的特征。
讓我們查看一下數(shù)據(jù)的維度、特征名稱(chēng)和特征類(lèi)型。
print(df.shape)
結(jié)果:
(3333, 20)
所以我們的表格包含3333行和20列。下面我們嘗試打印列名:
print(df.columns)
結(jié)果:
Index(['State', 'Account length', 'Area code', 'International plan',
'Voice mail plan', 'Number vmail messages', 'Total day minutes',
'Total day calls', 'Total day charge', 'Total eve minutes',
'Total eve calls', 'Total eve charge', 'Total night minutes',
'Total night calls', 'Total night charge', 'Total intl minutes',
'Total intl calls', 'Total intl charge', 'Customer service calls',
'Churn'],
dtype='object')
我們可以使用info()方法輸出dataframe的一些總體信息:
print(df.info())
RangeIndex: 3333 entries, 0 to 3332
Data columns (total 20 columns):
State 3333 non-null object
Account length 3333 non-null int64
Area code 3333 non-null int64
International plan 3333 non-null object
Voice mail plan 3333 non-null object
Number vmail messages 3333 non-null int64
Total day minutes 3333 non-null float64
Total day calls 3333 non-null int64
Total day charge 3333 non-null float64
Total eve minutes 3333 non-null float64
Total eve calls 3333 non-null int64
Total eve charge 3333 non-null float64
Total night minutes 3333 non-null float64
Total night calls 3333 non-null int64
Total night charge 3333 non-null float64
Total intl minutes 3333 non-null float64
Total intl calls 3333 non-null int64
Total intl charge 3333 non-null float64
Customer service calls 3333 non-null int64
Churn 3333 non-null bool
dtypes: bool(1), float64(8), int64(8), object(3)
memory usage: 498.1+ KB
None
bool、int64、float64和object是我們特征的數(shù)據(jù)類(lèi)型。這一方法同時(shí)也會(huì)顯示是否有缺失的值。在上面的例子中答案是沒(méi)有缺失,因?yàn)槊苛卸及?333個(gè)觀測(cè),和我們之前使用shape方法得到的數(shù)字是一致的。
我們可以通過(guò)astype方法更改列的類(lèi)型。讓我們將這一方法應(yīng)用到Churn特征以將其修改為int64:
df['Churn'] = df['Churn'].astype('int64')
describe方法可以顯示數(shù)值特征(int64和float64)的基本統(tǒng)計(jì)學(xué)特性:未缺失值的數(shù)值、均值、標(biāo)準(zhǔn)差、范圍、四分位數(shù)。
df.describe()
查看非數(shù)值特征的統(tǒng)計(jì)數(shù)據(jù)時(shí),需要通過(guò)include參數(shù)顯式指定包含的數(shù)據(jù)類(lèi)型:
df.describe(include=['object', 'bool'])
類(lèi)別(類(lèi)型為object)和布爾值(類(lèi)型為bool)特征可以應(yīng)用value_counts方法。讓我們看下Churn的分布:
df['Churn'].value_counts()
結(jié)果:
0 2850
1 483
Name: Churn, dtype: int64
3333位客戶(hù)中,2850位是忠實(shí)客戶(hù);他們的Churn值為0。調(diào)用value_counts函數(shù)時(shí),帶上normalize=True參數(shù)可以顯示比例:
df['Churn'].value_counts(normalize=True)
結(jié)果:
0 0.855086
1 0.144914
Name: Churn, dtype: float64
排序
DataFrame可以根據(jù)某個(gè)變量的值(也就是列)排序。比如,根據(jù)每日消費(fèi)額排序(ascending=False倒序):
df.sort_values(by='Total day charge', ascending=False).head()
此外,還可以根據(jù)多個(gè)列的數(shù)值排序:
df.sort_values(by=['Churn', 'Total day charge'], ascending=[True, False]).head()
索引和獲取數(shù)據(jù)
DataFrame可以以不同的方式索引。
使用DataFrame['Name']可以得到一個(gè)單獨(dú)的列。比如:離網(wǎng)率有多高?
df['Churn'].mean()
結(jié)果:
0.14491449144914492
對(duì)一家公司而言,14.5%的離網(wǎng)率是一個(gè)很糟糕的數(shù)據(jù);這么高的離網(wǎng)率可能導(dǎo)致公司破產(chǎn)。
布爾值索引同樣很方便。語(yǔ)法是df[P(df['Name'])],P是檢查Name列每個(gè)元素的邏輯條件。這一索引的結(jié)果是DataFrame的Name列中滿足P條件的行。
讓我們使用布爾值索引來(lái)回答這樣一個(gè)問(wèn)題:
離網(wǎng)用戶(hù)的數(shù)值變量的均值是多少?
df[df['Churn'] == 1].mean()
結(jié)果:
Account length 102.664596
Area code 437.817805
Number vmail messages 5.115942
Total day minutes 206.914079
Total day calls 101.335404
Total day charge 35.175921
Total eve minutes 212.410145
Total eve calls 100.561077
Total eve charge 18.054969
Total night minutes 205.231677
Total night calls 100.399586
Total night charge 9.235528
Total intl minutes 10.700000
Total intl calls 4.163561
Total intl charge 2.889545
Customer service calls 2.229814
Churn 1.000000
dtype: float64
離網(wǎng)用戶(hù)在白天打電話的總時(shí)長(zhǎng)的均值是多少?
df[df['Churn'] == 1]['Total day minutes'].mean()
結(jié)果:
206.91407867494814
未使用國(guó)際套餐的忠實(shí)用戶(hù)(Churn == 0)所打的最長(zhǎng)的國(guó)際長(zhǎng)途是多久?
df[(df['Churn'] == 0) & (df['International plan'] == 'No')]['Total intl minutes'].max()
結(jié)果:
18.899999999999999
DataFrame可以通過(guò)列名、行名、行號(hào)進(jìn)行索引。loc方法為通過(guò)名稱(chēng)索引,iloc方法為通過(guò)數(shù)字索引。
loc的例子:給我們0至5行(含)、State(州)至Area code(區(qū)號(hào))(含)的數(shù)據(jù);iloc的例子:給我們前5行、前3列的數(shù)據(jù)(和典型的Python切片一樣,不含最大值)。
df.loc[0:5, 'State':'Area code']
df.iloc[0:5, 0:3]
df[:1]和df[-1:]可以得到DataFrame的首行和末行。(譯者注:個(gè)人更喜歡用df.head(1)和df.tail(1)。)
應(yīng)用函數(shù)到單元格、列、行
使用apply()方法應(yīng)用函數(shù)至每一列:
df.apply(np.max)
結(jié)果:
State WY
Account length 243
Area code 510
International plan Yes
Voice mail plan Yes
Number vmail messages 51
Total day minutes 350.8
Total day calls 165
Total day charge 59.64
Total eve minutes 363.7
Total eve calls 170
Total eve charge 30.91
Total night minutes 395
Total night calls 175
Total night charge 17.77
Total intl minutes 20
Total intl calls 20
Total intl charge 5.4
Customer service calls 9
Churn 1
dtype: object
apply方法也可以應(yīng)用函數(shù)至每一行,指定axis=1即可。lambda函數(shù)在這一場(chǎng)景下十分方便。比如,選中所有以W開(kāi)頭的州:
df[df['State'].apply(lambda state: state[0] == 'W')].head()
map方法可以替換某一列中的值:
d = {'No' : False, 'Yes' : True}
df['International plan'] = df['International plan'].map(d)
df.head()
其實(shí)也可以直接使用replace方法:
df = df.replace({'Voice mail plan': d})
df.head()
組
Pandas下分組數(shù)據(jù)的一般形式為:
df.groupby(by=grouping_columns)[columns_to_show].function()
groupby方法根據(jù)grouping_columns的值進(jìn)行分組。
接著,選中感興趣的列(columns_to_show)。如果不包括這一項(xiàng),那么會(huì)包括所有非groupby列。
最后,應(yīng)用一個(gè)或多個(gè)函數(shù)。
在下面的例子中,我們根據(jù)Churn變量的值分組數(shù)據(jù),顯示每組的統(tǒng)計(jì)數(shù)據(jù):
columns_to_show = ['Total day minutes', 'Total eve minutes',
'Total night minutes']
df.groupby(['Churn'])[columns_to_show].describe(percentiles=[])
和上面的例子類(lèi)似,只不過(guò)這次將一些函數(shù)傳給agg():
columns_to_show = ['Total day minutes', 'Total eve minutes',
'Total night minutes']
df.groupby(['Churn'])[columns_to_show].agg([np.mean, np.std,
np.min, np.max])
匯總表
假設(shè)我們想知道樣本的Churn(離網(wǎng))和International plan(國(guó)際套餐)的分布,我們可以使用crosstab方法構(gòu)建一個(gè)列聯(lián)表(contingency table):
pd.crosstab(df['Churn'], df['International plan'])
Churn(離網(wǎng))和Voice mail plan(語(yǔ)音郵件套餐)的分布:
pd.crosstab(df['Churn'], df['Voice mail plan'], normalize=True)
我們可以看到,大部分用戶(hù)是忠實(shí)的,同時(shí)并不使用額外的服務(wù)(國(guó)際套餐、語(yǔ)音郵件)。
對(duì)熟悉Excel的而言,這很像Excel中的透視表(pivot table)。當(dāng)然,Pandas實(shí)現(xiàn)了透視表:pivot_table方法接受以下參數(shù):
values需要計(jì)算統(tǒng)計(jì)數(shù)據(jù)的變量列表
index分組數(shù)據(jù)的變量列表
aggfunc需要計(jì)算哪些統(tǒng)計(jì)數(shù)據(jù),例如,總和、均值、最大值、最小值,等等。
讓我們看下不同區(qū)號(hào)下白天、夜晚、深夜的電話量的均值:
df.pivot_table(['Total day calls', 'Total eve calls', 'Total night calls'], ['Area code'], aggfunc='mean')
轉(zhuǎn)換DataFrame
和其他很多Pandas任務(wù)一樣,在DataFrame中新增列有很多方法。
比如,為所有用戶(hù)計(jì)算總的電話量:
total_calls = df['Total day calls'] + df['Total eve calls'] +
df['Total night calls'] + df['Total intl calls']
df.insert(loc=len(df.columns), column='Total calls', value=total_calls)
df.head()
上面的代碼創(chuàng)建了一個(gè)中間Series實(shí)例,其實(shí)可以直接添加:
df['Total charge'] = df['Total day charge'] + df['Total eve charge'] +
df['Total night charge'] + df['Total intl charge']
df.head()
使用drop方法刪除列和行,將相應(yīng)的索引和axis參數(shù)(1表示刪除列,0表示刪除行,默認(rèn)值為0)傳給drop方法。inplace參數(shù)表示是否修改原始DataFrame(False表示不修改現(xiàn)有DataFrame,返回一個(gè)新DataFrame,True表示修改當(dāng)前DataFrame)。
# 移除先前創(chuàng)建的列
df.drop(['Total charge', 'Total calls'], axis=1, inplace=True)
# 如何刪除行
df.drop([1, 2]).head()
4. 預(yù)測(cè)離網(wǎng)率的首次嘗試
讓我們看下International plan(國(guó)際套餐)變量和離網(wǎng)率的相關(guān)性。我們將通過(guò)crosstab列聯(lián)表來(lái)查看這一關(guān)系,我們也將使用Seaborn進(jìn)行可視化分析(不過(guò),可視化分析的更多內(nèi)容將在本系列的下一篇文章介紹):
pd.crosstab(df['Churn'], df['International plan'], margins=True)
# 加載模塊,配置繪圖
%matplotlib inline
import matplotlib.pyplot as plt
# pip install seaborn
import seaborn as sns
plt.rcParams['figure.figsize'] = (8, 6)
sns.countplot(x='International plan', hue='Churn', data=df);
我們看到,開(kāi)通了國(guó)際套餐的用戶(hù)的離網(wǎng)率要高很多,這是一個(gè)很有趣的觀測(cè)結(jié)果。也許,國(guó)際電話高昂、難以控制的話費(fèi)很容易引起爭(zhēng)端,讓電信運(yùn)營(yíng)商的客戶(hù)很不滿意。
接下來(lái),讓我們查看下另一個(gè)重要特征——客服呼叫。我們同樣編制一張匯總表,并繪制一幅圖像。
pd.crosstab(df['Churn'], df['Customer service calls'], margins=True)
sns.countplot(x='Customer service calls', hue='Churn', data=df);
也許匯總表不是很明顯,但圖形很明顯地顯示了,從4次客戶(hù)呼叫開(kāi)始,離網(wǎng)率顯著提升了。
讓我們給DataFrame添加一個(gè)二元屬性——客戶(hù)呼叫超過(guò)3次。同樣讓我們看下它與離網(wǎng)率的相關(guān)性:
df['Many_service_calls'] = (df['Customer service calls'] > 3).astype('int')
pd.crosstab(df['Many_service_calls'], df['Churn'], margins=True)
sns.countplot(x='Many_service_calls', hue='Churn', data=df);
讓我們創(chuàng)建另一張列聯(lián)表,將Churn(離網(wǎng))與International plan(國(guó)際套餐)及新創(chuàng)建的Many_service_calls(多次客服呼叫)關(guān)聯(lián)起來(lái):
pd.crosstab(df['Many_service_calls'] & df['International plan'] , df['Churn'])
因此,預(yù)測(cè)客戶(hù)呼叫客服超過(guò)3次,且已開(kāi)通國(guó)際套餐的情況下會(huì)離網(wǎng)(Churn=1),我們可以期望的精確度為85.8%的精確度(我們只有464+9次弄錯(cuò)了)。我們基于非常簡(jiǎn)單的推理得到的數(shù)字85.8%,可以作為一個(gè)良好的開(kāi)端(我們即將創(chuàng)建的更多機(jī)器學(xué)習(xí)模型的基線)。
復(fù)習(xí)一下我們介紹的內(nèi)容:
樣本中忠實(shí)客戶(hù)的份額為85.5%。這意味著最幼稚總是預(yù)測(cè)“忠實(shí)客戶(hù)”的模型有85.5%的概率猜對(duì)。也就是說(shuō),后續(xù)模型的正確答案的比例(精確度)不應(yīng)該比這個(gè)數(shù)字少,并且很有希望顯著高于這個(gè)數(shù)字;
基于一個(gè)簡(jiǎn)單的預(yù)測(cè)“(客服呼叫次數(shù) > 3) & (國(guó)際套餐 = True) => Churn = 1, else Churn = 0”,我們可以期望85.8%的精確度,剛剛超過(guò)85.5%。以后我們將討論決策樹(shù),看看如何僅僅基于輸入數(shù)據(jù)自動(dòng)找出這樣的規(guī)則;
我們沒(méi)有應(yīng)用機(jī)器學(xué)習(xí)就得到這兩條基線,它們將作為后續(xù)模型的開(kāi)端。如果經(jīng)過(guò)大量的努力,我們將正確答案的份額提高了0.5%,那么也許我們搞錯(cuò)了什么,限制使用一個(gè)包含兩個(gè)條件的簡(jiǎn)單模型已經(jīng)足夠了;
在訓(xùn)練復(fù)雜模型之間,建議擺弄下數(shù)據(jù),繪制一些圖表,檢查一下簡(jiǎn)單的假設(shè)。此外,在業(yè)務(wù)上應(yīng)用機(jī)器學(xué)習(xí)時(shí),通常從簡(jiǎn)單的方案開(kāi)始,接著嘗試更復(fù)雜的方案。
5. 作業(yè)一
第一次作業(yè)將分析包含美國(guó)居民的人口信息的UCI成人數(shù)據(jù)。我們建議你在Jupyter notebook中完成這些任務(wù),然后通過(guò)Google表單回答10個(gè)問(wèn)題。提交表單之后,同樣可以編輯你的回答。
截止日期:February 11, 23:59 CE
6. 相關(guān)資源
首先,當(dāng)然是Pandas官方文檔
10 minutes to pandas(十分鐘入門(mén)pandas)
Pandas cheatsheet PDF
GitHub倉(cāng)庫(kù):Pandas exercises(Pandas練習(xí))和Effective Pandas
scipy-lectures.org pandas、numpy、matplotlib、scikit-learn教程
-
機(jī)器學(xué)習(xí)
+關(guān)注
關(guān)注
66文章
8493瀏覽量
134151 -
數(shù)據(jù)分析
+關(guān)注
關(guān)注
2文章
1470瀏覽量
34821
原文標(biāo)題:機(jī)器學(xué)習(xí)開(kāi)放課程(一):使用Pandas探索數(shù)據(jù)分析
文章出處:【微信號(hào):jqr_AI,微信公眾號(hào):論智】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
評(píng)論