您當前的位置:首頁 > 歷史

ggplot2雙座標軸的解決方案

作者:由 杜雨 發表于 歷史時間:2017-12-10

本來沒有打算寫這一篇的,因為在一幅圖表中使用雙座標軸確實不是一個很好地習慣,無論是資訊傳遞的效率還是資料表達的準確性而言。

但是最近有好幾個小夥伴兒跟我諮詢關於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”)

ggplot2雙座標軸的解決方案

這段程式碼與我們經常用的有兩點不同:

第一次自定義對映——折線度量資料的對映轉換:

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