Lapply по нескольким параметрам, более быстрый метод

Предположим, у меня есть два вектора

 a <- c(1,2,3,4,5)
 b <- c(6,7,8,9,10)

и функция

calc <- function(x,y){x + y)

Я хочу применить эту функцию для 1-го значения в a для каждого значения в b. Предположим, что в моем случае calc допускает только одно значение из a и b в качестве входных данных, поэтому lapply(a,calc,b) не будет работать, потому что тогда длина (b) не равна 1 (выдает мне ошибку).

Кроме того, mapply также не дает мне желаемого решения, он применяет функцию только к парным значениям, то есть 1+6, 2+7 и т. д.

Поэтому я создал функцию, которая дала мне нужное решение

myfunc <- function(z){lapply(a,calc,z)}

и применил его на b

solution <- lapply(b,myfunc)

Здесь мы видим, что отличие от lapply(a,calc,b) или вложенного lapply(a,lapply,calc,b) заключается в том, что он дает мне все значения в своем собственном списке. Это то, что я хотел, или, по крайней мере, это была функция, которая давала мне правильный результат без ошибок.

Теперь, есть ли более быстрый/тривиальный метод, потому что я просто немного поэкспериментировал здесь. И с моей функцией, которая намного больше, чем calc, это занимает 10 минут, но, возможно, мне придется уменьшить исходную функцию, и здесь не будет более быстрого метода...

ИЗМЕНИТЬ:

В моей функции есть что-то вроде этого,

calc <- function(x,y){
# ...
number <- x
example <- head(number,n=y)
# ...
}

где вектор в качестве входных данных для y больше не работает. С lapply(a,lapply,calc,b) или lapply(a,calc,b) я получаю сообщение об ошибке,

Error in head.default(number, n = y) : length(n) == 1L is not TRUE 

person jackbauer    schedule 25.07.2018    source источник
comment
Не возвращает результаты в виде списка, но, может быть, вы могли бы использовать outer(a,b,calc)?   -  person Florian    schedule 25.07.2018
comment
@Florian нужно быть осторожным с внешним, function нужно правильно векторизовать   -  person MichaelChirico    schedule 25.07.2018
comment
Смотрите мой комментарий под ответом @AkselA...   -  person jackbauer    schedule 25.07.2018
comment
Я путаюсь. Мы уже знали, что ваша функция принимает в качестве входных данных только скалярные или одиночные значения. В комментарии вы спросили о том, как получить доступ к значениям, о чем это было? Имеет ли это какое-то отношение к упомянутым вами линейным регрессиям?   -  person AkselA    schedule 25.07.2018
comment
Что ж, мой результат моей функции - это соответствие линейной регрессии, и с моим решением я мог бы просто получить к ним доступ, но с вашим решением, которое приводит к матрице, совпадения кажутся разделенными. Решение должно иметь соответствие 5x5, то есть 25 элементов в матрице, но у вас около 300 элементов в матрице...   -  person jackbauer    schedule 25.07.2018
comment
Я упорядочил вывод в виде матрицы, потому что предположил, что ваша функция возвращает скаляр. Если этого не произойдет, все может запутаться. Как это выглядит, если не конвертировать в матрицу?   -  person AkselA    schedule 25.07.2018
comment
Ну m1 дает мне матрицу, как мне не получить матрицу с вашим кодом?   -  person jackbauer    schedule 25.07.2018
comment
Что, если вы установите SIMPLIFY=FALSE в вызове mapply()?   -  person AkselA    schedule 25.07.2018
comment
Было бы очень полезно, если бы ваш пример был ближе к вашей реальной проблеме. Функция, которую вы используете, действительно велика, поэтому вы не опубликуете ее?   -  person AkselA    schedule 25.07.2018
comment
Собственно, SIMPLIFY=FALSE и решил, почему я до этого не додумался, я даже пользовался им ранее. Вывод действительно лучше, потому что это список из 25 элементов, где я могу посмотреть его по горизонтали в eg, к какому входному параметру принадлежит n-й элемент.   -  person jackbauer    schedule 25.07.2018
comment
Однако на самом деле это не быстрее, чем мое решение в OP. Это был мой первоначальный вопрос, если есть более быстрый метод...   -  person jackbauer    schedule 25.07.2018
comment
Похоже, вам придется взглянуть на calc() и посмотреть, есть ли способ сделать это быстрее. Может быть, стоит перейти на Code Review?   -  person AkselA    schedule 25.07.2018
comment
Хорошо, если нет более быстрого способа, я мог бы сделать это, большое спасибо!   -  person jackbauer    schedule 25.07.2018
comment
@jackbauer, вы проверили мой ответ ниже?   -  person nadizan    schedule 25.07.2018
comment
@nadizan да, но, как указано, моя функция на самом деле не суммирует значения, выходные данные подходят, и в моей функции есть часть example <- head(number,n=y), которая требует одиночных скаляров в качестве входных значений, поэтому ваша функция может работать с первой показанной функцией calc, но не для моей собственно функция...   -  person jackbauer    schedule 25.07.2018


Ответы (2)


Как говорит Флориан, outer() может быть вариантом.

outer(a, b, calc)
#      [,1] [,2] [,3] [,4] [,5]
# [1,]    7    8    9   10   11
# [2,]    8    9   10   11   12
# [3,]    9   10   11   12   13
# [4,]   10   11   12   13   14
# [5,]   11   12   13   14   15

Но, как упоминает MichaelChirico, с функцией, которая не векторизована, она не будет работать. В этом случае что-то еще должно быть взломано вместе. Это может быть или не быть быстрее, чем ваше текущее решение.

Все комбинации (поэтому выполняются и calc(1, 6), и calc(6, 1), аналогично outer()

Количество вычислений: n2

eg <- expand.grid(a, b)

m1 <- mapply(calc, eg[,1], eg[, 2])
matrix(m1, 5)
#      [,1] [,2] [,3] [,4] [,5]
# [1,]    7    8    9   10   11
# [2,]    8    9   10   11   12
# [3,]    9   10   11   12   13
# [4,]   10   11   12   13   14
# [5,]   11   12   13   14   15

Только уникальные комбинации (предполагается, что ваша функция симметрична)

Количество вычислений: (n2 - n) / 2

cn <- t(combn(1:length(a), 2))

m2 <- mapply(calc, a[cn[, 1]], b[cn[, 2]])
mat <- matrix(, length(a), length(a))
mat[upper.tri(mat)] <- m2
mat
#      [,1] [,2] [,3] [,4] [,5]
# [1,]   NA    8    9   10   11
# [2,]   NA   NA   10   11   12
# [3,]   NA   NA   NA   12   13
# [4,]   NA   NA   NA   NA   14
# [5,]   NA   NA   NA   NA   NA

Этот второй игнорирует диагональ, но добавить эти значения легко, так как это то, что вернул вызов OPs mapply().

diag(mat) <- mapply(calc, a, b)
mat
#      [,1] [,2] [,3] [,4] [,5]
# [1,]    7    8    9   10   11
# [2,]   NA    9   10   11   12
# [3,]   NA   NA   11   12   13
# [4,]   NA   NA   NA   13   14
# [5,]   NA   NA   NA   NA   15
person AkselA    schedule 25.07.2018
comment
Как описано, outer() не работает для моей функции, потому что мне нужны одиночные входные значения (поэтому lapply / вложенные lapply тоже не работают). Ваше второе предложение с mapply рассчитано без ошибок. Полученные значения соответствуют линейной регрессии (fit <- lm(value1~value2)), как мне получить к ним доступ? (м есть matrix) - person jackbauer; 25.07.2018
comment
С моими собственными функциями вывод представляет собой список, и я могу получить к ним доступ с помощью for (i in 1:length(solution)){ for (j in 1:length(solution[[i]])){ print(summary(solution[[i]][[j]])) } - person jackbauer; 25.07.2018
comment
@jackbauer: Было бы лучше, если бы вы добавили эти детали в свой вопрос. Может быть, привести пример подмножества ваших фактических данных? Или вы можете использовать встроенный набор данных, например mtcars, чтобы объяснить свою проблему. - person AkselA; 25.07.2018

Это решило это для меня, добавив SIMPLIFY=FALSE к функции mapply, благодаря @AkselA.

eg <- expand.grid(a, b)

m1 <- mapply(calc, eg[,1], eg[, 2],SIMPLIFY=FALSE)

Однако этот метод лишь немного быстрее, чем мое собственное решение в моем OP.

person jackbauer    schedule 25.07.2018