A place for study and research

Python 100 Days Day73 Introduction to Pandas-4

|

author: jackfrued

Pandas的應用-4

DataFrame的應用

數據分析

經過前面的學習,我們已經將數據準備就緒而且變成了我們想要的樣子,接下來就是最為重要的數據分析階段了。當我們拿到一大堆數據的時候,如何從數據中迅速的解讀出有價值的信息,這就是數據分析要解決的問題。首先,我們可以獲取數據的描述性統計信息,通過描述性統計信息,我們可以了解數據的集中趨勢和離散趨勢。

例如,我們有如下所示的學生成績表。

import numpy as np
import pandas as pd

scores = np.random.randint(50, 101, (5, 3))
names = ('關羽', '張飛', '趙雲', '馬超', '黃忠')
courses = ('語文', '數學', '英語')
df = pd.DataFrame(data=scores, columns=courses, index=names)
df

輸出:

     語文   數學   英語
關羽  96    72    73
張飛  72    70	97
趙雲  74    51	79
馬超  100   54	54
黃忠  89    100	88

我們可以通過DataFrame對象的方法meanmaxminstdvar等方法分別獲取每個學生或每門課程的平均分、最高分、最低分、標準差、方差等信息,也可以直接通過describe方法直接獲取描述性統計信息,代碼如下所示。

計算每門課程成績的平均分。

df.mean()

輸出:

語文    86.2
數學    69.4
英語    78.2
dtype: float64

計算每個學生成績的平均分。

df.mean(axis=1)

輸出:

關羽    80.333333
張飛    79.666667
趙雲    68.000000
馬超    69.333333
黃忠    92.333333
dtype: float64

計算每門課程成績的方差。

df.var()

輸出:

語文    161.2
數學    379.8
英語    265.7
dtype: float64

說明:通過方差可以看出,數學成績波動最大,最不穩定。

獲取每門課程的描述性統計信息。

df.describe()

輸出:

        語文        數學         英語
count   5.000000	5.000000	5.000000
mean    86.200000	69.400000	78.200000
std     12.696456	19.488458	16.300307
min     72.000000	51.000000	54.000000
25%     74.000000	54.000000	73.000000
50%     89.000000	70.000000	79.000000
75%     96.000000	72.000000	88.000000
max     100.000000	100.000000	97.000000
排序和Top-N

如果需要對數據進行排序,可以使用DataFrame對象的sort_values方法,該方法的by參數可以指定根據哪個列或哪些列進行排序,而ascending參數可以指定升序或是降序。例如,下面的代碼展示了如何將學生表按語文成績排降序。

df.sort_values(by='語文', ascending=False)

輸出:

      語文   數學   英語
馬超	100    54	  54
關羽	96     72     73
黃忠	89     100    88
趙雲	74     51     79
張飛	72     70     97

如果DataFrame數據量很大,排序將是一個非常耗費時間的操作。有的時候我們只需要獲得排前N名或後N名的數據,這個時候其實沒有必要對整個數據進行排序,而是直接利用堆結構找出Top-N的數據。DataFramenlargestnsmallest方法就提供對Top-N操作的支持,代碼如下所示。

找出語文成績前3名的學生信息。

df.nlargest(3, '語文')

輸出:

      語文   數學   英語
馬超	100    54	  54
關羽	96     72     73
黃忠	89     100    88

找出數學成績最低的3名學生的信息。

df.nsmallest(3, '數學')

輸出:

      語文  數學  英語
趙雲  74    51	79
馬超  100   54	54
張飛  72    70	97
分組聚合操作

我們先從 Excel 文件中讀取一組銷售數據,然後再為大家演示如何進行分組聚合操作。

df = pd.read_excel('2020年銷售數據.xlsx')
df.head()

說明:如果需要上面例子中的 Excel 文件,可以通過百度雲盤進行獲取。鏈接:https://pan.baidu.com/s/1NhWtYcpFzF72cxcsoDoXjQ?pwd=swg1,提取碼:swg1。

輸出:

    銷售日期	 銷售區域   銷售渠道  銷售訂單     品牌    售價  銷售數量
0   2020-01-01  上海       拼多多    182894-455  八匹馬  99    83
1   2020-01-01  上海       抖音      205635-402  八匹馬  219   29
2   2020-01-01  上海       天貓      205654-021  八匹馬  169   85
3   2020-01-01  上海       天貓      205654-519  八匹馬  169   14
4   2020-01-01  上海       天貓      377781-010  皮皮蝦  249   61

如果我們要統計每個銷售區域的銷售總額,可以先通過“售價”和“銷售數量”計算出銷售額,為DataFrame添加一個列,代碼如下所示。

df['銷售額'] = df['售價'] * df['銷售數量']
df.head()

