Мурлыканье эквивалентно вложенному циклу

Что такое purrr::map эквивалент:

for (i in 1:4) {
  for (j in 1:6) {
    print(paste(i, j, sep = "-"))
  }
}

ИЛИ ЖЕ

lapply(1:4, function(i) 
  lapply(1:6, function(j) 
    print(paste(i, j, sep = "-"))))

Концептуально то, что я не получаю, это как ссылаться на внешний цикл в функции внутренней карты.

map(1:4, ~ map(1:6, ~ print(paste(.x, ????, sep = "-")))

2 ответа

Решение

Как указывает @r2evans, .x с вашего первого звонка маскируется. Однако вы можете создать лямбда-функцию, которая принимает 2 параметра .x а также .yи назначьте предыдущее .x к новому .y сквозь ... аргумент.

Я буду использовать walk скорее, чем map как в этом случае вас интересуют только побочные эффекты (печать)

walk(1:4,~ walk(1:6, ~ print(paste(.x, .y, sep = "-")),.y=.x))

Я считаю, что это гораздо удобнее читать expand.grid выложить комбинации, а затем итерации по тем, с pwalk (или же pmap в других обстоятельствах)

purrr::pwalk(expand.grid(1:4,1:6),~print(paste(.x, .y, sep = "-")))

Вывод в обоих случаях:

[1] "1-1"
[1] "2-1"
[1] "3-1"
[1] "4-1"
[1] "5-1"
[1] "6-1"
[1] "1-2"
[1] "2-2"
[1] "3-2"
[1] "4-2"
[1] "5-2"
[1] "6-2"
[1] "1-3"
[1] "2-3"
[1] "3-3"
[1] "4-3"
[1] "5-3"
[1] "6-3"
[1] "1-4"
[1] "2-4"
[1] "3-4"
[1] "4-4"
[1] "5-4"
[1] "6-4"

Использование функциональных формул ( ~ ) немного ограничен в попытках вложения, так как совершенно неясно, какой уровень map Вы пытаетесь ссылаться. (Ну, это не правильно. Мне совершенно ясно, что это ссылки наизнанку, и, поскольку они обе используют одну и ту же номенклатуру, внешние переменные маскируются внутренними переменными.)

Я думаю, что лучший способ обойти это - не использовать метод формулы, а использовать непосредственные / анонимные (или предопределенные) функции:

library(purrr)
str(map(1:2, function(x) map(1:3, function(y) paste(x, y, sep = "-"))))
# List of 2
#  $ :List of 3
#   ..$ : chr "1-1"
#   ..$ : chr "1-2"
#   ..$ : chr "1-3"
#  $ :List of 3
#   ..$ : chr "2-1"
#   ..$ : chr "2-2"
#   ..$ : chr "2-3"

Просто пробегаю через это сейчас.

walk(1:4,~ walk(1:6, ~ print(paste(.x, .y, sep = "-")),.y=.x)) 
[1] "1-1"
[1] "2-1"
[1] "3-1"
[1] "4-1"
[1] "5-1"
[1] "6-1"
[1] "1-2"

а также

purrr::pwalk(expand.grid(1:4,1:6),~print(paste(.x, .y, sep = "-")))
[1] "1-1"
[1] "2-1"
[1] "3-1"
[1] "4-1"
[1] "1-2"

но для соответствия вашим вложенным циклам for именно он играл, и это работает.

for (i in 1:4) {
  for (j in 1:6) {
    print(paste(i, j, sep = "-"))
  }
}
[1] "1-1"
[1] "1-2"
[1] "1-3"
[1] "1-4"
[1] "1-5"
[1] "1-6"
[1] "2-1"

purrr::pwalk(expand.grid(1:6,1:4),~print(paste(.y, .x, sep = "-")))
[1] "1-1"
[1] "1-2"
[1] "1-3"
[1] "1-4"
[1] "1-5"
[1] "1-6"
[1] "2-1"

#or even a map of this
walk(1:4,~ walk(1:6, ~ print(paste(.y, .x, sep = "-")),.y=.x))

Мне еще предстоит выяснить, почему .y=.x все же в конце.

Вот дополнение к уже очень хорошим ответам и ответам-комментариям. Я хотел сделать одну функцию, похожую на мурлыканье, которая выполняет цели OP. Итак, я сделалloop_map функция, которая ведет себя аналогично основному Purrr map функции.

loop_map <- function(why, ecks, fun) {
  
  # 2: for every call of this (the previous .f) the new .f is called for each
  # value of ecks, supplied the same value of why each time
  iterate_over_x = function(x_in,y_in,fun_in){
    return(pmap(.l = list(x = x_in), .f = fun_in ,y = y_in ) %>%
 set_names(nm = as.character(x_in))) 
  }
  
  # 1: this ".f"  argument is called once for each element of why, and is 
  # supplied one value of why and every value of ecks each time
  pmap(.l = list(y_in = why), .f = iterate_over_x, x_in = ecks, fun_in = fun) %>% 
set_names(nm = as.character(why))
  
}



my_paste <- function(x,y) {
  paste(x,y)
}



loop_map(list("a","b"),list("c","d"),my_paste)

В качестве бонуса я назвал вывод, чтобы его было легче проиндексировать или каким-то образом преобразовать в фрейм данных. Я хотел бы улучшить эту функцию, добавив возможности перебирать произвольное количество входных списков и, возможно, использовать функции, которые принимают...аргументы (прямо сейчас все должно быть названо). Если у кого-то есть идея, как это сделать, дайте мне знать.

Другие вопросы по тегам