なぜlapplyの代わりにpurrr :: mapを使用するのですか?


171

使用する理由はありますか

map(<list-like-object>, function(x) <do stuff>)

の代わりに

lapply(<list-like-object>, function(x) <do stuff>)

出力は同じであり、私が作成したベンチマークlapplyはそれがわずかに速いことを示しているようです(mapすべての非標準評価入力を評価する必要があるためです)。

それで、そのような単純なケースで実際に切り替えを検討する必要がある理由はありますpurrr::mapか?ここでは構文、purrrによって提供されるその他の機能についての好き嫌いについては質問していませんが、標準評価を使用purrr::mapしてlapply仮定することとの比較について厳密に説明しmap(<list-like-object>, function(x) <do stuff>)ます。purrr::mapパフォーマンス、例外処理などの点で利点はありますか?以下のコメントはそうではないことを示唆していますが、誰かがもう少し詳しく説明できるでしょうか?


8
確かに単純なユースケースの場合は、ベースRに固執し、依存関係を避けます。すでにロードした場合tidyverseしかしを、あなたはパイプから利益を得ることができる%>%と無名関数の~ .x + 1構文
Aurèle

49
これはほとんどスタイルの問題です。ただし、基本的なR関数の機能を知っておく必要があります。これは、これらの整然としたものすべてが、その上にあるシェルにすぎないためです。ある時点で、そのシェルは壊れます。
Hong Ooi

