2023年1月9日月曜日

Pythonで、e-Statの市区町村別の人口、面積、年齢データをCSVに変換する

本日はちょっとPython篇。なぜっていうと、Pythonしか使えないよーって人がいるから。こういうテーブルを加工する系はR(というかdplyr)のほうが断然使いやすいとは思いますが、まあバイリンガルになるのも良いでしょう。

例えば、欲しいのは市区町村別の人口や面積や平均年齢のデータ。で、e-Statに置いてあるのを発見するも(国勢調査 都道府県・市区町村別の主な結果 都道府県・市区町村別の主な結果 | ファイル | 統計データを探す | 政府統計の総合窓口)、ファイルがエクセルだわ、列名が自治体コードと都道府県名を連結した文字列だわで、なんとも使いづらい。ちゃちゃっと加工しちゃえばいいんでしょうが、全国のいろんなところで、いろんな人が同じような作業をするのが無駄なので、サンプルコードとそれで出力されたCSVファイルを置いておきます。

やっているのは、

  • "openpyxl"でエクセルファイルを読み込みデータフレームに変換
  • 必要な行や列を取り出す
  • 正規表現で列名を加工
  • CSVファイルに書き出し
あたりです。
import os
import openpyxl
import pandas as pd

os.chdir(r'(エクセルファイルを置いたパス)')

# ワークブックを開いた後、ワークシートを指定する
# 読み込んでいるのは、e-Statの国勢調査データ
wb = openpyxl.load_workbook('major_results_2020.xlsx')
ws = wb['第1面事項_2020年']

df = pd.DataFrame(ws.values)

# データが入っているのは9行目以降
# 0, 1, 4, 10, 11, 12列にはそれぞれ
# 都道府県、市区町村、人口、面積、平均年齢が入っている
df  = df.loc[9:, [0, 1, 4, 10, 12]]
dic = {0:'都道府県', 1:'市区町村', 4:'人口', 10:'面積', 12:'平均年齢'}
df  = df.rename(columns=dic) # 列名をつける

# アンダースコアの前の部分が、
# 全国地方公共団体コード(5桁)らしいので、
# 正規表現で、任意の文字「.」の1文字以上の繰り返し「+」を抽出する。
# 括弧の中が抽出される
df['コード'] = df['市区町村'].str.extract('(.+)_') 

# 扱いやすいように、コードの数値部分は削除して、
# 都道府県名、市区町村名は列名は文字列のみにしておく
df['都道府県'] = df['都道府県'].str.extract('_(.+)')
df['市区町村'] = df['市区町村'].str.extract('_(.+)')

# 列の順番の変更
df = df[['都道府県', '市区町村', '人口', '面積', '平均年齢']]

# CSVファイルへ出力
df.to_csv('市区町村別の人口と面積と平均年齢.csv', index = False)

上記のコードで出力されたCSVがこれになります。

"市区町村別の人口と面積と平均年齢.csv"





2018年2月9日金曜日

Rのデータフレームで、列名指定で列名の一部を変更する方法

まずは、動作確認用のサンプルコードです。

# サンプルコードのデータフレーム
age    <- c( 20,  30,  40)
height <- c(170, 168, 175)
wait   <- c( 67,  64,  70)
df <- data.frame(age, height, wait)


df

  age height wait
1  20    170   67
2  30    168   64
3  40    175   70


ちょ、待てよ。体重のweightがwaitになってんじゃん。

ってことで、この列名だけ変更したいと。

列名全体のベクトルをまとめて指定して変更する方法↓

names(df) <- c("age", "height", "weight")


とか、

インデックス番号を使って、一部を変更する方法↓

names(df)[3] <- "weight"

とかがあるんですが、列数がめちゃめちゃ多いデータだと数えるのが大変だし、列の削除や挿入に対してロバストでないし。できれば、列名の指定で変更を行いたい。

ちょっとコードがごちゃごちゃしますが、↓こんな感じでできます。

names(df)[ which( names(df)=="wait" ) ] <- "weight"


内側から順に見ていくと・・・

names(df)=="wait" で、列名が "wait" になっている位置が

FALSE FALSE TRUE

というベクトルで返ってきます。

それを which() に渡すと、「3」というインデックス番号が返ってきます。

なのでこの「3」を names(df)[] に対するインデックスの指定に使えばOKというわけです。




2017年9月14日木曜日

reorderを使ってggplotの棒グラフの並び順を降順にする方法

# サンプルデータの作成
fruits <- c("apple","durian","orange")
count  <- c(2, 1, 3)
df <- data.frame(fruits, count)

df # 中身の確認


  fruits count
1  apple     2
2 durian     1
3 orange     3


