R Visualization - Using ggplot2

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

For older slides (2013.07), see this link. Screencast on youtube.

台大計量經濟 TA 課 2013-12-04

R Visualization - Using ggplot2

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

今天的主題

想像一個情境

報告/專題/咪挺前夕…

很容易生出這樣的圖表…

excel

matlab

預設的風格……很醜

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

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

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

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

使用內建指令

優點

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

使用底層的套件

優點

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

使用 ggplot2

優點

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

本日大綱

  • 資料前處理
  • 基本畫圖指令
  • ggplot2 原則
  • 練習

資料前處理

# 放在 examples/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 其實只有幾個值

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")

            

可以一次更改很多東西

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

            

延伸閱讀

R 基礎語法

ggplot2 完整介紹

回到一開始的例子

使用 library(reshape2)

melt() ==>

科系名 (disp) 男 (male) 女 (female)
A 5 10
B 6 12
C 9 18

<== 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")

Thank You!

Fork me on Github