工作簿在此:Tableau Public: Marvel_SuperHeros
這次主要挑戰了一下Tableau制作雷(zhen)達(ma)圖(fan)和放射條形圖!!!
不過還是硬著頭皮死磕到底。。。所幸最后總算成功了,效果還算滿意~~~
整個流程分三步:
觀察數據 > 數據預處理 > 可視化
數據準備階段,Python小白順便拿這次的數據練練手
一、觀察/理解數據
import numpy as np
import pandas as pd
df=pd.read_csv('Superheroes.csv')
df.head()
Name | Attribute Name | Gender | Value | |
---|---|---|---|---|
0 | Black Widow | Agility | 0 | 14 |
1 | Elektra | Agility | 0 | 16 |
2 | Invisible Woman | Agility | 0 | 12 |
3 | Storm | Agility | 0 | 14 |
4 | Black Widow | Fighting Skills | 0 | 76 |
#查看一下數據框的詳細信息
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 180 entries, 0 to 179
Data columns (total 4 columns):
Name 180 non-null object
Attribute Name 180 non-null object
Gender 180 non-null int64
Value 180 non-null int64
dtypes: int64(2), object(2)
memory usage: 8.5+ KB
#看看有多少個英雄
df['Name'].describe()
count 180
unique 30
top Captain America
freq 6
Name: Name, dtype: object
#Attribute Name中的唯一值
list(df['Attribute Name'].unique())
['Agility', 'Fighting Skills', 'Height', 'Intelligence', 'Speed', 'Strength']
#數據透視表來一個
df.pivot_table(index='Name',columns='Attribute Name',values='Value').describe()
Attribute Name | Agility | Fighting Skills | Height | Intelligence | Speed | Strength |
---|---|---|---|---|---|---|
count | 30.000000 | 30.000000 | 30.00000 | 30.000000 | 30.000000 | 30.000000 |
mean | 15.200000 | 71.233333 | 181.70000 | 4.933333 | 6.000000 | 36.400000 |
std | 3.397768 | 8.605064 | 8.55066 | 1.964045 | 3.877432 | 15.612992 |
min | 10.000000 | 55.000000 | 165.00000 | 3.000000 | 1.000000 | 15.000000 |
25% | 12.000000 | 64.250000 | 177.25000 | 3.000000 | 3.250000 | 26.000000 |
50% | 14.000000 | 71.500000 | 181.00000 | 4.000000 | 4.500000 | 29.500000 |
75% | 17.750000 | 77.750000 | 185.75000 | 7.000000 | 8.750000 | 44.750000 |
max | 22.000000 | 85.000000 | 205.00000 | 9.000000 | 16.000000 | 81.000000 |
- 數據很完整,沒有缺失
- 根據這些數據,可以考慮利用雷達圖來展現每個超級英雄的能力圖譜。
- 根據常識判斷,每種能力屬性的單位不同,且從最后的透視表結果可以看出,數值范圍也有很大差距,因此需要對這些數值進行標準化處理。
Agility:10-22
Fighting Skills:55-85
Height:165-205
Intelligence:3-9
Speed:1-16
Strength:15-81
二、數據預處理
數據沒有大問題,跳過數據清洗階段,直接進入數據加工階段,這一階段主要就是進行數據計算,對“Value”這一度量進行標準化處理。
常見的標準化方法有:min-max標準化(Min-max normalization),log函數轉換,atan函數轉換,z-score標準化(zero-mena normalization,此方法最為常用),模糊量化法。本文只介紹min-max法(規范化方法)。
極差標準化法,是消除變量量綱和變異范圍影響最簡單的方法。
具體的操作方法為:首先需要找出該指標的最大值(Xmax)和最小值(Xmin),并計算極差(R = Xmax - Xmin),然后用該變量的每一個觀察值(X)減去最小值(Xmin),再除以極差(R),即:X' = (X-Xmin) / (Xmax-Xmin)
經過極差標準化方法處理后,無論原始數據是正值還是負值,該變量各個觀察值的數值變化范圍都滿足0≤X'≤1,并且正指標、逆指標均可轉化為正向指標,作用方向一致。但是如果有新數據加入,就可能會導致最大值(Xmax)和最小值(Xmin)發生變化,就需要進行重新定義,并重新計算極差(R)。
用Python進行標準化計算:(Python小白想不出好的寫法了,先將就著用用)
#從透視表里提取每個能力值的max和min
mm=df.pivot_table(index='Name',columns='Attribute Name',values='Value').describe()
df['V_N']=None
bool=(df['Attribute Name']=='Agility')
df['V_N'][bool]=df.Value[bool].apply(lambda x: (x-mm['Agility']['min'])/(mm['Agility']['max']-mm['Agility']['min']))
bool=(df['Attribute Name']=='Fighting Skills')
df['V_N'][bool]=df.Value[bool].apply(lambda x: (x-mm['Fighting Skills']['min'])/(mm['Fighting Skills']['max']-mm['Fighting Skills']['min']))
bool=(df['Attribute Name']=='Height')
df['V_N'][bool]=df.Value[bool].apply(lambda x: (x-mm['Height']['min'])/(mm['Height']['max']-mm['Height']['min']))
bool=(df['Attribute Name']=='Intelligence')
df['V_N'][bool]=df.Value[bool].apply(lambda x: (x-mm['Intelligence']['min'])/(mm['Intelligence']['max']-mm['Intelligence']['min']))
bool=(df['Attribute Name']=='Speed')
df['V_N'][bool]=df.Value[bool].apply(lambda x: (x-mm['Speed']['min'])/(mm['Speed']['max']-mm['Speed']['min']))
bool=(df['Attribute Name']=='Strength')
df['V_N'][bool]=df.Value[bool].apply(lambda x: (x-mm['Strength']['min'])/(mm['Strength']['max']-mm['Strength']['min']))
df.head(10)
#算個百分制,看著比較舒服
df['V_N*100']=df['V_N'].apply(lambda x: round(x*100))
df.head()
Name | AttributeName | Gender | Value | V_N | V_N*100 | |
---|---|---|---|---|---|---|
0 | Black Widow | Agility | 0 | 14 | 0.333333 | 33 |
1 | Elektra | Agility | 0 | 16 | 0.500000 | 50 |
2 | Invisible Woman | Agility | 0 | 12 | 0.166667 | 17 |
3 | Storm | Agility | 0 | 14 | 0.333333 | 33 |
4 | Black Widow | Fighting Skills | 0 | 76 | 0.700000 | 70 |
當然簡單的方法有很多,我這兒純粹是練習Python用的,下面進入可視化環節~
三、可視化
Tableau雷達圖的制作可以參考這里:用Tableau畫雷達圖
放射條形圖可以參考這個:用花型射線圖 Radial Chart 對比數據
磨刀不誤砍柴功——弄清楚基本概念
有時候總是覺得為什么tableau這么復雜,有些圖表連excel都超容易出。很多時候發現自己都在憑感覺作圖,而事實上卻連最基本的概念“維度”,“度量”,“連續”,“離散”都還沒搞清楚。所以,磨刀不誤砍柴工,我還是決定自己一邊詳細地理一理概念,一邊總結一下制作步驟。
雷達圖
你看的的雷達圖不只是一張圖,其實是由三張工作表疊加而成
1、同心六邊形
構建六邊形,關鍵在于要在直角坐標系中確定六邊形6個頂點的位置。
其實就是一道數學題:已知圓心(x0,y0),半徑r,角度a,求圓上的點坐標:假設圓上任一點為(x1,y1),則
x1 = x0 + r * cos( a )
y1 = y0 + r * sin( a )
所以在這里我們可以:
-圓心默認為直角坐標系原點(0,0);
-六邊形外接圓半徑[R]可以自定義;
-6個頂點在圓上的角度分別為:0',60',120',180',240',300',360'
-6個頂點的直角坐標就可以根據以上數據在Tableau中創建以下兩個計算字段求得:
· [sin]=SIN([point]PI()/180)[R]
· [cos]=COS([point]PI()/180)[R]
- 構造數據集
根據以上思路,就可以構造創建同心六邊形的數據集了,最終的同心六邊形完整的數據集如下圖
-
Tableau拖拉拽進行曲
把sin和cos兩個度量拖到視圖上,出來兩個坐標軸和一個點,為什么?
官方文檔:
當您第一次連接到數據源時,Tableau 會將包含離散分類信息的任何字段(例如,值為字符串或布爾值的字段)分配給“數據”窗格中的“維度”區域。
當您單擊并將字段從“維度”區域拖到“行”或“列”時,Tableau 將創建列或行標題。
當您第一次連接到數據源時,Tableau 會將包含定量數值信息的任何字段(即其中的值為數字的字段)分配給“數據”窗格中的“度量”區域。
當您將字段從“度量”區域拖到“行”或“列”時,Tableau 將創建連續軸。
所以那一點代表了所有點聚合的結果,當然這里我不想要它們聚合,所以這里需要右擊sin和cos將其設置成維度。
先將cos設置成維度,可以發現視圖發生了變化
再將sin設置成維度,視圖再一次發生了變化,也離同行六邊形越來越近了
接下去試著把標記類型改為線條,可以發現所有的點都被視為一個對象連成了一條線
而我們需要的其實是5條獨立的線組成的5個六邊形,這時候把[六邊形]維度拖到標記里,來對每一組頂點予以區分,得到的結果如下圖,確實產生了5組獨立的連線,但是點之間連接的順序還有些問題
記得在開始創建數據時有一個[point]字段,依此記錄了每一個點的角度值,將它作為標記的路徑拖進來,可以看到[point]默認為聚合,所有點又連成了一個整體
最后一步:將[point]設置為維度,要構造的同心六邊形就形成啦~
針對度量和維度,我們了解了基礎概念,同時特別要注意的點:
-離散字段創建標題、連續字段創建連續軸
-辨別視圖中的字段是度量還是維度的依據在于該字段是否已聚合
-維度和度量是可以相互轉化的
-離散字段和連續字段也是可以相互轉化的
-維度/度量和離散/連續不是必然對應的
2、放射線
先看一下最終成圖以及數據摘要和完整數據
“完整數據”中是需要構建的最原始的數據集,思路跟創建六邊形類似
最關鍵的就是計算弧度和半徑R,從而確定幾個點的直角坐標
先看一下幾個重要的計算字段:
弧度計算
[angle]=2 * PI() * (INDEX()-1) * (1/WINDOW_COUNT(COUNT([數值])))
其實這是個通用公式,在數據量大時尤其好用,構造6條射線這種情境下有點“殺雞焉用宰牛刀”的感覺,其實大可以這樣算2 * PI() * (INDEX()-1) * (1/6)。
但是想要一勞永逸,當然要一開始就突破難點啦~
所以還是不要偷懶,先琢磨透原理吧~
(對了,ps.[數值]列類似于輔助列,數值可以自由設定)
1/WINDOW_COUNT(COUNT([數值]))可以理解為將圓切分成幾個等份,每個占據1/6份
(INDEX()-1) * (1/WINDOW_COUNT(COUNT([數值]))),將數值代入看看:
(1-1) * (1/6)=0;
(2-1) * (1/6)=1/6;......
結果依此是:0,1/6,2/6......5/6
2 * PI() * (INDEX()-1) * (1/WINDOW_COUNT(COUNT([數值])))的結果就是得到每個點的弧度半徑計算
[R]=IIF(ATTR([內外環])= 0, 0, SUM([數值])/WINDOW_MAX(SUM([數值])))
依然是一個通用公式,適用于構建放射條形圖(每條線半徑不同的情況),這里也可以簡單化處理直接IIF(ATTR([內外環])= 0, 0, 1)
如果是內環,半徑為0;
如果是外環,則進行歸一化處理,處理后也可以乘以一個系數100,讓數字看起來好看一點計算直角坐標
[sin]=SIN([angle]) * [R]
[cos]=COS([angle]) * [R]
造好數據以后就是拖拉拽啦~
(計算字段的命名可能跟圖上顯示的有些出入,大家意會一下,其實計算字段可以整合的更簡單一些,現在有很多冗余,下次改進)
[sin]和[cos]先拽進來,發現是只有一個空值,為什么?
看看半徑的計算公式里有個條件判斷IIF(ATTR([內外環])= 0, ...)
試試把[內外環]拽到標記里并且設置成維度,有動靜了~
再把[線角度]拖到標記欄,設置成維度,作為[sin]和[cos]的“計算依據”,這里應該是用到了分區計算的概念 ,提一下
基于“區”的計算
計算依據中的“區”,就是指視圖中的子視圖或子數據表,在計算時,“區”不像“表”那樣貫穿到邊(底),而是根據分組,在分組中進行獨立計算。
基于“表”的計算
計算依據中的“表”,就是指視圖中的整個數據表,不論其計算方向是橫向、縱向、橫穿然后向下、向下然后橫穿中的哪一種,其計算可以理解貫穿到邊(底)。
基于“單元格”的計算
最特殊的一種,每個單元格只與自己進行計算,與其它單元格均無聯系
點就出來啦~
接著把標記類型改為“線”,再把[內外環]作為路徑依據,6條線就出來啦~
3、面積圖(不規則六邊形)
開始造吧!
目標依舊是計算得到每個點的直角坐標
-
創建計算字段
確定徑向角:
[Angel]=(INDEX()-1) * (1/WINDOW_COUNT(COUNT([Radial Field])))2PI()
**注:這里的[Radial Field]可以根據不同的可視化項目代入不同數據,這里我們要用到之前我們標準化處理后的能力值 [Value_N * 100]
確定半徑:
[radial normalized length]=[Radiul Inner]+IIF(ATTR([Path])=0,0,SUM([Radiul Field])/WINDOW_MAX(SUM([Radiul Field])) * ([Radiul Outer]-[Radiul Inner]))
參數:[radial inner]、[radial outer]
這個應該算是增強版的徑向長度通用計算公式,和之前的半徑計算公式
[R]=IIF(ATTR([內外環])= 0, 0, SUM([數值])/WINDOW_MAX(SUM([數值])))是一個原理,只是增加了兩個參數內徑和外徑,更適用于放射條形圖~
弄懂原理以后,自己可以簡化一下,比如:
[radial normalized length]=SUM([Radiul Field])/WINDOW_MAX(SUM([Radiul Field]))
計算(x,y)
[radial x]=[radial normalized length] * COS([Radial Angel])
[radial y]=[radial normalized length] * SIN([Radial Angel])
接著進行拖拉拽
依舊是[radial x],[radial y]先登場,再把[Attribute Name]拽進來作為分區計算的依據
羅列一下用到的幾個函數:
1、數字函數:
PI( ):返回數字常量 pi:3.14159。
SIN(number):返回角度的正弦。以弧度為單位指定角度。
COS(number):返回角度的余弦。以弧度為單位指定角度。
示例:COS(PI( ) /4)
2、聚合函數:
ATTR(expression):如果它的所有行都有一個值,則返回該表達式的值。否則返回星號。會忽略 Null 值。
COUNT(expression):返回組中的項目數。不對 Null 值計數。
SUM(expression):返回表達式中所有值的總計。SUM 只能用于數字字段。會忽略 Null 值。
3、邏輯函數:
IIF(test, then, else, [unknown]):檢查某個條件是否得到滿足,如果為 TRUE 則返回一個值,如果為 FALSE 則返回另一個值,如果未知,則返回可選的第三個值或 NULL。
4、表計算函數:
INDEX( ):返回分區中當前行的索引,不包含與值有關的任何排序。第一個行索引從 1 開始。
WINDOW_COUNT(expression, [start, end]):返回窗口中表達式的計數。窗口用與當前行的偏移定義。使用 FIRST()+n 和 LAST()-n 表示與分區中第一行或最后一行的偏移。如果省略了開頭和結尾,則使用整個分區。
WINDOW_SUM(expression, [start, end]):返回窗口中表達式的總計。窗口用與當前行的偏移定義。使用 FIRST()+n 和 LAST()-n 表示與分區中第一行或最后一行的偏移。如果省略了開頭和結尾,則使用整個分區。
偷個小懶,回補~