Neden lapply yerine purrr :: map kullanıyorsunuz?


172

Kullanmamın bir nedeni var mı?

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

onun yerine

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

çıktı aynı olmalı ve yaptığım ölçütlerin lapplybiraz daha hızlı olduğunu gösteriyor gibi görünüyor ( mapstandart dışı değerlendirme girişinin tümünü değerlendirmek için ihtiyaçlar olmalı ).

Öyleyse böyle basit durumlar için gerçekten geçiş yapmamın bir nedeni var purrr::mapmı? Ben kişinin beğenileri veya sözdizimi hakkında beğenmeme konusunda burada sormuyorum, diğer işlevsellikleri purrr vb tarafından sağlanan, ancak kesinlikle karşılaştırılması ile ilgili purrr::mapolan lapplyyani standart değerlendirmesini kullanarak varsayarak map(<list-like-object>, function(x) <do stuff>). purrr::mapPerformans, istisna yönetimi vb. Açısından herhangi bir avantajı var mı ? Aşağıdaki yorumlar bunun olmadığını gösteriyor, ancak belki birileri biraz daha ayrıntılı olabilir?


8
Gerçekten basit kullanım durumları için, R tabanına daha iyi yapışır ve bağımlılıkları önler. tidyverseYine de yüklerseniz , boru %>%ve anonim işlevler ~ .x + 1sözdiziminden yararlanabilirsiniz
Aurèle

49
Bu hemen hemen bir stil meselesidir. Temel R işlevlerinin ne yaptığını bilmelisiniz, çünkü tüm bu düzenli şeyler sadece üstündeki bir kabuktur. Bir noktada, bu kabuk kırılacak.
Hong Ooi

