1. 統計学の種類

  • 統計学は記述統計学 (descriptive statistics) と推測統計学 (inferential statistics) の二つに分類できる
統計学の種類 特徴
記述統計学: 統計量を使ってデータ傾向や性質をつかむ
推測統計学: 母集団の母数(母平均や母分散)を検定し推定する

記述統計学

  • 集めたデータの統計量(平均、分散、標準偏差など)を計算して、そのデータを我々が理解できるような形で記述し要約する
  • データの分布を明らかにすることで、データ傾向や性質をつかむための統計手法 

推測統計学

  • 入手可能な記述統計量を使って未観測の事象を予測、推定するスキルが「推測統計学」
  • 母集団 (population) から標本 (sample) を無作為に抽出し、その標本によって得られた標本平均や不偏分散などの統計量を使って、母集団の母数(母平均や母分散)を検定し推定する 
  • 推測統計学の基本的な考えは、母集団からランダムに抽出した標本を増やし、無限に試行を繰り返せば、全体の一部である標本から、巨大で未知の母集団を推測できる、ということ 
  • 推測統計学では、分析対象が確率分布すると考える 

・ここでは両統計学で共通して使う基礎知識を解説・演習する

2. 記述統計量

  • 記述統計量(descriptive statistics)とは「ある変数が持つ情報を要約した数値」

代表的な記述統計量

記述統計量 解説
mean() 平均値 mean
median() 中央値 median
sd() 不偏標準偏差 standard deviation
var() 不偏分散 variance
IQR() 四分位範囲 Interquartile range*
min() 最小値 minimum
max() 最小値と最大値 maximum

* 四分位範囲とは、75パーセンタイルと25パーセンタイルの差のこと

  • 例えば、学生 9 人の TOEFL (iBT) の平均点が得られたとする
  • 2 番目の学生のスコアーは欠損値 (NA) とする
x <- c(22, 33, 44, 55, 66, 77, 88, 99, 100)
x
[1]  22  33  44  55  66  77  88  99 100
  • x を代表する値(=代表値)として平均値、中央値、分散、標準偏差、四分位範囲、範囲を求めてみる

2.1 平均値

  • 変数\(x\) の平均\(\bar{x}\) は次の式で求めることができる:
    \[\bar{x} = \frac{\sum_{i=1}^n x_i}{n}\]

  • R を使って平均点を計算する方法① 

  • 最も手間の掛かる方法

(22 + 33 + 44 + 55 + 66 + 77 + 88 + 99 + 100)/9
[1] 64.88889
  • R を使って平均点を計算する方法② 
  • sum( ) を使う
sum(x/9)
[1] 64.88889
  • R を使って平均点を計算する方法③ 
  • mean( ) を使う
mean(x)
[1] 64.88889

2.2 中央値(真ん中の値)

median(x)
[1] 66
  • x を小さい順にソートしてみる
sort(x)
[1]  22  33  44  55  66  77  88  99 100
  • 66 が真ん中にある(=中央値)だとわかる

2.3 分散(ばらつき)

  • 分散 (variance) とは数値データのばらつき具合を表すための指標

  • 分散は「平均値と個々のデータの差の2乗の平均」

  • 平均値から離れた値をとるデータが多い → 分散が大きい

  • 平均点付近の値をとるデータ多い → 分散が小さい

分散

  • 一般的に分散は次の式で求められる 
    \[分散 = \frac{\sum_{i=1}^N (個々のデータ - 平均)^2}{N}\]

  • 次のように表記されることが多い

  • 分散 \(σ^2\)(シグマ)は次のように表記されることが多い 
    \[σ^2 = \frac{\sum_{i=1}^N (x_i - \bar{x})^2}{N}\]

  • x が得られたサンプルデータだとする

分散を計算してみる

x
[1]  22  33  44  55  66  77  88  99 100
  • データ x の平均値を求めて x_mean と名前を付ける 
x_mean <- mean(x)
x_mean
[1] 64.88889
  • 平均からの偏差(個々のデータ - 平均)を求めて x1 と名前を付ける 
x1 <- x - x_mean
x1
[1] -42.888889 -31.888889 -20.888889  -9.888889   1.111111  12.111111  23.111111
[8]  34.111111  35.111111
  • 例えば、[1]の右隣にある数値 (-42.888889) は、データ x の先頭の値 (22) から平均値 (64.88889) を引いた値
22 - 64.88889 # x[1] - mean(x)でも同じ結果が得られる
[1] -42.88889
  • 次に、上で求めた「平均からの偏差」を 2 乗して x2 と名付ける 
x2 <- x1^2
x2
[1] 1839.456790 1016.901235  436.345679   97.790123    1.234568  146.679012
[7]  534.123457 1163.567901 1232.790123
  • 「平均からの偏差」の 2 乗和を求め sum_x2 と名付ける 
sum_x2 <- sum(x2)
sum_x2
[1] 6468.889
  • これで分散を求める式の分子は完成 

  • 分母の数 (N) は 9

  • 従って、x の分散 \(σ^2\) は、 \[σ^2 = \frac{\sum_{i=1}^N (x_i - \bar{x})^2}{N} = \frac{6468.889}{9} = 718.7654\]

  • ここで求めた分散は「標本分散」と呼ばれる

var() を使って分散を求めてみる

var(x)
[1] 808.6111

上で求めた分散 (718.7654) とは異なる値が出た

→ その理由: var() が計算するのは「標本分散 \(σ^2\)」 ではなく「不偏分散 \(S^2\)だから

