129  練習問題 解答

まず、データを取り込みましょう

library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.2     ✔ readr     2.1.4
✔ forcats   1.0.0     ✔ stringr   1.5.0
✔ ggplot2   3.4.2     ✔ tibble    3.2.1
✔ lubridate 1.9.2     ✔ tidyr     1.3.0
✔ purrr     1.0.1     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(readxl)
dat <- read_excel("data/ifelse_casewhen.xlsx")

dat
# A tibble: 19 × 5
   height weight             bp     alcohol smoking
    <dbl> <chr>              <chr>    <dbl>   <dbl>
 1   176. 73.099999999999994 125/78       0       0
 2   180  78                 107/54       0       0
 3   150. S6.7               126/81       0       1
 4   156. 66.2               110/68       0       1
 5   180. 61.6               110/66       0       0
 6   174. 64.7               107/83       1       0
 7   169. 64.400000000000006 113/71       1       0
 8   172. 86.l               126/72       0       0
 9   165. 69.7               137/85       1       0
10   168. 66.900000000000006 110/71       1       0
11   180. 88.8               109/65       0       0
12   184  61.6               153/87       1       1
13   179. 51.4               112/77       0       1
14   173. 69.2               119/81       0       0
15   154. 64.5               113/82       1       1
16   165. 87.1               125/76       1       1
17   159. 86.3               131/80       1       0
18   169. 61.1               121/75       0       0
19   164. 63.O               132/81       0       0

課題1:

身長と体重を利用して、BMIを計算してその結果をbmiという名前の列に保存してください。

まず、datのweight列が文字列型になっているので数字に変換してみましょう。

dat_weight <- dat %>% 
  select(weight) %>% 
  mutate(num_weight = as.numeric(weight))
Warning: There was 1 warning in `mutate()`.
ℹ In argument: `num_weight = as.numeric(weight)`.
Caused by warning:
! NAs introduced by coercion
View(dat_weight)

ところどころ、欠損しています。

とりあえず、欠損している列のみを抜き出して眺めてみましょう。

欠損の有無は、is.na()関数というものを用います。

dat_weight %>% 
  filter(is.na(num_weight))
# A tibble: 3 × 2
  weight num_weight
  <chr>       <dbl>
1 S6.7           NA
2 86.l           NA
3 63.O           NA

はい。ここは、意図的に入力ミスをいれています。

5でなくてS 1でなくてl 0でなくてOが使われています。

他のデータはすべて数字にNAになることなく変換できているので、とりあえず、上の3つを置き換えてから数値に変換しましょう。

dat1 <- dat %>% 
  mutate(
    replace_weight = str_replace_all(weight, 
                                 c("S"="5",
                                   "l"="1",
                                   "O"="0"))
  ) %>% 
  mutate(num_weight = as.numeric(replace_weight))

View(dat1)

これで、無事、weight列が数字に変換されました。BMIを計算しましょう。

dat1 <- dat1 %>% 
  mutate(bmi = num_weight/(height/100)^2)

dat1$bmi
 [1] 23.49201 24.07407 25.09950 27.06353 18.97017 21.29656 22.46839 29.06976
 [9] 25.57047 23.59076 27.28600 18.19471 15.98831 23.09468 27.09118 32.07035
[17] 34.09341 21.36751 23.39502
summary(dat1$bmi)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  15.99   21.92   23.59   24.38   27.08   34.09 

課題2:

bmi列の値を利用して、if_else関数を使って、BMIが25以上であれば1、25未満であれば0となる列、obese、を作成してください。

ここでif_else関数の動作を確認しておきます。

dat2 <- dat1 %>% 
  mutate(obese = if_else(bmi >= 25, 1, 0))

ここで作成した変数の分布を確認するためにはsummaryでもよいのですが、

dat2$obese %>% summary()
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.0000  0.0000  0.0000  0.4211  1.0000  1.0000 

数値型の場合は平均値等がでてきてしまい、個数がわかりません。個数を確認したい場合は、dplyr::count関数という便利な関数があります。

dat2 %>% count(obese)
# A tibble: 2 × 2
  obese     n
  <dbl> <int>
1     0    11
2     1     8

あるいは、base::tableも使いやすいです。

dat2$obese %>% table()
.
 0  1 
11  8 

countは、パイプの中で変数名だけ書くような使い方ができますが、tableはベクトルを与えないといけないので注意が必要です

課題3:

血圧の表示が”120/80”のように入力されています。これを、sbpとdbpの二つの変数に分割してください。

dat2$bp[1:10]
 [1] "125/78" "107/54" "126/81" "110/68" "110/66" "107/83" "113/71" "126/72"
 [9] "137/85" "110/71"

このデータを分割するために、正規表現を利用してsbpとdbpを抜き出してみましょう。

dat3 <- dat2 %>% 
  mutate(
    sbp = str_extract(bp,"^\\d+(?=/)"),
    dbp = str_extract(bp,"(?<=/)\\d+$")
  )

dat3 %>% select(ends_with("bp"))
# A tibble: 19 × 3
   bp     sbp   dbp  
   <chr>  <chr> <chr>
 1 125/78 125   78   
 2 107/54 107   54   
 3 126/81 126   81   
 4 110/68 110   68   
 5 110/66 110   66   
 6 107/83 107   83   
 7 113/71 113   71   
 8 126/72 126   72   
 9 137/85 137   85   
