機器學習開放課程(一):使用Pandas探索數據分析

【編者按】Mail.Ru數據科學家Yury Kashnitsky和Segmento數據科學家Katya Demidova合作開設了機器學習開放課程。第一課介紹了如何使用Pandas進行數據分析。

註:感謝原作者Yury Kashnitsky和Katya Demidova授權論智(jqr_AI)編譯,未經授請勿轉載,詳情見轉載須知。

這一篇文章意味著,我們OpenDataScience的開放機器學習課程開始了。這一課程的目標並不是開發另一個全面的機器學習或數據分析的引導課程(所以這並不能代替基礎性的教育,在線和線下課程,培訓,以及書籍)。本系列文章的目的是幫你快速溫習知識,同時幫你找到進一步學習的主題。我們的角度和Deep Learning book的作者們類似,從回顧數學和機器學習基礎開始——簡短扼要,包含很多指向其他資源的鏈接。

這一課程的設計考慮到了理論和實踐的平衡;因此,每個主題附帶了作業。你可以參加Kaggle上相應的競賽。

我們在OpenDataScience的Slack小組中討論這一課程。請填寫這個表單申請加入。

概覽

  1. 關於本課程
  2. 作業說明
  3. 主要Pandas方法
  4. 預測電信運營商的客戶離網率
  5. 作業一
  6. 參考資源

1. 關於本課程

大綱

  1. 使用Pandas探索數據分析
  2. 使用Python可視化數據
  3. 分類、決策樹、K近鄰
  4. 線性分類、線性回歸
  5. Bagging、隨機森林
  6. 特徵工程、特徵選取
  7. 無監督學習:主成分分析、聚類
  8. Vowpal Wabbit:學習GB級數據
  9. 使用Python進行時序分析
  10. 梯度提升

社區

我們的課程的最突出的優勢之一就是活躍的社區。如果你加入OpenDataScience的Slack小組,你會發現文章和作業的作者們在#eng_mlcourse_open頻道熱心答疑。對初學者而言,這非常有用。請填寫這個表單申請加入。表單會詢問一些關於你的背景和技能的問題,包括幾道簡單的數學題。

我們的聊天比較隨意,喜歡開玩笑,經常使用表情。不是所有的MOOC敢誇口說自己有這樣一個活躍的社區。另外,Reddit上也有這一課程的subreddit。

預備知識

預備知識:

  1. 微積分的基本概念
  2. 線性代數
  3. 概率論和統計
  4. Python編程技能

如果你需要補習一下,可以參考「Deep Learning」一書的第一部分,以及眾多數學和Python的線上課程(例如,CodeAcademy的Python課程)。以後我們的wiki頁面將補充更多信息。

軟體

目前而言,你只需要Anaconda(基於Python 3.6),就可以重現當前課程中的代碼。在之後的課程中,你需要安裝其他庫,例如Xgboost和Vowpal Wabbit。

你也可以使用這個Docker容器,其中已經預裝了所有需要用到的軟體。更多信息請看相應的wiki頁面。

2. 作業

  • 每篇文章以Jupyter notebook的形式給出作業。作業包括補全代碼片段,以及通過Google表單回答一些問題。
  • 每個作業必須在一周之內完成。
  • 請在OpenDataScience的Slack小組#eng_mlcourse_open頻道或直接評論留言討論課程內容。
  • 作業的答案會發給提交了相應的Google表單的用戶。

Pandas的主要方法

好吧……其實已經有很多關於Pandas和可視化數據分析的教程了。如果你很熟悉這些主題,請等待本系列的第3篇文章,正式開始機器學習的內容。

下面的材料以Jupyter notebook的形式查看效果最佳。如果你克隆了本教程的代碼倉庫,你也可以在本地運行。

Pandas是一個Python庫,提供了大量數據分析的方法。數據科學家經常和表格形式的數據(比如.csv.tsv.xlsx)打交道。Pandas可以使用類似SQL的方式非常方便地載入、處理、分析這些表格形式的數據。搭配MatplotlibSeaborn效果更好。

