【ggplot2】limitsで指定した描画範囲外データの扱い(散布図と棒グラフ)

# 問題
- ggplotでは，scale_*_*などでlimitsを指定すると描画範囲の上限と下限を指定できます。ただ，デフォルトではこの上限や下限を超えたデータポイントは除外，無視して描画されます。

例えば，以下のような外れ値を含むデータがあります。

```r
library(tidyverse)
x <- rnorm(n=10, mean=4,sd=1)
e <- rnorm(n=10, mean=0,sd=3)

y <- 50 + 10*x + e 

df <- tibble(X=c(x,20),
             Y=c(y,0))
df
# A tibble: 11 × 2
       X     Y
   <dbl> <dbl>
 1  4.30  89.8
 2  5.06  96.0
 3  3.66  86.1
 4  4.63  93.9
 5  2.85  79.4
 6  3.56  87.8
 7  4.43  92.3
 8  3.56  90.1
 9  3.16  87.3
10  5.88 106. 
11 20      0
```

- これを描画するとこんな感じ。
```r
ggplot(data = df,
       mapping = aes(x=X,
                     y=Y))+
  geom_point()+
  geom_smooth(stat = "smooth",
              method = "lm")            
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2251263/afc744c1-04d7-a408-a5e0-1b83d6cc03f9.png)

---

- こんなことをする人がいるのかはわからないですが，`scale_y_continuous`と`scale_x_continuous`を用いて，上限と下限を設定してみます。

```r
ggplot(data = df,
       aes(x = X,
           y = Y))+
  geom_point()+
  geom_smooth(stat = "smooth",
              method = "lm")+
  scale_x_continuous(limits = c(1,8))+
  scale_y_continuous(limits = c(60,120))
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2251263/5aa6d0ec-ea98-5c2d-e59d-86168fea9bd1.png)

- 描画範囲に入らないデータポイントは除外されてしまいました。そのため，回帰直線の傾きが大きく異なるのがわかります。
- もちろん「除外されてるぞ」と出力が返ってきます。

```r
Warning messages:
1: Removed 1 rows containing non-finite values (`stat_smooth()`). 
2: Removed 1 rows containing missing values (`geom_point()`). 
```
---

# 解決
- 考えてみたら当たり前ですが，scale_*_*では描画範囲外のデータポイントをどのように扱うかの引数(`oob`)があります。
- `scales package`で提供される関数を指定すれば **「描画はしないけど，回帰直線などには反映する」** といったことが可能になります。
- 例えば以下のように書きます。

```r
install.packages(scales)
library(scales)

ggplot(data=df,
       aes(x=X,
           y=Y))+
  geom_point()+
  geom_smooth(stat = "smooth",
              method = "lm")+
  scale_x_continuous(limits = c(1,8), oob = oob_keep)+
  scale_y_continuous(limits = c(60,120), oob = oob_keep)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2251263/bb7f6377-e87a-83e1-d5cb-e1dbc164028c.png)

- `oob = oob_keep` と指定するとデータは除外されず， 図を拡大したような描画になります。
- その他にも
    - oob_censor
        - デフォルト
        - 範囲外の値はNAに変換
    - oob_squish
        - 範囲外の値を上限や下限の制限値に置き換える
        - 値が20で `limits=c(0,10)` などとした場合は 値が10 に変更されて描画される

- いくつかあるので以下を参照してください

https://scales.r-lib.org/reference/oob.html

---

- ggplot2の公式documentにもscalesパッケージが紹介されていました。
- 多様するパッケージのdocumentぐらいちゃんと読むべきですね。。。

---

# 棒グラフの場合

- 次は棒グラフです。
- こんな平均値とSEのデータがあります。

```r
df <- tibble(cnd = c("A", "B"),
             M = c(350, 400),
             SE = c(60, 40))
df
# A tibble: 2 × 3
  cnd       M    SE
  <chr> <dbl> <dbl>
1 A       350    60
2 B       400    40           
```

- 平均値を棒グラフで表現し，SEをエラーバーで表現します。

```r
ggplot(data = df,
       aes(x = cnd,
           y = M,
           fill = cnd))+
  geom_bar(stat = "identity")+
  geom_errorbar(aes(ymin = M - SE,
                    ymax = M + SE))
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2251263/356e58e6-6fb3-3793-b226-321c4a5301c0.png)

- こんな感じで出力されます。
- さて，この棒グラフのy軸を操作してみます。

```r
ggplot(data = df,
       aes(x = cnd,
           y = M,
           fill = cnd))+
  geom_bar(stat = "identity")+
  geom_errorbar(aes(ymin = M - SE,
                    ymax = M + SE))+
  scale_y_continuous(limits=c(300,500)) # ここだけ追加
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2251263/0f1c1541-883b-2e93-f4a9-cc8db36aba66.png)

- 棒とエラーバーの一部が消えてしましました。

- これも散布図の時と同じように描画範囲外のものは除外されるためです。
- 対応は散布図の時と同様で，`oob`を指定してやれば良いです。
```r
ggplot(data = df,
       aes(x = cnd,
           y = M,
           fill = cnd))+
  geom_bar(stat = "identity")+
  geom_errorbar(aes(ymin = M - SE,
                    ymax = M + SE))+
  scale_y_continuous(limits=c(300,500), oob = oob_keep)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2251263/35dc2c94-e67f-ca5c-a553-c5e163d5bbec.png)

- `oob_keep`ではなく，`oob_squish`と指定すると

```r
ggplot(data = df,
       aes(x = cnd,
           y = M,
           fill = cnd))+
  geom_bar(stat = "identity")+
  geom_errorbar(aes(ymin = M - SE,
                    ymax = M + SE))+
  scale_y_continuous(limits=c(300,500), oob = oob_squish) 
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2251263/67d02349-ae13-57bd-0bf2-0550cffe1312.png)

- ちょっとだけ違いますね。
    - `oob_squish`の方では，300未満の部分が消えています。
    - 制限値に変換しているためですね。

# 終わりに
- 昔の私が困っていたものについての記事でした。