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