R Visualization - Using ggplot2

Liang Bo Wang (亮亮), 2013-12-06

From Taiwan R User Group, more info on Meetup.

Online slide: http://ccwang002.github.io/2013-RConf-ggplot2-intro/

Taiwan R Conference, 2013-12-06

R Visualization Using ggplot2

Liang Bo Wang (亮亮)
Under CC 4.0 BY International License

About Me

  • 王亮博(亮亮)
  • 台大生醫電資所
  • Bioinfo. & Biostat. Core Lab,
    台大基因體中心
  • Taiwan R User Group 成員
  • 歡迎 Bioinfo 會後討論

今天的主題

想像一個情境

報告/專題/咪挺前夕…

很容易生出這樣的圖表…

excel

matlab

預設的風格……很醜

自己的經驗,常碰到的問題

這些問題,大多在 R 裡能解決

在選擇什麼工具作圖前,先想清楚

在 R 裡面,大概有
1. 內建指令 2. 底層的畫圖指令 3. ggplot2 類比較抽象的函式
幾種方式來畫圖。

使用內建指令

優點

  • 很簡單又快
  • 適合看 data 長什麼樣
  • 指令齊全,2D 圖都做得到
  • 要調一些版面也不難
    axis ticks, legend, margin
    都可以改

使用底層的套件

優點

  • lattice, grid, ... 等套件
  • 所有圖表都畫得出來
  • 可以仔細設計呈現的方式
  • 很適合用來做 Infographics
  • 很多期刊上圖(尤其是多圖併列)都可以用這個畫

使用 ggplot2

優點

  • 大多數的圖表都畫得出來
  • 在使用上遵照一定邏輯
    例如畫圖都從 geom_xxx(...) 開始
  • 短時間內能產出品質更高的圖表
  • 常用的設定存起來,會越用越順

本日大綱

  • 資料前處理
  • 基本畫圖指令
  • ggplot2 原則
  • Use Case Study

資料前處理

# 放在 Rcode/play_data_process
# 純文字,用 R 未必好操作
# 記得使用 UTF-8 不要 big5/cp950
df.ntu_sexratio <- read.table(
  'NTU_B02new_sexratio.raw.txt',
  comment.char="#",
  header=TRUE,
  strip.white=TRUE,
  stringsAsFactors=FALSE
)
# 把總人數中的 "人" 去掉
# 並且由字串轉成數字處理
df.ntu_sexratio[[2]] <- as.integer(
  strsplit(df.ntu_sexratio[[2]], "人")
)
# 使用 Python3 處理
import pandas as pd
RAW_PATH = 'NTU_B02new_sexratio.raw.txt'
CSV_PATH = 'NTU_B02new_sexratio.csv'
df = pd.read_table(
    RAW_PATH,    # 檔案路徑
    sep='[ ]*',  # 分隔符號
    skiprows=11  # 忽略前 11 行不讀
)
df = df.dropna() # df.head() 可以看到第 0 列是 NaN (NA)
# 去「人」
df.ix[:, 1] = df.icol(1).apply(lambda x: x[:-1])
df.to_csv(CSV_PATH, index=False)  # 存檔
df2 = pd.read_csv(CSV_PATH)  # 測試,再讀回來

光這檔案就夠麻煩了
以轉成 CSV 為目標

在 R 內讀 CSV 非常方便
原生檔則可以用 excel 另存 CSV
data.frame 操作詳見 Subsetting, Advanced R programming by Hadley Wickham

df.ntu <- read.csv('NTU_B02new_sexratio.csv')
df.ntu[, c("女", "男")]
df.ntu[df.ntu$女 > 30, ]
df.ntu[df.ntu$女 > 30 & df.ntu$男 < 20, ]
summary(df.ntu)
ncol(df.ntu)
nrow(df.ntu)

使用 ggplot2 基本畫圖指令

Scatter Plot

plot(mtcars$wt, mtcars$mpg)
ggplot(mtcars, aes(x=wt, y=mpg)) + geom_point()
        

Line Plot

ggplot(pressure, aes(x=temperature, y=pressure)) +
  geom_line() + geom_point()
        

Bar Plot

ggplot(BOD, aes(x=factor(Time), y=demand)) +
  geom_bar(stat="identity")
        

Bar Plot by Counting

g <- ggplot(mtcars, aes(x=factor(cyl)))  # 先存起來
g + geom_bar(stat="bin")  # 可以一步一步接
g + geom_bar(stat="bin") + theme_bw(16)  # 改主題
        

Scatter Plot

ggplot(mtcars, aes(x=wt, y=mpg)) + geom_point(color="blue", size=5, alpha=0.5)
ggplot(mtcars, aes(x=wt, y=mpg, color=gear)) + geom_point()
        

更改參數實際對應的值,用 scale_xxx(...)

g <- ggplot(
  mtcars,
  aes(x=wt, y=mpg, color=gear)
)
# 更改指定的顏色,目前是連續的
g + geom_point(size=5) +
  scale_color_continuous(
    low="yellow", high="red"
  ) + theme_bw(16) +
  theme(legend.position="top")
            