・標本を使った通常の分析では「不偏分散 \(S^2\)」を使う方が一般的
R で分散を計算する関数 var() では不偏分散 \(S^2\) をデフォルトに設定している

不偏分散と標本分散

「不偏分散」「標本分散」は使い分ける必要がある 

不偏分散・・・「母集団に興味がある場合」に使う散(=標本不偏分散)
標本分散・・・「抽出したサンプルにだけ興味がある場合」に使う

不偏分散 \(S^2\)を使う場合 (R ではデフォルト):

・母集団データの一部だけが手元にあり、入手したデータの背後にある母集団データの散らばりを知りたい場合 

【不偏分散 \(S^2\) を求める式】:var() で計算可能

・仮に \(x\) が母集団から抽出された標本だとしよう 
・母分散は未知なので、標本として得られた統計量を使って未知の母分散を推定する
・母分散を推定するための統計量が不偏分散 \(S^2\)
・不偏分散 \(S^2\)は次の式で求められる 

\[S^2 = \frac{\sum_{i=1}^N (x_i - \bar{x})^2}{N-1}\]

\(\bar{x}\): 標本平均

・Rで計算してみる

sum((x - mean(x))^2) / (length(x) - 1) # var(x) でも同じ結果を得る  
[1] 808.6111
  • var() でも x の不偏分散 \((S^2)\) の値が出力される 
# 不偏分散
var(x)
[1] 808.6111
標本分散 \(σ^2\)を使う場合:

・母集団から抽出された標本(サンプル)が手元にある
・興味の対象が母集団ではなく、標本(サンプル)だけにあるとき
・そのサンプルがどの程度散らばっているかということだけを知りたい時 

【分散 \(σ^2\) を求める式】
・標本分散 \(\sigma^2\)(シグマ)は次の式で求められる 

\[\sigma^2 = \frac{\sum_{i=1}^N (x_i - \bar{x})^2}{N}\]

\(\bar{x}\): 標本平均

R には標本分散 \(\sigma^2\) を求める機能がない
\(\sigma^2\) は次の式を作って計算しなければならない 

# 標本分散
var(x) * (length(x) - 1) / length(x)
[1] 718.7654

標本分散 \(\sigma^2\ \) (718.7654) より、不偏分散 (808.6111) \(S_x^2\) の方が常に大きい

・標本分散 \(\sigma^2\) を求める式では分母が N なのに、不偏分散 \(S^2\) を求める式では分母が N - 1 だから 

分散の計算で2乗 (\(σ^2\)\(S^2\)) の和の平均をとる理由 ・1つの群における各データの数値の平均からの差(=偏差)はぞれが平均値からどれだけ離れているかを表す指標
→ 偏差はプラスとマイナス両方を取り得る
→ 各データが平均値からどれだけ離れているかを表す指標として不適切
偏差を2乗することにより、平均値からの距離の基準を絶対値に換算することで適切な指標に変換できる

2.4 標準偏差(ばらつき)

  • 標準偏差は分散の平方根

\[標準偏差 = \sqrt{分散}\]

  • 分散は、計算する過程で 2 乗されている
    → 本来の単位ではない
    → 数値の意味が変わってしまう
  • 平方根をとることによって単位を揃えた → 標準偏差

\[不偏標準偏差 (S) = \sqrt{不偏分散 (S^2)}\]

  • x の不偏標準偏差 (\(S\)) は次のいずれの式でも表せる
sqrt(var(x))
[1] 28.43609
sd(x)
[1] 28.43609

2.5 四分位範囲

  • データの75パーセンタイル (3rd Qu.) と25パーセンタイル (1st Qu.) の差
IQR(x)
[1] 44

2.6 範囲

max(x, na.rm = TRUE) - min(x)
[1] 78

2.7 記述統計をまとめて表示: summary()

  • 変数 x の統計量のサマリーを表示させる 
summary(x)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  22.00   44.00   66.00   64.89   88.00  100.00 
  • ここでは変数ごとに次の情報が表示されている 
記述統計量 解説
Min: 最小値
1st Qu.: 1st Quantile (25%)
Median: 中央値 (50%)
Mean: 平均値
3rd Qu: 3rd Quantile (75%)
Max: 最大値

3. dplyrを用いた記述統計量の計算

  • dplyrパッケージを含む tidyverse パッケージをロード
library(tidyverse)

データの準備 (hr96-21.csv) hr96-21.csv をクリックしてデータをパソコンにダウンロード  

  • RProject フォルダ内に data という名称のフォルダを作成する
  • ダウンロードした hr96-21.csv を手動でRProject フォルダ内にある data フォルダに入れる
  • 選挙データの読み取る
hr <- read_csv("data/hr96-21.csv",
               na = ".")  
  • hr96_21.csv は1996年に衆院選挙に小選挙区が導入されて以来実施された 9 回の衆議院選挙(1996, 2000, 2003, 2005, 2009, 2012, 2014, 2017, 2021)の結果のデータ

  • 読み取った選挙データを確認

  • dim() 関数を使うと hr は9,660行、22列のデータであることが分かる

dim(hr)
[1] 9660   22
  • df1 には 22 個の変数が入っている
