R - умножить каждую строку df или матрицы на вектор

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

a <- data.table(t(1:4))
b <- matrix(data=2, nrow=3, ncol=4)

Желаемый результат (в форме матрицы, фрейма данных или данных):

     [,1] [,2] [,3] [,4]
[1,]    2    4    6    8  
[2,]    2    4    6    8
[3,]    2    4    6    8

Может ли кто-нибудь помочь мне, как это сделать (эффективно)?

Почему у вашего "векторного" a таблица данных здесь? Это важно? Это имеет большое значение для того, как работает умножение, когда единица представляет собой таблицу данных. Сравните, например, a * b с 1:4 * b. Это может быть причиной вашего разочарования.

Spacedman 24.03.2018 09:21

Поскольку это некоторые данные из файла .txt, я должен это прочитать. И я знаю только, как это сделать в виде таблицы данных fread или фрейма данных read_table. Если бы я мог читать это как вектор, то это было бы для меня нормально.

Z117 24.03.2018 09:28

Если вы знаете, что это однострочная таблица данных, сначала преобразуйте ее в вектор.

Spacedman 24.03.2018 09:30

Советы респондентам: спрашивающего спрашивают «эффективно» - используйте microbenchmark или другие инструменты тестирования производительности, чтобы оценить, насколько ваш эффективен по сравнению с другими; напишите my_name=function(a,b){...} с вашим решением, чтобы его можно было легко протестировать на различных данных; тестировать на данных больше 3х4;

Spacedman 24.03.2018 09:40

искать функцию развертки

MichaelChirico 24.03.2018 09:41

Пожалуйста, опубликуйте лучший пример b, который имеет разные данные в каждой строке, иначе люди не поймают ошибки при индексации, повторном использовании векторов и т. д. Кроме того, вы хотите, чтобы a был вектором (строкой) или таблицей данных? Проще сделать a вектором.

smci 22.03.2019 10:36
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
6
5 339
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

Опция 1: Использование возможностей data.table:

Примечание: это работает, потому что номер столбца и значение совпадают для a

a[,lapply(.SD,function(x)(x*b[,x]))]
#   V1 V2 V3 V4
#1:  2  4  6  8
#2:  2  4  6  8
#3:  2  4  6  8

Вариант 2: может быть:

t(t(b) * (as.matrix(a)[1,]))
     [,1] [,2] [,3] [,4]
[1,]    2    4    6    8
[2,]    2    4    6    8
[3,]    2    4    6    8

ОБНОВИТЬ

Вариант №3: Для обработки десятичных / фактических значений в a

#Cases when `a` contains decimal values can be handled as
a <- data.table(t(c(1, 0.24, 3, 4)))
b <- matrix(data=2, nrow=3, ncol=4)

a[,lapply(V1:V4,function(i)(a[[i]]*b[,i]))]
#   V1   V2 V3 V4
#1:  2 0.48  6  8
#2:  2 0.48  6  8
#3:  2 0.48  6  8

возможно, стоит упомянуть, что вы не используете матричное умножение, а повторно используете a для поэлементного умножения с t(b).

Moody_Mudskipper 24.03.2018 09:47

Спасибо за ответ. Мне нравится ваше первое решение, поскольку оно является самым быстрым, однако я не совсем понимаю, как это похоже на цикл по строкам b. Особенно мне непонятно подмножество b с x b[,x]. Вы можете мне это объяснить?

Z117 24.03.2018 10:28

Можете ли вы объяснить, почему это работает: a <- data.table(t(c(1, 2, 3, 4, 5))) ; b <- matrix(data=1, nrow=10, ncol=5) ; a[,lapply(.SD,function(x)(x+b[,x]))], но если изменить данные a на десятичный, он больше не работает: a <- data.table(t(c(1, 0.2, 3, 4, 5))) ; b <- matrix(data=1, nrow=10, ncol=5) ; a[,lapply(.SD,function(x)(x+b[,x]))]

Z117 24.03.2018 11:03

