基礎から学ぶpurrr

I.Purrrについて

一般に、繰り返しにはfor loopsが用いられることが多いですが、Rではあまり用いられません。それは、for loopsに代るapply系の関数が用意されているからです。

Purrrは、さらに、このapplyに代るmap系関数を導入するライブラリです。いわゆるfunctional programmingに沿うもので、tidiverseに含まれています。

R初学者の方も、繰り返しについては、purrrを学ぶことをお勧めします。

Purrrの基本的な説明と実際の使用例を紹介します。

次を参考にしています。

  1. Rebecca Barter, Learn to purr

  2. cheatsheet

  3. Jenny Bryan, purr tutorial

  4. The power of three: purrr-poseful iteration in R with map, pmap and imap

II.Listsについて

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

III.Mapの種類

算出結果によって次の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)

IV.シンプルなmap

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] "猪瀬直樹  様 突然のメール失礼いたします。"

V.データ・フレームの作成

小文字のリストから、小文字列と大文字列のデータ・フレームを作成するコードは次です。このように、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.

Rebecca, Barter. 2019. “Learn to Purr.”