参議院選結果からN国党への勢力を分析する〜得票率モデル

NHKから国民を守る党(N国党)のようなケッタイな政党を支持している地域はどこなのか、どういう人達が支持しているのかについて分析してみます。
N国党はネット上での選挙活動を主としているようで、これはドブ板選挙よりもデータ分析と親和性が高いです。

直前の参院選の投票結果を使います。

総務省|令和元年7月21日執行 参議院議員通常選挙 速報結果

【得票率の一般モデルとポジショニング】

まず都道府県別の有権者数と得票数の相関を見ます。

散布図:都道府県別有権者数と得票数

なお得票数とは選挙区と比例区との合計値です。
当たり前ですが有権者数が多いと得票数も多くなります。
その関係は両対数で直線関係になることが自然で、データにも実際当てはまりがよさそうです。
式で表すと


V=bP^a (1)
V=得票数, P=有権者数

です。
2つのパラメータabはそれぞれ、党の基本的な勢力b、人口に対する感度aと解釈できます。
なお自民党はb=0.0247, a=0.878, N国党はb=-2.74,a=1.11です。

念の為R^2を調べて、我々のモデルが問題ないことを確認します。

政党別得票数モデルのR2。社民党だけ当てはまりが悪い

ほとんどの政党についてかなり高いR2が得られます。
なぜか社民党だけ当てはまりが異常に悪く、特殊な政党なんだろうなと推測できますが今回ここは深堀しません。

こうして我々のモデルが得票数を説明する妥当なモデルであることが確認できました。
つぎに式(1)の両辺をPすなわち有権者数で割り算してみます。


R=bP^{a-1}
R=V/P=得票率

これにより得票数のモデルを得票率のモデルに変換できます。
ただしここでの得票率とは一般的な

得票数 / 投票数

ではなく、投票率の影響を無視した潜在的な得票率

得票数 / 有権者数

を意味します。
N国党は投票に行かない層をターゲットにしているようなので、一般的な得票率よりもこちらの方が目的に合っているでしょう。

得票率モデルを図示します。

自民党は右肩下がり(a-1<0)で、N国党は右肩上がり(a-1>0)であることが分かります。
つまり自民党は都会になるほど弱く、N国党は都会になるほど強いです。

他にも分かることがあります。
たとえばN国党は関東に拠点を置いて区議会議員もたくさん抱えていることもあり関東に強いと思われています。
しかし実のところN国党が関東で強いのは、都会に強いというN国党の傾向と関東には東京や神奈川や埼玉など人口が多い都会が多いという二つの要因によります。
なぜならば愛知や北海道や兵庫でも関東である埼玉や千葉と同等の得票数があるからです。
逆の見方をすればN国党は関東で活発に活動しているにもかかわらず、関東以外の都会にも同等水準の影響力を持っていると言えそうです。
なお大阪がやたらに悪いのは維新の勢力がやたら強いためです。

次にこの二つのパラメータ、基本勢力bと都会指数aを比較することで政党ごとの立ち位置というかポジショニングを調べます。

政党ごとのポジショニング

ざっと解釈すると政党は都会力aで4つのクラスタに分類できそうです。
すなわち

クラスタ1:田舎に強い
自民党、国民民主党
クラスタ2:中立
公明党
クラスタ3:都会に強い
立憲民主党、共産党、れいわ新選組、N国党
クラスタ4:都会特化
日本維新の会

です。

左派野党がクラスタ3に偏っているのに対して、右派野党が散らばっているのが印象的です。

右派野党(国民民主党、日本維新の会、そしておそらくN国党)はポジショニングが異なるため、支持基盤の棲み分けができています。
それぞれの支持基盤で勢力を拡大できますし、良いタイミングで連合できれば大きな飛躍が狙えそうです。
自民党から見ても日本維新の会は魅力的で、もし民主主義でなければ資本の論理でM&Aです。

