[Work/Class/Rの基礎とデータ処理/r_basic]

Rのdata.frame用の高階関数apply

高階関数

高階関数とは,関数を引数にとる関数のことである.

data.frameを処理するときに,ループをぶん回すのを一般的に考えがちだが,Rの場合は要素に適用する関数を取る高階関数で回すことが多い.

apply関数

行や列単位でループさせた結果をまとめて記述できるのがapply関数である.

まず,前項と同じく,八王子市の人口データをdata.frame形式で読み込んでおく.

# 国や自治体の機関が公開しているオープンデータは基本的にExcelからの書き出しであり
# 文字コードがSHIFT_JISなので,
# fileEncodingオプションに'Shift_JIS'を与えてやる
df <- read.csv('http://www.city.hachioji.tokyo.jp/contents/open/002/p005866_d/fil/nenreibetsu_jinkou_2506.csv', fileEncoding='Shift_JIS')
# data.frame形式で出力される
head(df)
読み込んだだけのCSVからできたDataFrameの冒頭部

apply関数は,data.frame形式の行方向や列方向に対してなんらかの処理を行う関数.行か列のベクトルが入り,処理をしたベクトルを返す.

使い方は,apply(dataframe, 方向, 処理する内容を記述した関数オブジェクト)

方向は1なら行ごとのベクトルが入り,2なら列ごとベクトルが入って回る.

# まず処理する関数オブジェクトを作っておく
line.loop.func <- function(v){
    # ベクトルが引数になる
    # 行ごと,つまり年齢ごとに男性の人口 - 女性の人口を返す関数を作ると,こうなる
    return(v[4] - v[5])
}

# apply関数の行方向に対して,それぞれ上で作ったline.loop.funcという関数を食わせて,結果のベクトルを得る
sub_vec = apply(df, 1, line.loop.func)

# 折れ線グラフで描画する
age = length(df$'年齢')
plot(0:(age-1), sub_vec, type='l', lty=1, xlab='age', ylab='population')
読み込んだだけのCSVからできたDataFrameの冒頭部

sapply関数

リストやベクトルの全ての「要素」に対して適用する.行列に適用した場合は,全ての値に対して適用されるので注意.

# 関数を定義しておいて
increase.func <- function(a){
    return(a+1)
}

# 偶数のベクトルを作り
vec1 <- seq(0, 10, by=2)
print(vec1)

# sapply関数を適用すると,奇数のベクトルができる
vec2 <- sapply(vec1, increase.func)
print(vec2)

# 同じく偶数の行列を作り
mat1 <- matrix(seq(0, 18, by=2), nrow=2, ncol=5)
print(mat1)
# spply関数を適用すると,奇数の行列ができる.入れたのが行列でも戻り値がベクトルになっていることに注意
mat2 <- sapply(mat1, increase.func)
print(mat2)

# なので受け取ったらこいつを行列にする
mat3 <- matrix(mat2, nrow=2, ncol=5)
print(mat3)

結果は以下のようになる.

[1]  0  2  4  6  8 10
[1]  1  3  5  7  9 11
     [,1] [,2] [,3] [,4] [,5]
[1,]    0    4    8   12   16
[2,]    2    6   10   14   18
 [1]  1  3  5  7  9 11 13 15 17 19
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    5    9   13   17
[2,]    3    7   11   15   19

lapply関数というのもあるが,これは必ず(ベクトルではなく)リストを返すのが特徴.(listというのは構造体みたいなものだった)

mapply関数

mapply関数は,apply関数の多変量版であり,複数のベクトルや行列を取ることができる.

例えば以下のように男女の人口のベクトルが分かれている場合,

df.man = df$'人口.男'
df.woman = df$'人口.女'

この2つのベクトルを行ごとに足した合計を出したいとする.

# まず関数を定義
sum.loop.func <- function(a, b){
    # 合計を返す
    return(a + b)
}

# df.manとdf.womanに適用して結果を得る
sum.of.pop <- mapply(sum.loop.func, df.man, df.woman)

# 折れ線グラフで描画する
age = length(df$'年齢')
plot(0:(age-1), sum.of.pop, type='l', lty=1, xlab='age', ylab='population')

# 実はsum関数は既にあるので,以下のように書くだけでもいける
mapply(sum, df.man, df.woman)

人口合計のグラフは当然わかっているので省略.