ggplot2雙座標軸的解決方案
本來沒有打算寫這一篇的,因為在一幅圖表中使用雙座標軸確實不是一個很好地習慣,無論是資訊傳遞的效率還是資料表達的準確性而言。
但是最近有好幾個小夥伴兒跟我諮詢關於ggplot2的次座標軸問題,平時的一些業務分析中,有些場景出於資料呈現的需要,或者閱讀習慣等,往往需要在一幅圖中呈現兩個量級不等的座標。
所以我覺得這一篇推送很有必要,確實在最新版的ggplot2(ggplot 2。2。0以上版本)中,已經加入了次座標軸引數,透過這個次座標軸的轉換,我們可以模擬出不同數量級的次座標軸效果。
因為其中用到了英文月份簡寫,這裡對系統日期顯示格式做了特殊設定:
lct <- Sys。getlocale(“LC_TIME”)
#備份本地預設日期顯示格式
Sys。setlocale(“LC_TIME”, “C”)
#指定標準日期顯示格式
Sys。setlocale(“LC_TIME”,lct)
#這一句是恢復預設系統日期顯示格式
#(記得要在使用完下面的month函式之後再執行這一句,否則月份返回的是中文)
載入包:
library(“lubridate”)
library(“ggplot2”)
library(“scales”)
library(“magrittr”)
library(“tidyr”)
生成作圖資料
作圖資料1——單序列柱形圖
data1 <- data。frame(
Month = seq(from = as。Date(‘2017-01-01’),to=as。Date(‘2017-06-01’),by=‘1 month’) %>% month(label=TRUE),
Value = runif(6,10,50) %>% round()
)
Month Value
1 Jan 39
2 Feb 38
3 Mar 50
4 Apr 33
5 May 18
6 Jun 49
作圖資料2——二分類折線圖(帶散點)
data2 <- data。frame(
Month = seq(from = as。Date(‘2017-01-01’),to=as。Date(‘2017-06-01’),by=‘1 month’) %>% month(label=TRUE),
Categroy1 = runif(6,0。1,0。5) %>% round(2),
Categroy2 = runif(6,0。1,0。5) %>% round(2)
) %>% gather(Category,Value,-1)
Month Category Value
1 Jan Categroy1 0。49
2 Feb Categroy1 0。23
3 Mar Categroy1 0。10
4 Apr Categroy1 0。38
5 May Categroy1 0。34
6 Jun Categroy1 0。13
7 Jan Categroy2 0。48
8 Feb Categroy2 0。38
9 Mar Categroy2 0。48
10 Apr Categroy2 0。15
11 May Categroy2 0。40
12 Jun Categroy2 0。16
以下是整個過程程式碼,基本是司空見慣的內容,這裡不做過多解釋,僅提示其中兩處重點,注意第二行geom_line內的y引數賦值以及第四行的scale_y_continuous語句:
ggplot() +
geom_col( data = data1,aes(x = Month,y = Value),fill=“#6794a7”) +
geom_line(data = data2,aes(x = Month,y = rescale(Value,c(0,55)),colour=Category,group=Category),size=1。5) +
geom_point(data = data2,aes(x = Month,y = rescale(Value,c(0,55)),colour=Category),shape=21,fill=“white”,size=4)+
scale_y_continuous(breaks=pretty_breaks(5),sec。axis = sec_axis( ~rescale(。,c(0,0。5)),name = “Categroy”,labels=sprintf(“%d%%”,(0:5)*10)))+
scale_color_manual(label = c(“Categroy1”, “Categroy2”),values = c(“#ee8f71”,“#C10534”)) +
labs(
title=“This is a Title!”,
subtitle=“This is a Subtitle”,
caption=“This is a Caption”
)+
theme_minimal(base_size=16) %+replace%
theme(
plot。caption = element_text(hjust=0),
plot。margin = unit(c(1,0。5,1,0。5), “lines”)
)
這段程式碼與我們經常用的有兩點不同:
第一次自定義對映——折線度量資料的對映轉換:
geom_line(geom_point,因為點圖是附屬於折線圖,僅做修飾之用,這裡只重點說折線圖層)中的y引數指定的物件使用了一個統計變換函式,rescale函式其實很好理解,就是將一個數值向量按照給定的另一個數值向量的極差(range),等比例標準化。
如果你知道如何將一組向量按照0~1標準化的話,那麼這個函式就不難理解 ,其實就是將標準化的尺度給了一個自定義的範圍。
因為在ggplot2標度系統中,不容許在一個圖形中出現兩個量級不等的標度(一山不容二虎),但是想要提供度量不等的次座標軸,折中的方法就是,將次座標軸的所有量級按照主座標軸的量級進行縮放(如果次座標軸量級大於主座標軸,那麼就是等比例放大,如果比主座標軸量級大則縮小)。
針對本例而言,就是將折線圖的資料來源量級(0。0~0。5)放大到0~35的區間上,所有的單個指標的縮放比例都是相同的,這樣你在圖上就不會感受到太大的視角誤差。
value1<-data1$Value
value21 <- data2[data2$Category == ‘Categroy1’,“Value”]
value22 <- data2[data2$Category == ‘Categroy2’,“Value”]
mydata <- data。frame(value1,value21,value22)
mydata$value31 <- rescale(mydata$value21,c(0,50))
mydata$value32 <- rescale(mydata$value22,c(0,50))
value1 value21 value22 value31 value32
1 39 0。49 0。48 50。000000 50。000000
2 38 0。23 0。38 16。666667 34。848485
3 50 0。10 0。48 0。000000 50。000000
4 33 0。38 0。15 35。897436 0。000000
5 18 0。34 0。40 30。769231 37。878788
6 49 0。13 0。16 3。846154 1。515152
這是最終的折現結果,在geom_line中使用rescale函式實際上就是做的這種度量重新自定義對映的過程。
第二次自定義對映——次座標軸刻度標籤轉換:
僅僅做以上步驟還不夠,因為這隻能保障次座標軸的資料點位置相對於整個座標系統而言,不會出現太大的視覺誤差,但是現在的問題是這個圖形物件中有兩套不同的度量,所以必須宣告不同的y軸度量標準,也就是y軸的刻度線及刻度標籤,刻度標籤的定義就是本案例的第二個重點,它仍然是透過rescale函式進行了一次度量的重新對映。
不過這次對映的過程剛好是相反的操作,即將之前已經被標準化到0~50區間內的原始度量標籤透過rescale函式再次標準化到0~0。5區間內,這樣保障顯示在次座標軸上的度量是符合原始資料極差範圍呢。
說的有些拗口了,實際上以上過程思路很簡單,就是先將資料對映到正確的位置,然後將次座標軸刻度線度量標籤再按照真實極差進行分佈,一虛一實,正好達到了模擬效果。
scale_y_continuous(
breaks=pretty_breaks(5), #建立主座標軸的刻度區間(這裡是5個區間6個刻度點)
sec。axis = sec_axis( ~rescale(。,c(0,0。5)), #對次座標軸刻度標籤的二次對映(極差範圍指定真實極差即可)
name = “Categroy”, #次座標軸名稱
labels=sprintf(“%d%%”,(0:5)*10)) #刻度標籤顯示格式(這裡是百分號)
)
思路大體上就是這樣子,希望這一篇文章可以幫到大家!
線上課程請點選文末原文連結:
Hellobi Live | R語言視覺化在商務場景中的應用
往期案例資料請移步本人GitHub:
https://
github。com/ljtyduyu/Dat
aWarehouse/tree/master/File
上一篇:ggplot2雙座標軸的解決方案
下一篇:ggplot2雙座標軸的解決方案