遍历多个表并仅在满足条件时保留的方法?
Way to loop over multiple tables and keep only if condition met?
所以我正在处理具有多个数据的项目 tables,按月分隔,我需要迭代。速度在这里至关重要,除非我通过数据 table 函数进行大量交叉连接,否则我似乎无法将时间缩短到合理的水平。这是我的 tables:
TABLE 1
Product Date Cost
A 8/1/2020 10
A 8/2/2020 20
A 8/3/2020 30
B 8/4/2020 15
B 8/5/2020 25
B 8/6/2020 35
和TABLE 2:
Product Date Price
A 9/1/2020 20
A 9/2/2020 30
A 9/3/2020 40
B 9/4/2020 27
B 9/5/2020 33
B 9/6/2020 42
所以我需要遍历 Table 2 价格 - Table 1 成本的每个组合,并按产品进行。所以输出将是:
新 TABLE
Product Date1 Date2 Profit
A 8/1/2020 9/1/2020 10
A 8/1/2020 9/2/2020 20
...
编辑:澄清一下,新 Table 应该继续。产品 A 应该有 27 个不同的利润(A 下的 3 个日期 x A 下的 3 个日期 x 3 折扣率)假设它们都高于 0。如果任何利润低于 0,那么我不希望它们作为新的一部分Table.
我还有一个折扣系数,我需要将其应用于价格的每个排列,因为我们提供相当多的折扣
Discount = c(10%,12%,18%)
我尝试过使用循环和各种使用 apply 的方法,但循环花费的时间太长而无法完成(数小时,有些永远不会完成)。这些组合导致数百万行,但我只想保留 profitable 行,其中 Price*Discount > Cost,可能只有 10,000 行。
我的解决方案是交叉连接数据 tables 以创建一个巨大的 table,我可以对其进行矢量化,这要快得多(大约 1 分钟),但有一些较大的 tables 我很快 运行 进入内存限制并且它不是很可扩展。
CTbl =setkey(CTbl[,c(k=1,.SD)],k)[Price[,c(k=1,.SD)],allow.cartesian=TRUE][,k:=NULL]
CTbl[,Profit:=(Discount*Price - Cost]
CTbl = setDT(CTbl)[, .SD[Price > Cost ]]
DT = CTbl[,list(MinProfit = min(Profit)),by = Product]
当然这是相当快的,但是当我真正想要的是 profitable 行时,这是一个巨大的内存浪费,当然还有持续的内存问题。
有人能帮忙吗?我问过一些工作中的 R 用户,但他们似乎也很困惑,他们制作的循环无法接近上面 运行 所需的不到 5 分钟。如果这意味着我可以扩大规模,我不介意多花点时间。
谢谢!
这不是您问题的完整答案,但也许您可以通过 products 迭代一个循环。以下函数查找指定产品的利润。该功能不包括discount,但如果功能正常,可以添加。
profit = function(product, df1, df2) {
cost = with(df1, df1[which(Product == product), 'Cost'])
price = with(df2, df2[which(Product == product), 'Price'])
date = merge(
with(df1, df1[which(Product == product), 'Date']),
(with(df2, df2[which(Product == product), 'Date']))
)
product = t(matrix(rep(price, length(cost)), nrow = length(cost)) - t(matrix(rep(cost, length(price)), ncol = length(price))))
product = data.frame(cbind(date[which(product > 0), ], product[which(product > 0)]))
names(product) = c('costdate', 'pricedate', 'profit')
return(product)
}
示例:
df1 = data.frame(Product = c('A', 'A', 'A', 'B', 'B', 'B'),
Date = c('8/1/2020', '8/2/2020', '8/3/2020', '8/4/2020', '8/5/2020', '8/6/2020'),
Cost = c(10, 20, 30, 15, 25, 35))
df2 = data.frame(Product = c('A', 'A', 'A', 'B', 'B', 'B'),
Date = c('9/1/2020', '9/2/2020', '9/3/2020', '9/4/2020', '9/5/2020', '9/6/2020'),
Price = c(20, 30, 40, 27, 33, 42))
> profit('A', df1, df2)
costdate pricedate profit
1 8/1/2020 9/1/2020 10
4 8/1/2020 9/2/2020 20
5 8/2/2020 9/2/2020 10
7 8/1/2020 9/3/2020 30
8 8/2/2020 9/3/2020 20
9 8/3/2020 9/3/2020 10
> profit('B', df1, df2)
costdate pricedate profit
1 8/4/2020 9/4/2020 12
2 8/5/2020 9/4/2020 2
4 8/4/2020 9/5/2020 18
5 8/5/2020 9/5/2020 8
7 8/4/2020 9/6/2020 27
8 8/5/2020 9/6/2020 17
9 8/6/2020 9/6/2020 7
由于数据有限,我无法正确测试它。
这听起来像是 dplyr
包的问题,它。 dplyr
包允许您在“管道”中将数据操作串在一起,以避免将内容存储在内存中。管道运算符 %>%
获取左侧函数的输出并将其用作右侧函数的第一个参数。 dplyr
包中的每个函数都适用于整个向量或数据 tibble,因此不需要循环。
因此,您的操作可能如下所示:
# Initialize random data like your first table
df1 <- data.frame(product = sample(LETTERS[1:10], 10000, replace = TRUE),
date1 = sample(seq(as.Date("2020/08/01"), as.Date("2020/08/31"),
by = "day"), 10000, replace = TRUE),
cost = round(runif(10000, 5, 100)))
# Initialize random data like your second table
df2 <- data.frame(product = sample(LETTERS[1:10], 10000, replace = TRUE),
date2 = sample(seq(as.Date("2020/09/01"), as.Date("2020/09/30"),
by = "day"), 10000, replace = TRUE),
price = round(runif(10000, 5, 100)))
# Initialize discounts
discounts <- data.frame(product = rep(LETTERS[1:10],4),
discount = rep(c(0, 0.1, 0.12, 0.18), 10))
library(dplyr)
out_table <- df1 %>%
full_join(df2) %>%
full_join(discounts) %>%
mutate(profit = price * discount - cost) %>%
filter(profit > 0)
对于我的随机数据,这在我的机器上大约需要 3 秒。此外,filter
动词只保留我们想要的那些行。
所以我正在处理具有多个数据的项目 tables,按月分隔,我需要迭代。速度在这里至关重要,除非我通过数据 table 函数进行大量交叉连接,否则我似乎无法将时间缩短到合理的水平。这是我的 tables:
TABLE 1
Product Date Cost
A 8/1/2020 10
A 8/2/2020 20
A 8/3/2020 30
B 8/4/2020 15
B 8/5/2020 25
B 8/6/2020 35
和TABLE 2:
Product Date Price
A 9/1/2020 20
A 9/2/2020 30
A 9/3/2020 40
B 9/4/2020 27
B 9/5/2020 33
B 9/6/2020 42
所以我需要遍历 Table 2 价格 - Table 1 成本的每个组合,并按产品进行。所以输出将是:
新 TABLE
Product Date1 Date2 Profit
A 8/1/2020 9/1/2020 10
A 8/1/2020 9/2/2020 20
...
编辑:澄清一下,新 Table 应该继续。产品 A 应该有 27 个不同的利润(A 下的 3 个日期 x A 下的 3 个日期 x 3 折扣率)假设它们都高于 0。如果任何利润低于 0,那么我不希望它们作为新的一部分Table.
我还有一个折扣系数,我需要将其应用于价格的每个排列,因为我们提供相当多的折扣
Discount = c(10%,12%,18%)
我尝试过使用循环和各种使用 apply 的方法,但循环花费的时间太长而无法完成(数小时,有些永远不会完成)。这些组合导致数百万行,但我只想保留 profitable 行,其中 Price*Discount > Cost,可能只有 10,000 行。
我的解决方案是交叉连接数据 tables 以创建一个巨大的 table,我可以对其进行矢量化,这要快得多(大约 1 分钟),但有一些较大的 tables 我很快 运行 进入内存限制并且它不是很可扩展。
CTbl =setkey(CTbl[,c(k=1,.SD)],k)[Price[,c(k=1,.SD)],allow.cartesian=TRUE][,k:=NULL]
CTbl[,Profit:=(Discount*Price - Cost]
CTbl = setDT(CTbl)[, .SD[Price > Cost ]]
DT = CTbl[,list(MinProfit = min(Profit)),by = Product]
当然这是相当快的,但是当我真正想要的是 profitable 行时,这是一个巨大的内存浪费,当然还有持续的内存问题。
有人能帮忙吗?我问过一些工作中的 R 用户,但他们似乎也很困惑,他们制作的循环无法接近上面 运行 所需的不到 5 分钟。如果这意味着我可以扩大规模,我不介意多花点时间。
谢谢!
这不是您问题的完整答案,但也许您可以通过 products 迭代一个循环。以下函数查找指定产品的利润。该功能不包括discount,但如果功能正常,可以添加。
profit = function(product, df1, df2) {
cost = with(df1, df1[which(Product == product), 'Cost'])
price = with(df2, df2[which(Product == product), 'Price'])
date = merge(
with(df1, df1[which(Product == product), 'Date']),
(with(df2, df2[which(Product == product), 'Date']))
)
product = t(matrix(rep(price, length(cost)), nrow = length(cost)) - t(matrix(rep(cost, length(price)), ncol = length(price))))
product = data.frame(cbind(date[which(product > 0), ], product[which(product > 0)]))
names(product) = c('costdate', 'pricedate', 'profit')
return(product)
}
示例:
df1 = data.frame(Product = c('A', 'A', 'A', 'B', 'B', 'B'),
Date = c('8/1/2020', '8/2/2020', '8/3/2020', '8/4/2020', '8/5/2020', '8/6/2020'),
Cost = c(10, 20, 30, 15, 25, 35))
df2 = data.frame(Product = c('A', 'A', 'A', 'B', 'B', 'B'),
Date = c('9/1/2020', '9/2/2020', '9/3/2020', '9/4/2020', '9/5/2020', '9/6/2020'),
Price = c(20, 30, 40, 27, 33, 42))
> profit('A', df1, df2)
costdate pricedate profit
1 8/1/2020 9/1/2020 10
4 8/1/2020 9/2/2020 20
5 8/2/2020 9/2/2020 10
7 8/1/2020 9/3/2020 30
8 8/2/2020 9/3/2020 20
9 8/3/2020 9/3/2020 10
> profit('B', df1, df2)
costdate pricedate profit
1 8/4/2020 9/4/2020 12
2 8/5/2020 9/4/2020 2
4 8/4/2020 9/5/2020 18
5 8/5/2020 9/5/2020 8
7 8/4/2020 9/6/2020 27
8 8/5/2020 9/6/2020 17
9 8/6/2020 9/6/2020 7
由于数据有限,我无法正确测试它。
这听起来像是 dplyr
包的问题,它。 dplyr
包允许您在“管道”中将数据操作串在一起,以避免将内容存储在内存中。管道运算符 %>%
获取左侧函数的输出并将其用作右侧函数的第一个参数。 dplyr
包中的每个函数都适用于整个向量或数据 tibble,因此不需要循环。
因此,您的操作可能如下所示:
# Initialize random data like your first table
df1 <- data.frame(product = sample(LETTERS[1:10], 10000, replace = TRUE),
date1 = sample(seq(as.Date("2020/08/01"), as.Date("2020/08/31"),
by = "day"), 10000, replace = TRUE),
cost = round(runif(10000, 5, 100)))
# Initialize random data like your second table
df2 <- data.frame(product = sample(LETTERS[1:10], 10000, replace = TRUE),
date2 = sample(seq(as.Date("2020/09/01"), as.Date("2020/09/30"),
by = "day"), 10000, replace = TRUE),
price = round(runif(10000, 5, 100)))
# Initialize discounts
discounts <- data.frame(product = rep(LETTERS[1:10],4),
discount = rep(c(0, 0.1, 0.12, 0.18), 10))
library(dplyr)
out_table <- df1 %>%
full_join(df2) %>%
full_join(discounts) %>%
mutate(profit = price * discount - cost) %>%
filter(profit > 0)
对于我的随机数据,这在我的机器上大约需要 3 秒。此外,filter
动词只保留我们想要的那些行。