1. 程式人生 > >R繪圖 第七篇:繪製條形圖(ggplot2)

R繪圖 第七篇:繪製條形圖(ggplot2)

使用geom_bar()函式繪製條形圖,條形圖的高度通常表示兩種情況之一:每組中的資料的個數,或資料框中列的值,高度表示的含義是由geom_bar()函式的引數stat決定的,stat在geom_bar()函式中有兩個有效值:count和identity。預設情況下,stat="count",這意味著每個條的高度等於每組中的資料的個數,並且,它與對映到y的圖形屬性不相容,所以,當設定stat="count"時,不能設定對映函式aes()中的y引數。如果設定stat="identity",這意味著條形的高度表示資料資料的值,而資料的值是由aes()函式的y引數決定的,就是說,把值對映到y,所以,當設定stat="identity"時,必須設定對映函式中的y引數,把它對映到數值變數。

geom_bar()函式的定義是:

geom_bar(mapping = NULL, data = NULL, stat = "count", width=0.9, position="stack")

引數註釋:

  • stat:設定統計方法,有效值是count(預設值) 和 identity,其中,count表示條形的高度是變數的數量,identity表示條形的高度是變數的值;
  • position:位置調整,有效值是stack、dodge和fill,預設值是stack(堆疊),是指兩個條形圖堆疊擺放,dodge是指兩個條形圖並行擺放,fill是指按照比例來堆疊條形圖,每個條形圖的高度都相等,但是高度表示的數量是不盡相同的。
  • width:條形圖的寬度,是個比值,預設值是0.9
  • color:條形圖的線條顏色
  • fill:條形圖的填充色

關於stat引數,有三個有效值,分別是count、identity和bin:

  • count是對離散的資料進行計數,計數的結果用一個特殊的變數..count.. 來表示,
  • bin是對連續變數進行統計轉換,轉換的結果使用變數..density..來表示
  • 而identity是直接引用資料集中變數的值

position引數也可以由兩個函式來控制,引數vjust和widht是相對值:

position_stack(vjust = 1, reverse = FALSE)
position_dodge(width 
= NULL) position_fill(vjust = 1, reverse = FALSE)

本文使用vcd包中的Arthritis資料集來演示如何建立條形圖。

head(Arthritis)
  ID Treatment  Sex Age Improved
1 57   Treated Male  27     Some
2 46   Treated Male  29     None
3 77   Treated Male  30     None
4 17   Treated Male  32   Marked
5 36   Treated Male  46   Marked
6 23   Treated Male  58   Marked

其中變數Improved和Sex是因子型別,ID和Age是數值型別。

一,繪製基本的條形圖

 使用geom_bar()函式繪製條形圖,

ggplot(data=ToothGrowth, mapping=aes(x=dose))+
  geom_bar(stat="count")

當然,我們也可以先對資料進行處理,得到按照Improved進行分類的頻數分佈表,然後使用geom_bar()繪製條形圖:

mytable <- with(Arthritis,table(Improved))
df <- as.data.frame(mytable)

ggplot(data=df, mapping=aes(x=Improved,y=Freq))+
  geom_bar(stat="identity")

繪製的條形圖是相同的,如下圖所示:

 二,修改條形圖的圖形屬性

條形圖的圖形屬性包括條形圖的寬度,條形圖的顏色,條形圖的標籤,分組和修改圖例的位置等。

1,修改條形圖的寬度和顏色

把條形圖的相對寬度設定為0.5,線條顏色設定為red,填充色設定為steelblue

ggplot(data=Arthritis, mapping=aes(x=Improved))+
  geom_bar(stat="count",width=0.5, color='red',fill='steelblue')

2,設定條形圖的文字

使用geom_text()為條形圖新增文字,顯示條形圖的高度,並調整文字的位置和大小。

當stat="count"時,設定文字的標籤需要使用一個特殊的變數 aes(label=..count..), 表示的是變數值的數量。

ggplot(data=Arthritis, mapping=aes(x=Improved))+
  geom_bar(stat="count",width=0.5, color='red',fill='steelblue')+
  geom_text(stat='count',aes(label=..count..), vjust=1.6, color="white", size=3.5)+
  theme_minimal()

當stat="identity"時,設定文字的標籤需要設定y軸的值,aes(lable=Freq),表示的變數的值。

mytable <- with(Arthritis,table(Improved))
df <- as.data.frame(mytable)