Pandas的主要數據結構是SeriesDataFrame類。前者是一個包含某種固定類型數據的一維數組,後者是一個二維數據結構——一張表格——其中每列包含相同類型的數據。你可以把它看成Series實例構成的字典。DataFrame很適合表示真實數據:行代表實例(對象、觀測,等等),列代表每個實例的相應特徵。

我們將通過分析電信運營商的客戶離網率數據集來展示Pandas的主要方法。我們首先通過read_csv讀取數據,然後使用head方法查看前5行數據:

import pandas as pdimport numpy as npdf = pd.read_csv(../../data/telecom_churn.csv)df.head()

每行對應一位客戶,我們的研究對象,列則是對象的特徵

讓我們查看一下數據的維度、特徵名稱和特徵類型。

print(df.shape)

結果:

(3333, 20)

所以我們的表格包含3333行和20列。下面我們嘗試列印列名:

print(df.columns)

結果:

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())<class pandas.core.frame.DataFrame>RangeIndex: 3333 entries, 0 to 3332Data columns (total 20 columns):State 3333 non-null objectAccount length 3333 non-null int64Area code 3333 non-null int64International plan 3333 non-null objectVoice mail plan 3333 non-null objectNumber vmail messages 3333 non-null int64Total day minutes 3333 non-null float64Total day calls 3333 non-null int64Total day charge 3333 non-null float64Total eve minutes 3333 non-null float64Total eve calls 3333 non-null int64Total eve charge 3333 non-null float64Total night minutes 3333 non-null float64Total night calls 3333 non-null int64Total night charge 3333 non-null float64Total intl minutes 3333 non-null float64Total intl calls 3333 non-null int64Total intl charge 3333 non-null float64Customer service calls 3333 non-null int64Churn 3333 non-null booldtypes: bool(1), float64(8), int64(8), object(3)memory usage: 498.1+ KBNone

boolint64float64object是我們特徵的數據類型。這一方法同時也會顯示是否有缺失的值。在上面的例子中答案是沒有缺失,因為每列都包含3333個觀測,和我們之前使用shape方法得到的數字是一致的。

我們可以通過astype方法更改列的類型。讓我們將這一方法應用到Churn特徵以將其修改為int64

df[Churn] = df[Churn].astype(int64)

describe方法可以顯示數值特徵(int64float64)的基本統計學特性:未缺失值的數值、均值、標準差、範圍、四分位數。

df.describe()

查看非數值特徵的統計數據時,需要通過include參數顯式指定包含的數據類型:

df.describe(include=[object, bool])

類別(類型為object)和布爾值(類型為bool)特徵可以應用value_counts方法。讓我們看下Churn的分布:

df[Churn].value_counts()

結果:

0 28501 483Name: Churn, dtype: int64

3333位客戶中,2850位是忠實客戶;他們的Churn值為0。調用value_counts函數時,帶上normalize=True參數可以顯示比例:

df[Churn].value_counts(normalize=True)

結果:

0 0.8550861 0.144914Name: Churn, dtype: float64

排序

DataFrame可以根據某個變數的值(也就是列)排序。比如,根據每日消費額排序(ascending=False倒序):

df.sort_values(by=Total day charge, ascending=False).head()

此外,還可以根據多個列的數值排序:

df.sort_values(by=[Churn, Total day charge], ascending=[True, False]).head()

索引和獲取數據

DataFrame可以以不同的方式索引。

使用DataFrame[Name]可以得到一個單獨的列。比如:離網率有多高?

df[Churn].mean()

結果:

0.14491449144914492

對一家公司而言,14.5%的離網率是一個很糟糕的數據;這麼高的離網率可能導致公司破產。

布爾值索引同樣很方便。語法是df[P(df[Name])]P是檢查Name列每個元素的邏輯條件。這一索引的結果是DataFrame的Name列中滿足P條件的行。

讓我們使用布爾值索引來回答這樣一個問題:

離網用戶的數值變數的均值是多少?

df[df[Churn] == 1].mean()

結果:

Account length 102.664596Area code 437.817805Number vmail messages 5.115942Total day minutes 206.914079Total day calls 101.335404Total day charge 35.175921Total eve minutes 212.410145Total eve calls 100.561077Total eve charge 18.054969Total night minutes 205.231677Total night calls 100.399586Total night charge 9.235528Total intl minutes 10.700000Total intl calls 4.163561Total intl charge 2.889545Customer service calls 2.229814Churn 1.000000dtype: float64

離網用戶在白天打電話的總時長的均值是多少?

df[df[Churn] == 1][Total day minutes].mean()

結果:

206.91407867494814

未使用國際套餐的忠實用戶(Churn == 0)所打的最長的國際長途是多久?

df[(df[Churn] == 0) & (df[International plan] == No)][Total intl minutes].max()

結果:

18.899999999999999

DataFrame可以通過列名、行名、行號進行索引。loc方法為通過名稱索引iloc方法為通過數字索引

loc的例子:給我們0至5行()、State(州)至Area code(區號)()的數據;iloc的例子:給我們前5行、前3列的數據(和典型的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)。)

應用函數到單元格、列、行

使用apply()方法應用函數至每一列:

df.apply(np.max)

結果:

State WYAccount length 243Area code 510International plan YesVoice mail plan YesNumber vmail messages 51Total day minutes 350.8Total day calls 165Total day charge 59.64Total eve minutes 363.7Total eve calls 170Total eve charge 30.91Total night minutes 395Total night calls 175Total night charge 17.77Total intl minutes 20Total intl calls 20Total intl charge 5.4Customer service calls 9Churn 1dtype: object

apply方法也可以應用函數至每一行,指定axis=1即可。lambda函數在這一場景下十分方便。比如,選中所有以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下分組數據的一般形式為:

df.groupby(by=grouping_columns)[columns_to_show].function()

  1. groupby方法根據grouping_columns的值進行分組。
  2. 接著,選中感興趣的列(columns_to_show)。如果不包括這一項,那麼會包括所有非groupby列。
  3. 最後,應用一個或多個函數。

在下面的例子中,我們根據Churn變數的值分組數據,顯示每組的統計數據:

columns_to_show = [Total day minutes, Total eve minutes, Total night minutes]df.groupby([Churn])[columns_to_show].describe(percentiles=[])

和上面的例子類似,只不過這次將一些函數傳給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])

匯總表

假設我們想知道樣本的Churn(離網)和International plan(國際套餐)的分布,我們可以使用crosstab方法構建一個列聯表(contingency table)

pd.crosstab(df[Churn], df[International plan])

Churn(離網)和Voice mail plan(語音郵件套餐)的分布:

pd.crosstab(df[Churn], df[Voice mail plan], normalize=True)

我們可以看到,大部分用戶是忠實的,同時並不使用額外的服務(國際套餐、語音郵件)。

對熟悉Excel的而言,這很像Excel中的透視表(pivot table)。當然,Pandas實現了透視表:pivot_table方法接受以下參數:

  • values 需要計算統計數據的變數列表
  • index 分組數據的變數列表
  • aggfunc 需要計算哪些統計數據,例如,總和、均值、最大值、最小值,等等。

讓我們看下不同區號下白天、夜晚、深夜的電話量的均值:

df.pivot_table([Total day calls, Total eve calls, Total night calls], [Area code], aggfunc=mean)

轉換DataFrame

和其他很多Pandas任務一樣,在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()

上面的代碼創建了一個中間Series實例,其實可以直接添加:

df[Total charge] = df[Total day charge] + df[Total eve charge] + df[Total night charge] + df[Total intl charge]df.head()

使用drop方法刪除列和行,將相應的索引和axis參數(1表示刪除列,0表示刪除行,默認值為0)傳給drop方法。inplace參數表示是否修改原始DataFrame(False表示不修改現有DataFrame,返回一個新DataFrame,True表示修改當前DataFrame)。

# 移除先前創建的列df.drop([Total charge, Total calls], axis=1, inplace=True)# 如何刪除行df.drop([1, 2]).head()

4. 預測離網率的首次嘗試