変数名 詳細
year 選挙年 (1996-2017)
pref 都道府県名
ku 小選挙区名
kun 小選挙区
rank 当選順位
wl 選挙の当落: 1 = 小選挙区当選、2 = 復活当選、0 = 落選
nocand 立候補者数
seito 候補者の所属政党
j_name 候補者の氏名(日本語)
name 候補者の氏名(ローマ字)
previous これまでの当選回数(当該総選挙結果は含まない)
gender 立候補者の性別: “male”, “female”
age 立候補者の年齢
exp 立候補者が使った選挙費用(総務省届け出)
status 候補者のステータス: 0 = 非現職、1 現職、2 = 元職
vote 得票数
voteshare 得票率 (%)
eligible 小選挙区の有権者数
turnout 小選挙区の投票率 (%)
seshu_dummy 世襲候補者ダミー: 1 = 世襲、0 = 非世襲(地盤世襲 or 非世襲)
jiban_seshu 地盤の受け継ぎ元の政治家の氏名と関係
nojiban_seshu 世襲元の政治家の氏名と関係

3.1 記述統計を個別に計算する: summarise()

summarise()関数の使い方 summarise(記述統計の関数, 変数名1, 変数名2,...)

  • 例えば、選挙費用 (exp) の平均を計算する場合
hr |> 
  summarise(mean(exp))
# A tibble: 1 × 1
  `mean(exp)`
        <dbl>
1          NA

選挙費用の平均値が計算されない!

→ その理由・・・exp に欠損値があるため
→ na.rm = TRUE(欠損値を取り除く指定)を加えてみる

hr |> 
  summarise(mean(exp, na.rm = TRUE))
# A tibble: 1 × 1
  `mean(exp, na.rm = TRUE)`
                      <dbl>
1                  7551393.
  • summarise() 内には複数の変数名を指定できる
  • 例えば、hrexpage の平均値 (mean()) と標準偏差 (sd()) を計算してみる
hr |> 
  summarise(mean(exp, na.rm = TRUE), # exp の平均値
         sd(exp, na.rm = TRUE), # exp の標準偏差
         mean(age, na.rm = TRUE), # age の平均値
         sd(age, na.rm = TRUE)) # age の標準偏差
# A tibble: 1 × 4
  `mean(exp, na.rm = TRUE)` `sd(exp, na.rm =…` `mean(age, na.…` `sd(age, na.rm…`
                      <dbl>              <dbl>            <dbl>            <dbl>
1                  7551393.           5482684.             51.2             11.1
  • 出力結果はデータフレーム(tibble)形式で表示される
  • 変数名が長く、非常に読みにくい
  • rename() 関数を使って変数名を修正できる
hr |> 
  summarise(Mean_exp = mean(exp, na.rm = TRUE),
            SD_exp   = sd(exp, na.rm = TRUE),
            Mean_age = mean(age, na.rm = TRUE), 
            SD_age   = sd(age, na.rm = TRUE)) 
# A tibble: 1 × 4
  Mean_exp   SD_exp Mean_age SD_age
     <dbl>    <dbl>    <dbl>  <dbl>
1 7551393. 5482684.     51.2   11.1
  • かなりみやすくなった

3.2 グルーピング: group_by()

1 グループ

  • 特定の変数の記述統計量を計算する場合は mean()sd() などの関数のみを使った方が効率的
  • グループごとに記述統計量を計算する場合は、dplyr パッケージが大変便利
  • 例えば、dplyr を使わずに総選挙ごとの選挙費用 (exp) の平均値を計算してみる
  • 1996年総選挙の値が「1996」である場合の選挙費用 (exp) の平均値を計算する場合は以下のように書く必要がある
    注意exp に欠損値があるため na.rm = TRUE(欠損値を取り除く指定)を加えている
mean(hr$exp[hr$year == 1996], na.rm = TRUE)
[1] 9136316
  • これを 1996年から2021年までの 8 つの総選挙ごとに計算しなければならないので、次のように入力する必要がある
mean(hr$exp[hr$year == 1996], na.rm = TRUE)
mean(hr$exp[hr$year == 2000], na.rm = TRUE)
mean(hr$exp[hr$year == 2003], na.rm = TRUE)
mean(hr$exp[hr$year == 2005], na.rm = TRUE)
mean(hr$exp[hr$year == 2009], na.rm = TRUE)
mean(hr$exp[hr$year == 2012], na.rm = TRUE)
mean(hr$exp[hr$year == 2014], na.rm = TRUE)
mean(hr$exp[hr$year == 2017], na.rm = TRUE)
mean(hr$exp[hr$year == 2019], na.rm = TRUE)
mean(hr$exp[hr$year == 2021], na.rm = TRUE)
  • しかし、dplyr パッケージの group_by() 関数を使うと驚くほどコマンドが簡素化できる  

group_by()関数の使い方 group_by(グループ化する変数名) |>
  summarise(...)

  • hryear ごとにデータをグループ化し、exp の平均値を計算する場合
  • summarise() の前 に group_by(year) を使ってパイプをつなぎ
    → year ごとに exp の平均値を計算する
hr |> 
  group_by(year) |> 
  summarise(Mean_exp = mean(exp, na.rm = TRUE))
# A tibble: 9 × 2
   year Mean_exp
  <dbl>    <dbl>
1  1996 9136316.
2  2000 8388889.
3  2003 7935408.
4  2005 8142244.
5  2009 6118181.
6  2012 5769988.
7  2014 7440127.
8  2017 9298783.
9  2021     NaN 

複数でのグループ化

  • group_by() 関数は 2 つ以上の変数でのグルーピングも可能
  • 政党ごとにどれだけ選挙費用を使っているかを時系列で調べたいとする
  • hryearseito でデータをグループ化し、exp の平均値を計算してみる
hr |> 
  group_by(year, seito) |> 
  summarise(Mean_exp = mean(exp, na.rm = TRUE))