一方、左派野党のポジショニングは偏っています。
左派野党たちは彼らの中でパイの奪い合いをしているだけなのではないでしょうか。
そういう意味でれいわ新選組は今の路線で大きな飛躍は難しいような気がします。
れいわ新選組には路線を変更し、都会のワープアではなくマイルドヤンキーや限界集落を支えるなど田舎に活動を移すことをお勧めします。
N国党のヒトラー路線に対抗した毛沢東路線です。

【N国党の人気の地域差】

上の節で我々は得票率のモデルを構築し、このモデルによりN国党は都会に強い政党であり、有権者数が多い方が得票率が高くなることが分かりました。
このストーリーはあくまで一般論です。
一般論とは別に地域による特色はあるのでしょうか?
もちろんあります。

グラフの直線で表示しているのが我々のモデルで有権者数から予測できる得票数です。
よって線よりも上に表示されているのが一般論よりも強い支持がある地方で、逆に線よりも下であれば支持が弱い地方だと言えます。

赤色は候補者を擁立していなかった都道府県です。
候補者を擁立しなかった場合はモデルよりも得票が低いことが分かります。

線から離れていればいるほどこの傾向が大きくなりますので、人気順に並び替えます。

これからも色々と面白いことが分かります。
たとえば人気が高い地域のトップ5は上から徳島、岐阜、鳥取、山形、北海道ですが、全く地理的に偏っていません。
人気ワースト地域についても同様です。
たとえば岩手県、宮城県、秋田県は人気があるにもかかわらず、候補者を擁立した青森県では得票が少ないです。

上記からN国党は特定の地域に人気が偏っているわけではないと予想されます。
これを確認するために地図上にプロットします。
画像の見せ方の問題で沖縄と小笠原諸島を隠しています。すいません。

色が明るい方が人気が高いことを意味しています。
あえて言うと九州が弱いようにも見えますので、今後はN国党は九州に注力してもいいかもしれません。

一方東北地方は全体的に人気が高いですが、青森だけはめちゃくちゃ悪い。
このような隣接しているが人気が大きく異なる地域、たとえば『岐阜と富山』『青森と岩手』などをさらに深く比較分析すれば、N国党の支持の源泉や得票数の予測に繋げられる可能性があります。
これについてはそのうちまた続編として別の記事でやってみたいです。

【まとめ】

  • N国党は都会の方が強いという傾向がある。関東だけで強いわけではない
  • 都会度の影響を除くと人気に地理的な偏りは小さい。隣接している地域でも支持に極端な差があったりする。ここを深堀すれば得票数の予測ができるかもしれない

元データ

分析コード

#R
library(tidyverse)
library(readxl)
library(stringr)
library(httr)
library(estatapi)
library(modelr)
library(broom)
library(jpndistrict)


## get results of election ----
url <- "http://www.soumu.go.jp/main_content/000636675.xls"
if(!exists("tf")){
  GET(url, write_disk(tf <- tempfile(fileext = ".xls")))
}

prefs <- read_excel(tf, sheet = "5110", range = "A9:A55", col_names = "pref", col_types = "text")

ranges <- c(
  jimin = "B9:E55",
  rikken = "F9:I55",
  kokumin = "J9:M55",
  koumei = "N9:Q55",
  ishin = "B63:E109",
  kyosan = "F63:I109",
  shamin = "J63:M109",
  reiwa = "N63:Q109",
  nkoku = "B117:E163",
  olive = "F117:I163",
  kofuku = "J117:M163",
  rodosha = "N117:Q163",
  anraku = "B171:E217"
  )
