
---
title: "R 資料管理與繪圖"
author: "許清芳，鄭中平"
date: 2024-04-27
output: slidy_presentation
---

## R 簡介（一）

### R 是什麼
- R 是免費的統計軟體，提供基礎的程式功能以及許多基礎與進階的統計功能

### 為何要用 R 
- R 具開放性，每個 R使用者都可以撰寫自己的函數，組織常用功能，讓程式碼重複使用
- 套件就是將某些函數集中，可以想成一堆功能類似或有相同主題的函數（功能）
- 發展新的統計方法的研究者亦常伴隨推出 R 套件，因此 R 能更快速跟上新技術
- R 幾乎可以控制統計圖形的每個細節，畫出品質很好的統計圖形
- R 套件中的所有程式碼都公開，可供檢查

## R 簡介（二）

### 點視窗vs寫指令
- R 是用寫指令的方式操控，也有一些視窗軟體使用了 R 套件，例如，Ｒcommander, JASP與jamovi
- 如果只做很少次，用視窗比較方便；如果常常用，寫指令比較方便
- 如果用新的程序，視窗版未必有將該程序放進去

### 使用 R 的基本場景
- 讀進資料
- 使用 R 基本功能，並呼叫一堆套件幫忙
- 找範例程式，並修改套用到自己的情境

## RStudio 與 R Markdown 
- R 的程式介面比較素樸，通過 RStudio 的 R 程式的整合溝通介面，可以方便的看到程式碼、資料、圖形
- RSudio 支援 R Markdown，可以很好的整合程式碼與結果，並加入註解，可以將整個分析流程記錄下來，存成 WORD、HTML 或 PDF 格式
- 將 R Markdown 分享給其他研究者，其他研究者可以重置你的分析
- 我們建議的工作方式：在 Rstudio 中使用 R，並在 RMD 上寫程式碼

## 整齊資料（tidy）搭配管線運算子（pipe operator）
- R 可以有不同寫作風格，不同套件也常可以做到同樣的事
- 近期趨勢是採用整齊資料（tidy）有關的資料整理套件與繪圖，並搭配管線運算子

### 整齊資料
- 整齊資料要求資料就是一個單純的長方形表格，橫邊對應分析單位（人或個體或事件），縱邊是變項
- 可以想成沒有亂加註解或是複雜表格的 EXCEL 或 SPSS 資料
- 大家講好整齊資料，程式就很好寫
- tidyverse 是個超級套件庫，包含了一堆以 tidy 規格設計的軟體，在資料處理與繪圖上非常方便，很多人使用 

### 管線運算子
- 管線運算子就是水管，把資料導來導去
- 我們常常在資料加工再加工，會需要一堆臨時符號，搞到自己都看不懂

```{r echo=TRUE, cache=FALSE, message=FALSE, warning=FALSE}
#整體設定，含載入套件
source("https://raw.githubusercontent.com/ChungPingCheng/R4BS2/main/R4BS_setup.R")
```

```{r eval=FALSE}
dta <- read.csv("https://raw.githubusercontent.com/ChungPingCheng/R4BS2/main/Data/HEXACO.csv", 
                na.strings='NA', stringsAsFactors = TRUE)

dta2 <- select(dta, where(is.factor)) 
head(dta2)
```
- 改用管線運算子
```{r eval=FALSE}
dta |> 
  dplyr::select(where(is.factor))|> 
  head()
```

## 資料整理

### 基本概念
- 資料讀進 R 後就是一個資料框架（data frame），一橫列對應一個分析單位（人或個體等），一縱欄對應一個變項。
- 兩個基本資料處理能力：挑出某些列（人或個體），挑出某些欄（變項）或加
- 整理資料常常是「分割-應用-合併」，例如，不同性別下各科成績的平均數

- 資料有長資料與寬資料兩種。長資料常常是單個反應變項，寬資料則是多個，我們需要能在兩種資料中互換

### dplyr 及 tidyr
- 套件 dplyr 及 tidyr 做到「分割-應用-合併」，以上兩個基本資料處理以及 長寬互轉
- dplyr用「動詞」表示這些指令（如 select, filter, mutate），並搭配「副詞」修飾，讓指令在某些條件下進行，或用某種方式進行。
- 猜猜看在做什麼？
```{r eval=FALSE}
dta |> 
 dplyr::filter(國高中 == '國中') |> 
 dplyr::mutate(攻擊 = 攻擊行為 > (mean(攻擊行為) + 1.96*sd(攻擊行為))) |>
 dplyr::group_by(攻擊, 性別)
```