gear 其實只有幾個值 (3, 4, 5)

g <- ggplot(
  mtcars,
  aes(x=wt, y=mpg, color=factor(gear))
) + geom_point(size=5) + theme_bw(16) +
  theme(legend.position="top")
g  # 印預設結果
# 手動改顏色
g + scale_color_manual(
  values=c("red", "yellow", "purple")
)
# 系統內建色組
g + scale_color_brewer(palette="Set3")

            

aes(...) 一資料欄位可以同時指定多個參數

g <- ggplot(
  mtcars,
  aes(x=wt, y=mpg,
      color=factor(gear),
      size=factor(gear)
)) + theme_bw(16) +
  theme(legend.position="top")

g + geom_point(alpha=0.7) +
  scale_size_discrete(range=c(4, 10))

            

其他類型的圖都是同樣規則

g <- ggplot(mtcars, aes(
  x=factor(cyl),
  fill=factor(cyl)
))

g + geom_bar(stat="bin") +
  scale_fill_brewer(palette="Set2") +
  theme_classic()

# 試著把 fill 改成 color

            

回到一開始的例子

melt()

科系名 (disp) 男 (male) 女 (female)
A 5 10
B 6 12
C 9 18
  • 使用 library(reshape2)
  • melt, dcast 做 W/L, L/W 轉換

dcast()

科系名 (disp) 性別 (sex) 人數 (number)
A male 5
A female 10
B male 6
B female 12
C male 9
C female 18
library(reshape2)
df.ntu <- read.csv('NTU_B02new_sexratio.csv')
colnames(df.ntu) <- c("disp", "total", "male", "female", "ratio")

# 只取一部份的資料
df.part <- df.ntu[c(1, 10, 20, 30, 40, 50, 56), ]
df.part$ratio <- NULL
df.part$total <- NULL

# Wide-to-Long
df.part <- melt(
  df.part,             # data.frame
  id.vars="disp",      # 每行固定出現的 (disp)
  variable.name="sex", # 多欄合併後欄位名 (存 male/female)
  value.name="number"  # 放值的地方
)
        
g <- ggplot(df.part,
  aes(x=disp, y=number, fill=sex)) +
  theme(text=element_text(
    family="Heiti TC Medium",
    size=18))

# Stack 男女疊在一起
g + geom_bar(stat="identity")

# Dodge 同科系男女靠比較近
g + geom_bar(stat="identity",
             position="dodge") +
  scale_fill_brewer(palette="Set1") +
  theme(legend.position="top")

延伸閱讀

R 基礎語法

ggplot2 完整介紹

To Infinity and Beyond - Geo Visualization

googleVis 現在跟 RStudio 有非常好的整合

Ex. 北部空氣汙染監測

資料前處理,使用 IPython Notebook

library(plyr)
library(ggmap)
df.air <- read.csv("play_air_map/air_nothern_2012.csv", as.is=TRUE)
df.loc <- data.frame(loc=unique(df.air$loc), stringsAsFactors=FALSE)

# retreive GEO location (lon, lat)
locator <- function(loc) {
  geo_loc <- geocode(location=loc$loc)
  df_temp <- data.frame(loc=loc, lon=geo_loc$lon, lat=geo_loc$lat)
  return(df_temp)
}
df.loc <- ddply(df.loc, .(loc), locator)
# combined with the air data
df.air_loc <- merge(df.air, df.loc, by="loc", all.y=TRUE)
df.air_loc$pollutant <- factor(df.air_loc$pollutant)
g <- ggmap(
  get_googlemap(
    center=c(121.49, 25.04), # Long/lat of centre
    zoom=10, maptype='satellite',
    scale=2,
  ), extent='device', darken = 0
)

# given a data filtered by pollutant and hourtime
g + geom_point(
  data=df.air[df.air$pollutant == "PM10", ],
  aes(x=lon, y=lat, size=value, alpha=value),
  color="yellow") +
  scale_size_continuous(range=c(5, 30)) +
  scale_alpha_continuous(range=c(0.2, 1))
  

Ex. 地震頻率空間分布

show_over_scale <- function(scale) {
  df_temp <- df.earthquake[df.earthquake$scale > scale, ]
  g_layered <- g + geom_point(data=df_temp, aes(x=lon, y=lat), color="red") +
    stat_density2d(
      aes(x = lon, y = lat, fill = ..level.., alpha = ..level..),
      bins = 12, geom = "polygon",
      data = df_temp
    ) + scale_alpha_continuous(range=c(0.3, 1), guide=FALSE) +
    scale_fill_continuous(low="yellow", high="red", guide=FALSE)
  ggsave(filename=paste0(scale*10, ".png"), scale=2, width=2, height=2)
}

for(s in seq(from=2, to=5, by=0.5))
  show_over_scale(s)
        

靜態

動態

Thank You!

Fork me on Github