@ Z117 Это решение было основано на умножении matrix/vector на одно значение. Это был небольшой взлом, поскольку номер столбца и значение в столбце совпадали. Для каждого столбца было передано одно значение. Если вы хотите, чтобы это работало со значениями decimal, тогда это должно быть: a[,lapply(V1:V4,function(i)(a[[i]]*b[,i]))]

MKR 24.03.2018 11:45

@Moody_Mudskipper Вы абсолютно правы. Он был основан на умножении matrix/vector с одним значением. Ваше решение тоже выглядит неплохо.

MKR 24.03.2018 12:04

@Moody_Mudskipper На самом деле, я удивлен результатом скорости моего option#2, который использует t.

MKR 24.03.2018 12:08

Я добавил в свое решение собственный тест с некоторыми дополнениями

Moody_Mudskipper 24.03.2018 12:40

Сравнительный анализ @Moody_Mudskipper действительно полезен. Это дает истинную картину. Хороший напарник.

MKR 24.03.2018 12:43
dplyr::bind_rows(apply(b, 1, `*`, a))
   V1 V2 V3 V4
1:  2  4  6  8
2:  2  4  6  8
3:  2  4  6  8

Сложность заключается в том, что ваш a представляет собой таблицу данных. Если это на самом деле вектор, то все намного проще:

apply(b, 1, `*`, 1:4)
     [,1] [,2] [,3]
[1,]    2    2    2
[2,]    4    4    4
[3,]    6    6    6
[4,]    8    8    8

Со своей стороны я бы использовал встроенный метод R для матричного умножения %*%.

Учитывая вектор: [NB: data.table - это нетvector]

a <- c(1:4)

и учитывая матрицу:

b <- matrix(data=2, nrow=3, ncol=4)

Ваш результат определяется следующим образом:

output <- b %*% diag(a)

     [,1] [,2] [,3] [,4]
[1,]    2    4    6    8
[2,]    2    4    6    8
[3,]    2    4    6    8

Если вы считаете, что это решение очень неэффективно для ваших нужд, я предлагаю использовать встроенную функцию sweep:

sweep(b, 2, a, FUN = "*")

     [,1] [,2] [,3] [,4]
[1,]    2    4    6    8
[2,]    2    4    6    8
[3,]    2    4    6    8

Это действительно неэффективный метод, поскольку R выполняет полное матричное умножение, что означает, что он выполняет много умножения на ноль и складывает полученный ноль ...

Spacedman 24.03.2018 09:27

Ваша точка зрения верна, но актуальна только для больших матриц порядка миллионов строк. В данном случае @ Z117 этого не уточнил.

Seymour 24.03.2018 09:42

Нет. sweep примерно в четыре раза быстрее на моей тестовой матрице 57 * 75, так что даже на маленьких матрицах есть выигрыш. Пользователь явно указал «эффективность», которую я приравняю к скорости и использованию памяти - создание большой пустой диагональной матрицы и последующее ее умножение неэффективно в обоих направлениях.

Spacedman 24.03.2018 09:44

это очень интуитивно понятно, поэтому по-прежнему является ценным решением для интерактивного использования на небольшой и средней матрице

Moody_Mudskipper 24.03.2018 09:54

глядя на код sweep, он сводится к созданию матрицы, повторяющей a в виде строк перед умножением обоих объектов, обратный вызов можно резюмировать как b*aperm(array(a,rev(dim(b)))), который быстрее, потому что мы пропускаем накладные расходы, он может быть оптимизирован в b*t(a)[rep_len(1,nrow(b)),] для увеличения примерно на 30% скорость по сравнению с оригинальным sweep

Moody_Mudskipper 24.03.2018 11:55
b*rep(unlist(a),each=nrow(b))
#      [,1] [,2] [,3] [,4]
# [1,]    2    4    6    8
# [2,]    2    4    6    8
# [3,]    2    4    6    8

или просто b*rep(a,each=nrow(b)), если вы определяете a <- 1:4

Это просто векторизованное поэлементное умножение без преобразования из rep.

редактировать:

Кажется, что репутация тормозит мое решение. Вот тест, в котором я включил опцию с предварительно вычисленным представлением и некоторые улучшения в опции развертки (взяв только соответствующие части из исходного кода).

a <- data.table(t(1:200))
b <- matrix(data=2, nrow=100000, ncol=200)

