使用 merge() 和 sqldf::sqldf() 获得相同的交叉连接
Obtain the same cross join with merge() and sqldf::sqldf()
我有两个数据框:Sales
和 Clients
。我想使用 sqldf::sqldf()
和 merge()
对这些数据帧执行交叉连接,并使用这两种方法获得完全相同的结果。
到目前为止,我只能获得两个行排序不同的数据框。
这是生成 Sales
和 Clients
数据帧的代码:
set.seed(1)
Sales <- data.frame(
Product = sample(c("Toaster", "Radio", "TV"), size = 7, replace = TRUE),
CustomerID = c(rep("1_2019", 2), paste(2:3, "2019", sep = "_"), paste(1:3, "2020", sep = "_"))
)
Sales$Price <- round(ifelse(Sales$Product == "TV", rnorm(1, 400, 20),
ifelse(Sales$Product == "Toaster", rnorm(1, 40, 2),
rnorm(1, 35, 2))))
Clients <- data.frame(
CustomerID = c(paste(2:4, "2019", sep = "_"), paste(1:2, "2020", sep = "_")),
State = sample(c("CA", "AZ", "IL", "MA"), size = 5, replace = TRUE)
)
这是我得到的:
library(sqldf)
# cross join with base R
out1 <- merge(x = Sales, y = Clients, by = NULL)
# cross join with sqldf
out2 <- sqldf("SELECT *
FROM Sales
CROSS JOIN Clients")
out1
和 out2
具有不同的行顺序。如何调整 sqldf()
调用以使 out1
和 out2
完全相同?
这是我得到的最接近的:
merge(x = Sales, y = Clients, by = NULL)
sqldf("SELECT *
FROM Sales
CROSS JOIN Clients
ORDER BY State DESC, Clients.CustomerID")
我认为在 sqldf
中包含 ORDER BY
很重要,因为它清楚地表明在 SQL 中,除非明确指示,否则永远无法保证顺序。
如果你在两个变量上只用 "increasing" 做简单的 ORDER BY
,那么在 R 中到 order
的翻译将是直接的。但是,由于一个变量在减少,一个在增加,order
本身并不能解决这个问题。然而,正如 所建议的,我们可以对 xtfrm
.
做同样的事情
out1 <- merge(x = Sales, y = Clients, by = NULL)
out1 <- out1[order(-xtfrm(out1$State), out1$CustomerID.y),]
out2 <- sqldf::sqldf(
"SELECT *
FROM Sales
CROSS JOIN Clients
ORDER BY State DESC, Clients.CustomerID")
### proof they are identical
all(unlist(Map(`==`, out1, out2)))
# [1] TRUE
这里的 xtfrm
辅助函数允许我们为了排序的目的否定列的 "values"。来自 ?xtfrm
:
A generic auxiliary function that produces a numeric vector which will sort in the same order as 'x'.
如果该字段已经是数字,我们可以只做 order(-State, CustomerID.y)
,但它是 character
的事实需要进一步的步骤。阿尔戈 xtfrm
.
Edit:在评论中,确定 OP 想要在 SQL 语句中模仿 merge
的排序顺序。不幸的是,因为这是两个帧的笛卡尔积,所以没有应用排序:merge
只是 cbind
将第一帧的所有行与第二帧的第一行进行比较,然后对每一行重复第二个。
这可以通过使用来自 merge
:
的一些代码来证明
nx <- nrow(x) # Sales
ny <- nrow(y) # Clients
expand.grid(seq_len(nx), seq_len(ny))
# Var1 Var2
# 1 1 1
# 2 2 1
# 3 3 1
# 4 4 1
# 5 5 1
# 6 1 2
# ...
# 33 3 7
# 34 4 7
# 35 5 7
其中每个数字都是来自相应帧的一行(x
表示 Var1
,y
表示 Var2
)。如果原始数据是:
## Sales ## Clients
Product CustomerID Price CustomerID State
1 Toaster 1_2019 37 1 2_2019 AZ
2 Radio 1_2019 33 2 3_2019 MA
3 Radio 2_2019 33 3 4_2019 AZ
4 TV 3_2019 408 4 1_2020 IL
5 Toaster 1_2020 37 5 2_2020 MA
6 TV 2_2020 408
7 TV 3_2020 408
然后这导致
out1
# Product CustomerID.x Price CustomerID.y State
# 1 Toaster 1_2019 37 2_2019 AZ
# 2 Radio 1_2019 33 2_2019 AZ
# 3 Radio 2_2019 33 2_2019 AZ
# 4 TV 3_2019 408 2_2019 AZ
# 5 Toaster 1_2020 37 2_2019 AZ
# 6 TV 2_2020 408 2_2019 AZ
# 7 TV 3_2020 408 2_2019 AZ
# 8 Toaster 1_2019 37 3_2019 MA
# ...
# 33 Toaster 1_2020 37 2_2020 MA
# 34 TV 2_2020 408 2_2020 MA
# 35 TV 3_2020 408 2_2020 MA
这将极大地破坏 x
(Sales
) 中存在的任何排序,即使 y
(Clients
) 已预先排序(它确实如此) .
因为这个,如果你想要 R 和 SQL 交叉连接解决方案之间的一致性,我建议最 transparent/clear 的方法是 merge
,然后以类似于 SQL 的方式应用 post-merge
排序。事实上,从教学的角度来看,问这个问题:*"What ordering makes sense to humans?" 如果你在课程计划中断言,在明确强势进入过程之前可能无法保证排序(通过 dplyr::arrange
、x[order(...),]
,或 SQL 的 ORDER BY
子句)。找到数据的直观排序,然后在 R 和 SQL.
中证明这一点
旁注:
- 您的
sqldf
查询会产生同名列,如果您开始使用列,这会导致一些错误 post-sqldf
。这可以通过 select ... as ...
字段命名来缓解。
- 不幸的是,目前您的数据按字典顺序排序是违反直觉的:在客户 ID 末尾有年份 建议(是的,我在推断)时间线客户入职,但他们将首先按领先数字排序。类似于
"2020-05-04"
即使作为字符串也能正确排序,而 "05/04/2020"
不能,它可能支持更直观的排序,使最重要的部分成为 id 字符串的前导部分。或者使它们成为整数。或者 UUID(当然是 v4),这些总是很有趣。
我有两个数据框:Sales
和 Clients
。我想使用 sqldf::sqldf()
和 merge()
对这些数据帧执行交叉连接,并使用这两种方法获得完全相同的结果。
到目前为止,我只能获得两个行排序不同的数据框。
这是生成 Sales
和 Clients
数据帧的代码:
set.seed(1)
Sales <- data.frame(
Product = sample(c("Toaster", "Radio", "TV"), size = 7, replace = TRUE),
CustomerID = c(rep("1_2019", 2), paste(2:3, "2019", sep = "_"), paste(1:3, "2020", sep = "_"))
)
Sales$Price <- round(ifelse(Sales$Product == "TV", rnorm(1, 400, 20),
ifelse(Sales$Product == "Toaster", rnorm(1, 40, 2),
rnorm(1, 35, 2))))
Clients <- data.frame(
CustomerID = c(paste(2:4, "2019", sep = "_"), paste(1:2, "2020", sep = "_")),
State = sample(c("CA", "AZ", "IL", "MA"), size = 5, replace = TRUE)
)
这是我得到的:
library(sqldf)
# cross join with base R
out1 <- merge(x = Sales, y = Clients, by = NULL)
# cross join with sqldf
out2 <- sqldf("SELECT *
FROM Sales
CROSS JOIN Clients")
out1
和 out2
具有不同的行顺序。如何调整 sqldf()
调用以使 out1
和 out2
完全相同?
这是我得到的最接近的:
merge(x = Sales, y = Clients, by = NULL)
sqldf("SELECT *
FROM Sales
CROSS JOIN Clients
ORDER BY State DESC, Clients.CustomerID")
我认为在 sqldf
中包含 ORDER BY
很重要,因为它清楚地表明在 SQL 中,除非明确指示,否则永远无法保证顺序。
如果你在两个变量上只用 "increasing" 做简单的 ORDER BY
,那么在 R 中到 order
的翻译将是直接的。但是,由于一个变量在减少,一个在增加,order
本身并不能解决这个问题。然而,正如 所建议的,我们可以对 xtfrm
.
out1 <- merge(x = Sales, y = Clients, by = NULL)
out1 <- out1[order(-xtfrm(out1$State), out1$CustomerID.y),]
out2 <- sqldf::sqldf(
"SELECT *
FROM Sales
CROSS JOIN Clients
ORDER BY State DESC, Clients.CustomerID")
### proof they are identical
all(unlist(Map(`==`, out1, out2)))
# [1] TRUE
这里的 xtfrm
辅助函数允许我们为了排序的目的否定列的 "values"。来自 ?xtfrm
:
A generic auxiliary function that produces a numeric vector which will sort in the same order as 'x'.
如果该字段已经是数字,我们可以只做 order(-State, CustomerID.y)
,但它是 character
的事实需要进一步的步骤。阿尔戈 xtfrm
.
Edit:在评论中,确定 OP 想要在 SQL 语句中模仿 merge
的排序顺序。不幸的是,因为这是两个帧的笛卡尔积,所以没有应用排序:merge
只是 cbind
将第一帧的所有行与第二帧的第一行进行比较,然后对每一行重复第二个。
这可以通过使用来自 merge
:
nx <- nrow(x) # Sales
ny <- nrow(y) # Clients
expand.grid(seq_len(nx), seq_len(ny))
# Var1 Var2
# 1 1 1
# 2 2 1
# 3 3 1
# 4 4 1
# 5 5 1
# 6 1 2
# ...
# 33 3 7
# 34 4 7
# 35 5 7
其中每个数字都是来自相应帧的一行(x
表示 Var1
,y
表示 Var2
)。如果原始数据是:
## Sales ## Clients
Product CustomerID Price CustomerID State
1 Toaster 1_2019 37 1 2_2019 AZ
2 Radio 1_2019 33 2 3_2019 MA
3 Radio 2_2019 33 3 4_2019 AZ
4 TV 3_2019 408 4 1_2020 IL
5 Toaster 1_2020 37 5 2_2020 MA
6 TV 2_2020 408
7 TV 3_2020 408
然后这导致
out1
# Product CustomerID.x Price CustomerID.y State
# 1 Toaster 1_2019 37 2_2019 AZ
# 2 Radio 1_2019 33 2_2019 AZ
# 3 Radio 2_2019 33 2_2019 AZ
# 4 TV 3_2019 408 2_2019 AZ
# 5 Toaster 1_2020 37 2_2019 AZ
# 6 TV 2_2020 408 2_2019 AZ
# 7 TV 3_2020 408 2_2019 AZ
# 8 Toaster 1_2019 37 3_2019 MA
# ...
# 33 Toaster 1_2020 37 2_2020 MA
# 34 TV 2_2020 408 2_2020 MA
# 35 TV 3_2020 408 2_2020 MA
这将极大地破坏 x
(Sales
) 中存在的任何排序,即使 y
(Clients
) 已预先排序(它确实如此) .
因为这个,如果你想要 R 和 SQL 交叉连接解决方案之间的一致性,我建议最 transparent/clear 的方法是 merge
,然后以类似于 SQL 的方式应用 post-merge
排序。事实上,从教学的角度来看,问这个问题:*"What ordering makes sense to humans?" 如果你在课程计划中断言,在明确强势进入过程之前可能无法保证排序(通过 dplyr::arrange
、x[order(...),]
,或 SQL 的 ORDER BY
子句)。找到数据的直观排序,然后在 R 和 SQL.
旁注:
- 您的
sqldf
查询会产生同名列,如果您开始使用列,这会导致一些错误 post-sqldf
。这可以通过select ... as ...
字段命名来缓解。 - 不幸的是,目前您的数据按字典顺序排序是违反直觉的:在客户 ID 末尾有年份 建议(是的,我在推断)时间线客户入职,但他们将首先按领先数字排序。类似于
"2020-05-04"
即使作为字符串也能正确排序,而"05/04/2020"
不能,它可能支持更直观的排序,使最重要的部分成为 id 字符串的前导部分。或者使它们成为整数。或者 UUID(当然是 v4),这些总是很有趣。