このデータを使って、ggplotで棒グラフを描いてみると、

library(ggplot2)
ggplot(df, aes(x=fruits, y=count))

  + geom_bar(stat="identity")

並び順はデータのまま

当然ながら、棒の順番はデータ通りに、2、1、3と並びますね。

これをソートしたい場合は、reorderを使います↓

ggplot(df, aes(x=reorder(fruits, count), y=count)) +
  geom_bar(stat="identity")

reorderすると昇順に並ぶ

x軸はfruitsなんだけど、reorder関数の第二引数であるcountの値で並べ替えてから使ってね、という指定です。

で、次の課題です。

reorderを使うと、昇順で1、2、3という並びになりましたが、これを降順の3、2,1という並びにしたいときは、どうやればいいか。

データフレームをソートするときなんかに使うorder関数でいうところの「decreasing=TRUE」みたいな指定があればいいのですが、reorder関数にはそのようなオプションはなさそうです。

で、実はごく簡単で、reorderの第二引数(count)の値の正負が逆になれば、順番も逆になるじゃんという理屈です。

ggplot(df, aes(x=reorder(fruits, -count), y=count)) +
  geom_bar(stat="identity")

reorder関数の第二引数にマイナスをつければ降順にできる

無事、降順に並んでくれました。




Rのデータフレームから少数の行を削除する

例えば、↓こんなデータがあったとして、

# サンプルデータの作成
name  <- c("Anne", "Bob", "Carl", "Dann", "Eric", "Fred")
fruits <- c("orange", "apple", "orange", "durian", "orange", "apple")
df <- data.frame(name, fruits)

df # 中身を見てみる


  name fruits
1 Anne orange
2  Bob  apple
3 Carl orange
4 Dann durian
5 Eric orange
6 Fred  apple


みんなの好きな果物のデータだとして、1人しかいないような少数派の行は除去したいと。この例だと、ダンのドリアンを取り除きたいと。

ddplyを使って度数をカウントし、新たに度数(count)の列として追加(transform)。

library(plyr)
ddply(df, "fruits", transform, count=length(fruits))


  name fruits count
1  Bob  apple     2
2 Fred  apple     2
3 Dann durian     1
4 Anne orange     3
5 Carl orange     3
6 Eric orange     3


で、このcount列を条件として、データフレームをフィルタすればいいかなと。

dd <- ddply(df, "fruits", transform, count=length(fruits))
dd[ dd$count > 1, ] # 少数派の行を削除


  name fruits count
1  Bob  apple     2
2 Fred  apple     2
4 Anne orange     3
5 Carl orange     3
6 Eric orange     3


count列が邪魔だったら、後から削除しよう。

dd2 <- dd[ dd$count > 1, ]      # 少数派の行を削除
dd2[, colnames(dd2) != "count"] # countの列を削除


  name fruits
1  Bob  apple
2 Fred  apple
4 Anne orange
5 Carl orange
6 Eric orange

一応、できました。

でも、もっといいやり方があるような気がします・・・




2017年4月13日木曜日

table関数の出力結果をmatrixに変換して、corresp関数で対応分析を行う(R言語)

 ↓この本に載っていた例で、

Rによるデータサイエンス データ解析の基礎から最新手法まで
Rによるデータサイエンス データ解析の基礎から最新手法まで

library(MASS)
caith

       fair red medium dark black
blue    326  38    241  110     3
light   688 116    584  188     4
medium  343  84    909  412    26
dark     98  48    403  681    85

↑このようなデータ(縦に並んでいるのが目の色、横に並んでいるのが髪の色)に対して、↓こんな感じで対応分析を行う、という例が載っていました。

caith.ca <- corresp(caith, nf=4)
biplot(caith.ca)

Rでのcaithデータの対応分析結果

と、ここまでが前置き。

今、手元にあるのが↓こんなデータだった、としましょう。

gender <- c("M","M","M","M","F","F","F","F","F","F")
blood  <- c("A","B","B","O","A","B","A","O","A","AB")

gender.blood <- data.frame(gender, blood)

gender.blood

   gender blood
1       M     A
2       M     B
3       M     B
4       M     O
5       F     A
6       F     B
7       F     A
8       F     O
9       F     A
10      F    AB

このデータに対して対応分析を行いたい、としましょう。

先ほどのデータと違うのは、まだ集計されていないデータであるということ。correspの入力はクロス集計して分割表になったものでしたので、その形式にしてやらなければなりません。

こういうときにはtable関数ですね

gender.blood.tbl <- table(gender.blood)
gender.blood.tbl

      blood
gender A AB B O
     F 3  1 1 1
     M 1  0 2 1

