一般に、繰り返しにはfor loopsが用いられることが多いですが、Rではあまり用いられません。それは、for loopsに代るapply系の関数が用意されているからです。
Purrrは、さらに、このapplyに代るmap系関数を導入するライブラリです。いわゆるfunctional programmingに沿うもので、tidiverseに含まれています。
R初学者の方も、繰り返しについては、purrrを学ぶことをお勧めします。
Purrrの基本的な説明と実際の使用例を紹介します。
次を参考にしています。
Rebecca Barter, Learn to purr
Jenny Bryan, purr tutorial
The power of three: purrr-poseful iteration in R with map, pmap and imap
Purrrの対象はlistsです。Listsとvecorsとdata framesの違いについて確認しておきます。
A vector is a way of storing many individual elements (a single number or a single character or string) of the same type together in a single object.
A data frame is a way of storing many vectors of the same length but possibly of different types together in a single object
A list is a way of storing many objects of any type (e.g. data frames, plots, vectors) together in a single object (Rebecca 2019)
例を示します。次は、my_numberとmy_vectorとmy_dataframeの名のリストのリストです。
my_first_list <- list(my_number = 5,
my_vector = c("a", "b", "c"),
my_dataframe = data.frame(a = 1:3, b = c("q", "b", "z"), c = c("bananas", "are", "so very great")))
my_first_list
## $my_number
## [1] 5
##
## $my_vector
## [1] "a" "b" "c"
##
## $my_dataframe
## a b c
## 1 1 q bananas
## 2 2 b are
## 3 3 z so very great
算出結果によって次の5つに分けられます。
map(.x, .f) is the main mapping function and returns a list
map_df(.x, .f) returns a data frame
map_dbl(.x, .f) returns a numeric (double) vector
map_chr(.x, .f) returns a character vector
map_lgl(.x, .f) returns a logical vector (Rebecca 2019)
Mapsの基本は次です。
map(リスト, 関数)
リストのエレメントを順に繰り返し関数に代入し、その結果をリストあるいは、データ・フレームとして算出します。
実例を示します。
先頭に氏名を書き込む関数を作成します。
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.0 ──
## ✓ ggplot2 3.3.3 ✓ purrr 0.3.4
## ✓ tibble 3.0.4 ✓ dplyr 1.0.2
## ✓ tidyr 1.1.2 ✓ stringr 1.4.0
## ✓ readr 1.4.0 ✓ forcats 0.5.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
myfunc <- function(name) {
paste(name, " 様 突然のメール失礼いたします。")
}
氏名のリストを作成します。
name <- c("森喜朗", "安倍晋三", "竹田恒和", "猪瀬直樹")
Mapします。
map(name, myfunc)
## [[1]]
## [1] "森喜朗 様 突然のメール失礼いたします。"
##
## [[2]]
## [1] "安倍晋三 様 突然のメール失礼いたします。"
##
## [[3]]
## [1] "竹田恒和 様 突然のメール失礼いたします。"
##
## [[4]]
## [1] "猪瀬直樹 様 突然のメール失礼いたします。"
Pipe (%>%)を使用して次のように書くこともできます。
name %>%
map(myfunc)
## [[1]]
## [1] "森喜朗 様 突然のメール失礼いたします。"
##
## [[2]]
## [1] "安倍晋三 様 突然のメール失礼いたします。"
##
## [[3]]
## [1] "竹田恒和 様 突然のメール失礼いたします。"
##
## [[4]]
## [1] "猪瀬直樹 様 突然のメール失礼いたします。"
小文字のリストから、小文字列と大文字列のデータ・フレームを作成するコードは次です。このように、argumentsに、みょう字のリストと関数を書くこともできます。
map_df(c("mori", "abe", "takeda", "inose"), function(.x) {
return(data.frame(Name = .x,
Upper = toupper(.x)))
})
## Name Upper
## 1 mori MORI
## 2 abe ABE
## 3 takeda TAKEDA
## 4 inose INOSE
「みょう字 名」の列と「名 みょう字」の列からなるデータ・フレームを作成します。
みょう字と名を逆にする関数を作成し、mapを適用します。
# create a list
name <- c("竹田 恒和", "猪瀬 直樹", "安倍 晋三", "森 喜朗")
# create a reverse function
reverse_names <- function(string){
paste(word(string, 2, sep=" "), word(string, 1, sep=" "))
}
# create a list of a list
name_reversed <- name %>%
purrr::set_names() %>%
purrr::map(reverse_names)
# show the names
print(names(name_reversed))
## [1] "竹田 恒和" "猪瀬 直樹" "安倍 晋三" "森 喜朗"
# show the reversed names
print(map(name_reversed, 1))
## $`竹田 恒和`
## [1] "恒和 竹田"
##
## $`猪瀬 直樹`
## [1] "直樹 猪瀬"
##
## $`安倍 晋三`
## [1] "晋三 安倍"
##
## $`森 喜朗`
## [1] "喜朗 森"
# create a data frame
map_df(name_reversed, function(.x) {
return(data.frame(reversed = .x,
name = reverse_names(.x)))
})
## reversed name
## 1 恒和 竹田 竹田 恒和
## 2 直樹 猪瀬 猪瀬 直樹
## 3 晋三 安倍 安倍 晋三
## 4 喜朗 森 森 喜朗
To be continued.