[1] 82 45 69 94 88 73 NA 51 90 63
第10回講義資料
データハンドリング (2)
スライド
記述統計量
記述統計量(descriptive statistics)とはある変数が持つ情報を要約した数値である。例えば、10人の学生の数学成績が格納されている以下のようなnumeric型ベクトルがあるとしよう。7番目の学生は試験を受けたものの、ポンコツな教師が解答用紙を紛失して欠損値となっている。
このクラスの数学成績について語る時、「うちのクラスの数学成績は82、45、69、94、…、63点ですよ」という人はいないだろう。数人のクラスならまだしも、数十人のクラスならあり得ない。我々は普段、「うちのクラスの数学成績はだいたいYY点ですよ」とか、「真ん中の成績はXX点ですよ」と言うだろう。また、クラス内の成績の格差/ばらつきを語るときも標準偏差や範囲(「できる子とできない子の点差は49点ですよ」)を言うのが普通である。このように元の長い情報を一つの数値として要約したものが記述統計量である。
たとえば、MathScore
を代表する値(=代表値)としては平均値(mean)、中央値(median)、最頻値(mode)などがある。
また、MathScore
の格差、ばらつきの具合としては分散(variance)、標準偏差(standard deviation)、四分位範囲(interquartile range)、範囲(range)がある。
今回は{dplyr}パッケージを用いて変数の記述統計量を計算する方法を紹介する。まず、今回の実習用データを読み込む。第9回に使用したデータと同じデータを使う。
# A tibble: 186 × 18
Country Population Area GDP PPP GDP_per_capita PPP_per_capita G7
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Afghani… 38928346 6.53e5 1.91e4 8.27e4 491. 2125. 0
2 Albania 2877797 2.74e4 1.53e4 3.97e4 5309. 13781. 0
3 Algeria 43851044 2.38e6 1.70e5 4.97e5 3876. 11324. 0
4 Andorra 77265 4.7 e2 3.15e3 NA 40821. NA 0
5 Angola 32866272 1.25e6 9.46e4 2.19e5 2879. 6649. 0
6 Antigua… 97929 4.4 e2 1.73e3 2.08e3 17643. 21267. 0
7 Argenti… 45195774 2.74e6 4.50e5 1.04e6 9949. 22938. 0
8 Armenia 2963243 2.85e4 1.37e4 3.84e4 4614. 12974. 0
9 Austral… 25499884 7.68e6 1.39e6 1.28e6 54615. 50001. 0
10 Austria 9006398 8.24e4 4.46e5 5.03e5 49555. 55824. 0
# ℹ 176 more rows
# ℹ 10 more variables: G20 <dbl>, OECD <dbl>, HDI_2018 <dbl>,
# Polity_Score <dbl>, Polity_Type <chr>, FH_PR <dbl>, FH_CL <dbl>,
# FH_Total <dbl>, FH_Status <chr>, Continent <chr>
記述統計を計算する関数はsummarise()
である。使い方は以下の通りである。
たとえば、Population
列の平均値を計算する場合は、
と入力する。HDI_2018
のように欠損値が含まれている変数ならna.rm = TRUE
を忘れないようにしよう。
# A tibble: 1 × 1
`mean(HDI_2018, na.rm = TRUE)`
<dbl>
1 0.713
平均値を計算するmean()
以外にも様々な変数が使える。ここでは代表的な記述統計量の関数を紹介する。他にも様々な記述統計量があるが、詳細は教科書第13.1.2章を参照されたい。
mean()
: 平均値median()
: 中央値sd()
: 不偏標準偏差var()
: 不偏分散IQR()
: 四分位範囲min()
、max()
: 最小値と最大値
summarise()
内には一つの変数、あるいは一つの記述統計量のみ書く必要はない。()
内にいくらでも書ける。たとえば、df
のPopulation
とArea
の平均値(mean()
)と標準偏差(sd()
)を計算する場合、計4つの記述統計量を計算することとなる。
Code 12
# A tibble: 1 × 4
`mean(Population)` `sd(Population)` `mean(Area)` `sd(Area)`
<dbl> <dbl> <dbl> <dbl>
1 41737773. 151270298. 696069. 1872412.
出力結果はデータフレーム(tibble)形式で表示されるが、列名が長く、非常に読みにくい。この列名を修正するためにはsummarise()
の後にrename()
を使うことで修正することもできるが、summarise()
内で指定することもできる。たとえば、df
のPopulation
とArea
の平均値(mean()
)と標準偏差(sd()
)を計算し、結果の列名をMean_Pop
、SD_Pop
などとする。
Code 13
# A tibble: 1 × 4
Mean_Pop SD_Pop Mean_Area SD_Area
<dbl> <dbl> <dbl> <dbl>
1 41737773. 151270298. 696069. 1872412.
出力される列名 = 記述統計の関数()
を指定することで、このように出力結果が読みやすくなる。
グルーピング
特定の変数の記述統計量を計算する場合はmean()
やsd()
などの関数のみを使った方が効率的なケースが多い。しかし、グループごとに記述統計量を計算する場合は、{dplyr}パッケージが大変便利である。{dplyr}を使わずに大陸ごとのPPP_per_capita
の平均値を計算するとしよう。たとえば、Continent
の値が"Africa"
である国のPPP_per_capita
の平均値を計算する場合は以下のように書く。
Code 14
[1] 5667.087
これを全ての大陸に対して同じ計算を行う場合、以下のようなコードになる。
たった5行のコードであるが、一行一行がかなり長く、可読性も優れているとは言えない。ここで{dplyr}のgroup_by()
関数が力を発揮する。
たとえば、df
のContinent
でデータをグループ化し、PPP_per_capita
の平均値を計算する場合、summarise()
の前にgroup_by(Continent)
を使いしてパイプをつなぐことでContinent
の値ごとにPPP_per_capita
の平均値が計算される。
Code 21
# A tibble: 5 × 2
Continent Mean_PPP
<chr> <dbl>
1 Africa 5667.
2 America 18100.
3 Asia 22728.
4 Europe 37783.
5 Oceania 27573.
コードが3行になっただけではなく、可読性も大きく改善されていることが分かる。group_by()
関数は二つ以上の変数でグルーピングすることもできる。たとえば、df
のContinent
とG20
でデータをグループ化し、HDI_2018
の平均値を計算してみよう。
`summarise()` has grouped output by 'Continent'. You can override using the
`.groups` argument.
# A tibble: 10 × 3
# Groups: Continent [5]
Continent G20 Mean_HDI
<chr> <dbl> <dbl>
1 Africa 0 0.550
2 Africa 1 0.705
3 America 0 0.727
4 America 1 0.84
5 Asia 0 0.710
6 Asia 1 0.798
7 Europe 0 0.859
8 Europe 1 0.877
9 Oceania 0 0.729
10 Oceania 1 0.938
Continent
の値は5種類、G20
の値は2種類であるため、計10個のグループができる。ちなみにこれを{dplyr}を使わず行うなら以下のようなコードになる。
Code 23
mean(df$HDI_2018[df$Continent == "Africa" & df$G20 == 0], na.rm = TRUE)
mean(df$HDI_2018[df$Continent == "Africa" & df$G20 == 1], na.rm = TRUE)
mean(df$HDI_2018[df$Continent == "America" & df$G20 == 0], na.rm = TRUE)
mean(df$HDI_2018[df$Continent == "America" & df$G20 == 1], na.rm = TRUE)
mean(df$HDI_2018[df$Continent == "Asia" & df$G20 == 0], na.rm = TRUE)
mean(df$HDI_2018[df$Continent == "Asia" & df$G20 == 1], na.rm = TRUE)
mean(df$HDI_2018[df$Continent == "Europe" & df$G20 == 0], na.rm = TRUE)
mean(df$HDI_2018[df$Continent == "Europe" & df$G20 == 1], na.rm = TRUE)
mean(df$HDI_2018[df$Continent == "Oceania" & df$G20 == 0], na.rm = TRUE)
mean(df$HDI_2018[df$Continent == "Oceania" & df$G20 == 1], na.rm = TRUE)
{dplyr}の素晴らしさが分かるだろう。次の内容に進む前に、以下のような謎のメッセージが出力されたのではないだろうか。
## `summarise()` has grouped output by 'Continent'. You can override using
the `.groups` argument.
このメッセージの理由は教科書第13.2章に譲るが、この気持ち悪いメッセージをなくすためには、とりあえず、group_by()
の後にsummarise()
を使う場合、summarise()
の最後に.groups = "drop"
を追加する。
Code 24
# A tibble: 10 × 3
Continent G20 Mean_HDI
<chr> <dbl> <dbl>
1 Africa 0 0.550
2 Africa 1 0.705
3 America 0 0.727
4 America 1 0.84
5 Asia 0 0.710
6 Asia 1 0.798
7 Europe 0 0.859
8 Europe 1 0.877
9 Oceania 0 0.729
10 Oceania 1 0.938
これで謎のメッセージが出力されなくなっただろう。
グルーピングを行う場合、各グループに属するケース数を調べたい場合もあろう。たとえば、大陸ごとにPPP_per_capita
の平均値と標準偏差を出すだけでなく、各大陸に属する国数も表示したいとする。この時に使う関数がn()
である。()
内に別途の引数は不要である。
Code 25
# A tibble: 5 × 4
Continent Mean_PPP SD_PPP Cases
<chr> <dbl> <dbl> <int>
1 Africa 5667. 6015. 54
2 America 18100. 12601. 36
3 Asia 22728. 24067. 42
4 Europe 37783. 21276. 50
5 Oceania 27573. 21984. 4
最後に、詳細は解説しないが、across()
関数を利用することで、複数の変数に対して複数の記述統計量をより短いコードで計算することができる。たとえば、df
のPopulation
からPPP
列まで平均値と標準偏差を計算し、結果の変数名は元の変数名_Mean
、元の変数名_SD
とするコードを書いてみよう。
Code 26
df |>
summarise(Population_Mean = mean(Population, na.rm = TRUE),
Population_SD = sd(Population, na.rm = TRUE),
Area_Mean = mean(Area, na.rm = TRUE),
Area_SD = sd(Area, na.rm = TRUE),
GDP_Mean = mean(GDP, na.rm = TRUE),
GDP_SD = sd(GDP, na.rm = TRUE),
PPP_Mean = mean(PPP, na.rm = TRUE),
PPP_SD = sd(PPP, na.rm = TRUE),)
# A tibble: 1 × 8
Population_Mean Population_SD Area_Mean Area_SD GDP_Mean GDP_SD PPP_Mean
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 41737773. 151270298. 696069. 1872412. 473031. 1999504. 717953.
# ℹ 1 more variable: PPP_SD <dbl>
以上の作業は、across()
関数を使う場合、以下のようにたった4行でできる。
df |>
#| filename: "Code 27"
summarise(across(Population:PPP,
.fns = list(Mean = ~mean(.x, na.rm = TRUE),
SD = ~mean(.x, na.rm = TRUE))))
# A tibble: 1 × 8
Population_Mean Population_SD Area_Mean Area_SD GDP_Mean GDP_SD PPP_Mean
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 41737773. 41737773. 696069. 696069. 473031. 473031. 717953.
# ℹ 1 more variable: PPP_SD <dbl>
across()
関数の詳細については教科書第13.1章を参照されたい。ただし、無名関数(ラムダ式)の知識が必要である(そこまで難しいものではない)。
変数の計算
以下ではmutate()
関数を利用して、データフレームの変数を用いた計算を行い、新しい列として追加する方法を紹介する。mutate()
関数の使い方は以下の通りである。
ここで、新しい列名
が既に存在するの列名である場合、既存の列が上書きされる。一方、データフレームに存在しない列名の場合、新しい列が最後の列として追加される1。
ここでは、df
のPopulation
をArea
で割り(=人口密度)、Density
という名の列として追加してみよう。{dplyr}を使わずにこの処理を行う場合、以下のようなコードとなる。
一方、{dplyr}のmutate()
関数を使用する場合、以下のようなコードになる。
# A tibble: 186 × 19
Country Population Area GDP PPP GDP_per_capita PPP_per_capita G7
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Afghani… 38928346 6.53e5 1.91e4 8.27e4 491. 2125. 0
2 Albania 2877797 2.74e4 1.53e4 3.97e4 5309. 13781. 0
3 Algeria 43851044 2.38e6 1.70e5 4.97e5 3876. 11324. 0
4 Andorra 77265 4.7 e2 3.15e3 NA 40821. NA 0
5 Angola 32866272 1.25e6 9.46e4 2.19e5 2879. 6649. 0
6 Antigua… 97929 4.4 e2 1.73e3 2.08e3 17643. 21267. 0
7 Argenti… 45195774 2.74e6 4.50e5 1.04e6 9949. 22938. 0
8 Armenia 2963243 2.85e4 1.37e4 3.84e4 4614. 12974. 0
9 Austral… 25499884 7.68e6 1.39e6 1.28e6 54615. 50001. 0
10 Austria 9006398 8.24e4 4.46e5 5.03e5 49555. 55824. 0
# ℹ 176 more rows
# ℹ 11 more variables: G20 <dbl>, OECD <dbl>, HDI_2018 <dbl>,
# Polity_Score <dbl>, Polity_Type <chr>, FH_PR <dbl>, FH_CL <dbl>,
# FH_Total <dbl>, FH_Status <chr>, Continent <chr>, Density <dbl>
出力される画面にDensity
列が見当たらない。これは新しく追加されたDensity
列が最後の列であり、画面に収まらないからである。むろん、Density
列は問題なく追加されており、出力画面の下段に省略されている変数名に含まれていることが分かる。
もし、新しく追加される列の順番を指定したい場合はrelocate()
同様、.after
、または.before
を指定すればよい。以下のコードはDensity
列をArea
列の後に追加するコードである。
# A tibble: 186 × 19
Country Population Area Density GDP PPP GDP_per_capita
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Afghanistan 38928346 652860 59.6 1.91e4 8.27e4 491.
2 Albania 2877797 27400 105. 1.53e4 3.97e4 5309.
3 Algeria 43851044 2381740 18.4 1.70e5 4.97e5 3876.
4 Andorra 77265 470 164. 3.15e3 NA 40821.
5 Angola 32866272 1246700 26.4 9.46e4 2.19e5 2879.
6 Antigua and Barbuda 97929 440 223. 1.73e3 2.08e3 17643.
7 Argentina 45195774 2736690 16.5 4.50e5 1.04e6 9949.
8 Armenia 2963243 28470 104. 1.37e4 3.84e4 4614.
9 Australia 25499884 7682300 3.32 1.39e6 1.28e6 54615.
10 Austria 9006398 82409 109. 4.46e5 5.03e5 49555.
# ℹ 176 more rows
# ℹ 12 more variables: PPP_per_capita <dbl>, G7 <dbl>, G20 <dbl>, OECD <dbl>,
# HDI_2018 <dbl>, Polity_Score <dbl>, Polity_Type <chr>, FH_PR <dbl>,
# FH_CL <dbl>, FH_Total <dbl>, FH_Status <chr>, Continent <chr>
応用
それでは前回と今回の内容を復習してみよう。最初の問題は「各国が世界人口に占める割合を計算し、降順で出力する」ことである。この問題を解くためには以下のような手順でデータをハンドリングする必要がある。
df
のPopulation
の合計をTotal_Pop
という列として追加する。mutate()
使用
Population
をTotal_Pop
で割り、100を掛ける。結果はShare_Pop
という名の列としてPopulation
後に追加する。mutate()
使用
Country
からShare_Pop
までの列のみ残すが、Total_Pop
列は除外する。select()
使用
Share_Pop
が大きい順で行を並び替えるarrange()
使用
以上の計算仮定の内、1と2は一つのmutate()
関数内にまとめることができる。したがって、データ |> mutate() |> select() |> arrange()
の順番でコードを書く必要がある。
Code 32
# A tibble: 186 × 3
Country Population Share_Pop
<chr> <dbl> <dbl>
1 China 1447470092 18.6
2 India 1380004385 17.8
3 United States 334308644 4.31
4 Indonesia 273523615 3.52
5 Pakistan 220892340 2.85
6 Brazil 212559417 2.74
7 Nigeria 206139589 2.66
8 Bangladesh 164689383 2.12
9 Russia 145934462 1.88
10 Mexico 128932753 1.66
# ℹ 176 more rows
次の問題は「G7、G20、OECDのいずれかに加盟している国を"先進国"
、それ以外は"その他"
とし、二つのグループの人口密度、人間開発指数、民主主義度の平均値を出力する」ことである。
df
のDeveloped
という列を追加し、G7
、G20
、OECD
のいずれかに加盟した国なら"先進国"
、それ以外なら"その他"
とする。mutate()
使用
- 人口密度を
Density
という名の列として追加する。mutate()
使用
HDI_2018
とPolity_Score
のいずれかが欠損した行を除外する。filter()
使用
Developed
変数でデータをグルーピングする。group_by()
使用
HDI_2018
、Polity_Score
、Density
の平均値を求める。summarise()
使用
df2
という名前のオブジェクトとして作業環境内に格納する。- 代入演算子(
<-
)使用
- 代入演算子(
df2
を出力する。
今回も手順1と2は同じmuate()
関数を使用するため、新しいオブジェクト名 <- データ |> mutate() |> filter() |> group_by() |> summarise()
の順番でコードを各必要がある。
Code 33
# A tibble: 2 × 4
Developed Density HDI Polity
<chr> <dbl> <dbl> <dbl>
1 その他 197. 0.695 3.92
2 先進国 174. 0.892 7.91
いきなり出てきたif_else()
関数だが、これは何だろう。これから解説する。
名目変数の扱い方
先ほどのdf2
だが、出力順番が"その他"
\(\rightarrow\) "先進国"
になっている。これを"先進国"
\(\rightarrow\) "その他"
の順番にするならどうすれば良いだろうか。summarise()
を行う場合、グルーピング変数のアルファベット順で表示される。ただし、これはアルファベットや数字に限定される話であり、日本語の場合、50音順になるとは限らない。ひらがな、カタカナなら50音順になるが、漢字はそうではない。そもそも読み方が複数あるので、どの基準にすればもよく分からない。しかも、漢字は日本だけが使う文字でもないため、読み方も国によって異なるだろう。この場合、summarise()
を使う前にグルーピング変数をfactor型に変換する必要がある。
ある変数をfactor型に変換する場合はfactor()
関数を使う。第一引数は元となる変数名であり、levels =
に順番を指定する。スペルミスに注意すること。
Code 34
df |>
mutate(Developed = G7 + G20 + OECD,
Developed = if_else(Developed > 1, "先進国", "その他"),
Developed = factor(Developed, levels = c("先進国", "その他")),
Density = Population / Area) |>
filter(!is.na(HDI_2018), !is.na(Polity_Score)) |>
group_by(Developed) |>
summarise(Density = mean(Density),
HDI = mean(HDI_2018),
Polity = mean(Polity_Score))
# A tibble: 2 × 4
Developed Density HDI Polity
<fct> <dbl> <dbl> <dbl>
1 先進国 174. 0.892 7.91
2 その他 197. 0.695 3.92
それではこれまで放置してきたif_else()
について解説しよう。if_else()
は後ほど解説するcase_when()
やrecode()
のように変数のリコーディング(re-coding)に使う関数である。if_else()
関数の使い方は以下の通りである。
たとえば、df
のOECD
が1
なら"OECD加盟国"
、それ以外なら"OECD非加盟国"
に変換し、OECD_J
という列として追加する場合、以下のコードになる。リコーディングの場合、既存の変数を上書きするより、新しい列として追加することを推奨する。
# A tibble: 186 × 19
Country Population Area GDP PPP GDP_per_capita PPP_per_capita G7
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Afghani… 38928346 6.53e5 1.91e4 8.27e4 491. 2125. 0
2 Albania 2877797 2.74e4 1.53e4 3.97e4 5309. 13781. 0
3 Algeria 43851044 2.38e6 1.70e5 4.97e5 3876. 11324. 0
4 Andorra 77265 4.7 e2 3.15e3 NA 40821. NA 0
5 Angola 32866272 1.25e6 9.46e4 2.19e5 2879. 6649. 0
6 Antigua… 97929 4.4 e2 1.73e3 2.08e3 17643. 21267. 0
7 Argenti… 45195774 2.74e6 4.50e5 1.04e6 9949. 22938. 0
8 Armenia 2963243 2.85e4 1.37e4 3.84e4 4614. 12974. 0
9 Austral… 25499884 7.68e6 1.39e6 1.28e6 54615. 50001. 0
10 Austria 9006398 8.24e4 4.46e5 5.03e5 49555. 55824. 0
# ℹ 176 more rows
# ℹ 11 more variables: G20 <dbl>, OECD <dbl>, HDI_2018 <dbl>,
# Polity_Score <dbl>, Polity_Type <chr>, FH_PR <dbl>, FH_CL <dbl>,
# FH_Total <dbl>, FH_Status <chr>, Continent <chr>, OECD_J <chr>
それではまず、OECD
変数でデータをグルーピングし、HDI_2018
とFH_Total
の平均値を計算してみよう。
Code 37
# A tibble: 2 × 3
OECD HDI FH
<dbl> <dbl> <dbl>
1 0 0.667 49.9
2 1 0.894 89.1
続いて、OECD
でなく、先ほど作成したOECD_J
列でグルーピングしてみよう。
Code 38
# A tibble: 2 × 4
OECD_J PPP HDI FH
<chr> <dbl> <dbl> <dbl>
1 OECD加盟国 46000. 0.894 89.1
2 OECD非加盟国 14229. 0.667 49.9
出力される結果の第1列がOECD_J
になっているが、これをOECD
に変更したい場合はgroup_by(OECD_J)
をgroup_by(OECD = OECD_J)
に変更すれば良い。
if_else()
は条件が一つのみの場合に使用する関数である。もし、条件式を複数使いたい場合はどうすれば良いだろうか。ここではまず、mutate()
内にcase_when()
を使用する方法から紹介する。
.default = 新しい値
は「上記の条件全てが満たされない場合の値」を意味する(.default ~ 新しい値
でなく、.default = 新しい値
であることに注意)。ここではdf
のContinent
列を日本語にし、Continent_J
として追加してみよう。
Code 40
# A tibble: 5 × 3
大陸 OECD加盟国比率 国家数
<chr> <dbl> <int>
1 アジア 0.0714 42
2 アフリカ 0 54
3 アメリカ 0.139 36
4 オセアニア 0.5 4
5 ヨーロッパ 0.54 50
この「新しい値」は条件の数だけ存在する必要はない。たとえば、Continent
列の値が"Asia"
か"Oceania"
、"America"
なら"Asia-Pafific"
に、それ以外は"Others"
に変更し、その結果をAP
という名の新しい列として追加するとしよう。
Code 41
# A tibble: 2 × 3
Continent Population Countries
<chr> <dbl> <int>
1 Asia-Pacific 68064598. 82
2 Others 20980085. 104
以上の例は戻り値が2種類、つまり、"Asia-Pacific"
か"Others"
のみである。実はこの場合、if_else()
関数を使うこともできる。ここで便利な演算子が%in%
演算子である。これは左側の値が右側のベクトルに含まれていればTRUE
、含まれていなければFALSE
が返ってくる関数である。たとえば、df
のContinent
列が"Asia"
か"Oceania"
、"America"
のいずれかに該当するか確認してみよう。
[1] TRUE FALSE FALSE FALSE FALSE TRUE TRUE FALSE TRUE FALSE FALSE TRUE
[13] TRUE TRUE TRUE FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE TRUE
[25] TRUE FALSE FALSE TRUE FALSE FALSE TRUE FALSE TRUE FALSE FALSE TRUE
[37] TRUE TRUE FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE
[49] FALSE TRUE TRUE TRUE FALSE TRUE FALSE FALSE FALSE FALSE FALSE TRUE
[61] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE FALSE
[73] TRUE TRUE FALSE TRUE FALSE FALSE TRUE TRUE TRUE TRUE FALSE TRUE
[85] FALSE TRUE TRUE TRUE TRUE FALSE TRUE FALSE TRUE TRUE TRUE FALSE
[97] TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE FALSE
[109] FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE
[121] FALSE TRUE TRUE FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE
[133] TRUE TRUE FALSE FALSE TRUE FALSE FALSE FALSE TRUE TRUE TRUE FALSE
[145] FALSE TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
[157] FALSE TRUE FALSE TRUE FALSE FALSE TRUE TRUE TRUE FALSE TRUE TRUE
[169] FALSE TRUE FALSE FALSE FALSE FALSE TRUE FALSE TRUE TRUE TRUE TRUE
[181] TRUE TRUE FALSE TRUE FALSE FALSE
これをif_else()
関数の条件式として使用する。
Code 43
# A tibble: 2 × 3
Continent Population Countries
<chr> <dbl> <int>
1 Asia-Pacific 68064598. 82
2 Others 20980085. 104
最後にrecode()
関数について解説する。これまで紹介したif_else()
とcase_when()
は条件式を使うため、==
、!=
、%in%
以外にも>
や<=
なども使える。ただし、書き方がやや複雑である。recode()
は使う条件式が==
のみの場合、if_else()
やcase_when()
を代替可能な関数である。ここでは例だけ紹介しよう。
Code 44
# A tibble: 2 × 3
Continent Population Countries
<chr> <dbl> <int>
1 Asia-Pacific 68064598. 82
2 Others 20980085. 104
recode()
の第一引数はリコーディングする変数名であり、.default
はいずれの条件にも該当しない場合の値を意味する。
欠損値の扱い方
世論調査などの場合、欠損値がNA
でなく、9
や99
、""
などの場合がある。たとえば、以下のようなmy_data
というデータがあるとしよう。
Code 45
# A tibble: 10 × 3
ID Age HighEduc
<int> <dbl> <dbl>
1 1 32 1
2 2 35 0
3 3 57 0
4 4 999 1
5 5 74 0
6 6 66 9
7 7 999 1
8 8 49 1
9 9 78 9
10 10 67 9
ID
は回答者の識別番号、Age
は回答者の年齢、HighEduc
は回答者が大卒以上なら1、それ以外は0を意味する。そして、年齢を回答しなかった回答者の年齢は999、学歴については9となっている。ここで回答者の年齢と学歴の平均値を出せばどうなるだろうか。
回答者の平均年齢は245.6歳(!!)、学歴の平均値は3.1となっている。医学の発展によって人間の寿命が伸びたとしても、最大値が1のはずのHighEduc
の平均値が3.1ということはナンセンスである。データ分析の前に欠損値の確認と処理は必須であり、これを怠ると研究結果に大きな歪みな生じかねない。とりわけ、欠損値がNA
でなく、9
や999
、""
などになっているケースはより注意が必要である。今回の実習データは既に欠損値の処理済み、つまり、欠損している値はNA
になっている。ここでは上のmy_data
の使って考えてみよう。
まず、YoungAge
変数を作成し、Age
が39以下なら1
、それ以外は0
にする。ただし、999ならNA
とする。続いて、HighEduc2
変数を作成し、HighEduc
が1なら"大卒以上"
、それ以外は"大卒未満"
にする。ただし、9ならNA
とする。この作業を行うにはmutate()
内にcase_when()
を使えば良いだろう。
Code 48
# A tibble: 10 × 5
ID Age HighEduc YoungAge HighEduc2
<int> <dbl> <dbl> <dbl> <chr>
1 1 32 1 1 大卒以上
2 2 35 0 1 大卒未満
3 3 57 0 0 大卒未満
4 4 999 1 NA 大卒以上
5 5 74 0 0 大卒未満
6 6 66 9 0 <NA>
7 7 999 1 NA 大卒以上
8 8 49 1 0 大卒以上
9 9 78 9 0 <NA>
10 10 67 9 0 <NA>
もう一つの例として特定の値を欠損値とし、それ以外の値は元も値にする場合を考えよう。つまり、Age
とHighEduc
列においてそれぞれ999、9をNA
にし、そのまま上書きするものである。例えばAge
はAge
の値が999なら(Age == 999
)、NA
を返し、それ以外の場合は既存のAge
の値を取る。この作業は以下のコードで実行できる。
Code 49
# A tibble: 10 × 3
ID Age HighEduc
<int> <dbl> <dbl>
1 1 32 1
2 2 35 0
3 3 57 0
4 4 NA 1
5 5 74 0
6 6 66 NA
7 7 NA 1
8 8 49 1
9 9 78 NA
10 10 67 NA
欠損を意味する値が複数の場合もある。my_data
のAge
は999
のみが欠損値であるが、世論調査によっては888
、999
、-1
のように複数の欠損値が存在するケースがある。この場合はcase_when()
を使うか、if_else()
関数内にOR演算子(|
/ %in%
)を使えば良い。
最後に欠損値処理に特化した{naniar}パッケージのreplace_with_na()
関数を紹介する。引数はリスト型オブジェクトであり、リストの各要素は変数名 = 欠損値の値
となる。たとえば、Age
の欠損値は999
だからAge = 999
である。これは条件式ではないため、==
でなく、=
を使う。欠損値の値が複数ある場合は、変数名 = c(値1, 値2, ...)
のように書く。
Code 50
# A tibble: 10 × 3
ID Age HighEduc
<int> <dbl> <dbl>
1 1 32 1
2 2 35 0
3 3 57 0
4 4 NA 1
5 5 74 0
6 6 66 NA
7 7 NA 1
8 8 49 1
9 9 78 NA
10 10 67 NA
他にも似たような関数として、{expss}パッケージのna_if()
関数などがあるが、自分が使いやすいものを使えば良い。以下のコードは、{expss}のna_if()
関数を使って上記のコードを再現したものである2。
教科書
- 『私たちのR: ベストプラクティスの探求』