在 R 中比较分组变量的值时比 For 循环更好的解决方案
Better Solution than For Loops when Comparing Values of Grouped Variables in R
我是 R 的新手,花了很长时间寻找更好的方法来解决下面的问题,但没有成功。我能够使用有效的 for
循环开发解决方案;但是,我违反了 this GitHub tutorial 讨论编写循环时要避免什么的规则。
我正在处理销售数据。我的特定数据框包括产品类别(“CAT_NO”)、客户十等分(“CUST_DECILE”)(客户被分为 1 到 10 的十等分组,其中 1 是“最佳”客户)以及该产品类别、客户十分位组合的最低毛利率(“floorGM”)。可能值得注意的是,并非每个产品类别都代表所有客户小数位数(例如,样本类别“A”可能只有客户小数位数 4、7 和 9。为简单起见,下面的可重现示例确保每个产品类别都有所有 10 个客户十分位数)。我的数据集可以表示为:
df <- data.frame(CAT_NO = c(rep(c("A"), times = 10), rep(c("B"), times = 10),
rep(c("C"), times = 10), rep(c("D"), times = 10))
, CUST_DECILE = rep(c(1:10), times = 4), floorGM = runif(40, 0.2, 0.8))
df
我的目标是查看每个产品类别并比较每个客户等分位点的底线毛利率;如果较低等分的客户比较高等分的客户具有更高的 floorGM,则较高等分的客户应使用较低等分的客户。
我使用的逻辑按每个 CAT_NO 对数据进行子集化,然后应用一个循环来比较 CAT_NO 中每个 CUST_DECILE 的 floorGM。我的代码是:
Product_Categories <- as.character(unique(df$CAT_NO))
for(k in seq_along(Product_Categories)) {
subdata <- subset(df, CAT_NO == Product_Categories[k])
deciles <- sort(unique(subdata$CUST_DECILE))
for(k in 2:length(deciles)) {
if(subdata[subdata$CUST_DECILE == subdata$CUST_DECILE[k], "floorGM"< subdata[subdata$CUST_DECILE == subdata$CUST_DECILE[k-1], "floorGM"]) {
subdata[subdata$CUST_DECILE == subdata$CUST_DECILE[k], "floorGM"] <- subdata[subdata$CUST_DECILE == subdata$CUST_DECILE[k-1], "floorGM"]
}
}
if (!exists("temp")) {
temp <- subdata
} else {
temp <- rbind(temp, subdata)
}
}
虽然这可行,但我确信有一种更快的方法来执行此操作,特别是在循环期间使用 rbind()
增加我的数据集会影响性能,因为我将此解决方案扩展到数百万交易量。
感谢您提供任何输入 and/or 额外的参考资料!
无法保证数百万行的速度有多快(在我的慢速系统上肯定需要一段时间才能处理 40,000 行),但这里有一个解决方案(使用 dplyr
):
df<-group_by(df,CAT_NO)
df<-mutate(df, lag=lag(floorGM))
while (any(df$floorGM<df$lag,na.rm=T)) {
df<-mutate(df, floorGM=ifelse(!is.na(lag),ifelse(floorGM<lag,lag,floorGM),floorGM))
df<-mutate(df, lag=lag(floorGM))
}
while
循环基本上使 floorGM
数字遍及整个类别。
(其实想想,反正应该不需要很多循环——因为每个类别只能有10个小分位数——所以我觉得应该没问题)。
我是 R 的新手,花了很长时间寻找更好的方法来解决下面的问题,但没有成功。我能够使用有效的 for
循环开发解决方案;但是,我违反了 this GitHub tutorial 讨论编写循环时要避免什么的规则。
我正在处理销售数据。我的特定数据框包括产品类别(“CAT_NO”)、客户十等分(“CUST_DECILE”)(客户被分为 1 到 10 的十等分组,其中 1 是“最佳”客户)以及该产品类别、客户十分位组合的最低毛利率(“floorGM”)。可能值得注意的是,并非每个产品类别都代表所有客户小数位数(例如,样本类别“A”可能只有客户小数位数 4、7 和 9。为简单起见,下面的可重现示例确保每个产品类别都有所有 10 个客户十分位数)。我的数据集可以表示为:
df <- data.frame(CAT_NO = c(rep(c("A"), times = 10), rep(c("B"), times = 10),
rep(c("C"), times = 10), rep(c("D"), times = 10))
, CUST_DECILE = rep(c(1:10), times = 4), floorGM = runif(40, 0.2, 0.8))
df
我的目标是查看每个产品类别并比较每个客户等分位点的底线毛利率;如果较低等分的客户比较高等分的客户具有更高的 floorGM,则较高等分的客户应使用较低等分的客户。
我使用的逻辑按每个 CAT_NO 对数据进行子集化,然后应用一个循环来比较 CAT_NO 中每个 CUST_DECILE 的 floorGM。我的代码是:
Product_Categories <- as.character(unique(df$CAT_NO))
for(k in seq_along(Product_Categories)) {
subdata <- subset(df, CAT_NO == Product_Categories[k])
deciles <- sort(unique(subdata$CUST_DECILE))
for(k in 2:length(deciles)) {
if(subdata[subdata$CUST_DECILE == subdata$CUST_DECILE[k], "floorGM"< subdata[subdata$CUST_DECILE == subdata$CUST_DECILE[k-1], "floorGM"]) {
subdata[subdata$CUST_DECILE == subdata$CUST_DECILE[k], "floorGM"] <- subdata[subdata$CUST_DECILE == subdata$CUST_DECILE[k-1], "floorGM"]
}
}
if (!exists("temp")) {
temp <- subdata
} else {
temp <- rbind(temp, subdata)
}
}
虽然这可行,但我确信有一种更快的方法来执行此操作,特别是在循环期间使用 rbind()
增加我的数据集会影响性能,因为我将此解决方案扩展到数百万交易量。
感谢您提供任何输入 and/or 额外的参考资料!
无法保证数百万行的速度有多快(在我的慢速系统上肯定需要一段时间才能处理 40,000 行),但这里有一个解决方案(使用 dplyr
):
df<-group_by(df,CAT_NO)
df<-mutate(df, lag=lag(floorGM))
while (any(df$floorGM<df$lag,na.rm=T)) {
df<-mutate(df, floorGM=ifelse(!is.na(lag),ifelse(floorGM<lag,lag,floorGM),floorGM))
df<-mutate(df, lag=lag(floorGM))
}
while
循环基本上使 floorGM
数字遍及整个类别。
(其实想想,反正应该不需要很多循环——因为每个类别只能有10个小分位数——所以我觉得应该没问题)。