輸出:

    銷售日期	 銷售區域   銷售渠道  銷售訂單     品牌    售價  銷售數量  銷售額
0   2020-01-01  上海       拼多多    182894-455  八匹馬  99    83        8217
1   2020-01-01  上海       抖音      205635-402  八匹馬  219   29        6351
2   2020-01-01  上海       天貓      205654-021  八匹馬  169   85        14365
3   2020-01-01  上海       天貓      205654-519  八匹馬  169   14        2366
4   2020-01-01  上海       天貓      377781-010  皮皮蝦  249   61        15189

然後再根據“銷售區域”列對數據進行分組,這里我們使用的是DataFrame對象的groupby方法。分組之後,我們取“銷售額”這個列在分組內進行求和處理,代碼和結果如下所示。

df.groupby('銷售區域').銷售額.sum()

輸出:

銷售區域
上海    11610489
北京    12477717
南京     1767301
安徽      895463
廣東     1617949
江蘇      537079
浙江      687862
福建    10178227
Name: 銷售額, dtype: int64

如果我們要統計每個月的銷售總額,我們可以將“銷售日期”作為groupby`方法的參數,當然這里需要先將“銷售日期”處理成月,代碼和結果如下所示。

df.groupby(df['銷售日期'].dt.month).銷售額.sum()

輸出:

銷售日期
1     5409855
2     4608455
3     4164972
4     3996770
5     3239005
6     2817936
7     3501304
8     2948189
9     2632960
10    2375385
11    2385283
12    1691973
Name: 銷售額, dtype: int64

接下來我們將難度升級,統計每個銷售區域每個月的銷售總額,這又該如何處理呢?事實上,groupby方法的第一個參數可以是一個列表,列表中可以指定多個分組的依據,大家看看下面的代碼和輸出結果就明白了。

df.groupby(['銷售區域', df['銷售日期'].dt.month]).銷售額.sum()

輸出:

銷售區域  銷售日期
上海      1       1679125
          2       1689527
          3       1061193
          4       1082187
          5        841199
          6        785404
          7        863906
          8        734937
          9       1107693
         10       412108
         11       825169
         12       528041
北京     1       1878234
         2       1807787
         3       1360666
         4       1205989
         5        807300
         6       1216432
         7       1219083
         8        645727
         9        390077
        10       671608
        11       678668
        12       596146
南京     7        841032
        10       710962
        12       215307
安徽     4        341308
         5        554155
廣東     3        388180
         8        469390
         9        365191
        11       395188
江蘇     4        537079
浙江     3        248354
         8        439508
福建     1       1852496
         2       1111141
         3       1106579
         4        830207
         5       1036351
         6        816100
         7        577283
         8        658627
         9        769999
        10       580707
        11       486258
        12       352479
Name: 銷售額, dtype: int64

如果希望統計出每個區域的銷售總額以及每個區域單筆金額的最高和最低,我們可以在DataFrameSeries對象上使用agg方法並指定多個聚合函數,代碼和結果如下所示。

df.groupby('銷售區域').銷售額.agg(['sum', 'max', 'min'])

輸出:

          sum        max        min
銷售區域			
上海      11610489   116303     948
北京      12477717   133411     690
南京      1767301    87527      1089
安徽      895463     68502      1683
廣東      1617949    120807     990
江蘇      537079     114312     3383
浙江      687862     90909      3927
福建      10178227   87527      897

如果希望自定義聚合後的列的名字,可以使用如下所示的方法。

df.groupby('銷售區域').銷售額.agg(銷售總額='sum', 單筆最高='max', 單筆最低='min')

輸出:

          銷售總額   單筆最高    單筆最低
銷售區域			
上海      11610489   116303     948
北京      12477717   133411     690
南京      1767301    87527      1089
安徽      895463     68502      1683
廣東      1617949    120807     990
江蘇      537079     114312     3383
浙江      687862     90909      3927
福建      10178227   87527      897

如果需要對多個列使用不同的聚合函數,例如“統計每個銷售區域銷售額的平均值以及銷售數量的最低值和最高值”,我們可以按照下面的方式來操作。

df.groupby('銷售區域')[['銷售額', '銷售數量']].agg({
    '銷售額': 'mean', '銷售數量': ['max', 'min']
})

輸出:

         銷售額        銷售數量
         mean          max    min
銷售區域			
上海     20622.538188  100    10
北京     20125.350000  100    10
南京     22370.898734  100    11
安徽     26337.147059  98     16
廣東     32358.980000  98     10
江蘇     29837.722222  98     15
浙江     27514.480000  95     20
福建     18306.163669  100    10
透視表和交叉表

上面的例子中,“統計每個銷售區域每個月的銷售總額”會產生一個看起來很長的結果,在實際工作中我們通常把那些行很多列很少的表成為“窄表”,如果我們不想得到這樣的一個“窄表”,可以使用DataFramepivot_table方法或者是pivot_table函數來生成透視表。透視表的本質就是對數據進行分組聚合操作,根據 A 列對 B 列進行統計,如果大家有使用 Excel 的經驗,相信對透視表這個概念一定不會陌生。例如,我們要“統計每個銷售區域的銷售總額”,那麽“銷售區域”就是我們的 A 列,而“銷售額”就是我們的 B 列,在pivot_table函數中分別對應indexvalues參數,這兩個參數都可以是單個列或者多個列。

pd.pivot_table(df, index='銷售區域', values='銷售額', aggfunc='sum')

輸出:

注意:上面的結果操作跟之前用groupby的方式得到的結果有一些區別,groupby操作後,如果對單個列進行聚合,得到的結果是一個Series對象,而上面的結果是一個DataFrame 對象。

如果要統計每個銷售區域每個月的銷售總額,也可以使用pivot_table函數,代碼如下所示。

pd.pivot_table(df, index=['銷售區域', df['銷售日期'].dt.month], values='銷售額', aggfunc='sum')

上面的操作結果是一個DataFrame,但也是一個長長的“窄表”,如果希望做成一個行比較少列比較多的“寬表”,可以將index參數中的列放到columns參數中,代碼如下所示。

pd.pivot_table(
    df, index='銷售區域', columns=df['銷售日期'].dt.month, 
    values='銷售額', aggfunc='sum', fill_value=0
)

說明pivot_table函數的fill_value=0會將空值處理為0

輸出:

使用pivot_table函數時,還可以通過添加marginsmargins_name參數對分組聚合的結果做一個匯總,具體的操作和效果如下所示。

df['月份'] = df['銷售日期'].dt.month
pd.pivot_table(
    df, index='銷售區域', columns='月份',
    values='銷售額', aggfunc='sum', fill_value=0, 
    margins=True, margins_name='總計'
)

輸出:

image-20211106181707655

交叉表就是一種特殊的透視表,它不需要先構造一個DataFrame對象,而是直接通過數組或Series對象指定兩個或多個因素進行運算得到統計結果。例如,我們要統計每個銷售區域的銷售總額,也可以按照如下所示的方式來完成,我們先準備三組數據。

sales_area, sales_month, sales_amount = df['銷售區域'], df['月份'], df['銷售額']

使用crosstab函數生成交叉表。

pd.crosstab(
    index=sales_area, columns=sales_month, values=sales_amount, aggfunc='sum'
).fillna(0).applymap(int)

說明:上面的代碼使用了DataFrame對象的fillna方法將空值處理為0,再使用applymap方法將數據類型處理成整數。

數據可視化

一圖勝千言,我們對數據進行透視的結果,最終要通過圖表的方式呈現出來,因為圖表具有極強的表現力,能夠讓我們迅速的解讀數據中隱藏的價值。和Series一樣,DataFrame對象提供了plot方法來支持繪圖,底層仍然是通過matplotlib庫實現圖表的渲染。關於matplotlib的內容,我們在下一個章節進行詳細的探討,這里我們只簡單的講解plot方法的用法。

例如,我們想通過一張柱狀圖來比較“每個銷售區域的銷售總額”,可以直接在透視表上使用plot方法生成柱狀圖。我們先導入matplotlib.pyplot模塊,通過修改繪圖的參數使其支持中文顯示。

import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = 'FZJKai-Z03S'

說明:上面的FZJKai-Z03S是我電腦上已經安裝的一種支持中文的字體的名稱,字體的名稱可以通過查看用戶主目錄下.matplotlib文件夾下名為fontlist-v330.json的文件來獲得,而這個文件在執行上面的命令後就會生成。

使用魔法指令配置生成矢量圖。

%config InlineBackend.figure_format = 'svg'

繪制“每個銷售區域銷售總額”的柱狀圖。

temp = pd.pivot_table(df, index='銷售區域', values='銷售額', aggfunc='sum')
temp.plot(figsize=(8, 4), kind='bar')
plt.xticks(rotation=0)
plt.show()

說明:上面的第3行代碼會將橫軸刻度上的文字旋轉到0度,第4行代碼會顯示圖像。

輸出:

如果要繪制餅圖,可以修改plot方法的kind參數為pie,然後使用定制餅圖的參數對圖表加以定制,代碼如下所示。

temp.sort_values(by='銷售額', ascending=False).plot(
    figsize=(6, 6), kind='pie', y='銷售額', 
    autopct='%.2f%%', pctdistance=0.8,
    wedgeprops=dict(linewidth=1, width=0.35)
)
plt.legend(loc='center')
plt.show()

輸出:

Comments