我正在处理一个大型数据集,并且已经编写了使用for循环的工作代码。由于数据集很大,我想优化代码的运行效率,并认为必须有一种使用Tidyverse的方法。简而言之,我有一个包含对象I(种子I)及其x和y坐标的数据框。
object <- c('A','B','C')
x <- c(147, 146, 143)
y <- c(17, 80, 155)
df_Seeds <- data.frame(object, x, y)
df_Seeds$object <- as.character(df_Seeds$object)我还有一个包含另一组对象(根)及其x和y坐标的数组。
x1 <- c(180, 146, 143, 17, 17, 155, 30, 30, 30)
array_Radicles <- array(x1,dim = c(3,3))下面的代码输出一个包含每个种子一定距离内的任意根对象索引的数组,以及另一个包含种子对象ID的数组。
seedID_Array <-array(dim=(0:1)) #blank array for seedID
radicleIndex_Array <-array(dim=(0:1)) #blank array for radicle index
for(i in 1:dim(df_Seeds)[1]) { #loops through each seed object
indexRadicles <- which(abs(array_Radicles[,1] - df_Seeds[i, 2]) <= 50 & abs(array_Radicles[,2]- df_Seeds[i,3]) <= 25) #generates vector index of any radicle within distance of seed
if (length(indexRadicles) > 0) { #some seed objects will not have an associated radicle
for (j in 1:length(indexRadicles)) { #loops through each radicle index
singleIndexRadicles <- indexRadicles[j]
seedID_Array <- rbind(seedID_Array, df_Seeds[i,1]) #adds seed object ID to array
radicleIndex_Array <- rbind(radicleIndex_Array, singleIndexRadicles) #adds radicle index to array
}
}
}
combinedArray <- cbind(seedID_Array, radicleIndex_Array)我很感谢对另一个已经解决的类似问题的建议或指导。
发布于 2021-05-06 01:45:54
首先,我强烈建议你看看this resource和this other resource。他们都是关于如何避免一些简单的错误和让你的R代码运行得更快的很好的参考。
对于您的特定问题,我认为最大的性能瓶颈是当您使用rbind和cbind时。这些函数创建原始对象的副本,然后用第二个参数填充。这效率不是很高。
此外,在您的内循环中,您实际上是在indexRadicles中添加所有索引,并反复rbind df_Seeds[i,1]。
要解决此问题,一种可能的解决方案是使用按种子ID索引的列表。例如:
output <- list()
for(i in 1:dim(df_Seeds)[1]) { #loops through each seed object
indexRadicles <- which(abs(array_Radicles[,1] - df_Seeds[i, 2]) <= 50 & abs(array_Radicles[,2]- df_Seeds[i,3]) <= 25) #generates vector index of any radicle within distance of seed
if (length(indexRadicles) > 0) { #some seed objects will not have an associated radicle
output[df_Seeds$object[i]] <- list(indexRadicles)
}
}
seeds_that_had_index_radicles <- names(output)
all_index_radicles <- unlist(output)请注意,我们在这里没有使用任何tidyverse解决方案。我认为,即使假设tidyverse解决方案总是更快或更高效,也是错误的。我个人认为它们可以帮助你更好地理解一些操作,或者至少更好地可视化它们。但是你通常可以使用基数R在相同的性能下做同样的事情。
另外:顺便说一句,你可以随时使用profvis来帮助你找出代码中的性能瓶颈。它将显示哪些线路花费的时间更长,或者哪些线路被调用的次数最多。强烈建议您查看一下:https://rstudio.github.io/profvis/
发布于 2021-05-06 01:54:27
您的问题是一个非对等连接的示例,其中您的距离要求限制了两个表之间的匹配。dplyr目前不允许非对等连接,但在许多情况下,我的数据足够小(例如,笛卡尔乘积表仍然适合内存),暴力方法可以工作得很好,速度也足够快。
选项1:笛卡尔乘积然后过滤
在这里,我将每个根连接到每个seedID (如果您的数据足够大,这可能是站不住脚的),然后过滤掉我不需要的那些。
library(tidyverse)
df_Radicles <- tibble(x = array_Radicles[,1],
y = array_Radicles[,2],
misc = array_Radicles[,3],
rad_idx = 1:length(array_Radicles[,1]))
# brute force non-equi join: join all then filter
crossing(object = df_Seeds$object, df_Radicles) %>%
left_join(df_Seeds, by = "object") %>%
filter(abs(x.x - x.y) <= 50, abs(y.x - y.y) <= 25) %>%
select(object, rad_idx)
# A tibble: 3 x 2
object rad_idx
<chr> <int>
1 A 2
2 A 1
3 C 3选项2: fuzzyjoin
fuzzyjoin包允许非对等连接,并内置了用于距离连接的方法。在本例中,您使用的是曼哈顿距离度量,但是由于y距离不同,我在这里对其进行了缩放,以便可以在与x距离相同的+/- 50尺度上对其进行计算。如果您正在处理经度/经度坐标,还有一个geo_join选项。
library(fuzzyjoin)
df_Seeds %>%
mutate(y = y * 2) %>% # to use manhattan distance with x + y on same scale
distance_inner_join(
df_Radicles %>% mutate(y = y*2),
by = c("x", "y"),
method = "manhattan",
max_dist = 50) %>%
select(object, rad_idx)
object rad_idx
1 A 1
2 A 2
3 C 3如果这些方法在您的数据上表现不佳,我建议您使用data.table,它在处理这类事情时速度非常快。
https://stackoverflow.com/questions/67405970
复制相似问题