裏 RjpWiki

Julia ときどき R, Python によるコンピュータプログラム,コンピュータ・サイエンス,統計学

e-Stat から入手したデータの利用にあたり,やるべき事は単純だ

2020年01月08日 | ブログラミング

西原史暁 (Fumiaki Nishihara)氏のページ,

「Rによるデータクリーニング実践――政府統計からのグラフ作成を例として」

西原氏の作業手順は以下の通り。

1. 整然データを作るという前提
2. すべての年のファイルに以下の処理を行う
    注釈的要素の除去
    空行や空列の除去
    整然データを作るために転置する
    転置すると行列になり扱いにくいのでデータフレームに戻す
    変数の名前を付ける
    「公     立」のような余計な空白の削除
    結合が解除されてできる NA を、そのセルの上にある内容で穴埋めする
    合計を示すデータの削除
    データは文字列として扱われているので整数に変換
    元のデータに含まれない調査年を示す列の不可
3. 結果を一つのデータフレームにする
 
ずいぶんと処理内容が多い。西原氏自身が「この記事は非常に長いものになっている。この長さは、データクリーニングの繁雑さに比例したものである。つまり、データクリーニングが容易ではなく、うんざりするほどのものであることを反映している。自らの手でデータを扱わない人は、この分量を見てデータクリーニングの大変さを感じていただければと思う。」というほどだ。
 
しかし,手順を逆にすれば,話は比較的簡単になる。

西原氏は,余計なものを除くこと,余計なものでなくすることを目標にしている。しかし,官製 Excel データファイルは,余計なものばかりなのだ。

これに対して,必要なもの(データ)は全体から見ればほんのわずかだ。

やるべき事は各ファイルを読み込み,必要なデータだけを取りだし,一つのデータフレームを作る。
これだけで,データ分析(例えば図を描く)ができるようになる。
整然データにしたければ,そのデータフレームを変換すればよい(多くの場合はそれさえも不要)。

まあ,西原氏の書いたとおり,一定の書式で作られたデータではないが,それでもいくつかの書式に準拠しているのでその規則を利用して,必要なデータを取り出そう。

1986 年のデータ(csv ファイル)は以下のようになっている。

"60 男女別学校数","...2","...3","...4","...5","...6","...7","...8","...9","...10","...11"
NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA
NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA
"区分","計",NA,NA,"国立","公立",NA,NA,"私立",NA,NA
NA,"計","本校","分校","本校","計","本校","分校","計","本校","分校"
"計","5491","5295","196","17","4178","3990","188","1296","1288","8"
"男女ともにいる学校","4362","4188","174","15","3865","3695","170","482","478","4"
"男のみの学校","400","393","7","1","124","120","4","275","272","3"
"女のみの学校","708","693","15","1","189","175","14","518","517","1"
"生徒のいない学校","21","21","0","0","0","0","0","21","21","0"
"この表は,男子校あるいは女子校という分類ではなく,現実に在学している生徒の状況により分類して集計した。",NA,NA,NA,NA,NA,NA,NA,NA,NA,NA

西原氏がデータ分析の一例として挙げた図を描くには,上の赤字で示した2つの数値(400, 708)があれば十分だ(それ以外の場所の数値も同時に取り出すことは容易だ)。

ファイルを読み取り,"男のみの学校"で始まる行とその次の行の,カンマで区切られた2番目の文字列を整数に変換する。これだけでできるかどうかやってみる。

get.value = function(txt) { # 必要なデータを取り出す関数
  a = unlist(strsplit(txt, ",")) # カンマで区切られたデータをバラす
  return(as.integer(gsub('"', '', a[2]))) # 2番目のデータを整数にして返す
}

dat = matrix(0, 31, 2)
for (year in 1986:2016) {
  file.name = paste0(year, ".csv")
  text = readLines(file.name)
  for (i in 1:length(text)) {
    txt = text[i]
    if (grepl("男のみの学校", txt)) { # "男のみの学校"を含む行なら
      a = get.value(text[i]) # その行からデータを取り出す
      b = get.value(text[i+1]) # 次の行からデータを取り出す
      cat(file.name, a, b, "\n")
      dat[year-1985, ] = c(a, b)
      break # 次のファイルの処理へ
    }
  }
}

2007 年までのデータはこれで取り出せた。2008 年からは必要なデータの位置が少し違うので,
データを取り出す関数を,以下のようにする。

get.value = function(txt) { # 必要なデータを取り出す関数
  a = unlist(strsplit(txt, ",")) # カンマで区切られたデータをバラす
  if (a[1] != "NA") { # 最初のデータが NA でなければ,2番目のデータを整数にする
    return(as.integer(gsub('"', '', a[2])))
  } else if (a[1] == "NA") { # 2008 年以降の場合は最初のデータが NA で,必要なデータは4番目にある
    return(as.integer(gsub('"', '', a[4])))
  }
}

私もビックリするくらい,実に簡単な規則にそってデータを取り出すことができた。

後はデータフレームにして,列に名前を付ける。必要ならこれを整然データにすれば良いが,そんなことはどうでもよい。

df = data.frame(dat)
colnames(df) = c("male", "female")
rownames(df) = 1986:2016
print(df)

     male female
1986  400    708
1987  384    696
1988  366    690
1989  352    689
  :
2014  125    320
2015  117    314
2016  112    311
 
西原氏と同じ図を描いてみよう。ただし,ggplot ではなく,graphics::matplot() で

old = par(mar=c(4, 4, 1.5, 1), mgp=c(2.5, 0.8, 0), las=1, tck=-0.01)
matplot(df, type="l", ylim=c(0, 700),
  xaxt = "n",
  xlab = "年", ylab = "学校数",
  main="日本における男子のみ・女子のみの高校(通信制除く)の数")
text(25, c(450, 200), c("男子のみ", "女子のみ"))
axis(1, at=1:31, labels=1986:2016)
par(old)

コメント    この記事についてブログを書く
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする
« e-Stat からデータの入手 ---... | トップ | Web ページからデータスクレ... »
最新の画像もっと見る

コメントを投稿

ブログラミング」カテゴリの最新記事