【ggpairs】データを丁寧に見るための ggpairs の使い方【R】

# はじめに

東京大学で社会心理学の研究をしている M2 の[髙橋龍](https://ryu-thakahashi.github.io/)です．

**`GGally` パッケージ** は，`ggplot2` を拡張して散布図行列（ペアプロット）などを手軽に作成できる便利なパッケージです．中でも `ggpairs()` 関数は，複数の数値変数・カテゴリ変数をまとめて可視化する際に非常に重宝します．
（こんな感じ）

![iris_pairplot_final.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726043/eed98f47-bf6a-4c3a-8fc7-f3ca987b08c2.png)


本記事では，はじめて `ggpairs()` を使う方向けに，**基本的な使い方からオプション指定，そしてどのように図を読み取るか** までをステップバイステップで解説します．サンプルとして `iris` データセットを使い，さらに別の例として `tips` データを使った条件間交互作用の可視化例もご紹介します．

なお，この記事は[昔書いた記事](https://qiita.com/ryu-takahashi2718/items/0462535e89f76e9c667b)をアップデートしたものです（若書きすぎて，いまみると恥ずかしい...）

# 0. 準備

## 0.1. ライブラリのインストール

まずは可視化のために必要な準備をしていきます．
（もし `GGally` がインストールされていない場合は，以下のコードでインストールしてください）

```r
install.packages("GGally")
```

## 0.2. ライブラリの読み込みと保存用のフォルダ指定

次に，必要なライブラリを読み込みます．

```r
library(ggplot2)
library(dplyr)
library(GGally)

# 図を保存するためのフォルダを指定
FIG_DIR <- paste0(getwd(), "/figs/")
# 必要に応じてフォルダがなければ作成
if(!dir.exists(FIG_DIR)) dir.create(FIG_DIR)
```

それでは，早速可視化してみましょう！


# 1. `iris` データの可視化

## 1.1. データの中身をチェック

まずは有名な `iris` データセットを利用します．

```r
# iris データの中身をチェック
glimpse(iris)
```

すると，こんな感じの出力がされると思います．

![1741593826980.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726043/8aada594-fac3-4486-b471-14c52d71003c.png)

Sepal というのは「がく」，Petal というのは「花弁」を表しています．

> ![2-Petal-and-Sepal-of-Iris-flowers-As-input-data-the-network-should-be-provided-a-dataset.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726043/2632c7f9-c150-4146-be4d-fae5b2ea2436.png)
> [引用元](https://www.researchgate.net/profile/Gayan_De_Silva2/publication/366849054/figure/fig24/AS:11431281110951871@1672824232108/2-Petal-and-Sepal-of-Iris-flowers-As-input-data-the-network-should-be-provided-a-dataset.png)

`iris` データセットは，アイリスという花についてのデータなので，`Species` は「種」を表しています．
各 `Species` ごとに50個のデータ（計150行のデータ）があります．

## 1.2. まずは全変数を可視化してみる

ひとまずすべてのデータを可視化してみましょう！

```r
ggpairs(iris)
```

すると，以下のような結果になると思います

![1741594049356.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726043/5838b704-399c-4d62-beb8-f9e8bdf38b2c.png)

これだけでも十分きれいな図で満足感は得られるのですが，どう読み取ればいいのか悩みますね...
関心のある変数間の関係だけに絞って，再度可視化してみましょう．

## 1.3. 関心のある変数だけを選択して可視化

不要な変数が多い場合などは，`dplyr::select()` で変数を絞ると見やすくなります．

```r
selected_iris <- 
  iris |> 
  dplyr::select(Sepal.Length, Sepal.Width, Species)

ggpairs(selected_iris)
```

結果はこちらです．

![1741594150181.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726043/2353ef9b-3eee-4761-9c41-9d3f8958c4b9.png)

だいぶすっきりしました．

出力が絞られたので，各図についてちょっと丁寧に見てみましょう．
（詳細な説明は不要，という方は「[**1.4 色分けしてプロットする**](https://qiita.com/ryu-takahashi2718/items/82dc8bf8872b941ffe0d#14-%E8%89%B2%E5%88%86%E3%81%91%E3%81%97%E3%81%A6%E3%83%97%E3%83%AD%E3%83%83%E3%83%88%E3%81%99%E3%82%8B)」まで飛んでください）

### 1.3.1. 対角成分

**対角成分**とは，左上から右下にかけて並んでいるグラフ（**オレンジ色の枠**で囲われている部分の図）のことです．

![1741596112879.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726043/e5ee2bd2-6f80-48a1-b34a-cb84eda37db3.png)


例えば，下の図の左上のグラフ（**青枠と緑枠が交差している部分**のグラフ）を見てください．
左上のグラフは `Sepal.Length` (青枠) と `Sepal.Length` （緑枠）が交差する場所です．つまり，**同じ変数同士の関係**を示している = **その変数の分布**を示しています．

![1741594446441.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726043/18040b90-4d0c-4fac-a80a-b986cbfeddee.png)

**対角成分はすべて同じ変数同士が交差**している，つまり**その変数の分布を示している**ため，ヒストグラムや密度分布などが描かれています．

### 1.3.2. 下三角

**下三角**は，対角成分を除いた左下の部分です．（**オレンジ色の枠で囲われている部分**の図）

![1741594625631.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726043/856cb66f-b9a6-4a7c-866c-246f0a7c4078.png)

下三角のうち，**左上のグラフ**（**青枠と緑枠が交差している部分**のグラフ）を見てください．
左上のグラフは **`Sepal.Length` (青枠) と `Sepal.Width` （緑枠）が交差する場所**です．つまり，**それぞれの変数間の関係**を示しています．

![1741594803328.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726043/c84c5596-cad2-49ae-b90c-4fd2c55c0d9e.png)

（広く分布しているものの，この図から右肩上がりの傾向，つまり正の相関がうっすらと見て取れますね）


次に，下三角のうち**左下のグラフ**を見てください．
左下のグラフは **`Sepal.Width` (青枠) と `Species` （緑枠）が交差する場所** です．つまり，**連続変数とカテゴリ変数の関係** を示しています．

![1741594889648.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726043/c7e06943-e7ca-4fb2-b9ec-54758eca52cd.png)

これは，`Species` ごとに `Sepal.Width` の分布がどのように異なるかを示しています．
図を見ると，　**`Species` が異なれば `Sepal.Width` の値も異なる** ことがわかりますね．
なので， **`Species` ごとに色分け**することで，違いがもっと分かりやすくなるかもしれません．


## 1.4. 色分けしてプロットする

`mapping = aes(color = Species)` を指定すると，カテゴリ変数ごとに色分けした図が得られます．

```r
ggpairs(
  selected_iris, 
  mapping = aes(color = Species) # このコードを追加
)
```

その結果がこちらです．

![1741595074327.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726043/57fd4895-44a6-4463-ad85-106a8cb009ae.png)

このように， **`Species` ごとに色分け** することで， **それぞれのグループの分布がより明確に** なります．
例えば，下三角の右上の成分（**散布図**）を見ると， **`Species` ごとに `Sepal.Length` と `Sepal.Width` の関係が異なる** ことがわかります．
これらの効果について，さらに細かい設定をしてみましょう．

## 1.5. 散布図に回帰直線を引く

`lower = list(continuous = "smooth")` を指定すると，下三角の散布図に回帰直線を引くことができます．

```r
ggpairs(
  selected_iris, 
  mapping = aes(color = Species), 
  lower = list(continuous = "smooth") # このコードを追加
)
```

![1741595148958.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726043/af56acc1-0841-40e7-b549-bb9bc54e398f.png)

このように，**回帰直線を引くことで，変数間の関係がより明確に**なります．

## 1.6. 対角成分の密度分布を見やすくする

対角成分で使うプロットを `wrap("densityDiag", alpha = .5)` とすることで，やや透過した密度分布を描画可能です．

```r
ggpairs(
  selected_iris, 
  mapping = aes(color = Species), 
  lower = list(continuous = "smooth"),
  # このコードを追加
  diag = list(
    continuous = wrap("densityDiag", alpha = 0.5))
  # ここまで
)
```

![1741595162345.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726043/f157693d-4b4b-4172-862f-458e2eb66167.png)

## 1.7. 列名を編集する

プロットに表示される列名を日本語などに変更したい場合は，`columnLabels` を利用します．

```r
ggpairs(
  selected_iris, 
  mapping = aes(color = Species), 
  lower = list(continuous = "smooth"),
  diag = list(
    continuous = wrap("densityDiag", alpha = 0.5)),
  # このコードを追加
  columnLabels = c(
    Sepal.Length = "がくの長さ", 
    Sepal.Width  = "がくの幅", 
    Species      = "種"
  )
  # ここまで
)
```

![1741595173642.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726043/fef8233e-2f50-496f-87d7-73f4b94b14f7.png)

## 1.8. さらに散布図を見やすくする

回帰直線に標準誤差の帯 (`se`) をつけず，点を小さくしたい場合は，次のように `wrap("smooth", se = FALSE, size = .3)` を使います．

```r
ggpairs(
  selected_iris, 
  mapping = aes(color = Species), 
  # lower を以下のコードに変更
  lower = list(
    continuous = wrap("smooth", se = FALSE, size = 0.3)
  ),
  # ここまで
  diag = list(continuous = wrap("densityDiag", alpha = 0.5)),
  columnLabels = c(
    Sepal.Length = "がくの長さ", 
    Sepal.Width  = "がくの幅", 
    Species      = "種"
  )
)
```

![1741595181769.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726043/52bbf295-3361-47a7-8365-b8e166fd82fb.png)

## 1.9. 図の読み取りポイント

- **下三角**: 各変数ペアの散布図・回帰直線から，ペアごとのおおまかな相関や分布状況を把握  
- **対角成分**: それぞれの変数の分布の特徴を確認  
- **上三角**: ここに相関係数 (`"cor"`) などを表示することも可能  

`ggpairs` はこのように多くの情報を一度に可視化できるため，**変数間の関係を大まかに掴む** 場面で力を発揮します．

## 1.10. 保存する場合

完成した図を保存する場合は，`ggsave()` 関数が便利です．

```r
# 例: ファイル名を指定して保存
p <- ggpairs(
  selected_iris, 
  mapping = aes(color = Species), 
  lower = list(continuous = wrap("smooth", se = FALSE, size = 0.3)),
  diag = list(continuous = wrap("densityDiag", alpha = 0.5))
)
ggsave(
  filename = paste0(FIG_DIR, "iris_pairplot.png"),
  plot = p,
  width = 6, height = 6, dpi = 300
)
```

こうすることで，作成した図を`/figs` フォルダに保存することができます．

# 2. もうひとつの例: `tips` データセット

条件間の交互作用を可視化したい場合の参考例として，`tips` データを使ってみます．

## 2.1. データの概要

```r
glimpse(tips)
```

すると，こんな感じの出力がされると思います．

![1741595254139.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726043/5ddc033a-af9a-445d-99e6-11e8a4c78970.png)

- `total_bill`, `tip` などの数値変数と，`sex`, `smoker` などのカテゴリ変数が含まれています．

## 2.2. 必要な項目に絞り込む

このままだと図が冗長になってしまうので，必要な変数だけを選択して可視化します．

```r
selected_tips <- 
  tips |> 
  dplyr::select(
    total_bill,
    tip,
    sex,
    smoker
  )
```

## 2.3. 基本の可視化

```r
ggpairs(selected_tips)
```

![1741595516109.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726043/c49450d4-f3bd-420a-be74-edef8db28f2f.png)

まずは素の状態でペアプロットを確認．これだけでも各変数間のざっくりした傾向がわかります．

## 2.4. 交互作用を反映させ，factor で色分けする

条件同士の交互作用を検討します．「交互作用が見たい！」という状況とは，例えば以下のような状況です．

- 性別（`sex`）によって，`total_bill` と `tip` の関係が異なるかも
- さらにその性別（`sex`）の効果は，喫煙有無（`smoker`）によって変わるかも（i.e., 交互作用）
- このような交互作用を可視化するために，`interaction()` 関数を使って可視化する！

```r
color_factor <- interaction(tips$sex, tips$smoker)
ggpairs(
  selected_tips,
  mapping = aes(color = color_factor)
)
```
結果はこんな感じ

![1741595544146.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726043/1e76a3ce-a4b0-4f41-8512-f3885ab15faf.png)


`interaction()`を用いることで，交互作用の効果が視覚的に把握しやすくなります．

# 3. さらに細かい設定をしたいとき

## 3.1. `ggpairs()` の主な引数

- **data**: プロット対象データ（例: `iris`）  
- **columns**: プロット対象変数（例: `columns = c("Sepal.Length", "Sepal.Width")`）  
- **columnLabels**: 変数名のラベル変更（例: `columnLabels = c(Sepal.Length="がく長")`）  
- **mapping**: `aes()` による色分けなどの指定（例: `aes(color=Species)`)  
- **upper** / **diag** / **lower**: 上三角・対角成分・下三角部分それぞれへの描画指定  
- **wrap()**: さらに詳細な引数（`se=FALSE` など）を指定するときに使う

## 3.2. 描画オプションの例

- `lower = list(continuous = "smooth")` で散布図に回帰直線を表示  
- `diag = list(continuous = wrap("densityDiag", alpha = .5))` で対角成分の密度プロットを半透明に  
- `upper = list(continuous = "cor")` で上三角に相関係数を表示

## 3.3. 見やすくする設定する例

点の形状や透明度などを変えたい場合も `aes()` で指定できます．

```r
ggpairs(
  selected_iris,
  mapping = aes(
    color = Species,
    shape = Species,
    alpha = 0.7
  ),
  lower = list(continuous = wrap("smooth", se = FALSE))
)
```

こうすることで，さらに識別しやすい可視化が可能です．


# 4. まとめ: 完成形のコード例

最後に，これまで紹介してきたオプションを一度に使うサンプルコードを示します． `iris` データの一部を取り出し，対角に密度分布，下三角に回帰直線，上三角には相関係数を表示し，色分けを行います．

```r
final_plot <- ggpairs(
  data = iris,
  columns = c("Sepal.Length", "Sepal.Width", "Species"),
  mapping = aes(
    color = Species,
    shape = Species
  ),
  lower = list(
    continuous = wrap("smooth", se = FALSE, size = 0.3)
  ),
  diag = list(
    continuous = wrap("densityDiag", alpha = 0.5)
  ),
  upper = list(
    continuous = "cor"
  ),
  columnLabels = c(
    Sepal.Length = "がくの長さ", 
    Sepal.Width  = "がくの幅", 
    Species      = "種"
  )
)

# 確認
final_plot

# 保存する場合
ggsave(
  filename = paste0(FIG_DIR, "iris_pairplot_final.png"),
  plot = final_plot,
  width = 6, height = 6, dpi = 300
)
```

このプロットの結果はこんな感じになります．

![iris_pairplot_final.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726043/041dccf4-a59a-4ace-844a-3b878c8e449d.png)



このように，**`lower` / `diag` / `upper`** の三領域をうまく使い分けることで，**一つの散布図行列に多くの情報を詰め込む** ことができます．



## おわりに

`ggpairs()` は，**探索的データ解析 (EDA) の初期段階で特に強力なツール** です．  
- 複数の変数関係をまとめて視覚化できる  
- 図をカスタマイズして情報量を増やせる  
- 上下三角，対角など領域ごとに異なるプロットを配置できる  
 

今回は，`ggpairs()` を使った基本的な可視化と，その応用の一例をご紹介しました．少しでも皆さんのデータ分析に役立てば幸いです．

# 5. 細かな設定（補足）

ここからは，`ggpairs()`を詳しく理解した人が，細かな設定をいじって理想のプロットを得るための細かな設定について紹介しています．

## 5.1. `upper`，`diag`，`lower` で指定できる引数

### 変数の型別にプロットを指定する

- **continuous**: 連続値のプロットを指定する場所
  - 例: upper = list(continuous="cor")
- **discrete**: 離散値のプロットを指定する場所
  - 例: upper = list(discrete="bar")
- **combo**: 連続値と離散値のプロットを指定する場所
  - 例: upper = list(combo="box")

## 5.2. `continuous`，`discrete`，`combo` で指定できる引数

### 5.2.1. `continuous`

- **points**: 散布図をプロットする
  - 例: upper = list(continuous="points")
- **smooth**: 回帰直線をプロットする
  - 例: upper = list(continuous="smooth")
- **smooth_loess**: ローレス回帰直線をプロットする
  - 例: upper = list(continuous="smooth_loess")
- **density**: 等高線をプロットする
  - 例: upper = list(continuous="density")
- **cor**: 相関係数をプロットする
  - 例: upper = list(continuous="cor")
- **blank**: 何もプロットしない
  - 例: upper = list(continuous="blank")
  
### 5.2.2. `discrete`

- **facetbar**: 棒グラフをプロットする
  - 例: upper = list(discrete="facetbar")
- **ratio**: 比率をプロットする
  - 例: upper = list(discrete="ratio")
- **blank**: 何もプロットしない
  - 例: upper = list(discrete="blank")

### 5.2.3. `combo`

- **box**: 箱ひげ図をプロットする
  - 例: upper = list(combo="box")
- **box_no_facet**: 箱ひげ図をプロットする（facetなし）
  - 例: upper = list(combo="box_no_facet")
- **dot**: jitter した散布図をプロットする
  - 例: upper = list(combo="dot")
- **dot_no_facet**: jitter した散布図をプロットする（facetなし）
  - 例: upper = list(combo="dot_no_facet")
- **facthist**: ヒストグラムをプロットする
  - 例: upper = list(combo="facthist")
- **denstrip**: 等高線をプロットする
  - 例: upper = list(combo="denstrip")
- **blank**: 何もプロットしない
  - 例: upper = list(combo="blank")