vote_by_pref <- map(
                    ranges,
                    read_excel,
                    path = tf,
                    sheet = "5110",
                    col_names = c("vote_total", "vote_rel", "vote_party", "vote_identity"),
                    col_types = rep("text", 4)
                  ) %>%
  map(mutate_all, parse_number) %>%
  map(~bind_cols(., prefs)) %>%
  imap(~mutate(.x, party = .y)) %>%
  bind_rows %>%
  mutate(pname = recode(party,
                        jimin = "自由民主党", rikken = "立憲民主党", kokumin = "国民民主党", koumei = "公明党", ishin = "日本維新の会",
                        kyosan = "共産党", shamin = "社民党", reiwa = "れいわ新選組", nkoku = "N国党",
                        .default = "その他")
  )



# get population ----
appId <- mycode
datId <- "0003312321"

#meta <- estat_getMetaInfo(appId, datId)

if(!exists("pop_raw")){
pop_raw <- estat_getStatsData(
                          appId,
                          datId,
                          cdCat01 = c("001", "002"),
                          cdCat02 = paste0("0", c(1001:1017, 4018)),
                          cdCat03 = "002",
                          lvArea = 2) %>%
           transmute(pref = `全国・都道府県`, age = 年齢5歳階級, sex = 男女別, value = value * 1000)
}

voters <- pop_raw %>%
  filter(!(age %in% c("0~4歳", "5~9歳", "10~14歳", "15~19歳")))


# analysis ----
vote_pop <- vote_by_pref %>%
  left_join(voters %>%
            group_by(pref) %>%
            summarise(voter_pop = sum(value)),
          by = "pref"
          ) %>%
  mutate(
    logvote = log10(vote_total),
    logpop = log10(voter_pop)
  )

## 図1
gp_dist_vote <- vote_pop %>%
  filter(party %in% c("jimin", "nkoku")) %>%
  mutate(pref = str_replace(pref, "(県|府|都)$", "")) %>%
  ggplot(aes(voter_pop, vote_total)) +
    geom_text(aes(label = pref, color = pname), size = 5, alpha = 0.8) +
    geom_smooth(method = "lm", aes(color = pname), se = FALSE, alpha = 0.5) +
    scale_x_log10(breaks = c(1, 3, 10) * 10^6, labels = c("100万", "300万", "1000万")) +
    scale_y_log10(breaks = c(1, 10, 100)*10^4, labels = c("1万", "10万", "100万")) +
    theme(
      axis.text = element_text(size = 12),
      axis.title = element_text(size = 14),
      legend.text = element_text(size = 10),
      legend.position = c(1, 0),
      legend.justification = c(1,0)
    ) +
    labs(
      x = "有権者数(人)(対数軸)",
      y = "得票数(人)(対数軸)",
      color = "",
      title = "両対数軸で見ると有権者数と得票数は直線の関係になる。N国党の方が自民党よりも傾きが大きい。",
      caption = "令和元年7月21日参院選選挙結果, 総務省 都道府県別人口推計 平成30年10月1日"
    )


vote_models <- vote_pop %>%
  nest(-party, -pname) %>%
  mutate(model = map(data, ~lm(data = ., logvote ~ logpop)))

## 図2
gp_r2 <- vote_models %>%
  mutate(rsq = map_dbl(model, . %>% summary %>% .[["r.squared"]])) %>%
  filter(pname != "その他") %>%
  ggplot(aes(pname, rsq)) +
    geom_point(size = 10, shape = 18) +
    theme(axis.text = element_text(size = 12)) +
    labs(
      x = "",
      y = "R-square",
      title = "R^2は十分高く我々のモデルは正しいことが期待できる。社民党以外では"
    )

