2
votes

It seems that purrr functions evaluate differently if lists have titles or not, but why? Is this just "one of those things"?

Example:

func_b <- function(x,y,z) paste(x,y,z)

## Works as expected
pmap(list(iris$Sepal.Length, iris$Sepal.Width, iris$Petal.Length), func_b) %>% head(2)

[[1]] [1] "5.1 3.5 1.4"

[[2]] [1] "4.9 3 1.4"

## Doesn't work
pmap(map(iris[,1:3],list),func_b)

Error in .f(Sepal.Length = .l[[c(1L, 1L)]], Sepal.Width = .l[[c(2L, 1L)]], : unused arguments (Sepal.Length = .l[[c(1, 1)]], Sepal.Width = .l[[c(2, 1)]], Petal.Length = .l[[c(3, 1)]])

But the only difference seems to be that one of the lists retains its titles while the other one doesn't?

list(iris$Sepal.Length, iris$Sepal.Width, iris$Petal.Length) %>% map(head)

[[1]] [1] 5.1 4.9 4.7 4.6 5.0 5.4

[[2]] [1] 3.5 3.0 3.2 3.1 3.6 3.9

[[3]] [1] 1.4 1.4 1.3 1.5 1.4 1.7

as.list(iris[,1:3]) %>% map(head)

$Sepal.Length [1] 5.1 4.9 4.7 4.6 5.0 5.4

$Sepal.Width [1] 3.5 3.0 3.2 3.1 3.6 3.9

$Petal.Length [1] 1.4 1.4 1.3 1.5 1.4 1.7

class(list(iris$Sepal.Length, iris$Sepal.Width, iris$Petal.Length)) == class(as.list(iris[,1:3]))

[1] TRUE

str(list(iris$Sepal.Length, iris$Sepal.Width, iris$Petal.Length)); str(as.list(iris[,1:3]))

List of 3
 $ : num [1:150] 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
 $ : num [1:150] 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
 $ : num [1:150] 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
List of 3
 $ Sepal.Length: num [1:150] 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
 $ Sepal.Width : num [1:150] 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
 $ Petal.Length: num [1:150] 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...

If we just get rid of the names of the second list, it works without a problem.

aa <- as.list(iris[,1:3]) 
names(aa) <- NULL      
pmap(aa,func_b) %>% head(2)

[[1]] [1] "5.1 3.5 1.4"

[[2]] [1] "4.9 3 1.4"

So my specific question: WHY do the titles affect the evaluation method? Is there any way to convert things without breaking the dplyr pipe to eliminate the names?

1
Unnamed lists are assigned to the function arguments by position, named lists by name. That means purrr is trying to do func_b(Sepal.Length = ....., etc). You can always pipe into unname if necessary.Axeman
Nice, the unname works beautifully: map(iris[,1:3],list) %>% unname() %>% pmap(func_b) %>% map(head)Amit Kohli

1 Answers

4
votes

Axeman is spot-on but you can defensibly & dynamically program around it:

library(purrr)

func_b <- function(...) {
  args <- list(...)
  paste(args[[1]], args[[2]], args[[3]])
}

list(iris$Sepal.Length, iris$Sepal.Width, iris$Petal.Length) %>%
  pmap(func_b) %>%
  head(2)
## [[1]]
## [1] "5.1 3.5 1.4"
## 
## [[2]]
## [1] "4.9 3 1.4"

iris[,1:3] %>% pmap(func_b) %>% head(3)
## [[1]]
## [1] "5.1 3.5 1.4"
## 
## [[2]]
## [1] "4.9 3 1.4"
## 
## [[3]]
## [1] "4.7 3.2 1.3"

func_c <- function(...) {
  args <- list(...)
  paste0(args, collapse = " ")
}

list(iris$Sepal.Length, iris$Sepal.Width, iris$Petal.Length) %>%
  pmap(func_c) %>%
  head(2)
## [[1]]
## [1] "5.1 3.5 1.4"
## 
## [[2]]
## [1] "4.9 3 1.4"

iris[,1:3] %>% pmap(func_c) %>% head(3)
## [[1]]
## [1] "5.1 3.5 1.4"
## 
## [[2]]
## [1] "4.9 3 1.4"
## 
## [[3]]
## [1] "4.7 3.2 1.3"

iris[,1:2] %>% pmap(func_c) %>% head(3)
## [[1]]
## [1] "5.1 3.5"
## 
## [[2]]
## [1] "4.9 3"
## 
## [[3]]
## [1] "4.7 3.2"