この記事のコードをまとめたものはGithubにあります。
モチベーション
恩師 HP のこのページについての自分用メモです。
より詳しく何をしているか補足を入れ、コードを簡潔にするように心がけた。
正直、データクリーニングってアイディア勝負なところあるよね。
使用するデータ
Freedom House の HP
Freedom House のオープンソース(Excel)
今回は Freedom House から提供されている「Country and Territory Ratings and Statuses」
(筆者アクセス日 : 2021/1/15)を使用する。
使用するパッケージ
library(tidyverse) # RStudio を使うなら一番最初に読み込むパッケージ
library(magrittr) # パイプ処理用のパッケージ
library(readxl) # 今回使用するファイル形式が Excel のため、このパッケージを使用する
データの取り込み
今回は Excel ファイルをダウンロードしてきて、そのまま使用する。
ちなみに、余裕があるなら用事のあるシートだけを CSV ファイルに変換したほうが良いかも...
sheet = 2
: 使用するシートは 2 つ目と指定する。
skip = 2
: 上から 2 列目までを無視する。
na = "-"
: "-" を na に置き換える。
FH <- read_excel("Data/2020_Country_and_Territory_Ratings_and_Statuses_FIW1973-2020.xlsx", sheet = 2, skip = 2, na = "-")
データクリーニング
ここからデータの中身を確認して、データを整理していく。
各変数の class を確認&修正
...1 : character
PR...n : numeric
CL...n : numeric
Status...n : character
であればOK
str()
で各変数の class を確認してみる。
str(FH)
## tibble [205 x 142] (S3: tbl_df/tbl/data.frame)
## $ ...1 : chr [1:205] "Afghanistan" "Albania" "Algeria" "Andorra" ...
## $ PR...2 : chr [1:205] "4" "7" "6" "4" ...
## $ CL...3 : chr [1:205] "5" "7" "6" "3" ...
## $ Status...4 : chr [1:205] "PF" "NF" "NF" "PF" ...
## $ PR...5 : num [1:205] 7 7 6 4 NA NA 2 NA 1 1 ...
## $ CL...6 : num [1:205] 6 7 6 4 NA NA 2 NA 1 1 ...
## $ Status...7 : chr [1:205] "NF" "NF" "NF" "PF" ...
## $ PR...8 : num [1:205] 7 7 6 4 NA NA 2 NA 1 1 ...
## $ CL...9 : num [1:205] 6 7 6 4 NA NA 4 NA 1 1 ...
## $ Status...10 : chr [1:205] "NF" "NF" "NF" "PF" ...
## $ PR...11 : num [1:205] 7 7 7 4 6 NA 2 NA 1 1 ...
## $ CL...12 : num [1:205] 6 7 6 4 6 NA 4 NA 1 1 ...
## $ Status...13 : chr [1:205] "NF" "NF" "NF" "PF" ...
## $ PR...14 : num [1:205] 7 7 6 4 6 NA 6 NA 1 1 ...
## $ CL...15 : num [1:205] 6 7 6 4 6 NA 5 NA 1 1 ...
## $ Status...16 : chr [1:205] "NF" "NF" "NF" "PF" ...
## $ PR...17 : num [1:205] 6 7 6 NA 7 NA 6 NA 1 1 ...
## $ CL...18 : num [1:205] 6 7 6 NA 7 NA 6 NA 1 1 ...
## $ Status...19 : chr [1:205] "NF" "NF" "NF" NA ...
## $ PR...20 : num [1:205] 7 7 6 NA 7 NA 6 NA 1 1 ...
## $ CL...21 : num [1:205] 7 7 6 NA 7 NA 5 NA 1 1 ...
## $ Status...22 : chr [1:205] "NF" "NF" "NF" NA ...
## $ PR...23 : num [1:205] 7 7 6 NA 7 NA 6 NA 1 1 ...
## $ CL...24 : num [1:205] 7 7 6 NA 7 NA 5 NA 1 1 ...
## $ Status...25 : chr [1:205] "NF" "NF" "NF" NA ...
## $ PR...26 : num [1:205] 7 7 6 NA 7 NA 6 NA 1 1 ...
## $ CL...27 : num [1:205] 7 7 6 NA 7 NA 5 NA 1 1 ...
## $ Status...28 : chr [1:205] "NF" "NF" "NF" NA ...
## $ PR...29 : num [1:205] 7 7 6 NA 7 2 6 NA 1 1 ...
## $ CL...30 : num [1:205] 7 7 6 NA 7 2 5 NA 1 1 ...
## $ Status...31 : chr [1:205] "NF" "NF" "NF" NA ...
## $ PR...32 : num [1:205] 7 7 6 NA 7 2 3 NA 1 1 ...
## $ CL...33 : num [1:205] 7 7 6 NA 7 3 3 NA 1 1 ...
## $ Status...34 : chr [1:205] "NF" "NF" "NF" NA ...
## $ PR...35 : num [1:205] 7 7 6 NA 7 2 2 NA 1 1 ...
## $ CL...36 : num [1:205] 7 7 6 NA 7 3 2 NA 1 1 ...
## $ Status...37 : chr [1:205] "NF" "NF" "NF" NA ...
## $ PR...38 : num [1:205] 7 7 6 NA 7 2 2 NA 1 1 ...
## $ CL...39 : num [1:205] 7 7 6 NA 7 3 2 NA 1 1 ...
## $ Status...40 : chr [1:205] "NF" "NF" "NF" NA ...
## $ PR...41 : num [1:205] 7 7 6 NA 7 2 2 NA 1 1 ...
## $ CL...42 : num [1:205] 7 7 6 NA 7 3 1 NA 1 1 ...
## $ Status...43 : chr [1:205] "NF" "NF" "NF" NA ...
## $ PR...44 : num [1:205] 7 7 6 NA 7 2 2 NA 1 1 ...
## $ CL...45 : num [1:205] 7 7 6 NA 7 3 1 NA 1 1 ...
## $ Status...46 : chr [1:205] "NF" "NF" "NF" NA ...
## $ PR...47 : num [1:205] 6 7 5 NA 7 2 2 NA 1 1 ...
## $ CL...48 : num [1:205] 6 7 6 NA 7 3 1 NA 1 1 ...
## $ Status...49 : chr [1:205] "NF" "NF" "NF" NA ...
## $ PR...50 : num [1:205] 7 7 6 NA 7 2 1 NA 1 1 ...
## $ CL...51 : num [1:205] 7 7 4 NA 7 3 2 NA 1 1 ...
## $ Status...52 : chr [1:205] "NF" "NF" "PF" NA ...
## $ PR...53 : num [1:205] 7 7 4 NA 7 3 1 NA 1 1 ...
## $ CL...54 : num [1:205] 7 6 4 NA 7 2 3 NA 1 1 ...
## $ Status...55 : chr [1:205] "NF" "NF" "PF" NA ...
## $ PR...56 : num [1:205] 7 4 4 NA 6 3 1 5 1 1 ...
## $ CL...57 : num [1:205] 7 4 4 NA 4 3 3 5 1 1 ...
## $ Status...58 : chr [1:205] "NF" "PF" "PF" NA ...
## $ PR...59 : num [1:205] 6 4 7 NA 6 3 2 4 1 1 ...
## $ CL...60 : num [1:205] 6 3 6 NA 6 3 3 3 1 1 ...
## $ Status...61 : chr [1:205] "NF" "PF" "NF" NA ...
## $ PR...62 : num [1:205] 7 2 7 2 7 4 2 3 1 1 ...
## $ CL...63 : num [1:205] 7 4 6 1 7 3 3 4 1 1 ...
## $ Status...64 : chr [1:205] "NF" "PF" "NF" "F" ...
## $ PR...65 : num [1:205] 7 3 7 1 7 4 2 3 1 1 ...
## $ CL...66 : num [1:205] 7 4 7 1 7 3 3 4 1 1 ...
## $ Status...67 : chr [1:205] "NF" "PF" "NF" "F" ...
## $ PR...68 : num [1:205] 7 3 6 1 6 4 2 4 1 1 ...
## $ CL...69 : num [1:205] 7 4 6 1 6 3 3 4 1 1 ...
## $ Status...70 : chr [1:205] "NF" "PF" "NF" "F" ...
## $ PR...71 : num [1:205] 7 4 6 1 6 4 2 5 1 1 ...
## $ CL...72 : num [1:205] 7 4 6 1 6 3 3 4 1 1 ...
## $ Status...73 : chr [1:205] "NF" "PF" "NF" "F" ...
## $ PR...74 : num [1:205] 7 4 6 1 6 4 2 5 1 1 ...
## $ CL...75 : num [1:205] 7 4 6 1 6 3 3 4 1 1 ...
## $ Status...76 : chr [1:205] "NF" "PF" "NF" "F" ...
## $ PR...77 : num [1:205] 7 4 6 1 6 4 3 4 1 1 ...
## $ CL...78 : num [1:205] 7 5 5 1 6 3 3 4 1 1 ...
## $ Status...79 : chr [1:205] "NF" "PF" "NF" "F" ...
## $ PR...80 : num [1:205] 7 4 6 1 6 4 2 4 1 1 ...
## $ CL...81 : num [1:205] 7 5 5 1 6 3 3 4 1 1 ...
## $ Status...82 : chr [1:205] "NF" "PF" "NF" "F" ...
## $ PR...83 : num [1:205] 7 4 6 1 6 4 1 4 1 1 ...
## $ CL...84 : num [1:205] 7 5 5 1 6 2 2 4 1 1 ...
## $ Status...85 : chr [1:205] "NF" "PF" "NF" "F" ...
## $ PR...86 : num [1:205] 7 3 6 1 6 4 3 4 1 1 ...
## $ CL...87 : num [1:205] 7 4 5 1 6 2 3 4 1 1 ...
## $ Status...88 : chr [1:205] "NF" "PF" "NF" "F" ...
## $ PR...89 : num [1:205] 6 3 6 1 6 4 3 4 1 1 ...
## $ CL...90 : num [1:205] 6 3 5 1 5 2 3 4 1 1 ...
## $ Status...91 : chr [1:205] "NF" "PF" "NF" "F" ...
## $ PR...92 : num [1:205] 6 3 6 1 6 4 2 4 1 1 ...
## $ CL...93 : num [1:205] 6 3 5 1 5 2 2 4 1 1 ...
## $ Status...94 : chr [1:205] "NF" "PF" "NF" "F" ...
## $ PR...95 : num [1:205] 5 3 6 1 6 2 2 5 1 1 ...
## $ CL...96 : num [1:205] 6 3 5 1 5 2 2 4 1 1 ...
## $ Status...97 : chr [1:205] "NF" "PF" "NF" "F" ...
## $ PR...98 : num [1:205] 5 3 6 1 6 2 2 5 1 1 ...
## $ CL...99 : num [1:205] 5 3 5 1 5 2 2 4 1 1 ...
## [list output truncated]
PR...2 と CL...3 は numeric であるはずが character になっている。
unique()
で変数の値を確認してみる。
FH$PR...2 %>% unique()
## [1] "4" "7" "6" NA "1" "2" "5" "3" "2(5)"
FH$CL...3 %>% unique()
## [1] "5" "7" "6" "3" NA "1" "4" "2" "3(6)"
どうやら "2(5)" や "3(6)" のような数値でないものが混ざっているみたいだ。
これを if_elese()
で NA に置き換える。その後、as.numeric()
で character から numeric に変数の class を変換する。
ついでに、...1 は国名なので、変数名を country に変えておく
FH %<>%
mutate(PR...2 = if_else(PR...2 == "2(5)", "NA", PR...2),
CL...3 = if_else(CL...3 == "3(6)", "NA", CL...3)) %>%
mutate(across(c(PR...2, CL...3), as.numeric)) %>% # 2 つの列に同時に as.numeric を施す
rename(country = 1) # 1 という変数名を country に変更
wide 形式を long 形式に変形する
このデータは wide と呼ばれる形式のため、tidy な R 生活を送っている人にとって非常に使いにくい。
これを long と呼ばれる形式に変形する。
wide の状態で行う処理
まずは"いじりたくない変数"と"いじりたい変数"で分割する。
FH_country <- FH %>% select(country) # いじりたくない変数
FH_value <- FH %>% select(-country) # いじりたい変数
いじりたい変数である FH_value の変数名に年数を付けていく。
pr_xxxx という感じにしたい。そのため、まずは stringr パッケージ(tidyverse に内包されている)の関数を使う。
colnames(FH_value) <- # 変数名に代入する
str_replace_all(colnames(FH_value), # 変数名の
c("\\.\\.\\." = "_")) %>% # 「...」を「_」に置き換える
str_subset("PR|CL|Status") %>% # PR, CL, Statusと名の付く変数を対象とする
str_replace_all(c("[0-9]" = "", # 数字を消す
"PR" = "pr", # 小文字に変更
"CL" = "cl", # 小文字に変更
"Status" = "st")) %>% # 小文字に変更
str_c(., rep(setdiff(1972:2019, 1981), # 変数名の後ろに1972~2019までの数字を付ける。あと、1981を飛ばす
each = 3)) # 1年につき、3つの変数があるので、3回同じ数字を付けて、次の年に行くようにする
FH_value %>% names()
## [1] "pr_1972" "cl_1972" "st_1972" "pr_1973" "cl_1973" "st_1973" "pr_1974"
## [8] "cl_1974" "st_1974" "pr_1975" "cl_1975" "st_1975" "pr_1976" "cl_1976"
## [15] "st_1976" "pr_1977" "cl_1977" "st_1977" "pr_1978" "cl_1978" "st_1978"
## [22] "pr_1979" "cl_1979" "st_1979" "pr_1980" "cl_1980" "st_1980" "pr_1982"
## [29] "cl_1982" "st_1982" "pr_1983" "cl_1983" "st_1983" "pr_1984" "cl_1984"
## [36] "st_1984" "pr_1985" "cl_1985" "st_1985" "pr_1986" "cl_1986" "st_1986"
## [43] "pr_1987" "cl_1987" "st_1987" "pr_1988" "cl_1988" "st_1988" "pr_1989"
## [50] "cl_1989" "st_1989" "pr_1990" "cl_1990" "st_1990" "pr_1991" "cl_1991"
## [57] "st_1991" "pr_1992" "cl_1992" "st_1992" "pr_1993" "cl_1993" "st_1993"
## [64] "pr_1994" "cl_1994" "st_1994" "pr_1995" "cl_1995" "st_1995" "pr_1996"
## [71] "cl_1996" "st_1996" "pr_1997" "cl_1997" "st_1997" "pr_1998" "cl_1998"
## [78] "st_1998" "pr_1999" "cl_1999" "st_1999" "pr_2000" "cl_2000" "st_2000"
## [85] "pr_2001" "cl_2001" "st_2001" "pr_2002" "cl_2002" "st_2002" "pr_2003"
## [92] "cl_2003" "st_2003" "pr_2004" "cl_2004" "st_2004" "pr_2005" "cl_2005"
## [99] "st_2005" "pr_2006" "cl_2006" "st_2006" "pr_2007" "cl_2007" "st_2007"
## [106] "pr_2008" "cl_2008" "st_2008" "pr_2009" "cl_2009" "st_2009" "pr_2010"
## [113] "cl_2010" "st_2010" "pr_2011" "cl_2011" "st_2011" "pr_2012" "cl_2012"
## [120] "st_2012" "pr_2013" "cl_2013" "st_2013" "pr_2014" "cl_2014" "st_2014"
## [127] "pr_2015" "cl_2015" "st_2015" "pr_2016" "cl_2016" "st_2016" "pr_2017"
## [134] "cl_2017" "st_2017" "pr_2018" "cl_2018" "st_2018" "pr_2019" "cl_2019"
## [141] "st_2019"
変数名を変更し終わったら、bind_cols()
で国名とがっちゃんこする。
FH_wide <- bind_cols(FH_country, FH_value)
long に変形する
ここでも、2 回に分けて wide から long に変形する。
まずは pr と cl を type という変数にまとめ、wide から long に変形させる。
PR_CL_long <- FH_wide %>%
select(country, # country と
starts_with(c("pr", "cl"))) %>% # pr と cl で初まる変数だけを選ぶ
pivot_longer(pr_1972:cl_2019, # 変換したい変数の範囲を指定
names_to = "type", # 「変数名(pr_1972 など)を type に入れる
values_to = "value") %>% # 「変数(pr_1972 など) の値」を value に入れる
separate(type,
into = c("type", "year"), # type の中身を type と year 二つの変数に分ける
sep = "_") %>% # type の中身は pr_1973 のように "_" で分けられている
drop_na() # 欠損値を省く
PR_CL_long %>% head()
## # A tibble: 6 x 4
## country type year value
## <chr> <chr> <chr> <dbl>
## 1 Afghanistan pr 1972 4
## 2 Afghanistan pr 1973 7
## 3 Afghanistan pr 1974 7
## 4 Afghanistan pr 1975 7
## 5 Afghanistan pr 1976 7
## 6 Afghanistan pr 1977 6
つぎに st を status という変数にし、wide から long に変形させる。
ST_long <- FH_wide %>%
select(country, # country と
starts_with("st")) %>% # st で初まる変数だけを選ぶ
pivot_longer(st_1972:st_2019, # 変換したい変数の範囲を指定
names_to = "name", # 「変数名(st_1972など)をnameに入れる
values_to = "status") %>% # 「変数(st_1972など) の値」をstatusに入れる
separate(name,
into = c("name", "year"), # nameの中身(st_1972など)をnameとyear 二つの変数に分ける
sep = "_") %>% # nameの中身はst_1972のように "_" で分けられている
select(-name)%>% # nameは不要なので削除
drop_na() # 欠損値を省く
ST_long %>% head()
## # A tibble: 6 x 3
## country year status
## <chr> <chr> <chr>
## 1 Afghanistan 1972 PF
## 2 Afghanistan 1973 NF
## 3 Afghanistan 1974 NF
## 4 Afghanistan 1975 NF
## 5 Afghanistan 1976 NF
## 6 Afghanistan 1977 NF
wide に変形させた 2 個のデータを left_join()
でがっちゃんこする。
そして、完成した long 形式のデータを見てみよう。
FH_long <- left_join(PR_CL_long, ST_long, by = c("country", "year"))
# by = c("country", "year") は何を目印にがっちゃんこするかを示す
rmarkdown::paged_table(FH_long) # 完成品を見てみよう
rmarkdown::paged_table()
はこんな感じで出力される。👇