## 図3
gp_dist_voterel <- vote_pop %>%
  filter(party %in% c("jimin", "nkoku"))%>%
  mutate(pref = str_replace(pref, "(県|府|都)$", ""))%>%
  ggplot(aes(voter_pop, vote_total/voter_pop)) +
    geom_text(aes(label = pref, color = pname), size = 5, alpha = 0.8) +
    geom_smooth(method = "lm", aes(color = pname), se = FALSE, alpha = 0.5) +
    scale_x_log10(breaks = c(1, 3, 10) * 10^6, labels = c("100万", "300万", "1000万")) +
    scale_y_log10() +
    theme(
      axis.text = element_text(size = 12),
      axis.title = element_text(size = 14),
      legend.text = element_text(size = 10),
      legend.position = c(1, 0),
      legend.justification = c(1,0)
    ) +
    labs(
      x = "有権者数(人)(対数軸)",
      y = "潜在得票率(対数軸)",
      color = "",
      title = "傾きが右肩上がりならば都会で強い、右肩下がりならば田舎で強い。",
      caption = "令和元年7月21日参院選選挙結果, 総務省 都道府県別人口推計 平成30年10月1日"
    )

## 図4
gp_position <- vote_models  %>%
  mutate(coef = map(model, tidy)) %>%
  unnest(coef) %>%
  select(party, pname, term, estimate) %>%
  spread(term, estimate)%>%
  filter(pname != "その他", party != "shamin") %>%
  ggplot(aes(`(Intercept)`, logpop - 1)) +
    geom_text(aes(label = pname)) +
    geom_hline(yintercept = 0, alpha = 0.3, size = 2) +
    theme(
      axis.text = element_text(size = 12),
      axis.title = element_text(size = 12)
    ) +
    labs(
      x = "基本勢力b",
      y = "都会力a-1",
      caption = "社民党はモデルが適合していないので除外した。"
      )


no_yoritu <- c("富山県", "石川県", "奈良県", "和歌山県", "山口県", "佐賀県", "宮崎県", "鹿児島県")

vote_models_nkoku <- vote_models %>%
  filter(party == "nkoku") %>%
  mutate(data = map2(data, model, add_predictions)) %>%
  mutate(data = map2(data, model, add_residuals)) %>%
  unnest(data) %>%
  mutate(yoritu = if_else(pref %in% no_yoritu, FALSE, TRUE))

## 図5
gp_model_resid_nkoku <- vote_models_nkoku %>%
  mutate(pref = str_replace(pref, "(県|府|都)$", "")) %>%
  ggplot(aes(logpop, logvote)) +
    geom_text(aes(label = pref, color = yoritu), size = 5, alpha = 0.8) +
    geom_smooth(method = "lm", se = FALSE, color = "gray40", alpha = 0.5) +
    geom_linerange(aes(ymin = logvote, ymax = pred)) +
    scale_x_continuous(breaks = log10(c(1, 3, 10) * 10^6), labels = c("100万", "300万", "1000万")) +
    scale_y_continuous(breaks = log10(c(1, 3, 10) * 10^4), labels = c("1万", "3万", "10万")) +
    theme(
      axis.text = element_text(size = 12),
      axis.title = element_text(size = 12)
    ) +
    labs(
      x = "有権者数(対数)",
      y = "N国党得票数(対数)",
      color = "候補者擁立"
    )



## model residual analysis

## 図6
gp_resid_order_nkoku <- vote_models_nkoku %>%
  mutate(pref = str_replace(pref, "(県|府|都)$", "") %>% reorder(resid)) %>%
  ggplot(aes(pref, resid)) +
    geom_point(aes(color = resid > 0), size = 2) +
    geom_hline(yintercept = 0, size = 2, alpha = 0.3) +
    theme(
      axis.text.x = element_text(size = 12, angle = 90, vjust = 0.5),
      legend.position = "none"
    ) +
    labs(
      x = "",
      y = "N国党の人気度"
    )


## plot on maps

### get map data
if(!exists("pref_maps")){
  pref_maps <- map(1:47, jpn_pref, district = FALSE)
}

## 図7
gp_nkoku_maps <- ggplot() +
  map(pref_maps, ~geom_sf(data = ., aes(fill = filter(vote_resids, pref == .$prefecture, party == "nkoku")$resid))) +
  theme(axis.text = element_blank()) +
  labs(fill = "strong<->weak")

コメントを残す