### 我需要記指令嗎
- 不用，抄書上或網路上的程式碼，調整成自己情境
- 看的懂（而非寫得出）程式比較重要
- 知道哪些事做的到很重要
- 也可以叫 chatgpt 寫，不過自己還是要會確認

## 資料整理範例

```{r pkgs, echo=TRUE, cache=FALSE, message=FALSE, warning=FALSE}
#整體設定，含載入套件
source("https://raw.githubusercontent.com/ChungPingCheng/R4BS2/main/R4BS_setup.R")
```

### 資料
資料來自於許功餘、張玉鈴（2015），討論性格向度與青少年問題間的關聯。性格模式包括六個向度（HEXACO）：誠實／謙遜，情緒性，外向性，和悅性，嚴謹性與開放性。青少年問題則測量焦慮／憂鬱、社會退縮兩項內化問題行為，與違反規定以及攻擊行為兩類外化問題行為。

```{r }
#讀檔案
dta <- read.csv("https://raw.githubusercontent.com/ChungPingCheng/R4BS2/main/Data/HEXACO.csv", na.strings='NA', stringsAsFactors = TRUE)
```

```{r}
#檢視資料結構
#程式報表1.1
str(dta)

# 看前六筆
head(dta)
```

## 選取變項

### 選取變項方式一：利用資料型態
```{r}
#本章主要利用 tidyverse 中的 dplyr 和 tidyr 兩個套件做資料處理  
#程式報表1.2
dta |> 
  dplyr::select(where(is.factor)) |> 
  head()
```

### 選取變項方式二：利用變項名稱（部份）文字
```{r}
#高誠實／謙遜個體出現較少違反規定的行為嗎？
dta |> 
  dplyr::select(contains(c('誠', '規'))) |> 
  cor(use='pair') |> 
  round(3)
```

### 選取變項方式三：利用變項位置
```{r}
#焦慮／憂鬱、社會退縮屬於內化問題行為
#違反規定、攻擊行為屬於外化問題行為
#我們將類別相同變項重排在一起
#程式報表1.3
dta |> 
  dplyr::select(c(11, 13, 12, 14)) |>
  cor(use='pair') |> 
  round(3)
```

## 選取觀察值

### 選取觀察值（slice）方式一：利用列的位置
```{r}
#程式報表1.4
dta |> 
  dplyr::slice(11:12) |> 
  dplyr::select(1:4) 
```
 
### 選取觀察值（slice）方式二：利用列的位置（隨機若干位）
```{r}
#程式報表1.5
dta |> 
  dplyr::slice_sample(n=3) |> 
  dplyr::select(contains(c('親'))) 
```


### 篩選觀察值（filter）方式：指定變項與條件

```{r}
dta |> 
  dplyr::filter(性別 == '男') |>
  dplyr::select(contains(c('誠', '規'))) |> 
  cor(use='pair') |> 
  round(3)
```

### 篩選觀察值（filter）方式：可以同時利用多個條件篩選
```{r}
dta |> 
  dplyr::filter(母親教育程度=='研究所以上',
                父親教育程度=='研究所以上') |>
  dplyr::select(性別) |> 
  table()
```


## 轉換變項,轉換變項值

```{r}
#類別變項水準預設排列順序對應編碼大小，常常需要重排
#程式報表1.7前
with(dta, levels(母親教育程度))
```

### 變項轉換一：製造新變項
```{r}

#程式報表1.7後
dta |> 
  dplyr::mutate(母教育 = forcats::fct_relevel(母親教育程度,
                                              c("小學或不識字", 
                                                "國中", 
                                                "高中", 
                                                "大學或專科", 
                                                "研究所以上")),
                父教育 = forcats::fct_relevel(父親教育程度,
                                              c("小學或不識字", 
                                                "國中", 
                                                "高中", 
                                                "大學或專科", 
                                                "研究所以上")),
                .keep='none') |> table()
```

### 變項轉換二：修改舊變項
```{r}

dta <- dta |> 
  dplyr::mutate(母親教育程度 = forcats::fct_relevel(母親教育程度,
                                              c("小學或不識字", 
                                                "國中", 
                                                "高中", 
                                                "大學或專科", 
                                                "研究所以上")),
                父親教育程度 = forcats::fct_relevel(父親教育程度,
                                              c("小學或不識字", 
                                                "國中", 
                                                "高中", 
                                                "大學或專科", 
                                                "研究所以上")))
```

## 分組