# A tibble: 113 × 3
# Groups:   year [9]
    year seito                   Mean_exp
   <dbl> <chr>                      <dbl>
 1  1996 さわやか神戸・市民の会      NaN 
 2  1996 共産                    3158354.
 3  1996 国民党                      NaN 
 4  1996 市民新党にいがた        3044160 
 5  1996 政事公団太平会             5540 
 6  1996 文化フォーラム              NaN 
 7  1996 新党さきがけ           13030901 
 8  1996 新社会                  4545681 
 9  1996 新進                   12395369.
10  1996 日本新進                1898969 
# … with 103 more rows
  • これだと政党の数が多すぎて煩雑なので、政党を自民党と民主党の 2 つだけに絞って計算してみる
unique(hr$seito)
 [1] "新進"                   "自民"                   "民主"                  
 [4] "共産"                   "文化フォーラム"         "国民党"                
 [7] "無所"                   "自由連合"               "政事公団太平会"        
[10] "新社会"                 "社民"                   "新党さきがけ"          
[13] "沖縄社会大衆党"         "市民新党にいがた"       "緑の党"                
[16] "さわやか神戸・市民の会" "民主改革連合"           "青年自由"              
[19] "日本新進"               "公明"                   "諸派"                  
[22] "保守"                   "無所属の会"             "自由"                  
[25] "改革クラブ"             "保守新"                 "ニューディールの会"    
[28] "新党尊命"               "世界経済共同体党"       "新党日本"              
[31] "国民新党"               "新党大地"               "幸福"                  
[34] "みんな"                 "改革"                   "日本未来"              
[37] "日本維新の会"           "当たり前"               "政治団体代表"          
[40] "安楽死党"               "アイヌ民族党"           "次世"                  
[43] "維新"                   "生活"                   "立憲"                  
[46] "希望"                   "緒派"                   ""                      
[49] "N党"                   "国民"                   "れい"                  
# `ggplot` で文字バケしない設定(マックユーザーのみ)  
theme_set(theme_classic(base_size = 10,
                        base_family = "HiraginoSans-W3"))
hr |>  
  group_by(year, seito) |> 
  summarise(Mean_exp = mean(exp, na.rm = TRUE)) |> 
  filter(seito == "自民" | seito == "民主") 
# A tibble: 16 × 3
# Groups:   year [9]
    year seito  Mean_exp
   <dbl> <chr>     <dbl>
 1  1996 民主   9961458.
 2  1996 自民  14460093.
 3  2000 民主  10207109.
 4  2000 自民  14251423.
 5  2003 民主   9772028.
 6  2003 自民  12821990.
 7  2005 民主   9574473.
 8  2005 自民  12710075.
 9  2009 民主   7802585.
10  2009 自民  11374974.
11  2012 民主   7728045.
12  2012 自民   9335490.
13  2014 民主   9757272 
14  2014 自民  11450459.
15  2017 自民  12338476.
16  2021 自民       NaN 
  • ggplot() 関数を使って自民党と民主党それぞれの候補者の選挙費用の平均値を可視化してみる
hr |>  
  group_by(year, seito) |> 
  summarise(Mean_exp = mean(exp, na.rm = TRUE)) |> 
  filter(seito == "自民" | seito == "民主") |> 
  ggplot(aes(x = year, y = Mean_exp, color = seito)) +
  geom_point() +
  geom_line() 

  • 自民党と民主党それぞの選挙費用の平均値を計算してみる
hr |> 
  filter(seito == "自民" | seito == "民主") |> 
  group_by(seito) |> 
  summarise(Mean_exp = mean(exp, na.rm = TRUE))
# A tibble: 2 × 2
  seito  Mean_exp
  <chr>     <dbl>
1 民主   9096866.
2 自民  12456211.

グループごとのケース数を知りたい場合: n()

  • グルーピングを行う場合、各グループに属するケース数を調べたい場合
  • 例えば、1996年から2019年までの総選挙ごとに政党が使った選挙費用の平均値だけでなく、立候補者の人数も表示したい時はコマンドに n()を加える
hr |> 
  filter(seito == "自民" | seito == "民主") |> 
  group_by(year, seito) |> 
  summarise(Mean_exp = mean(exp, na.rm = TRUE),
            Cand_number = n())
# A tibble: 16 × 4
# Groups:   year [9]
    year seito  Mean_exp Cand_number
   <dbl> <chr>     <dbl>       <int>
 1  1996 民主   9961458.         143
 2  1996 自民  14460093.         288
 3  2000 民主  10207109.         242
 4  2000 自民  14251423.         271
 5  2003 民主   9772028.         267
 6  2003 自民  12821990.         277
 7  2005 民主   9574473.         289
 8  2005 自民  12710075.         290
 9  2009 民主   7802585.         271
10  2009 自民  11374974.         291
11  2012 民主   7728045.         264
12  2012 自民   9335490.         289
13  2014 民主   9757272          178
14  2014 自民  11450459.         283
15  2017 自民  12338476.         276
16  2021 自民       NaN          277
  • ggplot() 関数を使って自民党と民主党それぞれの立候補者数を総選挙ごとに可視化してみる
  • geom_tex()関数を使って立候補者数を図に表示することも可能
hr |> 
  filter(seito == "自民" | seito == "民主") |> 
  group_by(year, seito) |> 
  summarise(Mean_exp = mean(exp, na.rm = TRUE),
            Cand_number = n()) |> 
  ggplot(aes(x = year, y = Cand_number, color = seito)) +
  geom_point() +
  geom_line() +
  geom_text(aes(y = Cand_number + 3, label = Cand_number),
            size = 4, vjust = 0)

  • 1996年から2019年までの総選挙に立候補した自民党と民主党の候補者総数を知りたければ