うん、集計されました。でも、これをそのまま corresp(gender.blood.tbl[,]) とやっちゃうと、「invalid table specification」というエラーが出ちゃうんですよね。どうやら、table型は受け付けてくれないようです。

そういうときは、↓こんな風に書いてやると、テーブルがmatrixになるみたいで、corresp関数が入力を受け付けてくれます。

gender.blood.tbl.ca <- corresp(gender.blood.tbl[,], nf=2)
biplot(gender.blood.tbl.ca)

データはデタラメなので、表示結果についてはスルーしてください。




2017年3月29日水曜日

Rで、データフレームの中身を一括で因子型に変換する方法

例えば、Rで↓こんなデータを扱っているとします。

# サンプルデータを作る
Q1 <- c(1, 1, 2, 2)
Q2 <- c(1, 2, 1, 2)
Q3 <- c(1, 2, 3, 1)
df <- data.frame(Q1, Q2 ,Q3)

# 中身の確認
df

  Q1 Q2 Q3
1  1  1  1
2  1  2  2
3  2  1  3
4  2  2  1

読み込んだときの都合か何かで、データはinteger型とかnumeric型になっていると。

でも、実は質問 Q1、Q2、Q3に、1:はい、2:いいえ、3:どちらともいえない、とかで答えたもので、因子型として扱いたい。

多重対応分析のmcaとかを使おうとすると、

mca(df = df) でエラー: all variables must be factors

が出ちゃうとか、そんなシチュエーション。

因子型に変換したいときには、as.factor関数ですが、これはデータフレームに対しては使えない。

こんな時は、あの一家。そう、applyファミリーの登場です。

lapplyを使って、1列ごとにas.factor関数を適用、リストとして返ってきたものを、またデータフレームに戻してやるという流れです。

df.fctr <- data.frame( lapply(df, as.factor) )

# 型の確認
df.fctr$Q1 

[1] 1 1 2 2
Levels: 1 2

無事、因子型になりました。




2017年2月15日水曜日

Rでヒストグラムの一部に色をつける(colオプション指定で可)

「R ヒストグラム 一部 色をつける」で検索してみると、hist関数でヒストグラムを描いた後に、polygon関数で色をつける、なんて方法がヒットしました。

polygon使えばなんでもできそうだけど、なんか、ちょっと違うよなあ、とか思ってしまいまして。

で、実はhist関数のcolオプションでも、できるんですよね。

colオプションに1つの値(スカラー)を指定すると、全体が一色で塗りつぶされてしまいますが、ここにベクトルを指定すると、それぞれの棒の色を指定することができます。

例えば、ヒストグラムに10個のビンがあって、それぞれを任意の色で塗りたい場合は、10個の要素を持つベクトルをcolオプションに指定すればOKです。

set.seed(0)      # 再現性のために
rd <- rnorm(100) # 100個の乱数
cols <- c("white", "white", "red"  , "white", "white",
          "blue" , "white", "white", "white", "white")
hist(rd, col=cols)

ヒストグラムの一部に色をつける

色を塗りたくない場合は(パワポなどの「塗りつぶしなし」みたいな感じ)、色名の代わりにNAを指定すればいいです。

cols <- c(NA    , NA, "red", NA, NA,
          "blue", NA, NA   , NA, NA)
hist(rd, col=cols)

最初の例と全く同じ見た目になると思いますが、add=T指定で重ねたときなんかに差がでますね。

階級がいっぱいあって、いちいち全部書き出すのが面倒なときは、下記のような感じで、塗りたいところだけを指定すればいいですね。

cols <- rep("white", 100) # ”white”を詰めた、長めのベクトルを作っておく
cols[3] <- "red"
cols[6] <- "blue"
hist(rd, col=cols)

下記のようにすれば、最頻値に対応する棒を赤く塗る、なんてこともできます。

最初のhistは絵を描くためではなく、階級がどのように分けられるか、それぞれの度数がいくつか、なんて情報を得るためにやっています。

h <- hist(rd)                           # 度数分布に関する情報を得るために
cols <- rep("white", length(h$counts))  # 今度はビンの個数が分かるぞ
cols[which.max(h$counts)] <- "red"      # 最頻値のビンの色を赤にする
hist(rd, col=cols)

ヒストグラムの最頻値に色をつける

あと、線の色を変えたい場合は、borderオプションですね。

やってみると・・・

cols <- rep("black", length(h$counts)) # 枠線のデフォルトはblackなので
cols[which.max(h$counts)] <- "red"     # 最頻値の枠線を赤にする
hist(rd, border=cols)

border指定は右側の線で上書きされてしまう・・・

あー、重なった線の部分が、右側の黒で上書きされてしまった。

こういうときは・・・、polygon関数でも使ってください