python-資料分析-Pandas-4、DataFrame-資料透視

little小新發表於2024-06-10
經過前面的學習,我們已經將資料準備就緒而且變成了我們想要的樣子
接下來就是最為重要的資料透視階段了。當我們拿到一大堆資料的時候,如何從資料中迅速的解讀出有價值的資訊
把繁雜的資料變成容易解讀的統計圖表並再此基礎上產生業務洞察,這就是資料分析要解決的核心問題。

資料透視

# -*- coding: utf-8 -*-
#資料透視

import numpy
import pandas

scores = numpy.random.randint(50, 101, (5, 3))  # 生成5行3列的隨機整數矩陣
names = ('關羽', '張飛', '趙雲', '馬超', '黃忠')
courses = ('語文', '數學', '英語')
# 生成DataFrame
#index: 索引列(第一列)
#columns: 表頭
df = pandas.DataFrame(data=scores, columns=courses, index=names)
print(df)
'''
    語文  數學  英語
關羽  77  74  70
張飛  64  93  64
趙雲  75  50  97
馬超  90  86  76
黃忠  52  58  75
'''

#我們可以透過DataFrame物件的方法:
# mean、max、min、std、var等方法分別獲取每個學生或每門課程的平均分、最高分、最低分、標準差、方差等資訊,
# 也可以直接透過describe方法直接獲取描述性統計資訊
print(df.mean())
print(df.mean(axis=1))  # axis=1表示按行計算
print(df.var()) # 方差
print(df.describe())  # 統計資訊 -推薦

print('-------------------------------------')
#排序和取頭部值""

# 排序 sort_values(by='指定哪一列進行排序', ascending=True/False) 預設升序True
print(df.sort_values(by='語文', ascending=False))

#nlargest和nsmallest方法就提供對Top-N操作 - 如取排名前幾名的資料
#找出語文成績前3名的學生
#df.nalargest(n, columns, keep='first') 引數n表示取前幾名,columns表示哪一列,keep表示保留排名前幾名的資料
print(df.nlargest(3, '語文'))

#找出數學成績最低的3名學生的資訊。
print(df.nsmallest(3, '數學'))

print('========================================================================')

#分組聚合
df1 = pandas.read_excel('file/2020年銷售資料.xlsx')
print(df1.head())