hr |> 
  filter(seito == "自民" | seito == "民主") |> 
  group_by(seito) |> 
  summarize(Cand_number = n())
# A tibble: 2 × 2
  seito Cand_number
  <chr>       <int>
1 民主         1654
2 自民         2542

3.3 より短いコードで入力可能: across()

  • across() 関数を利用することで、複数の変数に対して複数の記述統計量をより短いコードで計算することが可能
names(hr)
 [1] "year"          "pref"          "ku"            "kun"          
 [5] "wl"            "rank"          "nocand"        "seito"        
 [9] "j_name"        "gender"        "name"          "previous"     
[13] "age"           "exp"           "status"        "vote"         
[17] "voteshare"     "eligible"      "turnout"       "seshu_dummy"  
[21] "jiban_seshu"   "nojiban_seshu"
  • 例えば、hrprevious から exp までお互い隣同士の 3 つの変数の平均値と標準偏差を計算する場合、次のコードを打ち込む必要がある
hr |> 
  summarise(previous_Mean = mean(previous, na.rm = TRUE),
            previous_SD   = mean(previous, na.rm = TRUE),
            age_Mean      = mean(age, na.rm = TRUE),
            age_SD        = mean(age, na.rm = TRUE),
            exp_Mean      = mean(exp, na.rm = TRUE),
            exp_SD        = mean(exp, na.rm = TRUE))
# A tibble: 1 × 6
  previous_Mean previous_SD age_Mean age_SD exp_Mean   exp_SD
          <dbl>       <dbl>    <dbl>  <dbl>    <dbl>    <dbl>
1          1.48        1.48     51.2   51.2 7551393. 7551393.
  • この作業は、across() 関数を使うと 4 行でよい
hr |> 
  summarise(across(previous:exp,
                   .fns = list(Mean = ~mean(.x, na.rm = TRUE),
                               SD = ~mean(.x, na.rm = TRUE))))
# A tibble: 1 × 6
  previous_Mean previous_SD age_Mean age_SD exp_Mean   exp_SD
          <dbl>       <dbl>    <dbl>  <dbl>    <dbl>    <dbl>
1          1.48        1.48     51.2   51.2 7551393. 7551393.

3.4 変数の作成: mutate()

mutate()関数の使い方 データフレーム名 |>
  mutate(新しい変数名 = 計算式)

  • 新しい変数名が既に存在するの変数名だと
    → 既存の変数が上書きされてしまう
  • 既存にない変数名だと
    → 新しい変数が最後の変数として追加される
  • hr の 選挙費用 (exp) を有権者数 (eligible) で割り「有権者一人あたりに費やす選挙費用」(exppv) を作成する
  • dplyr を使わずにこの処理を行う場合、以下のようなコードとなる
hr$exppv <- hr$exp / hr$eligible
names(hr)
 [1] "year"          "pref"          "ku"            "kun"          
 [5] "wl"            "rank"          "nocand"        "seito"        
 [9] "j_name"        "gender"        "name"          "previous"     
[13] "age"           "exp"           "status"        "vote"         
[17] "voteshare"     "eligible"      "turnout"       "seshu_dummy"  
[21] "jiban_seshu"   "nojiban_seshu" "exppv"        
  • 最後に exppv が追加されている
  • dplyr::mutate() を使うと次のようなコードになる
hr |> 
  dplyr::mutate(exppv = exp / eligible)
# A tibble: 9,660 × 23
    year pref  ku      kun    wl  rank nocand seito j_name gender name  previous
   <dbl> <chr> <chr> <dbl> <dbl> <dbl>  <dbl> <chr> <chr>  <chr>  <chr>    <dbl>
 1  1996 愛知  aichi     1     1     1      7 新進  河村…  male   KAWA…        2
 2  1996 愛知  aichi     1     0     2      7 自民  今枝…  male   IMAE…        2
 3  1996 愛知  aichi     1     0     3      7 民主  佐藤…  male   SATO…        2
 4  1996 愛知  aichi     1     0     4      7 共産  岩中…  female IWAN…        0
 5  1996 愛知  aichi     1     0     5      7 文化… 伊東…  female ITO,…        0
 6  1996 愛知  aichi     1     0     6      7 国民… 山田浩 male   YAMA…        0
 7  1996 愛知  aichi     1     0     7      7 無所  浅野…  male   ASAN…        0
 8  1996 愛知  aichi     2     1     1      8 新進  青木…  male   AOKI…        2
 9  1996 愛知  aichi     2     0     2      8 自民  田辺…  male   TANA…        0
10  1996 愛知  aichi     2     2     3      8 民主  古川…  male   FURU…        0
# … with 9,650 more rows, and 11 more variables: age <dbl>, exp <dbl>,
#   status <dbl>, vote <dbl>, voteshare <dbl>, eligible <dbl>, turnout <dbl>,
#   seshu_dummy <dbl>, jiban_seshu <chr>, nojiban_seshu <chr>, exppv <dbl>
  • 出力される画面に exppv がない
    → 新しく追加された exppv が最後の列なので、画面に収まらないから
  • exppv は問題なく追加されており、出力画面の下段に省略されている変数名に含まれている
names(hr)
 [1] "year"          "pref"          "ku"            "kun"          
 [5] "wl"            "rank"          "nocand"        "seito"        
 [9] "j_name"        "gender"        "name"          "previous"     
[13] "age"           "exp"           "status"        "vote"         
[17] "voteshare"     "eligible"      "turnout"       "seshu_dummy"  
[21] "jiban_seshu"   "nojiban_seshu" "exppv"        

