mapply для лучшей производительности

Я хочу применить функцию к входу матрицы a, эта функция изменит первый элемент на c[a[1]], а следующие элементы на b[a[i],a[i+1]], начиная с i = 1 до i = ncol(a) - 1.

пример ввода:

a <- matrix(c(1,4,3,1),nrow=1)
b <- matrix(1:25,ncol=5,nrow=5)
c <- matrix(4:8,ncol=5,nrow=1)

ожидаемый результат:

>a
4 16 14 3

#c[a[1]] gave us the first element: 4
#b[a[1],a[2]] gave us the second element: 16 
#b[a[2],a[3]] gave us the third element: 14
#b[a[3],a[4]] gave us the fourth element: 3

Я пытался использовать mapply() пока безуспешно. Идея состоит в том, чтобы избежать циклов, поскольку они могут привести к значительному снижению производительности в R.


person Imlerith    schedule 22.06.2016    source источник


Ответы (1)


Шаг 1. Использование единого индекса для матрицы адресации

В R элементы матрицы хранятся в порядке столбцов в векторе, поэтому A[i, j] совпадает с A[(j-1)*nrow(A) + i]. Рассмотрим пример случайной матрицы 3 на 3:

set.seed(1); A <- round(matrix(runif(9), 3, 3), 2)

> A
     [,1] [,2] [,3]
[1,] 0.27 0.91 0.94
[2,] 0.37 0.20 0.66
[3,] 0.57 0.90 0.63

Теперь эта матрица имеет 3 строки (nrow(A) = 3). Сравнивать:

A[2,3]  # 0.66
A[(3-1) * 3 + 2]  # 0.66

Шаг 2: векторизация

Вы можете обращаться к нескольким элементам матрицы одновременно. Однако это можно сделать только в режиме одиночного индексирования (здесь не слишком точно, см. замечание @alexis_laz позже). Например, если вы хотите извлечь A[1,2] и A[3,1], но если вы это сделаете:

A[c(1,3), c(2,1)]
#      [,1] [,2]
# [1,] 0.91 0.27
# [2,] 0.90 0.57

Вы действительно получаете блок. Теперь, если вы используете единую индексацию, вы получите то, что вам нужно:

A[3 * (c(2,1) - 1) + c(1,3)]
# [1] 0.91 0.57

Шаг 3: получение единого индекса для вашей проблемы

Предположим, n <- length(a) и вы хотите обратиться к этим элементам b:

a[1]    a[2]
a[2]    a[3]
 .       .
 .       .
a[n-1]  a[n]

вы можете использовать один индекс nrow(b) * (a[2:n] - 1) + a[1:(n-1)].

Шаг 4: готовое решение

Поскольку у вас есть только одна строка для a и c, вы должны хранить их как векторы, а не матрицы.

a <- c(1,4,3,1)
c <- 4:8

Если вам дали матрицу и у вас нет выбора (поскольку они в настоящее время находятся в вашем вопросе), вы можете преобразовать их в векторы:

a <- as.numeric(a)
c <- as.numeric(c)

Теперь, как обсуждалось, у нас есть индекс для матрицы адреса b:

n <- length(a)
b_ind <- nrow(b) * (a[2:n] - 1) + a[1:(n-1)]

Вы также обращаетесь к элементу a[1] из c как к первому элементу вашего окончательного результата, поэтому нам нужно объединить: c[a[1]] и b[b_ind] по:

a <- c(c[a[1]], b[b_ind])
# > a
# [1]  4 16 14  3

Этот подход полностью векторизован, даже лучше, чем семейство *apply.


замечание alexis_laz

alexis_laz напоминает мне, что мы также можем использовать «matrix-index», то есть мы также можем обращаться к матрице b через:

b[cbind(a[1:(n-1)],a[2:n])]  ## or b[cbind(a[-n], a[-1])]

Однако я думаю, что использование одиночного индекса немного быстрее, потому что нам нужно получить доступ к матрице индекса построчно, чтобы адресовать b, поэтому мы платим более высокую задержку памяти, чем при использовании векторного индекса.

person Zheyuan Li    schedule 22.06.2016
comment
спасибо за быстрый ответ. Кажется, что b_ind = b[b_ind]... Я не совсем понимаю логику этого утверждения b_ind ‹- nrow(b) * (a[-1] - 1) + a[1:(length(a) -1)] Я поработаю над этим и посмотрю, лучше ли производительность, чем простой цикл, и вернусь к вам завтра - person Imlerith; 22.06.2016
comment
должен был дать ему еще одну попытку... Теперь я понял (не знал, что R-матрицы организуют таким образом, что приводит к этим своеобразным отношениям), было бы также интересно увидеть прикладную реализацию решения, поскольку я мало понимаю, как они работают над сложными проблемами - person Imlerith; 22.06.2016
comment
Если я что-то не упустил, другой альтернативой является «матричное индексирование», которое можно использовать здесь — c(c[a[1]], b[cbind(a[-length(a)], a[-1])]) — где R внутренне обрабатывает смещение i + j*nrow - person alexis_laz; 22.06.2016