ggplot(data=df, mapping=aes(x=Improved,y=Freq))+
  geom_bar(stat="identity",width=0.5, color='red',fill='steelblue')+
  geom_text(aes(label=Freq), vjust=1.6, color="white", size=3.5)+
  theme_minimal()

新增文字資料之後,顯示的條形圖是:

3,按照分組修改條形圖的圖形屬性

 把條形圖按照Improved變數進行分組,設定每個分組的填充色,這通過aes(fill=Improved)來實現,每個分組的填充色依次是scale_color_manual()定義的顏色:

ggplot(data=Arthritis, mapping=aes(x=Improved,fill=Improved))+
  geom_bar(stat="count",width=0.5)+
  scale_color_manual(values=c("#999999", "#E69F00", "#56B4E9"))+
  geom_text(stat='count',aes(label=..count..), vjust=1.6, color="white", size=3.5)+
  theme_minimal()

4,修改圖例的位置

修改圖例的位置,通過theme(legend.position=) 來實現,預設的位置是right,有效值是right、top、bottom、left和none,其中none是指移除圖例。

p <- ggplot(data=Arthritis, mapping=aes(x=Improved,fill=Improved))+
  geom_bar(stat="count",width=0.5)+
  scale_color_manual(values=c("#999999", "#E69F00", "#56B4E9"))+
  geom_text(stat='count',aes(label=..count..), vjust=1.6, color="white", size=3.5)+
  theme_minimal()

p + theme(legend.position="top")
p + theme(legend.position="bottom")
# Remove legend
p + theme(legend.position="none")

5,修改條形圖的順序

通過scale_x_discrete()函式修改標度的順序:

p <- ggplot(data=Arthritis, mapping=aes(x=Improved,fill=Improved))+
  geom_bar(stat="count",width=0.5)+
  scale_color_manual(values=c("#999999", "#E69F00", "#56B4E9"))+
  geom_text(stat='count',aes(label=..count..), vjust=1.6, color="white", size=3.5)+
  theme_minimal()

p + scale_x_discrete(limits=c("Marked","Some", "None"))

三,包含分組的條形圖

分組的條形圖如何擺放,是由geom_bar()函式的position引數確定的,預設值是stack,表示堆疊擺放、dodge表示並行擺放、fill表示按照比例來堆疊條形圖。

1,堆疊擺放

設定geom_bar()的position引數為"stack",在向條形圖新增文字時,使用position=position_stack(0.5),調整文字的相對位置。

ggplot(data=Arthritis, mapping=aes(x=Improved,fill=Sex))+
  geom_bar(stat="count",width=0.5,position='stack')+
  scale_fill_manual(values=c('#999999','#E69F00'))+
  geom_text(stat='count',aes(label=..count..), color="white", size=3.5,position=position_stack(0.5))+
  theme_minimal()

2,並行擺放

調整y軸的最大值,使用position=position_dodge(0.5),vjust=-0.5 來調整文字的位置

y_max <- max(aggregate(ID~Improved+Sex,data=Arthritis,length)$ID)

ggplot(data=Arthritis, mapping=aes(x=Improved,fill=Sex))+
  geom_bar(stat="count",width=0.5,position='dodge')+
  scale_fill_manual(values=c('#999999','#E69F00'))+
  ylim(0,y_max+5)+
  geom_text(stat='count',aes(label=..count..), color="black", size=3.5,position=position_dodge(0.5),vjust=-0.5)+
  theme_minimal()

 3,按照比例堆疊條形圖

需要設定geom_bar(position="fill"),並使用geom_text(position=position_fill(0.5))來調整文字的位置,如果geom_text(aes(lable=..count..)),那麼表示文字顯示的值是變數的數量:

ggplot(data=Arthritis, mapping=aes(x=Improved,fill=Sex))+
  geom_bar(stat="count",width=0.5,position='fill')+
  scale_fill_manual(values=c('#999999','#E69F00'))+
  geom_text(stat='count',aes(label=..count..), color="white", size=3.5,position=position_fill(0.5))+
  theme_minimal()

該模式最大的特點是可以把文字顯示為百分比:

ggplot(data=Arthritis, mapping=aes(x=Improved,fill=Sex))+
  geom_bar(stat="count",width=0.5,position='fill')+
  scale_fill_manual(values=c('#999999','#E69F00'))+
  geom_text(stat='count',aes(label=scales::percent(..count../sum(..count..)))
              , color="white", size=3.5,position=position_fill(0.5))+
  theme_minimal()