9
~{}ショートカットラムダ(の有無にかかわらず{}、プレーンのために私のためのシールの契約purrr::map()。のタイプの執行がpurrr::map_…()便利かつより少ない鈍角ですvapply()purrr::map_df()超高価な機能ですが、それはまた、コードを簡素化します。ベースRに固執して絶対に何も間違ってはあります[lsv]apply()、しかし。
hrbrmstr

4
質問ありがとうございました。私も調べました。私は10年以上前からRを使用していますが、絶対に使用せず、使用しませpurrrん。私のポイントは次のとおりtidyverseです。プログラミングではなく、分析/インタラクティブ/レポートなどに最適です。使用する必要がある場合、lapplyまたはmapプログラミングをしている場合は、パッケージを作成することで1日になる可能性があります。次に、依存関係が少ないほど最適です。プラス:私は時々map、かなりあいまいな構文で人々が使っているのを見ます。そして今、私はパフォーマンステストを見applyています。家族に慣れている場合は、それに固執してください。
Eric Lecoutre 2017

4
あなたが書いたティム:「ここでは構文、purrrによって提供される他の機能などについての好き嫌いについては尋ねていませんが、厳密には、purrr :: mapと、標準評価を使用すると仮定したラップとの比較について」と、あなたが受け入れた答えはあなたがあなたが人々に行ってほしくないとあなたが言った正確に行くもの。
Carlos Cinelli

回答:


232

purrrから使用している関数がのみの場合、map()いいえ、その利点はそれほど大きくありません。Rich Paulooが指摘しているように、の主な利点map()は、一般的な特殊なケースでコンパクトなコードを記述できるヘルパーです。

  • ~ . + 1 に相当 function(x) x + 1

  • list("x", 1)と同等function(x) x[["x"]][[1]]です。これらのヘルパーは、より一般的です[[-詳細?pluckについては、を参照してください。データの修正について.defaultは、この 引数が特に役立ちます。

しかし、ほとんどの場合、単一の*apply()/ map() 関数を使用せず、それらの束を使用しています。purrrの利点は、関数間の一貫性がはるかに高いことです。例えば:

  • の最初の引数lapply()はデータです。の最初の引数 mapply()は関数です。すべてのマップ関数の最初の引数は常にデータです。

  • と、あなたが出力に抑制名を選択することができます。しかし、 その議論はありません。vapply()sapply()mapply()USE.NAMES = FALSElapply()

  • マッパー関数に一貫した引数を渡す一貫した方法はありません。ほとんどの機能は使用...が、mapply()用途 MoreArgs(あなたが呼ばれることを期待するMORE.ARGS)、そして Map()Filter()そしてReduce()あなたが新しい匿名関数を作成することを期待しています。マップ関数では、定数引数は常に関数名の後に続きます。

  • ほとんどすべてのpurrr関数は型が安定しています。関数名からのみ出力の型を予測できます。これはsapply()またはには当てはまりません mapply()。はい、ありvapply()ます。に相当するものはありませんmapply()

これらのマイナーな区別のすべては重要ではないと思うかもしれませんが(基本R正規表現よりもストリンガーの方が有利だと考える人もいるように)、私の経験では、プログラミング時に不必要な摩擦を引き起こします(異なる引数の順序は常にトリップに使用されます)そして私は大きな関数だけでなく、付随的な詳細もたくさん学ばなければならないので、関数型プログラミング手法を学ぶのを難しくしています。

Purrrは、ベースRにないいくつかの便利なマップバリアントも入力します。

  • modify()[[<-「インプレース」の変更に使用するデータのタイプを保持します。_ifバリアントと組み合わせて、これにより(IMOのような)コードが可能になりますmodify_if(df, is.factor, as.character)

  • map2()あなたが上で同時にマッピングすることができますxy。これにより、次のようなアイデアを簡単に表現できます。 map2(models, datasets, predict)

  • imap()xとそのインデックス(名前または位置)を同時にマッピングできます。これにより、(たとえば)csvディレクトリ内のすべてのファイルをロードし、filenameそれぞれに列を追加することが簡単になり ます。

    dir("\\.csv$") %>%
      set_names() %>%
      map(read.csv) %>%
      imap(~ transform(.x, filename = .y))
  • walk()入力を目に見えない形で返します。副作用のために関数を呼び出す場合(つまり、ファイルをディスクに書き込む場合)に役立ちます。

safely()andのような他のヘルパーは言うまでもありませんpartial()

個人的には、purrrを使用すると、摩擦が少なく簡単に関数コードを記述できることがわかりました。アイデアを考えてから実装するまでのギャップを減らします。しかし、走行距離は異なる場合があります。それが実際に役立つ場合を除いて、purrrを使用する必要はありません。

マイクロベンチマーク

はい、map()よりも少し遅いですlapply()。ただし、ループの実行によるオーバーヘッドではなく、マッピングするものを使用するmap()か、それlapply()によってコストが 決まります。以下のマイクロベンチマークは、map()と比較した場合のコストがlapply()要素あたり約40 ns であることを示唆しています。これは、ほとんどのRコードに実質的な影響を与える可能性は低いようです。

library(purrr)
n <- 1e4
x <- 1:n
f <- function(x) NULL

mb <- microbenchmark::microbenchmark(
  lapply = lapply(x, f),
  map = map(x, f)
)
summary(mb, unit = "ns")$median / n
#> [1] 490.343 546.880

2
その例では、transform()を使用するつもりでしたか?ベースRのtransform()と同様、または何か不足していますか?transform()は、ファイル名を要素として提供します。これにより、(当然)行をバインドするときに警告が生成されます。mutate()は、必要なファイル名の文字列を提供します。そこで使用しない理由はありますか?
DoctorG

2
はい、使用した方がいいmutate()です。他の担当者がいない簡単な例が欲しかっただけです。
ハドリー2017年

タイプ固有性はこの回答のどこかに表示されるべきではありませんか?多くのスクリプトでmap_*ロードpurrrしたのはこのためです。それは私のコードのいくつかの「制御フロー」の側面で私を助けました(stopifnot(is.data.frame(x)))。
神父

2
ggplotとdata.tableは素晴らしいですが、Rのすべての関数に新しいパッケージが本当に必要ですか?
adn bps 2018

58

比較するpurrrlapply利便性速度に要約されます


1. purrr::maplapplyより構文的に便利です

リストの2番目の要素を抽出する

map(list, 2)  

これは@Fです。プリヴェが指摘した、と同じです:

map(list, function(x) x[[2]])

lapply

lapply(list, 2) # doesn't work

無名関数を渡す必要があります...

lapply(list, function(x) x[[2]])  # now it works

...または@RichScrivenが指摘した[[ように、引数としてlapply

lapply(list, `[[`, 2)  # a bit more simple syntantically

そのためlapply、を使用して多くのリストに関数を適用し、カスタム関数を定義したり、無名関数を作成したりするのが面倒な場合は、利便性がを優先する理由の1つpurrrです。

2.タイプ固有のマップ関数は、単に数行のコードです

  • map_chr()
  • map_lgl()
  • map_int()
  • map_dbl()
  • map_df()

これらの型固有のマップ関数はそれぞれ、map()およびによって返されるリストではなく、ベクトルを返しますlapply()。ネストされたベクトルのリストを処理している場合、これらの型固有のマップ関数を使用して、ベクトルを直接引き出し、ベクトルをint、dbl、chrベクトルに直接強制変換できます。ベースのRバージョンは、などのようas.numeric(sapply(...))になりますas.character(sapply(...))

map_<type>機能はまた、彼らは指示されたタイプの原子ベクトルを返すことができない場合、彼らは失敗することに有用な品質を持っています。これは、厳密な制御フローを定義する場合に役立ちます。この場合、関数が[どういうわけか]間違ったオブジェクトタイプを生成した場合に関数を失敗させます。

3.利便性はさておき、lapply[少し]速いmap

使用purrr@Fとして、の便利な機能を。Privéが指摘したように、処理は少し遅くなります。上記で提示した4つのケースのそれぞれを競争してみましょう。

# devtools::install_github("jennybc/repurrrsive")
library(repurrrsive)
library(purrr)
library(microbenchmark)
library(ggplot2)

mbm <- microbenchmark(
lapply       = lapply(got_chars[1:4], function(x) x[[2]]),
lapply_2     = lapply(got_chars[1:4], `[[`, 2),
map_shortcut = map(got_chars[1:4], 2),
map          = map(got_chars[1:4], function(x) x[[2]]),
times        = 100
)
autoplot(mbm)

ここに画像の説明を入力してください

そして勝者は....

lapply(list, `[[`, 2)

つまり、生の速度があなたが求めているものである場合:(base::lapplyそれほど速くはありませんが)

単純な構文と表現可能性のために: purrr::map


この優れたpurrrチュートリアルでは、を使用するときに匿名関数を明示的に記述する必要がないという便利さpurrrと、型固有のmap関数の利点を強調しています。


2
function(x) x[[2]]代わりにを使用すると2、速度が低下することに注意してください。この余分な時間はすべて、lapply実行しないチェックが原因です。
F.プリヴェ

17
匿名関数を「必要とする」必要はありません。 [[関数です。できますlapply(list, "[[", 3)
リッチスクリーベン2017

意味のある@RichScriven。これはlapply over purrrを使用するための構文を単純化します。
リッチパウル

37

味の側面(そうでない場合、この質問は閉じる必要があります)または構文の一貫性、スタイルなどを考慮しない場合、答えは「いいえ」です。map代わりに、lapplyまたはより厳密ななど、適用ファミリーの他のバリアントを使用する特別な理由はありませんvapply

PS:不本意ながら反対票を投じている人々には、OPが書いたことを思い出してください:

ここでは、構文、purrrによって提供される他の機能などについての好き嫌いについては尋ねていませんが、標準の評価を使用すると仮定した場合の厳密なpurrr :: mapとlapplyの比較については尋ねています。

の構文やその他の機能を考慮しない場合purrr、を使用する特別な理由はありませんmap。私はpurrr自分自身を使用しており、ハドリーの答えで大丈夫ですが、皮肉なことに、OPが彼が求めていないことを前もって述べたまさにそのことを覆します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.