R 如何矢量化依赖于其他观测值的函数



嗨,我有一个数据集如下:

set.seed(100)
library(microbenchmark)
City=c("City1","City2","City2","City1","City2","City1","City2","City1")
Business=c("B","A","B","A","C","A","E","F")
SomeNumber=c(35,20,15,19,12,40,36,28)
zz=data.frame(City,Business,SomeNumber)
zz_new=do.call("rbind", replicate(1000,zz, simplify = FALSE))
zz_new$BusinessMax=0 #Initializing final variable of interest at 0

我只是将数据帧 zz 的行复制 1000 次,以便稍后测量性能。

我还有一个自定义函数,如下所示:

City1=function(full_data,observation){
NewSet=full_data[which(full_data$City==observation$City & !full_data$Business==observation$Business),]
NewSet2=max(NewSet$SomeNumber)
return(NewSet2)
}

我希望做的是仅将自定义函数应用于 City==City1 的那些zz_new行。 我可以创建一个逻辑对象 i1,该对象存储特定行是否满足以下条件:

i1 <- zz_new[["City"]] == "City1"

接下来,这就是我需要帮助的地方,我编写了一个 for 循环(占用了很长时间(,如下所示:

for (i in 1:nrow(zz_new[i1,])){
zz_new[i1,][i,"BusinessMax"]=City1(full_data=zz_new, observation = zz_new[i1,][i,])
}
zz_new[i1,]

上面的代码提供了正确的答案。但是,它非常缓慢且效率低下。我运行微基准测试并获得:

microbenchmark(
for (i in 1:nrow(zz_new[i1,])){
zz_new[i1,][i,"BusinessMax"]=City1(full_data=zz_new, observation = zz_new[i1,][i,])
},times = 5)
min       lq     mean   median       uq     max neval
4.369269 4.400759 4.433388 4.401734 4.450246 4.54493     5

我应该如何矢量化函数 City1?在我的实际代码中,我需要在函数 City1 中进行多个条件检查(这里我刚刚使用了两列 City 和 Business 来子集数据,但我需要包含其他几个变量(。SO 上的许多矢量化代码仅使用来自给定行的信息。不幸的是,就我而言,我需要组合来自给定行和数据集的信息。任何帮助将不胜感激。提前谢谢。

编辑 1:

功能说明 城市1

首先,它创建一个子集,该子集保留那些观测值,其中提供的观测值的"城市"与数据集的城市相同。从此子集中,它删除了观测值的"业务"与数据的"业务"相同的观测值。例如。如果提供的观测值的"城市"和"业务"分别是 City1 和 A,则子集将仅考虑城市 == City1 且业务不等于 A 的观测值。

我还需要为其他城市创建其他类似的功能。但是,如果有人可以帮助我矢量化 City1,我可以尝试对其他函数做同样的事情。

编辑 2:

例如,我为 City == City2 编写了一个替代函数,如下所示:

City2=function(full_data,observation){
NewSet=full_data[which(full_data$City==observation$City & full_data$Business==observation$Business),]
NewSet2=max(NewSet$SomeNumber)-(10*rnorm(1))
return(NewSet2)
}

在上面的函数中,请注意,与 City1 相比,我从 NewSet 中删除了 "!" 符号,并从值 NewSet2 中减去 (-10*rnorm(。

接下来,我只对城市 == 城市 2 的观察运行它。

i2 <- zz_new[["City"]] == "City2"
for (i in 1:nrow(zz_new[i2,])){
zz_new[i2,][i,"BusinessMax"]=City2(full_data=zz_new, observation = zz_new[i2,][i,])
}

这是一个快速版本,可以完成City1()for循环的功能。似乎你想在每个城市都这样做,所以我做了。

library(data.table)
# convert to data table and set key for speed
zzdt = as.data.table(zz_new)
setkey(zzdt, City, Business)
# calculate the max for each business, by city, in City1 only
biz_max = zzdt[, .(BusinessMax = max(SomeNumber)), by = .(City, Business)]
# self-join the max values and filter out where the business match
# to get the max of other businesses within the same city
other_biz_max = 
biz_max[biz_max, on = .(City), allow.cartesian = TRUE][
Business != i.Business,
.(BusinessMax = max(i.BusinessMax)),
by = .(City, Business)
]
# join back to the original data
result = zzdt[other_biz_max]

如果我们只想将其应用于City == "City1",我们可以在第一步中过滤并使最终连接成为完全连接 - 其余部分保持不变。

library(data.table)
# convert to data table and set key for speed
zzdt = as.data.table(zz_new)
setkey(zzdt, City, Business)
# calculate the max for each business in City1
biz_max = zzdt[City == "City1", .(BusinessMax = max(SomeNumber)), by = .(City, Business)]
# self-join the max values and filter out where the business match
# to get the max of other businesses within the same city
other_biz_max = 
biz_max[biz_max, on = .(City), allow.cartesian = TRUE][
Business != i.Business,
.(BusinessMax = max(i.BusinessMax)),
by = .(City, Business)
]
# join back to the original data
result = merge(zzdt, other_biz_max, by = c("City", "Business"), all = TRUE)

在我的电脑上,data.table方法需要 0.03 秒,您问题中的方法需要 10.28 秒,速度约为 300 倍。我在那段时间包含了 data.table 转换和键设置,但如果你使用 data.table 和那个键,其余的代码也可以加快速度。

相关内容

最新更新