3.5 練習問題

  • 衆院選挙データ (hr96-21.csv) を使って次の問題に答えなさい

Q3.5.1

  • group_by() 関数と ggplot() 関数を使って総選挙 (1996-2021) の立候補者の平均年齢をすべての政党ごとに計算し線グラフで可視化しなさい

Q3.5.2

  • 政党を自民党、民主党、公明党に絞って表示しなさい

Q3.5.3

  • 総選挙 (1996-2021) の当選者の平均年齢を 3 つの政党(自民党、民主党、公明党)ごとに計算し線グラフで可視化しなさい
  • その際、当選者の数も表示すること

4. 変数の re-coding

4.1 if_else()

  • 変数の re-coding に使う関数

if_else()関数の使い方 if_else(条件式, TRUE の場合の戻り値, FALSE の場合の戻り値)

  • 具体的な事例を示してみよう
  • 例えば、hr の変数 status の値は 0, 1, 2 の 3 つである
変数名 詳細
status 候補者のステータス: 0 = 非現職、1 現職、2 = 元職
  • status 変数でデータをグルーピングし、voteexp の平均値を計算してみる
hr |> 
  group_by(status) |> 
  summarise(Mean_vote = mean(vote, na.rm = TRUE),
            Mean_exp = mean(exp, na.rm = TRUE))
# A tibble: 3 × 3
  status Mean_vote  Mean_exp
   <dbl>     <dbl>     <dbl>
1      0    33255.  5110084.
2      1    89259. 11545342.
3      2    69632.  9335640.
  • 変数 status を使って「現職ダミー」inc を作りたいとしよう
  • inc の値を「1 = 現職、0 = それ以外」に re-coding する
hr <- hr |> 
  mutate(inc = if_else(status == 1, "現職", "非現職")) # status の値が 1 なら inc の値は 1、それ以外は 0 という意味
  • table() 関数と unique() 関数を使って、変数 inc が正しく作成されたかどうかチェックする
table(hr$status, hr$inc)
   
    現職 非現職
  0    0   5517
  1 3510      0
  2    0    633
unique(hr$inc)
[1] "現職"   "非現職"
  • re-coding する場合、既存の変数(この場合だと status)を上書せず、新しい変数(この場合だと inc)として追加することが望ましい

  • inc 変数でデータをグルーピングし、voteexp の平均値を計算してみる

hr |> 
  group_by(inc) |> 
  summarise(Mean_vote = mean(vote, na.rm = TRUE),
            Mean_exp = mean(exp, na.rm = TRUE))
# A tibble: 2 × 3
  inc    Mean_vote  Mean_exp
  <chr>      <dbl>     <dbl>
1 現職      89259. 11545342.
2 非現職    36999.  5482955.

4.2 case_when()

  • 1 つの条件を re-coding したいとき・・・if_else() 関数を使う
  • 2 つ以上の条件を re-coding したいとき・・・case_when() 関数を使う