10 110/71 110   71   
11 109/65 109   65   
12 153/87 153   87   
13 112/77 112   77   
14 119/81 119   81   
15 113/82 113   82   
16 125/76 125   76   
17 131/80 131   80   
18 121/75 121   75   
19 132/81 132   81   

抜き出せましたね。文字列型になっているので数値型に戻しておきましょう

dat3 <- dat3 %>% 
  mutate(
    sbp = as.numeric(sbp),
    dbp = as.numeric(dbp)
  )

課題4:

課題3で作成した変数、sbp、dbpを利用して、

sbpが120未満かつdbpが80未満であれば「至適血圧」 sbpが130未満かつ/あるいはdbp85未満であれば「正常血圧」 sbpが140未満かつ/あるいはdbp90未満であれば「正常高値血圧」 sbpが160未満かつ/あるいはdbp100未満であれば「1度高血圧」 sbpが180未満かつ/あるいはdbp110未満であれば「2度高血圧」 sbpが180以上かつ/あるいはdbp110以上であれば「3度高血圧」

とした因子型の列を作成してください。

ここではcase_whenを利用しましょう。だいぶ複雑です。かつ/あるいはを条件設定するのがややこしいので、収縮期、拡張期それぞれで判定してより重い方を採用する形にしました。

dat4 <- dat3 %>% 
  mutate(sbp_kubun = case_when(
    sbp <  120  ~ 1,
    sbp <  130  ~ 2,
    sbp <  140  ~ 3,
    sbp <  160  ~ 4,
    sbp <  180  ~ 5,
    sbp >= 180  ~ 6
  )) %>% 
  mutate(dbp_kubun = case_when(
    dbp <  80 ~ 1,
    dbp <  85 ~ 2,
    dbp <  90 ~ 3,
    dbp < 100 ~ 4,
    dbp < 110 ~ 5,
    dbp >=110 ~ 6
  ))

count関数は2変数でも使えます。

dat4 %>% 
  count(sbp_kubun, dbp_kubun)
# A tibble: 7 × 3
  sbp_kubun dbp_kubun     n
      <dbl>     <dbl> <int>
1         1         1     7
2         1         2     3
3         2         1     4
4         2         2     1
5         3         2     2
6         3         3     1
7         4         3     1

このsbp_kubunとdbp_kubunの値で大きいほうを採用すれば良いので、if_elseを利用しましょう。

dat4 <- dat4 %>% 
  mutate(bp_kubun = if_else(
    sbp_kubun >= dbp_kubun, sbp_kubun, dbp_kubun
  ))

dat4 %>% 
  count(bp_kubun, sbp_kubun, dbp_kubun)
# A tibble: 7 × 4
  bp_kubun sbp_kubun dbp_kubun     n
     <dbl>     <dbl>     <dbl> <int>
1        1         1         1     7
2        2         1         2     3
3        2         2         1     4
4        2         2         2     1
5        3         3         2     2
6        3         3         3     1
7        4         4         3     1

きちんと狙った通りに分けられていますね?あとは、数値を因子型のラベルをあててあげれば完成です。

bplabel <- c("至適","正常","正常高値","1度","2度","3度")

dat4 <- dat4 %>% 
 mutate(bp_kubun = factor(bp_kubun, levels = 1:6, labels=bplabel))

dat4 %>% count(bp_kubun)
# A tibble: 4 × 2
  bp_kubun     n
  <fct>    <int>
1 至適         7
2 正常         8
3 正常高値     3
4 1度          1
View(dat4)

できあがりです。

課題5:

課題1から課題4までで作成した変数を利用して、高血圧、肥満、飲酒、喫煙のリスクの個数を計算したriskという名前の列を作成してください。

(尚、ここでは、高血圧を1度から3度高血圧、肥満はBMI25以上とします)

血圧のカテゴリーが、0か1になっていないためこれを変換しておきましょう。

dat4 %>% 
  count(bp_kubun)
# A tibble: 4 × 2
  bp_kubun     n
  <fct>    <int>
1 至適         7
2 正常         8
3 正常高値     3
4 1度          1
dat5 <- dat4 %>% 
  mutate(risk_bp = if_else(as.numeric(bp_kubun) >= 4, 1, 0))

dat5 %>% count(risk_bp, bp_kubun)
# A tibble: 4 × 3
  risk_bp bp_kubun     n
    <dbl> <fct>    <int>
1       0 至適         7
2       0 正常         8
3       0 正常高値     3
4       1 1度          1

ちゃんと0と1で分けられていますね?後は、リスクの個数を足し合わせるだけです。一応、データを確認しておくと、

dat5 %>% 
  count(obese, risk_bp, alcohol, smoking)
# A tibble: 8 × 5
  obese risk_bp alcohol smoking     n
  <dbl>   <dbl>   <dbl>   <dbl> <int>
1     0       0       0       0     6
2     0       0       0       1     1
3     0       0       1       0     3
4     0       1       1       1     1
5     1       0       0       0     2
6     1       0       0       1     2
7     1       0       1       0     2
8     1       0       1       1     2

です。

risk列の作成は簡単ですね?

dat5 <- dat5 %>% 
  mutate(risk = obese + risk_bp + alcohol + smoking)

dat5 %>% count(risk)
# A tibble: 4 × 2
   risk     n
  <dbl> <int>
1     0     6
2     1     6
3     2     4
4     3     3

以上です!

どうでしょうか? 相当ややこしく感じられたかもしれませんが、ここまで学んでいただいた関数のみで、ここまでデータの加工ができます。

次以降はこれらのデータの加工をより便利にしてくれる関数群を解説していきます。