R のベクトルやデータフレームにリスト処理 (map, fold , filter) を適用する

R のデータフレームやベクトルに対してドカンとリスト処理を適用したいことが度々あるのですが、都度やり方を調べていて進歩が無いので、整理のためにまとめておきます。

操作 ベクトル データフレーム
map 関数 ベクトル演算 / sapply 関数 列ベクトル同士をベクトル演算
fold 関数 sum や mean 等の集約関数に突っ込む なし
filter 関数 which 関数の結果を添字とする subset 関数

色々と罠が多いし、簡単に実現できないこともあるので、難しくなってきたら awk なり Excel なりで前処理する方が良さそうです。

データ

以下の例では徳川時代の諸侯のデータを使います。

# shoko.txt
kamei       ryogoku kokudaka    type
徳川宗家    天領    400         将軍家
前田家      加賀    103         外様
島津家      薩摩    77          外様
伊達家      陸奥    63          外様
尾張徳川家  尾張    62          御三家
紀州徳川家  紀伊    58          御三家
細川家      肥後    54          外様

このデータを、 read.table 関数で読み込みます。

shoko <- read.table('shoko.txt', header = TRUE)
shoko
#        kamei ryogoku kokudaka   type
# 1   徳川宗家    天領      400 将軍家
# 2     前田家    加賀      103   外様
# 3     島津家    薩摩       77   外様
# 4     伊達家    陸奥       63   外様
# 5 尾張徳川家    尾張       62 御三家
# 6 紀州徳川家    紀伊       58 御三家
# 7     細川家    肥後       54   外様

ベクトルに対する map 関数の適用

スカラ値を引数に取る関数や演算子を使う時に、スカラ値の代わりにベクトルを指定すると、個々の要素に演算を適用した結果のベクトルが戻ります。

shoko$kokudaka * 10000
# [1] 4000000 1030000  770000  630000  620000  580000  540000

(function(x) { return(x * 10000) })(shoko$kokudaka)
# [1] 4000000 1030000  770000  630000  620000  580000  540000

sapply 関数を使っても同じことができます。

sapply(shoko$kokudaka, function(x) { return(x * 10000) })
# [1] 4000000 1030000  770000  630000  620000  580000  540000

ベクトルに対する fold 関数の適用

それっぽいものはなさそうです。数値ベクトルを取って集約する関数が豊富なので、大体何とかなる気がします。

sum(shoko$kokudaka)
# 合計
# [1] 817

mean(shoko$kokudaka)
# 平均値
# [1] 116.7143

sd(shoko$kokudaka)
# 不偏標準偏差
# [1] 126.0128

ベクトルに対する filter 関数の適用

which 関数で、条件を満たす要素の添字ベクトルが取れます。この添字ベクトルで要素を絞ります。

which(shoko$kokudaka >= 100)
# 100万石以上の諸侯の添字
# [1] 1 2

shoko$kokudaka[which(shoko$kokudaka >= 100)]
# 100万石以上の諸侯の石高
# [1] 400 103

shoko$kamei[which(shoko$kokudaka >= 100)]
# 100万石以上の諸侯の家名
# [1] 徳川宗家 前田家

データフレームに対する map 関数の適用

結果としてスカラ値のベクトルが欲しいなら、列ベクトル同士をベクトル演算します。

paste(shoko$kamei, shoko$kokudaka * 10000, '石')
# [1] "徳川宗家 4e+06 石"    "前田家 1030000 石"    "島津家 770000 石"    
# [4] "伊達家 630000 石"     "尾張徳川家 620000 石" "紀州徳川家 580000 石"
# [7] "細川家 540000 石"

結果としてデータフレームが欲しいなら、列ベクトルを data.frame 関数に突っ込みます。

> data.frame(kamei = shoko$kamei, kokudaka = shoko$kokudaka * 10000)
#        kamei kokudaka
# 1   徳川宗家  4000000
# 2     前田家  1030000
# 3     島津家   770000
# 4     伊達家   630000
# 5 尾張徳川家   620000
# 6 紀州徳川家   580000
# 7     細川家   540000

データフレームに対する fold 関数の適用

直接該当するものはなさそうです。ベクトルの集約を組み合わせて何とかすれば大体間に合う気がします。

データフレームに対する filter 関数の適用

subset 関数で絞り込みます。この時、論理 OR は | で、論理 AND は & で書く必要があります (id:miyakawa_taku:20121101:1351784444) 。

subset(shoko, type == '将軍家' | type == '御三家')
#        kamei ryogoku kokudaka   type
# 1   徳川宗家    天領      400 将軍家
# 5 尾張徳川家    尾張       62 御三家
# 6 紀州徳川家    紀伊       58 御三家