case_when関数の使い方 データフレーム名 |>
  mutate(新しい変数名 = case_when(条件1 ~ 新しい値,
                  条件2 ~ 新しい値,
                  ......
                  TRUE ~ 新しい値)

  • TRUE ~ 新しい値は「上記の条件全てが満たされない場合の値」を意味する
  • hr の 変数 status の値 (1, 2, 0) を日本語(現職・元職・新人)に変換し変数 status_j として追加する
hr %>%
  mutate(status_j = case_when(status == "1" ~ "現職",
                              status == "2" ~ "元職",
                             TRUE  ~ "新人")) %>% # 現職、元職以外は「新人」と指定
  group_by(ステータス = status_j) %>%
  summarise(平均得票数 = mean(vote, na.rm = TRUE),
            候補者数         = n())
# A tibble: 3 × 3
  ステータス 平均得票数 候補者数
  <chr>           <dbl>    <int>
1 元職           69632.      633
2 新人           33255.     5517
3 現職           89259.     3510

4.3 %in%

  • hr の変数 seito の値は 0, 1, 2 の 3 つである
変数名 詳細
seito 候補者の所属政党
  • hr から 2021年総選挙のデータだけを抜き出して、seito の値を確認してみる 
hr2021 <- hr |> 
  filter(year == 2021)
unique(hr2021$seito)
 [1] "自民" "立憲" "N党" "国民" "維新" "共産" "れい" "無所" "社民" "諸派"
[11] "公明"
hr2021 %>%
  mutate(party = case_when(seito == "自民" ~ "自民党",
                          seito == "公明" ~ "公明党",
                          seito == "立憲" ~ "立憲民主党",
                          seito == "維新" ~ "日本維新の会",
                          seito == "国民" ~ "国民民主党",
                          seito == "N党" ~ "NHK党",
                          seito == "れい" ~ "れいわ新撰組",
                          seito == "社民" ~ "社会民主党",
                          seito == "共産" ~ "共産党",
                          seito == "無所" ~ "無所属",
                           TRUE  ~ "諸派")) %>% 
  group_by(政党 = party) %>%
  summarise(得票総数             = sum(vote, na.rm = TRUE),
            候補者数             = n(),
            一人あたり得票平均数 = mean(vote, na.rm = TRUE))
# A tibble: 10 × 4
   政党         得票総数 候補者数 一人あたり得票平均数
   <chr>           <dbl>    <int>                <dbl>
 1 れいわ新撰組   248280       12               20690 
 2 公明党         872931        9               96992.
 3 共産党        2640801      105               25150.
 4 国民民主党    1245719       21               59320.
 5 日本維新の会  4802793       94               51094.
 6 無所属        2269166       80               28365.
 7 社会民主党     313193        9               34799.
 8 立憲民主党   17215620      214               80447.
 9 自民党       27626157      277               99733.
10 諸派           222368       36                6177.
  • 「新しい値」(TRUE ~ "") は条件の数だけ存在する必要はない
  • 例えば、変数 seito の値が”自民”か”公明”なら”政権与党”
  • それ以外は”野党”に変更
  • その結果を「与野党」という名の新しい変数として追加してみる
hr2021 %>%
  mutate(party = case_when(seito == "自民" ~ "政権与党",
                          seito == "公明" ~ "政権与党",
                           TRUE  ~ "野党")) %>% 
  group_by(与野党 = party) %>%
  summarise(得票総数             = sum(vote, na.rm = TRUE),
            候補者数             = n(),
            一人あたり得票平均数 = mean(vote, na.rm = TRUE))
# A tibble: 2 × 4
  与野党   得票総数 候補者数 一人あたり得票平均数
  <chr>       <dbl>    <int>                <dbl>
1 政権与党 28499088      286               99647.
2 野党     28957940      571               50714.
  • ここでは戻り値が 2 種類(“政権与党”か”野党”)のみ
    → if_else() 関数を使うこともできる

%in%演算子が使える

  • 左側の値が右側のベクトルに含まれていれば・・・TRUE
  • 含まれていなければ・・・FALSE
  • hr2021 の変数 seito が”自民”か”公明”のいずれかに該当するか確認する
head(hr2021$seito %in% c("自民", "公明"))
[1]  TRUE FALSE FALSE FALSE  TRUE FALSE
  • 1番目が「TRUE」・・・この候補者は「自民」もしくは「公明」の候補者

  • 2番目が「FALSE」・・・この候補者は「自民」「公明」以外の候補者

  • これを if_else() 関数の条件式として使える

hr2021 %>%
  mutate(party = if_else(seito %in% c("自民", "公明"), "政権与党", "野党")) %>%
  group_by(与野党 = party) %>%
  summarise(得票総数             = sum(vote, na.rm = TRUE),
            候補者数             = n(),
            一人あたり得票平均数 = mean(vote, na.rm = TRUE))
# A tibble: 2 × 4
  与野党   得票総数 候補者数 一人あたり得票平均数
  <chr>       <dbl>    <int>                <dbl>
1 政権与党 28499088      286               99647.
2 野党     28957940      571               50714.

4.4 recode() 関数

  • if_else()case_when() は書き方がやや複雑
  • recode() は使う条件式が == のみの場合、if_else()case_when() と代替可能
hr2021 %>%
  mutate(party = recode(seito,
                        "自民"   =  "政権与党",
                        "公明"   =  "政権与党",
                        .default =  "野党")) %>%
  group_by(与野党 = party) %>%
  summarise(得票総数             = sum(vote, na.rm = TRUE),
            候補者数             = n(),
            一人あたり得票平均数 = mean(vote, na.rm = TRUE))
# A tibble: 2 × 4
  与野党   得票総数 候補者数 一人あたり得票平均数
  <chr>       <dbl>    <int>                <dbl>
1 政権与党 28499088      286               99647.
2 野党     28957940      571               50714.
  • .default = "": いずれの条件にも該当しない場合の値を意味

4.5 練習問題

  • 衆院選挙データ (hr96-21.csv) を使って次の問題に答えなさい
Q4.5.1
  • 変数 wl の値は 0, 1, 2 の 3 つである
変数名 詳細
wl 選挙の当落: 1 = 小選挙区当選、2 = 復活当選、0 = 落選
  • if_else() 関数を使って、小選挙区で当選した候補者を 1、小選挙区で落選した候補者を 0 とした小選挙区当選ダミー (wlsmd) を作成しなさい
  • table() 関数と unique() 関数を使って、変数 inc が正しく作成されたかどうかチェックしなさい
Q4.5.2
  • filter() 関数を使って2021年総選挙だけのデータを抜き出し、if_else() 関数を使って、自民党候補者を 1、それ以外の候補者を 0 とした自民党ダミー (ldp) を作成しなさい
  • table() 関数と unique() 関数を使って、変数 ldp が正しく作成されたかどうかチェックしなさい
Q4.5.3
  • filter() 関数を使って2021年総選挙だけのデータを抜き出し、mutate() 関数、if_else() 関数、%in% 関数、group_by() 関数を使って、政権与党(自民党と公明党の合計)と野党候補者の平均年齢を比較して表示しなさい
  • その際、政権与党と野党それぞれの候補者人数も表示しなさい
Q4.5.4
  • filter() 関数を使って2021年総選挙だけのデータを抜き出し、mutate() 関数、if_else() 関数、%in% 関数、group_by() 関数を使って、政権与党(自民党と公明党の合計)と野党の当選者(比例復活当選者含む)の平均年齢を比較して表示しなさい
  • その際、政権与党と野党それぞれの候補者人数も表示しなさい
Q4.5.5
  • filter() 関数を使って2021年総選挙だけのデータを抜き出し、mutate() 関数、case_when() 関数と group_by() 関数を使って、自民党、公明党、立憲民主党、立憲民主党以外の野党の当選者(比例復活当選者含む)の平均年齢を比較して表示しなさい
  • その際、自民党、公明党、立憲民主党、野党それぞれの候補者人数も表示しなさい

5. 欠損値への対処法

  • 世論調査などのデータを扱う場合、欠損値が 99999 などで表示されることが多い
  • 例えば、次の架空データについて考えてみよう
df1 <- tibble(id      = 1:10,
              age     = c(19, 20, 22, 999, 35, 45, 50, 60, 70, 999),
              college = c(1, 0, 0, 1, 1, 99, 1, 1, 99, 0))
df1
# A tibble: 10 × 3
      id   age college
   <int> <dbl>   <dbl>
 1     1    19       1
 2     2    20       0
 3     3    22       0
 4     4   999       1
 5     5    35       1
 6     6    45      99
 7     7    50       1
 8     8    60       1
 9     9    70      99
10    10   999       0
変数 解説
id: ID番号
age: 年齢、999 = 欠損値
college: 1 = 大卒、0 = それ以外、99 = 欠損値
  • 回答者の年齢と学歴の平均値を出力してみよう
mean(df1$age)
[1] 231.9
  • 回答者の平均年齢は 231歳!?
mean(df1$college)
[1] 20.3
  • 回答者の大卒率は 20.3%?
  • データ分析の前に欠損値の確認と処理は必須
  • こに作業を怠ると研究結果に大きな歪みが生じる
  • 特に、欠損値が NA でなく、99999 の場合には注意が必要

欠損値の処置方法

Step 1:
  • 変数 AGE を作成し、age39 以下なら 1、それ以外は 0 にす
  • age999 なら NA とする
  • mutate() 内に case_when() を使って指定する
df1 %>%
  mutate(AGE  = case_when(age == 999 ~ NA,
                          age <=  39 ~ 1,
                          TRUE       ~ 0))
  • 次のようなエラーがでる!

エラーメッセージ
Error in mutate(): ! Problem while computing AGE = case_when(age == 999 ~ NA, age <= 39 ~ 1, TRUE ~ 0). Caused by error in names(message) <- `*vtmp*`: ! ‘names’ attribute [1] must be the same length as the vector [0]

  • 原因は NA の設定: age == 999 ~ NA
  • R において欠損値は NA のみ
  • しかし tidyverse の世界では、NA は次の 2 種類ある
    numeric 型の NA
    charcter 型の NA
  • 上のコードの場合、新たに作る変数 AGE の値は全て数値だから、AGEnumeric
    → age == 999 ~ NA_real_ と設定
df1 %>%
  mutate(AGE  = case_when(age == 999 ~ NA_real_,
                          age <=  39 ~ 1,
                          TRUE       ~ 0))
# A tibble: 10 × 4
      id   age college   AGE
   <int> <dbl>   <dbl> <dbl>
 1     1    19       1     1
 2     2    20       0     1
 3     3    22       0     1
 4     4   999       1    NA
 5     5    35       1     1
 6     6    45      99     0
 7     7    50       1     0
 8     8    60       1     0
 9     9    70      99     0
10    10   999       0    NA
  • うまく設定できた
Step 2:
  • 変数 COLLEGE を作成し、college1 なら “大卒以上”、0 は “大卒未満”、99 なら NA と指定
df1 %>%
  mutate(AGE  = case_when(age == 999 ~ NA_real_,
                          age <=  39 ~ 1,
                          TRUE       ~ 0),
         COLLEGE = case_when(college == 99 ~ NA,
                            college == 1 ~ "大卒以上",
                            TRUE          ~ "大卒未満"))
  • 次のようなエラーがでる!

エラーメッセージ

Error in mutate(): ! Problem while computing COLLEGE = case_when(edu) == 1 ~ "大卒以上, TRUE ~ "大卒未満"). Caused by error:

  • 原因は NA の設定: college == 99 ~ NA
  • NA は次の 2 種類ある
    numeric 型の NA
    charcter 型の NA
  • 上のコードの場合、新たに作る変数 COLLEGE の値は全て文字型だから、COLLEGEcharacter
    → college == 99 ~ NA_character_ と設定
df1 %>%
  mutate(AGE  = case_when(age == 999 ~ NA_real_,
                          age <=  39 ~ 1,
                          TRUE       ~ 0),
         COLLEGE = case_when(college == 99 ~ NA_character_,
                            college == 1 ~ "大卒以上",
                            TRUE          ~ "大卒未満"))
# A tibble: 10 × 5
      id   age college   AGE COLLEGE 
   <int> <dbl>   <dbl> <dbl> <chr>   
 1     1    19       1     1 大卒以上
 2     2    20       0     1 大卒未満
 3     3    22       0     1 大卒未満
 4     4   999       1    NA 大卒以上
 5     5    35       1     1 大卒以上
 6     6    45      99     0 <NA>    
 7     7    50       1     0 大卒以上
 8     8    60       1     0 大卒以上
 9     9    70      99     0 <NA>    
10    10   999       0    NA 大卒未満

欠損値 (NA) の指定方法まとめ
numeric 型の NA の場合:
   変数 == 値 ~ NA_real_
charcter 型の NA:
   変数 == 値 ~ NA_character_

参考文献
  • 宋財泫 (Jaehyun Song)・矢内勇生 (Yuki Yanai)「私たちのR: ベストプラクティスの探究」
  • 宋財泫「ミクロ政治データ分析実習(2022年度)」
  • 土井翔平(北海道大学公共政策大学院)「Rで計量政治学入門」
  • 矢内勇生(高知工科大学)授業一覧
  • 浅野正彦, 矢内勇生.『Rによる計量政治学』オーム社、2018年
  • 浅野正彦, 中村公亮.『初めてのRStudio』オーム社、2018年
  • Winston Chang, R Graphics Cookbook, O’Reilly Media, 2012.
  • Kieran Healy, DATA VISUALIZATION, Princeton, 2019
  • Kosuke Imai, Quantitative Social Science: An Introduction, Princeton University Press, 2017