Python數據分析之路(一)查詢和統計



0. 如何入門數據分析

關注沙漠之鷹的同學一定看過沙漠君寫得很多篇數據分析文章,比如分析房價,車價,預測機動車搖號這些話題。其實文章中所有的分析都使用了Python和它非常強大的數據分析庫Pandas。一些機器學習和預測的功能則用到了sklearn庫。掌握了這些工具,就能應對絕大多數的分析需求。

紙上得來終覺淺,即使你看了很多書和代碼,也未必比得上多接觸例子多敲一些代碼,三四個中等規模(約一兩百行代碼的)的案例就能讓你有整體的把握。至於數據采集,沙漠之鷹有開源的數據抓取工具Hawk,網上也有眾多如何獲取數據的教程,因此本文不再詳述。

系列文章分為三個部分:

    1. 查詢與統計
    1. 可視化和高級用法
    1. 分類和預測(估計會分為幾篇文章)

好了,廢話不多說,進入正題。

1. 找到好的問題

好的問題其實比答案更重要。人認識問題,分為四種級別:

  1. 我們知道自己知道的(房價在漲)
  2. 知道自己不知道的(可度量的信息,如房價平均漲幅)
  3. 不知道自己知道的(如證明搖號系統漏洞)
  4. 不知道自己不知道的(最有價值,蘊含着最大的機會)

沙漠君期待於尋找3-4層級的問題,可是多數情況只能在第1和第2檔上徘徊。當你發現一個問題之后,還需要思考3個問題:

  • 我是否找到了一個值得解決的問題?
  • 我是否提出了一個足夠好的解決方案
  • 我真的想去解決這個問題嗎?

最后的動機往往反映了你能解決該問題的最大極限,很多人也許僅僅是完成任務,因此有了一點成果便停滯不前,錯過了最大的金礦。數據的質量比數量更重要,如果不知道什么樣的數據更重要,即使擁有更多的數據也只會造成嘔吐。數據分析需要專注,需要從紛繁的圖表和信息中找到問題核心。

通常來說,人們對變化的指標更感興趣,因此比率和增長幅度比靜態的數據更有說服力。而變化又分長短期,不同維度得出的結論往往全然不同。相關性很好,因果性更佳。有了因果性,便有了改變未來的能力。

下圖展示了數據分析的一般流程(圖片來自網絡):

一般流程

統計的三大核心:分組(map),聚合(reduce),排序(sort)。它們用的是如此普遍,因此MapReduce管道框架成了數據分析的標准,也非常適合做多機並行化。分組和排序很好理解,聚合指的是對各組內容做求和,分組等。

絕大部分數據分析操作,都可以通過查詢,分組,聚合,排序四個元素進行級聯組合進行。因此掌握這四大天王,應付一般的場景就都無問題了。 (這應該是這篇文章最重要的一句話了)

2. 查詢和過濾

DataFrame是pandas的核心數據結構,可以理解為Excel里的二維矩陣,它更高級,能表達3維或更高維的數據,支持多索引。在內存中存取,效率極高,絕大多數操作都和DataFrame相關。維度為2的DataFrame,行(column)和列(row)的axis分別為0和1。可以針對某些列做索引(index),高維DataFrame是相當少見的。

2.1 查詢

下面的表展示了Pandas對索引的介紹:

符號 說明 例子
[] 列值索引 df['房價'] ,df[['房價','車價']]
loc 行值索引 df.loc[0], df['2013':'2015']
iloc 行號索引 df.iloc[0], df.iloc[2:10]
ix 行索引 df.ix[0], df.ix[2:10]

時間也是比較重要的index,比較好用的是Timestamp,接受2016-12-24這種字符串,字符串到時間轉換代碼如下:

weather.index= weather[u'日期'].map(lambda x:Timestamp(x))

Pandas的索引功能非常強大,補充如下:

    1. loc也能支持先行后列的查詢:df.loc['20130101':'20130103' , ['A','B']],類似的如iloc
    1. 個人感覺ix的有些冗余,和ixiloc類似

所有索引都支持字符串和數組,以及切片(slice)用於指定范圍,索引還能傳遞一個bool類型的lambda表達式,或返回和其shape一致的bool數組
這種用法可以用在過濾上,這非常重要,我們再給幾個例子:

2.2 過濾

過濾有兩方面需求:找出特定數據進行針對性分析,或

  • 針對特定數據做分析,
  • 過濾異常值。

異常值非常重要,應該細致分析導致它們產生的原因,如果真是異常值,應該提早過濾,否則做聚合時會嚴重影響結果,如天價的房價。

先討論按行過濾:非空過濾,過濾掉col列為空的內容:

df=df[!data.col.isnull()]

字符串過濾:

db[db.city.str.contains(u'市')]

若需要對df對某個鍵去重:

qq['id'].unique()

isin能判斷單元格中的值是否在給定的數組內,若希望對多個列做過濾,Pandas提供了現成的方法df.filter,還支持正則。還能進行邏輯操作,實現更復雜的需求。

2.3 遍歷

有了索引和列操作,為何還要有遍歷?因為遍歷更加靈活,當然性能相對會差一些:

函數 遍歷目標 lambda參數 說明
map 一列的cell cell 最為常見
apply 列/行 列或行的Series axis:不填寫cell,1:行,2:列
applymap cell cell element-wise最為靈活
iterrows 行遍歷 提供行號 見備注
iteritems 列遍歷 提供列名 見備注
itertuples 行遍歷 提供index 見備注