# 統計每個銷售區域的銷售總額
#1、先透過“售價”和“銷售數量”計算出銷售額,為DataFrame新增一個列
df1['銷售額'] = df1['售價'] * df1['銷售數量']    #
print(df1.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
'''

# 2、 再根據“銷售區域”列對資料進行分組,這裡我們使用的是DataFrame物件的groupby方法。分組之後,我們取“銷售額”這個列在分組內進行求和處理
print(df1.groupby('銷售區域').銷售額.sum())
'''
銷售區域    (這一列就是一個區域銷售額的總和)
上海    11610489      
北京    12477717
安徽      895463
廣東     1617949
江蘇     2304380
浙江      687862
福建    10178227
Name: 銷售額, dtype: int64
'''

#如果我們要統計每個月的銷售總額,我們可以將“銷售日期”作為groupby`方法的引數,當然這裡需要先將“銷售日期”處理成月
#df1['銷售日期'].dt.month # 獲取月份
#df1['銷售日期'].dt.year  # 獲取年份
#df1['銷售日期'].dt.day  # 獲取日
print(df1.groupby(df1['銷售日期'].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
'''
# 3、統計每個銷售區域每個月的銷售總額
#groupby方法的第一個引數可以是一個列表,列表中可以指定多個分組的依據
print(df1.groupby(['銷售區域', df1['銷售日期'].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
安徽    4        341308
      5        554155
廣東    3        388180
      8        469390
      9        365191
      11       395188
江蘇    4        537079
      7        841032
      10       710962
      12       215307
浙江    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
'''

#統計出每個區域的銷售總額以及每個區域單筆金額的最高和最低
#在DataFrame或Series物件上使用agg方法並指定多個聚合函式
print(df1.groupby('銷售區域').銷售額.agg(['sum', 'max', 'min']))
'''
           sum     max   min
銷售區域                        
上海    11610489  116303   948
北京    12477717  133411   690
安徽      895463   68502  1683
廣東     1617949  120807   990
江蘇     2304380  114312  1089
浙江      687862   90909  3927
福建    10178227   87527   897
'''

#自定義聚合後的列的名字,可以使用如下所示的方法
print(df1.groupby('銷售區域').銷售額.agg(銷售總額='sum', 最高銷售額='max', 最低銷售額='min'))
'''
          銷售總額   最高銷售額  最低銷售額
銷售區域                         
上海    11610489  116303    948
北京    12477717  133411    690
安徽      895463   68502   1683
廣東     1617949  120807    990
江蘇     2304380  114312   1089
浙江      687862   90909   3927
福建    10178227   87527    897
'''

#對多個列使用不同的聚合函式,例如“統計每個銷售區域銷售額的總和以及銷售數量的最低值和最高值”
print(df1.groupby('銷售區域')[['銷售額', '銷售數量']].agg({'銷售額': 'sum', '銷售數量': ['max', 'min']}))
'''
           銷售額 銷售數量    
           sum  max min
銷售區域                   
上海    11610489  100  10
北京    12477717  100  10
安徽      895463   98  16
廣東     1617949   98  10
江蘇     2304380  100  11
浙江      687862   95  20
福建    10178227  100  10
'''

print('========================================================')
#透視表和交叉表
#在實際工作中我們通常把那些行很多列很少的表成為“窄表”
#如果我們不想得到這樣的一個“窄表”,可以使用DataFrame的pivot_table方法或者是pivot_table函式來生成透視表。
# 透視表的本質就是對資料進行分組聚合操作 根據 A 列對 B 列進行統計
#例如,我們要“統計每個銷售區域的銷售總額”,那麼“銷售區域”就是我們的 A 列,而“銷售額”就是我們的 B 列
#在pivot_table函式中分別對應index和values引數,這兩個引數都可以是單個列或者多個列
#pandas.pivot_table(data, index, values, aggfunc=指定聚合函式)
print(pandas.pivot_table(df1, index='銷售區域', values='銷售額', aggfunc='sum'))
'''
          銷售額
銷售區域          
上海    11610489
北京    12477717
安徽      895463
廣東     1617949
江蘇     2304380
浙江      687862
福建    10178227

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

#統計每個銷售區域每個月的銷售總額,也可以使用pivot_table函式
df1['月份'] = df1['銷售日期'].dt.month
print(pandas.pivot_table(df1, index=['銷售區域', '月份'], values='銷售額', aggfunc='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
安徽   4    341308
     5    554155
廣東   3    388180
     8    469390
     9    365191
     11   395188
江蘇   4    537079
     7    841032
     10   710962
     12   215307
浙江   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

#上面的操作結果是一個DataFrame,但也是一個長長的“窄表”
'''
#做成一個行比較少列比較多的“寬表”,可以將index引數中的列放到columns引數中
#說明:pivot_table函式的fill_value=0會將空值處理為0。
print(pandas.pivot_table(df1, index='銷售區域', columns='月份', values='銷售額', aggfunc='sum', fill_value=0))
'''
月份           1          2          3   ...        10        11        12
銷售區域                                   ...                              
上海    1679125.0  1689527.0  1061193.0  ...  412108.0  825169.0  528041.0
北京    1878234.0  1807787.0  1360666.0  ...  671608.0  678668.0  596146.0
安徽          NaN        NaN        NaN  ...       NaN       NaN       NaN
廣東          NaN        NaN   388180.0  ...       NaN  395188.0       NaN
江蘇          NaN        NaN        NaN  ...  710962.0       NaN  215307.0
浙江          NaN        NaN   248354.0  ...       NaN       NaN       NaN
福建    1852496.0  1111141.0  1106579.0  ...  580707.0  486258.0  352479.0

[7 rows x 12 columns]
'''

#使用pivot_table函式時,還可以透過新增margins和margins_name引數對分組聚合的結果做一個彙總,具體的操作和效果如下所示。
print(pandas.pivot_table(df1, index='銷售區域', columns='月份', values='銷售額', aggfunc='sum', fill_value=0, margins=True, margins_name='總計'))
'''
月份          1        2        3        4  ...       10       11       12        總計
銷售區域                                      ...                                     
上海    1679125  1689527  1061193  1082187  ...   412108   825169   528041  11610489
北京    1878234  1807787  1360666  1205989  ...   671608   678668   596146  12477717
安徽          0        0        0   341308  ...        0        0        0    895463
廣東          0        0   388180        0  ...        0   395188        0   1617949
江蘇          0        0        0   537079  ...   710962        0   215307   2304380
浙江          0        0   248354        0  ...        0        0        0    687862
福建    1852496  1111141  1106579   830207  ...   580707   486258   352479  10178227
總計    5409855  4608455  4164972  3996770  ...  2375385  2385283  1691973  39772087

[8 rows x 13 columns]
'''

#交叉表
#交叉表就是一種特殊的透視表,它不需要先構造一個DataFrame物件,
# 而是直接透過陣列或Series物件指定兩個或多個因素進行運算得到統計結果
#統計每個銷售區域的銷售總額
sales_area, sales_month, sales_amount = df1['銷售區域'], df1['月份'], df1['銷售額']
#使用crosstab函式生成交叉表
#說明:上程式碼使用了DataFrame物件的fillna方法將空值處理為0,再使用astype方法將資料型別處理成整數。
print(pandas.crosstab(index=sales_area, columns=sales_month, values=sales_amount, aggfunc='sum').fillna(0).astype('i8'))
'''
月份         1        2        3        4   ...       9       10      11      12
銷售區域                                      ...                                 
上海    1679125  1689527  1061193  1082187  ...  1107693  412108  825169  528041
北京    1878234  1807787  1360666  1205989  ...   390077  671608  678668  596146
安徽          0        0        0   341308  ...        0       0       0       0
廣東          0        0   388180        0  ...   365191       0  395188       0
江蘇          0        0        0   537079  ...        0  710962       0  215307
浙江          0        0   248354        0  ...        0       0       0       0
福建    1852496  1111141  1106579   830207  ...   769999  580707  486258  352479

[7 rows x 12 columns]
'''

print('================================================================================')

#資料呈現
#和Series一樣,DataFrame物件提供了plot方法來支援繪圖,底層仍然是透過matplotlib庫實現圖表的渲染
import matplotlib.pyplot as plt
#FZJKai-Z03S是我電腦上已經安裝的一種支援中文的字型的名稱,
# 字型的名稱可以透過檢視使用者主目錄下.matplotlib資料夾下名為fontlist-v330.json的檔案來獲得,而這個檔案在執行上面的命令後就會生成
#可以嘗試使用SimHei(黑體)、SimSun(宋體)或者其他專門設計用於支援中文的字型CJK
plt.rcParams['font.sans-serif'] = ['SimHei']
# %config InlineBackend.figure_format = svg
temp = pandas.pivot_table(df1, index='銷售區域', values='銷售額', aggfunc='sum')
temp.plot(figsize=(8, 4), kind='bar', title='銷售總額')
plt.xticks(rotation=0)
# plt.show()

#如果要繪製餅圖,可以修改plot方法的kind引數為pie,然後使用定製餅圖的引數對圖表加以定製,程式碼如下所示。
temp.sort_values(by='銷售額', ascending=False).plot(
    figsize=(6, 6),
    kind='pie',
    y='銷售額',
    ylabel='',
    autopct='%.2f%%',
    pctdistance=0.8,
    wedgeprops=dict(linewidth=1, width=0.35),
    legend=False
)
plt.show()

相關文章