a_vec <- unlist(a)
rep_a <- rep(a_vec,each=nrow(b))
microbenchmark::microbenchmark(
  mkr1 = a[,lapply(.SD,function(x)(x*b[,x]))],
  mkr2 = t(t(b) * (as.matrix(a)[1,])),
  mkr_update = a[,lapply(V1:V4,function(i)(a[[i]]*b[,i]))],
  mm = b*rep(unlist(a),each=nrow(b)),
  mm_cheat = b*rep_a,
  regular_sweep = sweep(b,2,unlist(a),`*`),
  regular_sweep2 = sweep(b,2,a_vec,`*`),
  improved_sweepA1 = b*aperm(array(unlist(a),rev(dim(b)))),
  improved_sweepA2 = b*aperm(array(a_vec,rev(dim(b)))),
  improved_sweepB1 = b*a[rep_len(1,nrow(b)),],
  improved_sweepB2 = b*t(a_vec)[rep_len(1,nrow(b)),],
  unit = "relative",
  times=50)


Unit: relative
             expr       min        lq      mean    median        uq       max neval
             mkr1  42.12228  44.15266  50.23959  46.35240  57.20280  65.07289    50
             mkr2 114.58427 124.19653 125.25660 131.08677 124.17058 114.91137    50
       mkr_update   1.00000   1.00000   1.00000   1.00000   1.00000   1.00000    50
               mm 231.34331 223.74365 217.50145 225.91117 215.90765 165.64814    50
         mm_cheat  13.38838  13.22556  14.94682  13.36649  12.95260  25.15564    50
    regular_sweep  96.15758 124.26746 121.04428 128.67282 129.19407 119.20210    50
   regular_sweep2  97.79001 124.69191 124.74650 134.64249 134.97407 107.47152    50
 improved_sweepA1  96.57837 124.86189 116.93736 127.08909 124.92805 105.83318    50
 improved_sweepA2  96.27737 122.49773 118.45262 128.13369 126.15029 106.58669    50
 improved_sweepB1 214.95773 227.39523 226.04339 248.38553 232.50401 161.45341    50
 improved_sweepB2  31.20967  32.61873  37.74552  33.70969  41.52149  55.93362    50

Это примерно в два раза дольше, чем решение sweep в моем тесте для корпуса 75 * 57.

Spacedman 24.03.2018 09:52

Тем не менее, я в 18 раз быстрее на этой маленькой матрице

Moody_Mudskipper 24.03.2018 10:02

Для более крупных операций rep является причиной того, что он медленнее, мое решение становится в 7,5 раз быстрее, если rep(a,each=nrow(b) предварительно вычислен, что является достойной стратегией в случаях использования, когда вычисления должны выполняться на нескольких матрицах одинаковых размеров.

Moody_Mudskipper 24.03.2018 10:09

Спасибо за ответы. Я протестировал предложенные выше решения по скорости (с фактическим размером моего вектора и матрицы), чтобы использовать наиболее эффективное:

a <- data.table(t(1:200))
b <- matrix(data=2, nrow=100000, ncol=200)

system.time(sweep(b, MARGIN=2, t(a), "*"))
#   user  system elapsed 
#   0.31    0.06    0.39 

system.time(a[,lapply(.SD,function(x)(x*b[,x]))])
#   user  system elapsed 
#    0.2     0.0     0.2 

#system.time(bind_rows(apply(b,1,`*`,a)))     
#took 100+ so stopped it manually

system.time(t(t(b)*(as.matrix(a)[1,])))
#   user  system elapsed 
#   0.31    0.05    0.36 

system.time(apply(b, 1, `*`, 1:200))
#   user  system elapsed 
#   1.20    0.11    1.31 

system.time(b*rep(unlist(a),each=nrow(b)))
#   user  system elapsed 
#   0.83    0.05    0.89 

system.time(b*rep((1:200),each=nrow(b)))
#   user  system elapsed 
#   0.36    0.06    0.42

Хорошая работа @ Z117. У нас есть полезная информация. Я удивлен скоростью моего option#2, основанного на t.

MKR 24.03.2018 12:07

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