### 分組，對各組做相同動作
```{r}
tmp <- dta |> 
  dplyr::select(性別, 攻擊行為, 違反規定) |>
  dplyr::group_by(性別) |>
  dplyr::mutate(攻擊 = case_when(攻擊行為 < quantile(攻擊行為, prob=.33,
                                  na.rm = TRUE) ~ '1_低',
                         攻擊行為 > quantile(攻擊行為, prob=.67,
                                  na.rm = TRUE) ~ '3_高',
                         .default = '2_中'), 
                違規 = case_when(違反規定 < quantile(違反規定, prob=.33,
                                  na.rm = TRUE) ~ '1_低',
                          違反規定 > quantile(違反規定, prob=.67,
                                  na.rm = TRUE) ~ '3_高',
                         .default = '2_中'), .keep='unused') |> as.data.frame()
```


```{r}
#剛剛資料作列聯表
#程式報表1.10
tmp |> ftable()
```

### 分組後合計

```{r}
#合計：算相關
#reframe 會對每一組做同樣動作，並且存成資料框，以便後續利用方便
#剛剛為展示緣故，將連續變項轉成類別變項（通常不必要）做列聯表
#這邊直接針對連續變項求相關
#程式報表1.11
dta |> 
  dplyr::select(性別, 攻擊行為, 違反規定) |>
  dplyr::group_by(性別) |>
  dplyr::reframe(相關係數 = cor(攻擊行為, 違反規定, use='pair'),
                  合計 = n())
```


```{r}
#合計：算基本統計量
#程式報表1.12
dta |> 
  dplyr::group_by(性別) |>
  dplyr::reframe(違規平均 = mean(違反規定, na.rm = TRUE), 
                 違規標準差 = sd(違反規定, na.rm = TRUE),
                 違規中位數 = median(違反規定, na.rm=T), 
                 違規四分位距 = IQR(違反規定, na.rm=T),
                 合計 = n())  
```


## 資料形式變換：寬形到長形

```{r}
#為示範方便，先選出2筆、4個變項，總共8個數值
#程式報表1.13前
dta |> 
  dplyr::select(11:14) |> 
  dplyr::slice(1:2) |> 
  as_tibble(rownames="識別碼")
```

```{r}
#將剛剛資料轉成長形
#請上下對照並注意 id，看看剛剛8個數值現在如何排列
#程式報表1.13後
dta |> dplyr::select(攻擊行為:社會退縮) |> 
  dplyr::slice(1:2) |>
  as_tibble(rownames="識別碼") |>
  tidyr::pivot_longer(cols = -識別碼, 
                      names_to = '行為問題', values_to = '分數')
```


```{r}
#利用 arrange指令，依性別排序
#程式報表1.14
dta |> dplyr::select(2, 11:14) |> 
  as_tibble(rownames="識別碼") |>
  tidyr::pivot_longer(cols = -c(識別碼, 性別),
                      names_to = '行為問題', values_to = '分數') |>
  dplyr::group_by(行為問題, 性別) |>
  dplyr::reframe(平均分數 = mean(分數, na.rm=T)) |> 
  dplyr::arrange(性別) |>
  dplyr::filter(性別 != 'NA')
```



```{r}
#綜合應用，最後面將資料由長形變成寬形
#程式報表1.15
dta |> dplyr::select(2, c(11:14)) |> 
  as_tibble(rownames="id") |>
  tidyr::pivot_longer(cols = -c(id, 性別),
                      names_to = '行為問題', values_to = '分數') |>
  dplyr::group_by(行為問題, 性別) |>
  dplyr::reframe(平均分數 = mean(分數, na.rm=T)) |> 
  dplyr::filter(性別 != 'NA') |>
  tidyr::pivot_wider(names_from = '行為問題',
                     values_from = '平均分數')
```


```{r}
#綜合應用，計算以違反規定PR90分類學生時，女生所佔各類比率
dta |>
 dplyr::mutate(違規九 = 違反規定 > quantile(違反規定, prob = 0.9, na.rm=T)) |>
 dplyr::group_by(違規九, 性別) |>
 dplyr::reframe(合計 = n()) |>  
 pivot_wider(names_from=性別, 
             values_from=合計) |>
 dplyr::mutate(違規九分位 = 違規九, 
               女性百分率 = 100*(女 / (女+男)), .keep='none')  |>
 dplyr::filter(違規九分位 != 'NA')
```


## 資料形式變換：長形到寬形

```{r}
#綜合應用，不同母親教育程度、性別下，內化問題的平均
#程式報表1.16
dta |> 
  dplyr::select(性別, 母親教育程度, 社會退縮, 焦慮.憂鬱) |>
  tidyr::pivot_wider(names_from = '性別',
                     values_from = c('社會退縮', '焦慮.憂鬱'),
                     values_fn = ~ mean(.x, na.rm = TRUE)) |>
  dplyr::filter(母親教育程度 != 'NA') |>
  dplyr::select(-contains('NA')) |>
  dplyr::arrange(社會退縮_女)
```
 

## 利用 ggplot2 繪製統計圖

### 簡單認識 ggplot2
- ggplot2 的名稱來自於圖形語法（grammar of graphics, gg）
- 圖形語法背後的主要概念是圖層；要繪製一張統計圖，就是依照順序，把圖層一層層叠加上去。
- 在ggplot2中，主要的圖層有：美學（aesthetic,也就是畫布）、幾何成分（geometric objects）、變換（transformations）、坐標（coordinates）、標籤（labels）和主題（themes）與分面（facets）。
- 畫布設定 x, y 是哪些，幾何成分設定圖的樣子，這兩個完成，圖主要就出來了，其他部份則是微調，分面則是講同類型的圖分資料畫出
- 繪製統計圖常見有四類變項角色，包括解釋變項（預測變項、自變項，x）、反應變項（依變項，y）、分組（分色）變項與分面（facet）的變項。
- 如果有兩個圖層用了不同幾何成分，會是兩張圖疊在一起
- 兩個圖層可以引用不同資料

### 好複雜！
- 不會，抄書上或網路上的 ggplot2 程式碼，調整成自己情境


## 練習認出角色與幾何成分
```{r}
knitr::include_graphics("GenderMarriage103.png")
```

## 繪圖範例

### 資料與管理

```{r}
#讀檔案
dta <- read.csv(file = "https://raw.githubusercontent.com/ChungPingCheng/R4BS2/main/Data/HEXACO_HS.csv",header = TRUE, stringsAsFactors = TRUE)
```


```{r}
#檢視資料結構
#程式報表2.1
str(dta)
```

```{r}
#設定類別變項的順序
dta <- dta |> 
  dplyr::mutate(母親教育程度 = forcats::fct_relevel(母親教育程度, 
                                              c("小學或不識字", 
                                                "國中", 
                                                "高中", 
                                                "大學或專科", 
                                                "研究所以上")),
                父親教育程度 = forcats::fct_relevel(父親教育程度, 
                                              c("小學或不識字", 
                                                "國中", 
                                                "高中", 
                                                "大學或專科", 
                                                "研究所以上")))
```

## 一步一步填滿圖層

```{r }
#ggplot 是一個一個圖層（layer）疊上去
#底下以散佈圖為例，呈現每一步驟的結果
#第一步驟設定圖的框架，注意圖的X軸與Y軸
#圖2.1
g0 <- ggplot(data = dta, 
             aes(x=社會退縮, y=攻擊行為)) 
g0
```


```{r}
#在前一步驟結果（圖的框架）上加入點
#圖2.2
g1 <- g0 + geom_point(alpha=.2) 
g1
```

```{r }
#在前一步驟結果上加入橢圓（資料的95%區間）與局部迴歸線
#圖2.3
g2 <- g1 + stat_smooth(method='lm', 
                       formula = y ~ x,
                       se=FALSE, 
                       linewidth = 0.5) 
g2
```


```{r}
#在前一步驟結果上要求以變項（性別）區分顏色
#圖2.4
g3 <- g2 + aes(color=性別) 
g3
```
```{r }
#在前一步驟結果上設定 x 軸與 y 軸刻度
#圖2.5
g4 <- g3 + 
  scale_y_continuous(breaks=seq(0, 25, by=5)) +
  scale_x_continuous(breaks=seq(0, 12, by=2))
g4
```

```{r }
#在前一步驟結果上要求以變項（父母教育）分面（facet）
#圖2.6
g5 <- g4 + facet_wrap(vars(母親教育程度),nrow=1)
g5
```

```{r }
#在前一步驟結果上加上X軸、Y軸的標示，以及整個圖形的標題
#圖2.7
g6 <- g5 + labs(x='社會退縮分數',
                y='攻擊行為分數',
                title='散布圖：攻擊行為與社會退縮')
g6
```

```{r }
#在前一步驟結果上改變主題，並要求圖示位置
#圖2.8
g7 <- g6 + theme_minimal() + 
  theme(legend.position='top')
g7
```