map, apply,applymap是只返回單元格或行列本身的,參數都是lambda,本節假設讀者對python的lambda表達式有足夠的了解。

但這樣不能實現如“奇數偶數行做分別作不同處理的需求,則這三個函數就無能為力。因此就有后面iterrows等三個函數。如iterrows,它會將行號和行迭代出來,從而方便自定義邏輯,示例如下:

for i,row in data.iterrows():
    pass

2.4 求值和合並

一張表可能很難包含所有的信息,因此需要計算新值(求值)或join其他表(合並),但Pandas本身的Join並不好用,經常出錯。

如果某個屬性可以通過計算獲得,可對各個列當做變量來處理,由於內部使用了C++和numpy加速,效率遠比for循環更高,下面是處理房價的一個例子:總價/單價,並做小數點截取:

table['面積']= np.round(table['總價']*10000/table['單價'])

numpy提供了絕大多數常見的函數算子,能滿足大部分需求。下面是合並:

  • 橫向合並(需保證行數一致)- 橫向合並 df = pd.concat([data_train, dummies_Cabin])
  • 刪除列 df.drop(['Pclass', 'Name', 'Sex')]
  • 縱向合並(join操作)
data.merge(right=prop_rates, how='inner',left_on='Property_Area',right_index=True, sort=False)

如果不加參數,則可以自動通過列名合並。join的參數比較復雜,建議直接參考Pandas官方文檔。

3. 分組,排序,聚合

排序,分組和聚合的組合都有無數種,這在技術層面不難。但如果要寫報告,避免大而全,因為客戶的注意力很容易浪費在沒有意義的圖表上。將客戶真正關心的搞出來,行業背景分析,用戶畫像,競品監測,銷售行為分析...如果是寫通俗文章,多問幾個人,你想要知道什么。

由於分組是基礎,我們先介紹分組:

3.1 分組

分組就是按照一個或多個鍵,將數據分為幾個組的過程。你可以直接傳列名做分組,df.groupby('column_name')

也可以傳遞相同行數的Series甚至DataFrame。下面的例子是按日期里的年做分組:

df2.groupby(df2.日期.map(lambda x:x.year))

Pandas也能支持傳遞多個列的數組,除了切片以外,能在索引上使用的基本都能在group,sort上使用,一致性的API上手非常容易。

值得注意的是,由於時間索引分組比較困難,例如每五個月一組,可以用針對TimeStamp特定優化的方案,如resample:

下面計算了北京按年平均的AQI:

db[db.city==u'北京'][u'平均值'].resample('12M').aqi.mean()

3.2 排序

Pandas的排序非常之快,大部分操作都能在瞬間完成。排序分為兩類:

對一般數據排序

一般排序,直接用sort即可,傳遞lambda,列名或多個列,或長度一致的Series,這與groupby等其他API一致,此處從略。

可指定ascending=True|False來指定升序,降序。

對分組后數據排序

groupby之后的數據,和一般的DataFrame不同,而像個字典(dict)。對鍵排序,需使用sort_index,值排序,需使用sort_values。

3.3 聚合

聚合可將分組后的數據按需求重新打平。如求每個分組的最大值(max),最小值(min),或數量等,例如:

df2.groupby(df2.日期.map(lambda x:x.year)).size()

我們來寫幾個例子大家說說是什么意思:

car.groupby(car.年月.map(lambda x:x.month)).銷量.sum().plot(kind='bar',title='汽車市場月度銷量匯總')

將汽車數據按照月份分組,按銷量求和。然后繪制直方圖:

image.png-133.4kB

Pandas支持直接將聚合結果繪圖輸出(雖然丑但是方便啊),下一節我們將詳細介紹它的使用細節。

這條語句統計了廣西省東風MPV的各車型總體銷量情況,並按數量降序:

df[(df.省=='廣西') & (df.車型分類=='MPV') & (df.品牌=='東風')].groupby('車型').size().sort_values(ascending=False)

3.4 數據透視表

如果我想一次性地針對多種分組方式實行多種聚合策略,有沒有更方便的API? 答案是數據透視表(pivot_table)。Excel也有該功能,異常強大,有了它,一般需求幾乎都能實現。

有篇文章講的非常詳細,此處就不班門弄斧了,參考:

http://python.jobbole.com/81212/

下面是同時按Name rep manager分組,按價格分別以總價和數量聚合,並將空值填為0.

pd.pivot_table(df,index=["Name","Rep","Manager"],values=["Price"],aggfunc=[np.sum,len],fill_value=0)

pivot_table的基本操作

pivot之后,生成的DataFrame是multiindex的,處理起來稍顯繁瑣,用xs可將某個子index的數據“提升”出來,例如:

df_pivot.xs(('12AM新坐標',2011))

至於更復雜的訪問和采樣,可配合loc和PD.IndexSlice, 可自行查看官方文檔。

4. 總結

Pandas本身異常強大,功能非常繁雜,筆者僅僅掌握了其中非常小的一部分。但是對於一般的需求都能通過簡單的操作組合出來。API的一致性非常重要,Pandas(包括numpy等)都繼承了Python的優良特性,因此只要能舉一反三,就能進步神速。

文章不能太長,否則就沒人看得完了。不過相信我,掌握文中說的用法,基本上就足夠混口飯吃了。因為寫SQL的速度和靈活性是遠遠不及Pandas語法的。下一篇是數據可視化,我們來討論如何做可視化,還有對應的Python庫。

有任何問題,歡迎交流。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM