Каков правильный способ умножения кадра данных по вектору?
Я пытаюсь умножить кадр данных df
на вектор v
, так что продукт является кадром данных, где строка i
-th задается df[i,]*v
. Я могу это сделать, например,
df <- data.frame(A=1:5, B=2:6); v <- c(0,2)
as.data.frame(t(t(df) * v))
A B
1 0 4
2 0 6
3 0 8
4 0 10
5 0 12
Я уверен, что должен быть более подход R-стиля (и очень простой!), Но ничего не приходит мне на ум. Я даже пробовал что-то вроде
apply(df, MARGIN=1, function(x) x*v)
но тем не as.data.frame(t(.))
конструкции, такие как as.data.frame(t(.))
.
Как найти эффективный и элегантный обходной путь здесь?
Ответы
Ответ 1
Это тоже работает:
data.frame(mapply('*',df,v))
В этом решении вы воспользуетесь тем фактом, что data.frame
является типом list
, поэтому вы можете перебирать оба элемента df
и v
одновременно с mapply
.
К сожалению, вы ограничены тем, что вы можете выводить из mapply
: как простой list
, так и matrix
. Если ваши данные огромны, это, вероятно, будет более эффективным:
data.frame(mapply('*',df,v,SIMPLIFY=FALSE))
Потому что он преобразует его в list
, который более эффективен для преобразования в data.frame
.
Ответ 2
Если вы ищете скорость и эффективность памяти - data.table
для спасения:
library(data.table)
dt = data.table(df)
for (i in seq_along(dt))
dt[, (i) := dt[[i]] * v[i]]
eddi = function(dt) { for (i in seq_along(dt)) dt[, (i) := dt[[i]] * v[i]] }
arun = function(df) { df * matrix(v, ncol=ncol(df), nrow=nrow(df), byrow=TRUE) }
nograpes = function(df) { data.frame(mapply('*',df,v,SIMPLIFY=FALSE)) }
N = 1e6
dt = data.table(A = rnorm(N), B = rnorm(N))
v = c(0,2)
microbenchmark(eddi(copy(dt)), arun(copy(dt)), nograpes(copy(dt)), times = 10)
#Unit: milliseconds
# expr min lq mean median uq max neval
# eddi(copy(dt)) 23.01106 24.31192 26.47132 24.50675 28.87794 34.28403 10
# arun(copy(dt)) 337.79885 363.72081 450.93933 433.21176 516.56839 644.70103 10
# nograpes(copy(dt)) 19.44873 24.30791 36.53445 26.00760 38.09078 95.41124 10
Как указывает Арун в комментариях, можно также использовать функцию set
из пакета data.table
, чтобы сделать эту модификацию на data.frame
в data.frame
:
for (i in seq_along(df))
set(df, j = i, value = df[[i]] * v[i])
Это, конечно, также работает для data.table
и может быть значительно быстрее, если количество столбцов велико.
Ответ 3
Язык, который позволяет объединять векторы с матрицами, должен в какой-то момент принять решение о том, упорядочены ли матрицы по строкам или столбцам. Причина:
> df * v
A B
1 0 4
2 4 0
3 0 8
4 8 0
5 0 12
потому что R сначала работает по столбцам. Выполнение двухпозиционного трюка приводит к этому. Извините, если это просто объясняет, что вы знаете, но я не знаю другого способа сделать это, за исключением явного расширения v
в матрицу того же размера.
Или напишите приятную функцию, которая обертывает не очень R-стиль кода в нечто, что R-стильно.
Ответ 4
Что случилось с
t(apply(df, 1, function(x)x*v))
?
Ответ 5
Я думаю, что самый быстрый способ (без тестирования data.table) - data.frame(t(t(df)*v))
.
Мои тесты:
testit <- function(nrow, ncol)
{
df <- as.data.frame(matrix(rnorm(nrow*ncol),nrow=nrow,ncol=ncol))
v <- runif(ncol)
r1 <- data.frame(t(t(df)*v))
r2 <- data.frame(mapply('*',df,v,SIMPLIFY=FALSE))
r3 <- df * rep(v, each=nrow(df))
stopifnot(identical(r1, r2) && identical(r1, r3))
microbenchmark(data.frame(t(t(df)*v)), data.frame(mapply('*',df,v,SIMPLIFY=FALSE)), df * rep(v, each=nrow(df)))
}
результат
> set.seed(1)
>
> testit(100,100)
Unit: milliseconds
expr min lq median uq max neval
data.frame(t(t(df) * v)) 2.297075 2.359541 2.455778 3.804836 33.05806 100
data.frame(mapply('*', df, v, SIMPLIFY = FALSE)) 9.977436 10.401576 10.658964 11.762009 15.09721 100
df * rep(v, each = nrow(df)) 14.309822 14.956705 16.092469 16.516609 45.13450 100
> testit(1000,10)
Unit: microseconds
expr min lq median uq max neval
data.frame(t(t(df) * v)) 754.844 805.062 844.431 1850.363 27955.79 100
data.frame(mapply('*', df, v, SIMPLIFY = FALSE)) 1457.895 1497.088 1567.604 2550.090 4732.03 100
df * rep(v, each = nrow(df)) 5383.288 5527.817 5875.143 6628.586 32392.81 100
> testit(10,1000)
Unit: milliseconds
expr min lq median uq max neval
data.frame(t(t(df) * v)) 17.07548 18.29418 19.91498 20.67944 57.62913 100
data.frame(mapply('*', df, v, SIMPLIFY = FALSE)) 99.90103 104.36028 108.28147 114.82012 150.05907 100
df * rep(v, each = nrow(df)) 112.21719 118.74359 122.51308 128.82863 164.57431 100
Ответ 6
library(purrr)
map2_dfc(df, v, '*')
эталонный тест
N = 1e6
dt = data.table(A = rnorm(N), B = rnorm(N))
v = c(0,2)
eddi = function(dt) { for (i in seq_along(dt)) dt[, (i) := dt[[i]] * v[i]]; dt }
arun = function(df) { df * matrix(v, ncol=ncol(df), nrow=nrow(df), byrow=TRUE) }
nograpes = function(df) { data.frame(mapply('*',df,v,SIMPLIFY=FALSE)) }
ryan = function(df) {map2_dfc(df, v, '*') }
library(microbenchmark)
microbenchmark(
eddi(copy(dt))
, arun(copy(dt))
, nograpes(copy(dt))
, ryan(copy(dt))
, times = 100)
# Unit: milliseconds
# expr min lq mean median uq max neval
# eddi(copy(dt)) 8.367513 11.06719 24.26205 12.29132 19.35958 171.6212 100
# arun(copy(dt)) 94.031272 123.79999 186.42155 148.87042 251.56241 364.2193 100
# nograpes(copy(dt)) 7.910739 10.92815 27.68485 13.06058 21.39931 172.0798 100
# ryan(copy(dt)) 8.154395 11.02683 29.40024 13.73845 21.77236 181.0375 100