c(... %*% ...) 和 sum(... * ...) 之间的区别
Difference between c(... %*% ...) and sum(... * ...)
这个问题是 回答的后续讨论。
在 dplyr
的 group_by()
函数中使用 c(... %*% ...)
和 sum(... * ...)
有什么区别?
这两个代码给出相同的结果:
#1
library(dplyr) # 1.0.0
library(tidyr)
df1 %>%
group_by(Date, Market) %>%
group_by(Revenue = c(Quantity %*% Price),
TotalCost = c(Quantity %*% Cost),
Product, .add = TRUE) %>%
summarise(Sold = sum(Quantity)) %>%
pivot_wider(names_from = Product, values_from = Sold)
#2
library(dplyr) # 1.0.0
library(tidyr)
df1 %>%
group_by(Date, Market) %>%
group_by(Revenue = sum(Quantity * Price),
TotalCost = sum(Quantity * Cost),
Product, .add = TRUE) %>%
summarise(Sold = sum(Quantity)) %>%
pivot_wider(names_from = Product, values_from = Sold)
# A tibble: 2 x 7
# Groups: Date, Market, Revenue, TotalCost [2]
# Date Market Revenue TotalCost Apple Banana Orange
# <chr> <chr> <dbl> <dbl> <int> <int> <int>
#1 6/24/2020 A 135 37.5 35 20 20
#2 6/25/2020 A 25 15 10 15 NA
是c(... %*% ...)
和sum(... * ...)
better/quicker/preferred/neater之一吗?
原答案中的DATA:
df1 <- structure(list(Date = c("6/24/2020", "6/24/2020", "6/24/2020",
"6/24/2020", "6/25/2020", "6/25/2020"), Market = c("A", "A",
"A", "A", "A", "A"), Salesman = c("MF", "RP", "RP", "FR", "MF",
"MF"), Product = c("Apple", "Apple", "Banana", "Orange", "Apple",
"Banana"), Quantity = c(20L, 15L, 20L, 20L, 10L, 15L), Price = c(1L,
1L, 2L, 3L, 1L, 1L), Cost = c(0.5, 0.5, 0.5, 0.5, 0.6, 0.6)),
class = "data.frame", row.names = c("1",
"2", "3", "4", "5", "6"))
我会把评论汇总成一个答案,如果我有任何遗漏,其他人可以加入。
%*%
和 *
是截然不同的运算符:*
进行逐元素乘法,而 %*%
进行线性代数矩阵乘法。这些是非常不同的操作,用以下方式演示:
1:4 * 2:5
# [1] 2 6 12 20
1:4 %*% 2:5
# [,1]
# [1,] 40
sum(1:4 * 2:5)
# [1] 40
如果您正在寻找两个向量相乘的单一汇总统计数据,并且线性代数的矩阵乘法有意义,那么 %*%
是适合您的工具。
关于声明式代码应该有一些说法;虽然您可以执行第三个操作 (sum(.*.)
),但对我来说,使用 %*%
可能更好,原因有两个:
声明性意图。我是说我有两个矩阵打算在上面做“线性代数”。
保障措施。如果存在任何维度不匹配(例如,sum(1:4 * 2:3)
在语法上仍然有效,但 1:4 %*% 2:3
无效),我想立即知道。在 sum(.*.)
中,不匹配被世界默默地忽略了(我认为回收可能是一个大问题的原因之一)。
原因是不是性能:虽然较小的vectors/matrices%*%
性能与[=18=相当],随着数据量变大,%*%
相对更昂贵。
m1 <- 1:100 ; m2 <- m1+1 ; m3 <- 1:100000; m4 <- m3+1
microbenchmark::microbenchmark(sm1 = sum(m1*m2), sm2 = m1%*%m2, lg1 = sum(m3*m4), lg2 = m3%*%m4)
# Unit: nanoseconds
# expr min lq mean median uq max neval
# sm1 800 1100 112900 1600 2100 11083600 100
# sm2 1100 1400 2143 1900 2450 10200 100
# lg1 239700 249550 411235 270800 355300 11102800 100
# lg2 547900 575550 634763 637850 678250 780500 100
到目前为止所有的讨论都在向量上,它们实际上是一维矩阵(据%*%
似乎认为.. . 尽管这并不完全准确)。一旦你开始接触真正的矩阵,交换它们就会变得更加困难......事实上,我不知道有更简单的方法来模拟 %*%
(缺少 for
循环等):
m1 %*% m2
# [,1] [,2] [,3] [,4]
# [1,] 22 49 76 103
# [2,] 28 64 100 136
t(sapply(seq_len(nrow(m1)), function(i) sapply(seq_len(ncol(m2)), function(j) sum(m1[i,] * m2[,j]))))
# [,1] [,2] [,3] [,4]
# [1,] 22 49 76 103
# [2,] 28 64 100 136
(虽然嵌套-sapply
可能不是最快的非%*%
方法来处理矩阵-y 的东西,%*%
是 1-2 个数量级 更快,因为它是 .Internal
和编译的,意味着 "Math!" 像这样.)
底线,虽然 %*%
在内部 使用 *
运算符(对于几个步骤之一),但两者在其他方面是不同的。哎呀,人们也可能会以同样的方式比较 *
和 ^
......结果相似。
干杯!
这个问题是
在 dplyr
的 group_by()
函数中使用 c(... %*% ...)
和 sum(... * ...)
有什么区别?
这两个代码给出相同的结果:
#1
library(dplyr) # 1.0.0
library(tidyr)
df1 %>%
group_by(Date, Market) %>%
group_by(Revenue = c(Quantity %*% Price),
TotalCost = c(Quantity %*% Cost),
Product, .add = TRUE) %>%
summarise(Sold = sum(Quantity)) %>%
pivot_wider(names_from = Product, values_from = Sold)
#2
library(dplyr) # 1.0.0
library(tidyr)
df1 %>%
group_by(Date, Market) %>%
group_by(Revenue = sum(Quantity * Price),
TotalCost = sum(Quantity * Cost),
Product, .add = TRUE) %>%
summarise(Sold = sum(Quantity)) %>%
pivot_wider(names_from = Product, values_from = Sold)
# A tibble: 2 x 7
# Groups: Date, Market, Revenue, TotalCost [2]
# Date Market Revenue TotalCost Apple Banana Orange
# <chr> <chr> <dbl> <dbl> <int> <int> <int>
#1 6/24/2020 A 135 37.5 35 20 20
#2 6/25/2020 A 25 15 10 15 NA
是c(... %*% ...)
和sum(... * ...)
better/quicker/preferred/neater之一吗?
原答案中的DATA:
df1 <- structure(list(Date = c("6/24/2020", "6/24/2020", "6/24/2020",
"6/24/2020", "6/25/2020", "6/25/2020"), Market = c("A", "A",
"A", "A", "A", "A"), Salesman = c("MF", "RP", "RP", "FR", "MF",
"MF"), Product = c("Apple", "Apple", "Banana", "Orange", "Apple",
"Banana"), Quantity = c(20L, 15L, 20L, 20L, 10L, 15L), Price = c(1L,
1L, 2L, 3L, 1L, 1L), Cost = c(0.5, 0.5, 0.5, 0.5, 0.6, 0.6)),
class = "data.frame", row.names = c("1",
"2", "3", "4", "5", "6"))
我会把评论汇总成一个答案,如果我有任何遗漏,其他人可以加入。
%*%
和*
是截然不同的运算符:*
进行逐元素乘法,而%*%
进行线性代数矩阵乘法。这些是非常不同的操作,用以下方式演示:1:4 * 2:5 # [1] 2 6 12 20 1:4 %*% 2:5 # [,1] # [1,] 40 sum(1:4 * 2:5) # [1] 40
如果您正在寻找两个向量相乘的单一汇总统计数据,并且线性代数的矩阵乘法有意义,那么
%*%
是适合您的工具。关于声明式代码应该有一些说法;虽然您可以执行第三个操作 (
sum(.*.)
),但对我来说,使用%*%
可能更好,原因有两个:声明性意图。我是说我有两个矩阵打算在上面做“线性代数”。
保障措施。如果存在任何维度不匹配(例如,
sum(1:4 * 2:3)
在语法上仍然有效,但1:4 %*% 2:3
无效),我想立即知道。在sum(.*.)
中,不匹配被世界默默地忽略了(我认为回收可能是一个大问题的原因之一)。原因是不是性能:虽然较小的vectors/matrices
%*%
性能与[=18=相当],随着数据量变大,%*%
相对更昂贵。m1 <- 1:100 ; m2 <- m1+1 ; m3 <- 1:100000; m4 <- m3+1 microbenchmark::microbenchmark(sm1 = sum(m1*m2), sm2 = m1%*%m2, lg1 = sum(m3*m4), lg2 = m3%*%m4) # Unit: nanoseconds # expr min lq mean median uq max neval # sm1 800 1100 112900 1600 2100 11083600 100 # sm2 1100 1400 2143 1900 2450 10200 100 # lg1 239700 249550 411235 270800 355300 11102800 100 # lg2 547900 575550 634763 637850 678250 780500 100
到目前为止所有的讨论都在向量上,它们实际上是一维矩阵(据
%*%
似乎认为.. . 尽管这并不完全准确)。一旦你开始接触真正的矩阵,交换它们就会变得更加困难......事实上,我不知道有更简单的方法来模拟%*%
(缺少for
循环等):m1 %*% m2 # [,1] [,2] [,3] [,4] # [1,] 22 49 76 103 # [2,] 28 64 100 136 t(sapply(seq_len(nrow(m1)), function(i) sapply(seq_len(ncol(m2)), function(j) sum(m1[i,] * m2[,j])))) # [,1] [,2] [,3] [,4] # [1,] 22 49 76 103 # [2,] 28 64 100 136
(虽然嵌套-
sapply
可能不是最快的非%*%
方法来处理矩阵-y 的东西,%*%
是 1-2 个数量级 更快,因为它是.Internal
和编译的,意味着 "Math!" 像这样.)
底线,虽然 %*%
在内部 使用 *
运算符(对于几个步骤之一),但两者在其他方面是不同的。哎呀,人们也可能会以同样的方式比较 *
和 ^
......结果相似。
干杯!