四,增加註釋和旋轉座標軸

在繪製條形圖時,需要動態設定註釋(annotate)的位置x和y,x和y的值是由條形圖的高度決定的,

annotate(geom="text", x = NULL, y = NULL)

在繪製條形圖時,可以動態設定x和y的大小:

library("ggplot2")
library("dplyr")
library("scales")

#win.graph(width=6, height=5,pointsize=8)

#data
df <- data.frame(
  rate_cut=rep(c("0 Change", "0 - 10", "10 - 20", "20 - 30", "30 - 40","40 - 50", "50 - 60", "60 - 70","70 - 80", "80 - 90", "90 - 100", ">100"),2)
  ,freq=c(1,3,5,7,9,11,51,61,71,13,17,9,
          5,7,9,11,15,19,61,81,93,17,21,13)
  ,product=c(rep('ProductA',12),rep('ProductB',12))
)

#set order
labels_order <- c("0 Change", "0 - 10", "10 - 20", "20 - 30", "30 - 40","40 - 50", "50 - 60", "60 - 70","70 - 80", "80 - 90", "90 - 100", ">100")

#set plot text
plot_legend <- c("Product A", "Product B")
plot_title <- paste0("Increase % Distribution")
annotate_title <-"Top % Increase"
annotate_prefix_1 <-"Product A = "
annotate_prefix_2 <-"Product B = "

df_sum <- df %>% 
  group_by(product) %>%
  summarize(sumFreq=sum(freq))%>%
  ungroup()%>%
  select(product,sumFreq)

df <- merge(df,df_sum,by.x = 'product',by.y='product')
df <- within(df,{rate <- round(freq/sumFreq,digits=4)*100})
df <- subset(df,select=c(product,rate_cut,rate))

#set order
df$rate_cut <- factor(df$rate_cut,levels=labels_order,ordered = TRUE)
df <- df[order(df$product,df$rate_cut),]

#set position
annotate.y <- ceiling(max(round(df$rate,digits = 0))/4*2.5)
text.offset <- max(round(df$rate,digits = 0))/25

annotation <- df %>%
  mutate(indicator = ifelse(substr(rate_cut,1,2) %in% c("70","80","90",'>1'),'top','increase' )) %>%
  filter(indicator=='top') %>%
  dplyr::group_by(product) %>%
  dplyr::summarise(total = sum(rate)) %>%
  select(product, total)

mytheme <- theme_classic() + 
  theme(
    panel.background = element_blank(),
    strip.background = element_blank(),
    panel.grid = element_blank(),
    axis.line = element_line(color = "gray95"),
    axis.ticks = element_blank(),
    text = element_text(family = "sans"),
    axis.title = element_text(color = "gray30", size = 12),
    axis.text = element_text(size = 10, color = "gray30"),
    plot.title = element_text(size = 14, hjust = .5, color = "gray30"),
    strip.text = element_text(color = "gray30", size = 12),
    axis.line.y = element_line(size=1,linetype = 'dotted'),
    axis.line.x = element_blank(),
    axis.text.x = element_text(vjust = 0),
    plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm"),
    legend.position = c(0.7, 0.9),
    legend.text = element_text(color = "gray30")
  )

##ggplot
ggplot(df,aes(x=rate_cut, y=rate)) + 
  geom_bar(stat = "identity", aes(fill = product), position = "dodge", width = 0.5) +
  guides(fill = guide_legend(reverse = TRUE)) +
  scale_fill_manual(values = c("#00188F","#00BCF2")
                    ,breaks = c("ProductA","ProductB")
                    ,labels = plot_legend
                    ,name = "") +
  geom_text(data = df
            , aes(label = comma(rate), y = rate +text.offset, color = product)
            ,position = position_dodge(width =1)
            , size = 3) + 
  scale_color_manual(values = c("#00BCF2", "#00188F"), guide = FALSE) +
  annotate("text", x = 3, y = annotate.y, hjust = 0, color = "gray30", label = annotate_title) + 
  annotate("text", x = 2.5, y = annotate.y, hjust = 0, color = "gray30", label = paste0(annotate_prefix_1, annotation$total[1])) + 
  annotate("text", x = 2, y = annotate.y, hjust = 0, color = "gray30", label = paste0(annotate_prefix_2, annotation$total[2])) + 
  labs(x="Increase Percentage",y="Percent of freq",title=plot_title) +
  mytheme + 
  coord_flip()

參考文件: