Harnessing .F List Names with Purrr::Pmap

Harnessing .f list names with purrr::pmap

The formula argument ~ Sepal.Length + Sepal.Width + Petal.Length + Petal.Width is passed to purrr::as_mapper.

purrr::as_mapper(~ Sepal.Length + Sepal.Width + Petal.Length + Petal.Width)
# function (..., .x = ..1, .y = ..2, . = ..1)
# Sepal.Length + Sepal.Width + Petal.Length + Petal.Width

You can see that there's no direct way for this function to know what these variables are.

I can think of 3 ways around this. I'll use @zacdav's example as it's more compact and readable than yours:

named_list <- list(one = c(1, 1),
two = c(2, 2),
three = c(3, 3))

Explicit definition

You can define explicitly these variables as shown in @zacdav's answer it will work.


Explore the dots argument

There is a way to access the named arguments through the ... parameter of the function returned by as_mapper.

The arguments of the function are named when names are available, as the doc states in other words.

That explains why pmap(named_list, function(x,y,z) x+y+z) will fail with error:

unused arguments (one = .l[[c(1, i)]], two = .l[[c(2, i)]], three = .l[[c(3, i)]])

See:

pmap(named_list, ~names(list(...)))
# [[1]]
# [1] "one" "two" "three"
#
# [[2]]
# [1] "one" "two" "three"

(pmap(unname(named_list), function(x,y,z) x+y+z) on the other hand will work fine)

So this will work:

pmap(named_list, ~ with(list(...), one + two + three))
# [[1]]
# [1] 6
#
# [[2]]
# [1] 6

Use pryr::f

pryr offers a neat shortcut for function definitions with pryr::f :

library(pryr)
f(one + two + three)
# function (one, three, two)
# one + two + three

pmap(named_list, f(one + two + three))
# [[1]]
# [1] 6
#
# [[2]]
# [1] 6
#

Be careful however when using it, global variables will still show up as parameters and functions will or will not be included in parameters depending on how they're called. For example :

x <- 1
test <- mean
f(test(x) + lapply(iris,test2))
# function (iris, test2, x)
# test(x) + lapply(iris, test2)

So it's not a general approach and you should use it only with simple cases. the second approach, though a bit of a hack, will be general.

Moreover f is ordering the parameters alphabetically, this should not be an issue when dealing with a named list, but be careful when dealing with partially named lists.

Use List Names as Row Names When Converting From pmap to pmap_dfr purrr

You could add a new column with rownames of the list.

purrr::map_dfr(lst_df, ~{.x$ticker <- rownames(.x);.x})

Contrarily, if you use

df1 <- do.call(rbind, lst_df)

the rownames are maintained which can be converted to column names if needed

df1$ticker <- rownames(df1)

where

lst_df <- pmap(df, ~reqMktData(tws, twsOption(local = "", symbol = ..1, 
expiry = ..2, strike = ..3, right = "P"),
eventWrapper = eWrapper.data(1), CALLBACK = snapShot))

setting list element names based on argument to `pmap`

From tidyverse package, you can also use lst function. lst is used for creating list. It is like tibble function to create tibble but for creating list. One of the difference with base list() is that it automatically names the list.
It is in dplyr, exported from tibble.

For the example, I also replace base alist by rlang::exprs as it is equivalent. Indeed, both are ok.

library(tidyverse)
library(groupedstats)
set.seed(123)

# creating the dataframes
data_1 <- tibble::as.tibble(iris)
data_2 <- tibble::as.tibble(mtcars)
data_3 <- tibble::as.tibble(airquality)

# creating a list
purrr::pmap(
.l = list(
data = lst(data_1, data_2, data_3),
grouping.vars = rlang::exprs(Species, c(am, cyl), Month),
measures = rlang::exprs(c(Sepal.Length, Sepal.Width), wt, c(Ozone, Solar.R, Wind))
),
.f = groupedstats::grouped_summary
) %>%
str(1)
#> List of 3
#> $ data_1:Classes 'tbl_df', 'tbl' and 'data.frame': 6 obs. of 16 variables:
#> $ data_2:Classes 'tbl_df', 'tbl' and 'data.frame': 6 obs. of 17 variables:
#> $ data_3:Classes 'tbl_df', 'tbl' and 'data.frame': 15 obs. of 16 variables:

Created on 2018-11-02 by the reprex package (v0.2.1)

R purrr:::pmap: how to refer to input arguments by name?

You can use with(...) to solve this :

pmap(args2, ~with(list(...),rnorm(n, mean, sd)))
# [[1]]
# [1] 2.733528
#
# [[2]]
# [1] 4.0967533 6.4926143 0.6083532
#
# [[3]]
# [1] 1.8836592 -0.2090425 -4.0030168 1.1834931 3.2771316

More explanations here: Harnessing .f list names with purrr::pmap

assigning `pmap` output to dataframes with pattern names

You can do it with map2:

library(purrr)
res <- pmap(.l = list(
x = list(iris$Sepal.Length, mtcars$wt, anscombe$y4),
probs = list(seq(0, 1, 0.10)),
na.rm = list(TRUE)
), .f = stats::quantile)

map2(.x = paste0('df_', seq_along(res)), .y = res,
.f = ~ assign(.x, .y, envir = .GlobalEnv))

# > df_1
# 0% 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%
# 4.30 4.80 5.00 5.27 5.60 5.80 6.10 6.30 6.52 6.90 7.90
# > df_2
# 0% 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%
# 1.5130 1.9555 2.3490 2.7730 3.1580 3.3250 3.4400 3.5550 3.7700 4.0475 5.4240
# > df_3
# 0% 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%
# 5.25 5.56 5.76 6.58 6.89 7.04 7.71 7.91 8.47 8.84 12.50

though I think it is better to keep the results in a list.

How to use purrr::pmap to invoke a user-defined function in R

Changing a couple of things in your code fixes this problem. First, your dataset shouldn't be read as a list, so you can take that out of list_1.

list_1 <- list(list(df$v1, df$v2), list("title_1", "title_2"), list("ylabel_1", "ylabel_2"))

From there, you can phrase your pmap call like this to get the results you're after:

pmap(list_1, ~wrap_vr(df, ..1, ..2, ..3))

R: Repeating elements of a list for usage in purrr::pmap()

rep(list(c("onResume", "onPause")), times = 3) should work. What seems to be issue?

If you need to repeat different vectors a different number of times, you can use map2 (Note the usage of flatten to convert the final list-of-lists to a list of vectors):

map2( list(c("ACTIVITY_RESUME", "ACTIVITY_PAUSE"),
c("FRAGMENT_RESUME", "FRAGMENT_PAUSE"),
c("onResume", "onPause")),
1:3, ~rep(list(.x), .y) ) %>% purrr::flatten()
# [[1]]
# [1] "ACTIVITY_RESUME" "ACTIVITY_PAUSE"
#
# [[2]]
# [1] "FRAGMENT_RESUME" "FRAGMENT_PAUSE"
#
# [[3]]
# [1] "FRAGMENT_RESUME" "FRAGMENT_PAUSE"
#
# [[4]]
# [1] "onResume" "onPause"
#
# [[5]]
# [1] "onResume" "onPause"
#
# [[6]]
# [1] "onResume" "onPause"


Related Topics



Leave a reply



Submit