讓我們看下International plan(國際套餐)變數和離網率的相關性。我們將通過crosstab列聯表來查看這一關係,我們也將使用Seaborn進行可視化分析(不過,可視化分析的更多內容將在本系列的下一篇文章介紹):

pd.crosstab(df[Churn], df[International plan], margins=True)

# 載入模塊,配置繪圖%matplotlib inlineimport matplotlib.pyplot as plt# pip install seabornimport seaborn as snsplt.rcParams[figure.figsize] = (8, 6)sns.countplot(x=International plan, hue=Churn, data=df);

我們看到,開通了國際套餐的用戶的離網率要高很多,這是一個很有趣的觀測結果。也許,國際電話高昂、難以控制的話費很容易引起爭端,讓電信運營商的客戶很不滿意。

接下來,讓我們查看下另一個重要特徵——客服呼叫。我們同樣編製一張匯總表,並繪製一幅圖像。

pd.crosstab(df[Churn], df[Customer service calls], margins=True)

sns.countplot(x=Customer service calls, hue=Churn, data=df);

也許匯總表不是很明顯,但圖形很明顯地顯示了,從4次客戶呼叫開始,離網率顯著提升了。

讓我們給DataFrame添加一個二元屬性——客戶呼叫超過3次。同樣讓我們看下它與離網率的相關性:

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);

讓我們創建另一張列聯表,將Churn(離網)與International plan(國際套餐)及新創建的Many_service_calls(多次客服呼叫)關聯起來:

pd.crosstab(df[Many_service_calls] & df[International plan] , df[Churn])

因此,預測客戶呼叫客服超過3次,且已開通國際套餐的情況下會離網(Churn=1),我們可以期望的精確度為85.8%的精確度(我們只有464+9次弄錯了)。我們基於非常簡單的推理得到的數字85.8%,可以作為一個良好的開端(我們即將創建的更多機器學習模型的基線)。

複習一下我們介紹的內容:

  • 樣本中忠實客戶的份額為85.5%。這意味著最幼稚總是預測「忠實客戶」的模型有85.5%的概率猜對。也就是說,後續模型的正確答案的比例(精確度)不應該比這個數字少,並且很有希望顯著高於這個數字;
  • 基於一個簡單的預測「(客服呼叫次數 > 3) & (國際套餐 = True) => Churn = 1, else Churn = 0」,我們可以期望85.8%的精確度,剛剛超過85.5%。以後我們將討論決策樹,看看如何僅僅基於輸入數據自動找出這樣的規則;
  • 我們沒有應用機器學習就得到這兩條基線,它們將作為後續模型的開端。如果經過大量的努力,我們將正確答案的份額提高了0.5%,那麼也許我們搞錯了什麼,限制使用一個包含兩個條件的簡單模型已經足夠了;
  • 在訓練複雜模型之間,建議擺弄下數據,繪製一些圖表,檢查一下簡單的假設。此外,在業務上應用機器學習時,通常從簡單的方案開始,接著嘗試更複雜的方案。

5. 作業一

第一次作業將分析包含美國居民的人口信息的UCI成人數據。我們建議你在Jupyter notebook中完成這些任務,然後通過Google表單回答10個問題。提交表單之後,同樣可以編輯你的回答。

截止日期:February 11, 23:59 CE

6. 相關資源

  • 首先,當然是Pandas官方文檔
  • 10 minutes to pandas(十分鐘入門pandas)
  • Pandas cheatsheet PDF
  • GitHub倉庫:Pandas exercises(Pandas練習)和Effective Pandas
  • scipy-lectures.org pandas、numpy、matplotlib、scikit-learn教程

原文 Open Machine Learning Course. Topic 1. Exploratory Data Analysis with Pandas

推薦閱讀:

LDA模型的前世今生
Python數據科學(一)- python與數據科學應用(Ⅰ)
不學好數學也想當數據科學家?不存在的
身為數據科學家怎麼能不掌握這四大技能!
Indeed報告:目前最熱門的10大AI類工作,數據科學家位居榜首

TAG:機器學習 | 數據分析 | 數據科學家 |