9
~{}olan veya olmayan kısayol lambda ( {}düz benim için anlaşma mühürler purrr::map(). tipi uygulayıcıları purrr::map_…()daha kullanışlı ve daha az kalın kafalı olan vapply(). purrr::map_df()bir süper pahalı fonksiyonudur ama aynı zamanda basitleştirir kodu. Baz R ile yapıştırma ile kesinlikle hiçbir şey yanlış yok [lsv]apply()gerçi, .
hrbrmstr

4
Soru için teşekkür ederim - ayrıca baktığım şeyler. 10 yıldan beri R kullanıyorum ve kesinlikle kullanmayın ve kullanmayın purrr. Demek istediğim şu: tidyverseanalizler / etkileşimli / raporlar için harika, programlama için değil. Kullanmak zorundaysanız lapplyveya mapdaha sonra programlama yapıyorsanız ve bir gün bir paket oluşturmakla sonuçlanabilir. Sonra daha az bağımlılık en iyisidir. Artı: Bazen insanlar mapsonra oldukça belirsiz bir sözdizimi kullanan insanlar görüyorum . Ve şimdi performans testlerini görüyorum: applyaileye alışkınsanız: buna bağlı kalın.
Eric Lecoutre

4
Tim sen şöyle yazdı: "Burada sözdizimi, purrr tarafından sağlanan diğer işlevler hakkında beğeniler veya sevmediklerim hakkında sormuyorum, ama kesinlikle purrr :: haritasının standart değerlendirmeyi kullanarak lapply ile karşılaştırması hakkında" ve kabul ettiğin cevap söylediklerinizin üzerinden geçen kişi insanların geçmesini istemiyordu.
Carlos Cinelli

Yanıtlar:


232

Eğer purrr'den kullandığınız tek fonksiyon map()hayır ise, avantajlar önemli değildir. Rich Pauloo'nun belirttiği gibi, ana avantajı, map()yaygın özel durumlar için kompakt kod yazmanıza izin veren yardımcılardır:

  • ~ . + 1 eşittir function(x) x + 1

  • list("x", 1)eşittir function(x) x[["x"]][[1]]. Bu yardımcılar biraz daha genel [[- ?pluckayrıntılar için bakın. İçin veri rectangling , .defaultargüman özellikle yararlıdır.

Ancak çoğu zaman tek bir *apply()/ map() işlev kullanmıyorsunuz, bir demet kullanıyorsunuz ve purrr'ın avantajı işlevler arasında çok daha fazla tutarlılık. Örneğin:

  • İlk argüman lapply()veridir; ilk argüman mapply()işlevdir. Tüm harita işlevlerinin ilk argümanı her zaman veridir.

  • İle vapply(), sapply()ve mapply()sen çıkışı ile ilgili bastırmak isimlerin seçebilir USE.NAMES = FALSE; ama lapply()bu argümana sahip değil.

  • Eşleyici bağımsız değişkenleri eşleyici işlevine aktarmanın tutarlı bir yolu yoktur. Çoğu işlevlerini kullanın ...ama mapply()kullanımları MoreArgs(beklediğiniz çağrılacak MORE.ARGS) ve Map(), Filter()ve Reduce()yeni bir anonim işlev oluşturmak bekliyoruz. Harita işlevlerinde, sabit argüman her zaman işlev adından sonra gelir.

  • Hemen hemen her purrr işlevi tip kararlıdır: çıktı türünü yalnızca işlev adından tahmin edebilirsiniz. Bu sapply()veya için doğru değil mapply(). Evet, var vapply(); ama bunun karşılığı yok mapply().

Tüm bu küçük ayrımların önemli olmadığını düşünebilirsiniz (bazı insanların temel R düzenli ifadeleri üzerinde stringr avantajı olmadığını düşündükleri gibi), ancak deneyimlerime göre programlama sırasında gereksiz sürtünmeye neden olurlar (farklı argüman siparişleri her zaman yolculuk için kullanılır) işlevsel programlama tekniklerini öğrenmeyi zorlaştırırlar çünkü büyük fikirlerin yanı sıra, bir dizi arızi ayrıntıyı da öğrenmeniz gerekir.

Purrr ayrıca, R tabanından yoksun bazı kullanışlı harita varyantlarını da doldurur:

  • modify()[[<-"yerinde" değiştirmek için kullanılan verilerin türünü korur . _ifVaryant ile birlikte bu (IMO güzel) koduna izin verirmodify_if(df, is.factor, as.character)

  • map2()eş zamanlı olarak eşlemenizi sağlar xve y. Bu, aşağıdaki gibi fikirleri ifade etmeyi kolaylaştırır map2(models, datasets, predict)

  • imap()eşzamanlı olarak eşlemelerini xve dizinlerini (adlar veya konumlar) eşleştirmenize olanak tanır . Bu, her birine csvbir filenamesütun ekleyerek bir dizindeki tüm dosyaları yüklemeyi (ör.) Kolaylaştırır .

    dir("\\.csv$") %>%
      set_names() %>%
      map(read.csv) %>%
      imap(~ transform(.x, filename = .y))
  • walk()girdisini görünmez bir şekilde döndürür; ve yan etkileri için bir işlev çağırırken kullanışlıdır (örneğin, dosyaları diske yazarken).

Gibi diğer yardımcıları cabası safely()ve partial().

Şahsen, purrr kullandığımda, daha az sürtünme ve daha kolay işlevsel kod yazabileceğimi görüyorum; bir fikri düşünmek ve uygulamak arasındaki boşluğu azaltır. Ancak kilometreniz değişebilir; size gerçekten yardımcı olmadıkça purrr kullanmaya gerek yoktur.

Microbenchmarks

Evet, map()biraz daha yavaştır lapply(). Ancak, map()veya maliyetini kullanarak lapply(), döngüyü gerçekleştirme yükü değil, eşlediğiniz şey tarafından yönlendirilir. Aşağıdaki mikrobenchmark, map()karşılaştırılan maliyetin, lapply()element başına yaklaşık 40 ns olduğunu ve bu da çoğu R kodunu maddi olarak etkilemesinin olası olmadığını göstermektedir.

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
Bu örnekte transform () kullanmak mı istediniz? Temel R dönüşümünde () olduğu gibi bir şey mi eksik? transform (), size (doğal olarak) satırları birbirine bağlamak istediğinizde uyarılar oluşturan bir faktör olarak dosya adı verir. mutate () bana istediğim dosya adlarının karakter sütununu veriyor. Orada kullanmamanın bir nedeni var mı?
doctorG

2
Evet, kullanmak daha iyi mutate(), sadece başka bir bölümü olmayan basit bir örnek istedim.
hadley

Bu cevapta herhangi bir yerde tip özgüllüğü görünmemeli midir? map_*beni purrrbirçok senaryoda yüklememizi sağlayan şey bu. Kodumun bazı 'kontrol akışı' yönlerinde bana yardımcı oldu ( stopifnot(is.data.frame(x))).
Fr.

2
ggplot ve data.table harika, ama R'deki her bir işlev için gerçekten yeni bir pakete ihtiyacımız var mı?
adn bps

58

Karşılaştırma purrrve kolaylık ve hızalapply kadar kaynar .


1. purrr::maplaktikten sözdizimsel olarak daha uygundur

listenin ikinci öğesini çıkart

map(list, 2)  

@F olarak. Privé dikkat çekti, aynı:

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

ile lapply

lapply(list, 2) # doesn't work

isimsiz bir fonksiyon geçirmemiz gerek ...

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

... ya da @RichScriven'in işaret ettiği [[gibi, bir argüman olaraklapply

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

Bu nedenle, birçok listeye işlevler uygularken lapplyve özel bir işlev tanımlamaktan veya anonim bir işlev yazmaktan yorulduysanız, kolaylık lehine bir nedendir purrr.

2. Tip spesifik harita fonksiyonları sadece birçok kod satırı

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

Bu türe özgü harita işlevlerinin her biri, map()ve ile döndürülen listeler yerine bir vektör döndürür lapply(). Yuvalanmış vektör listeleriyle uğraşıyorsanız, vektörleri doğrudan dışarı çekmek ve vektörleri doğrudan int, dbl, chr vektörlerine zorlamak için bu türe özgü harita işlevlerini kullanabilirsiniz. Baz R versiyonu gibi bir şey olmazdı as.numeric(sapply(...)), as.character(sapply(...))vb

map_<type>Fonksiyonları ayrıca, belirtilen türde bir atom vektör döndüremez halinde, başarısız olduğu faydalı bir kaliteye sahiptir. Bu, bir işlev [bir şekilde] yanlış nesne türünü oluşturuyorsa başarısız olmasını istediğiniz katı kontrol akışını tanımlarken yararlıdır.

3. Kolaylık bir yana, lapply[biraz] daha hızlımap

kullanma purrr@F gibi kullanışlı fonksiyonlarını . Privé, işlemeyi biraz yavaşlattığına dikkat çekti. Yukarıda sunduğum 4 vakanın her birini yarışalım.

# 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)

resim açıklamasını buraya girin

Ve kazanan....

lapply(list, `[[`, 2)

Özetle, ham hız peşinde olduğunuz şeyse: base::lapply(çok daha hızlı olmasa da)

Basit sözdizimi ve ifade edilebilirlik için: purrr::map


Bu mükemmel purrröğretici , kullanırken anonim işlevleri açıkça yazmak zorunda kalmamanın kolaylığınıpurrr ve türe özgü mapişlevlerin faydalarını vurgular .


2
function(x) x[[2]]Sadece yerine kullanırsanız 2, daha az yavaş olacağını unutmayın. Tüm bu ekstra zaman, lapplyyapılmayan kontrollerden kaynaklanmaktadır .
F. Privé

17
Anonim işlevlere "ihtiyacınız yok". [[bir işlevdir. Yapabilirsin lapply(list, "[[", 3).
Zengin Scriven

@RichScriven bu mantıklı. Bu, purrr üzerinde lapply kullanmak için sözdizimini basitleştirir.
Rich Pauloo

37

Lezzet yönlerini (aksi takdirde bu soru kapatılmalıdır) veya sözdizimi tutarlılığı, stili vb. Dikkate almazsak, cevap hayır, daha sıkı uygulama gibi ailenin diğer varyantları mapyerine kullanmak için özel bir neden yoktur .lapplyvapply

Not: Minnettarca aşağıya doğru vekil olanlara OP'nin şunu yazdığını unutmayın:

Ben burada sözdizimi, purrr vb tarafından sağlanan diğer işlevsellikler hakkında sevdiğim ya da sevmediğim hakkında sormuyorum, ama kesinlikle purrr :: haritasının standart değerlendirmeyi kullanarak lapply ile karşılaştırılması hakkında

Sözdizimini veya diğer işlevlerini dikkate almazsanız purrr, bunun özel bir nedeni yoktur map. Kendimi kullanıyorum purrrve Hadley'in cevabı ile iyiyim, ama ironik bir şekilde OP'nin sormadığı açıkça söylediği şeylerin üzerinden geçiyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.