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