裏 RjpWiki

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

Web ページからデータスクレイピング(その2)--- 超簡単な方法

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

Web ページからデータスクレイピング」の続き。


Web ページに,作表されたデータが表示されているとき,これを簡単に取り出すことができる。

1. R, Excel を立ち上げておく。

2. 目的のページを表示する

たとえば,
成人喫煙率(JT全国喫煙者率調査)

    http://www.health-net.or.jp/tobacco/product/pd090000.html

を表示し,表の左上「年度」から表の右下「8.7」までのセルを,マウスドラッグで選択し,バッファにコピーする(command + c)。

3. Excel へペーストする。


4. 表の本体,左上の 「20歳代」 から 右下の 「8.7」 までのセルを,マウスドラッグで選択し,バッファにコピーする(command + c)。

5. R のコンソールで,

data = read.table(pipe("pbpaste", "r"))

と入力する。

> head(data)
    V1   V2   V3   V4   V5   V6
1 80.5 84.7 86.7 81.4 74.6 82.3
2  6.6 13.5 19.0 23.0 23.0 15.7
3 83.5 84.8 87.3 83.4 78.0 83.7
4 10.6 14.3 22.0 24.1 24.1 18.0
5 83.2 84.1 85.8 82.3 73.3 82.3
6 11.0 16.4 20.9 23.1 20.3 17.7
   :

のようになる。

6. データは男女が交互になっているので,まず奇数行 data[1:54*2-1, ],次に偶数行 data[1:54*2, ] を取りだしたものを cbind() する。

data = cbind(data[1:54*2-1, ], data[1:54*2, ])

> head(data)
     V1   V2   V3   V4   V5   V6   V1   V2   V3   V4   V5   V6
1  80.5 84.7 86.7 81.4 74.6 82.3  6.6 13.5 19.0 23.0 23.0 15.7
3  83.5 84.8 87.3 83.4 78.0 83.7 10.6 14.3 22.0 24.1 24.1 18.0
5  83.2 84.1 85.8 82.3 73.3 82.3 11.0 16.4 20.9 23.1 20.3 17.7
7  78.0 79.3 82.5 81.3 70.8 78.5  8.1 13.6 17.8 21.1 20.4 15.4
9  78.5 80.6 83.7 80.3 71.1 79.1  9.9 13.1 16.8 20.7 19.8 15.4
11 79.9 78.4 81.0 78.3 67.8 77.5  9.8 13.0 16.1 23.3 20.0 15.6
   :

のようになる。

7. あとは,西暦年を列に付加するとか,変数名を付けるとか,どうしてもやりたければ整然データにするとか,どうにでも。

 

もう一つ例を挙げておく。

Wikipedia にある,日本の自殺」の中の「自殺者数および人口10万人中の自殺率の推移」

これを上に書いたように範囲を指定してコピーし Excel などにペーストすると,数値のはいっているセルがだんだん右の方にズレている。

Excel ではなく,普通のエディタにペーストすればよい。数値がカンマで 3 桁区切りになっているので,全文置換でカンマを削除する。また,戦中のデータがないときの "" を NA に変換し,ヘッダー行を整えた後に,データ部分をバッファにコピーし,R で data = read.table(pipe("pbpaste", "r")) する。

ここでの教訓は,なんでもかんでもプログラム(特に tidyverse ?? なんか)に頼らないこと。得手不得手があるので,単純なエディタで十分な作業もあるんだということ。



コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

Web ページからデータスクレイピング

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

あるところで,代金 200 円也でデータスクレイピングしたデータファイルが売られている。

成人喫煙率(JT全国喫煙者率調査)
http://www.health-net.or.jp/tobacco/product/pd090000.html

つまり,以下は少なくとも 200 円の価値のある作業だということか(笑)。

上に示した URL を開き,ページを「別名で保存...」する。

次に以下の R プログラムで処理する。

# 対象ファイル
file.name = "最新たばこ情報|統計情報|成人喫煙率(JT全国喫煙者率調査).html"
# 保存された html ファイルを読み込む(エンコーディングに注意)
con = file(file.name, encoding = "cp932")
text = readLines(con) # 入力
data = character(858 - 47) # データ保管用のベクトル
count = 0 # 数値データの存在行のカウント
for (txt in text) { # 各行について
    txt = gsub("<.*?>", "", txt) # html タグを除去する
    if (nchar(txt) > 0) { # 本文もしくは数値(文字)など
        count = count + 1
        if (count >= 47 && count <= 857) { # この範囲なら目的のデータ
            data[count - 46] = txt # 保存
        }
    }
}
data = data[data != "平成元年"] # 不要な要素を除去する
data = as.data.frame(matrix(data,
    ncol = 15, byrow = TRUE), stringsAsFactors = FALSE) # データフレームに変換する
data = data[-c(2, 9)] # 2 列目と 9 列目は性別データなので消去
colnames(data) = c("year", # 列名(変数名)を付ける
    "male20-29", "male30-39", "male40-49", "male50-59", "male60-69", "male all",
    "female20-29", "female30-39", "female40-49", "female50-59", "female60-69", "female all")
data$year = 1965:2018 # 1 列目を西暦年にする
data = sapply(data, as.numeric) # 文字列を数値に変換する

これで,以下のようなデータフレームができあがる。

> head(data)
     year male20-29 male30-39 male40-49 male50-59 male60-69 male all
[1,] 1965      80.5      84.7      86.7      81.4      74.6     82.3
[2,] 1966      83.5      84.8      87.3      83.4      78.0     83.7
[3,] 1967      83.2      84.1      85.8      82.3      73.3     82.3
[4,] 1968      78.0      79.3      82.5      81.3      70.8     78.5
[5,] 1969      78.5      80.6      83.7      80.3      71.1     79.1
[6,] 1970      79.9      78.4      81.0      78.3      67.8     77.5

     female20-29 female30-39 female40-49 female50-59 female60-69 female all
[1,]         6.6        13.5        19.0        23.0        23.0       15.7
[2,]        10.6        14.3        22.0        24.1        24.1       18.0
[3,]        11.0        16.4        20.9        23.1        20.3       17.7
[4,]         8.1        13.6        17.8        21.1        20.4       15.4
[5,]         9.9        13.1        16.8        20.7        19.8       15.4
[6,]         9.8        13.0        16.1        23.3        20.0       15.6

長いなあ。面倒だなぁ。とお思いの方,次の記事で超簡単なデータスクレイピングをお見せします。

それはさておき,早速 graphics::matplot() で図を描いてみる。

old = par(mar = c(3.5, 3, 1.5, 5), mgp = c(1.5, 0.4, 0), tck = -0.01,
    bty = "n", las = 1)
colors = c("black", "red", "blue", "brown", "mediumseagreen", "magenta")
matplot(data[, -1], type = "l", lwd = 2, xaxt = "n", ylim = c(0, 100),
    lty = 1, col = colors,
    xlab = "年", ylab = "喫煙率", main = "性別・年代別喫煙率の推移")
mtext("JT全国喫煙者率調査", xpd=TRUE, side = 3, line = -0.8, cex=0.8)
axis(1, at = 1:nrow(data), label = data[, 1], pos = 0)
delta = c(0, -1, 0, 0, 0, 0,  -0.1, -0.1, 0.8, 0.5, -0.7, 0)*strheight("H")
text(55, data[54, 2:13]+delta, colnames(data)[2:13], col = colors, pos = 4,
    cex = 0.7, xpd = TRUE)
par(old)

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

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でシェアする

PVアクセスランキング にほんブログ村

PVアクセスランキング にほんブログ村