```{r }
#前面步驟可以一次執行
ggplot(data = dta, aes(x=社會退縮, y=攻擊行為)) +
   geom_point(alpha=.2) +
 stat_smooth(method='lm', formula = y ~ x,
       se=FALSE, linewidth = 0.5)+
 aes(color=性別) +
 scale_y_continuous(breaks=seq(0, 25, by=5)) +
 scale_x_continuous(breaks=seq(0, 12, by=2)) +
 facet_wrap(vars(母親教育程度),nrow=1)+
 labs(x='社會退縮分數', y='攻擊行為分數',
     title='散布圖：攻擊行為與社會退縮') +
 theme_minimal() + 
 theme(legend.position='top')
```


## 繪製統計摘要

### 以 ggplot 直接繪製統計摘要結果（連續資料平均數與標準誤）
```{r }
#圖2.9
ggplot(data=dta, 
     aes(x=母親教育程度, y=誠實.謙遜, color=性別)) +
  stat_summary(fun.data = "mean_cl_boot", 
               position=position_dodge(.2)) +
  stat_summary(aes(group=性別), fun = mean, 
               geom="line",
               position=position_dodge(.2))+
  scale_color_grey(end=.7)+
  labs(y='誠實.謙遜平均分數',
       x='母親教育程度',
       title='不同性別的誠實.謙遜平均跟母親教育程度的關係',
       caption="來源: 許功餘")+
  theme(legend.position='top')
```

### 以 ggplot 直接繪製統計摘要結果（類別資料的百分比）
```{r }
#圖2.10
ggplot(dta, 
       aes(x=母親教育程度, group=父親教育程度)) + 
  geom_bar(aes(y=after_stat(prop), 
          fill = factor(after_stat(x))),
          width = .2) + 
  scale_y_continuous(labels=scales::percent) +
  scale_fill_grey()+
  coord_flip()+
  labs(y="百分比",
       title="門當戶對:父母親教育程度") +
  facet_wrap(vars(父親教育程度), ncol=1)+
  theme(legend.position="none")
```

### 以 ggplot 直接繪製統計摘要結果（連續資料平均數與標準誤） 
```{r }

#圖2.11
ggplot(data=dta, 
     aes(x=母親教育程度, y=誠實.謙遜)) +
  stat_summary(fun.data = "mean_cl_boot") +
  scale_color_grey(end=.7)+
  facet_wrap(vars(父親教育程度), nrow=1)+
  labs(y='誠實.謙遜平均分數',
       x='母親教育程度',
       title='不同父親教育程度的誠實.謙遜平均跟母親教育程度的關係',
       caption="來源: 許功餘")+
  theme(legend.position='top',
        axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
```

## 多變量圖形，利用寬轉長

```{r }
#多變量圖形 
#圖2.12,dpi=300
dta |> tidyr::pivot_longer(cols=4:9, 
                           names_to = '人格維度',
                           values_to = '分數') |>
  ggplot()+
  aes(x=違反規定, y=分數, color=性別)+
  geom_point(size=rel(.5))+
  stat_smooth(method='lm', 
              formula = y ~ x,
              se=F, linewidth=.5)+
  facet_grid(人格維度 ~ 母親教育程度) +
  scale_color_grey(start=.1, end=.6)+
  labs(x="違反規定分數",
       y="人格維度分數")+
  theme(legend.position='top')
```

## 延伸

```{r }
#同時繪製兩個變項的散布圖，以及各自的邊際分布
#圖2.13
p <- ggplot(dta, 
            aes(x=誠實.謙遜, y=違反規定)) +
  geom_point(shape=21, alpha=.5) +
  stat_ellipse() +
  geom_vline(xintercept=mean(dta$誠實.謙遜), col="gray") +
  geom_hline(yintercept=mean(dta$違反規定), col="gray") +
  stat_smooth(method="lm", 
              formula = y ~ x,
              linewidth=.7,
              linetype="dotted",
              se=FALSE,
              alpha=.5,
              col=1) +
  labs(y="違反規定分數", 
       x="誠實.謙遜分數") 
pacman::p_load(ggExtra, KernSmooth)
ggMarginal(p, 
           type="histogram",
           xparams=list(binwidth=dpih(dta$誠實.謙遜),
                          fill="gray90"),
           yparams=list(binwidth=dpih(dta$違反規定),
                          fill="gray90"))
```



## 小結

- R 加上 RStudio 的 Rmarkdown，是不錯的資料分析環境
- 不需要重頭開始寫，但要會認，可以由模仿其他人的程式碼開始
- 資料管理至少要會做「分割-應用-合併」 以及長寬互轉
- 善用 ggplot2 繪圖可以繪出各式統計圖
