R:避免使用 for 循环顺序选择一列中的值并使用另一列中的值向量应用函数

Ste*_*der 4 for-loop r dplyr

问题

我正在尝试修改当前采用嵌套 for 循环形式的代码,以便它在不依赖循环的情况下实现相同的结果,如果可能的话,以提高可读性并促进故障排除。我将介绍我实际尝试做的事情的真正简化版本。任何有助于避免循环的建议将不胜感激。

我有一个带有 2 个变量(x,y)的数据框(df)。对于 x1 的值,我想减去 y 的每个值。然后我想对 x2 到 xi 重复这个。

这是帮助说明的图像:

在此处输入图片说明

我试过的

一个简单的可重现示例:

数据:

x <- c(221.7, 285.9, 22.3, 67.2, 133.2)
y <- c(121.5, 193.8, 226.2, 313.6, 17.9)
df <- as.data.frame(cbind(x,y))
Run Code Online (Sandbox Code Playgroud)

循环:

outcome <- c()
for (i in seq(1,length(df$x),1)){
  eachX <- df[i,1]
  outcome[[i]] <- df$y-eachX
}
 outcome
Run Code Online (Sandbox Code Playgroud)

我需要帮助的地方

这种方法依赖于 for 循环,我的印象是通常可以避免 for 循环。

提前致谢!

r2e*_*ans 5

我认为你需要outer

outer(df$y, df$x, `-`)
#        [,1]   [,2]  [,3]  [,4]   [,5]
# [1,] -100.2 -164.4  99.2  54.3  -11.7
# [2,]  -27.9  -92.1 171.5 126.6   60.6
# [3,]    4.5  -59.7 203.9 159.0   93.0
# [4,]   91.9   27.7 291.3 246.4  180.4
# [5,] -203.8 -268.0  -4.4 -49.3 -115.3
Run Code Online (Sandbox Code Playgroud)

从您的代码:

do.call(cbind, outcome)
#        [,1]   [,2]  [,3]  [,4]   [,5]
# [1,] -100.2 -164.4  99.2  54.3  -11.7
# [2,]  -27.9  -92.1 171.5 126.6   60.6
# [3,]    4.5  -59.7 203.9 159.0   93.0
# [4,]   91.9   27.7 291.3 246.4  180.4
# [5,] -203.8 -268.0  -4.4 -49.3 -115.3
Run Code Online (Sandbox Code Playgroud)

请注意,函数(`-`在本例中)被调用一次,长向量覆盖每个向量的所有对。例如:

minus <- function(...) { `-`(...); }
outer(df$y, df$x, minus)
#        [,1]   [,2]  [,3]  [,4]   [,5]
# [1,] -100.2 -164.4  99.2  54.3  -11.7
# [2,]  -27.9  -92.1 171.5 126.6   60.6
# [3,]    4.5  -59.7 203.9 159.0   93.0
# [4,]   91.9   27.7 291.3 246.4  180.4
# [5,] -203.8 -268.0  -4.4 -49.3 -115.3
### same results

debug(minus)
outer(df$y, df$x, minus)
# debugging in: FUN(X, Y, ...)
# debug at #1: {
#     -...
# }
list(...) # <--- view what arguments were passed to our 'minus'
# [[1]]
#  [1] 121.5 193.8 226.2 313.6  17.9 121.5 193.8 226.2 313.6  17.9 121.5 193.8 226.2 313.6  17.9
# [16] 121.5 193.8 226.2 313.6  17.9 121.5 193.8 226.2 313.6  17.9
# [[2]]
#  [1] 221.7 221.7 221.7 221.7 221.7 285.9 285.9 285.9 285.9 285.9  22.3  22.3  22.3  22.3  22.3
# [16]  67.2  67.2  67.2  67.2  67.2 133.2 133.2 133.2 133.2 133.2
Run Code Online (Sandbox Code Playgroud)

因此,如果您的实际问题具有更复杂的函数(可能更喜欢一次处理一对数据),那么您可能需要考虑如何正确地对其进行矢量化,例如使用mapply.


有人问性能。

microbenchmark::microbenchmark(
  r2_vec = outer(y, x, `-`),
  r2_df = outer(df$y, df$x, `-`),
  akrun1_vec = crossing(y, x) %>% transmute(col = y - x),
  akrun2_vec = sapply(y, `-`, x),
  akrun2_df = sapply(df$y, `-`, df$x),
  hammoire_vec = map(x, function(x0){ y - x0 }),
  hammoire_df = map(df$x, function(x){ df$y - x })
)
# Unit: microseconds
#          expr    min      lq     mean  median      uq    max neval
#        r2_vec    5.1    6.45    9.967    8.50   12.45   44.7   100
#         r2_df   16.0   19.30   25.565   21.90   32.40   80.1   100
#    akrun1_vec 3433.6 3702.50 3912.103 3827.45 4041.50 5545.7   100
#    akrun2_vec   19.5   23.05   30.787   26.85   38.30   59.5   100
#     akrun2_df   31.9   36.85   47.008   41.50   56.25  111.3   100
#  hammoire_vec   19.8   25.10   28.527   28.35   31.30   45.4   100
#   hammoire_df   53.6   59.65   65.561   62.95   71.95   90.2   100
Run Code Online (Sandbox Code Playgroud)

我应该注意到,只有 hammoire 的和你的完全一样outcome:我的是一个矩阵;akrun1 是单列;akrun2 就像我的,但一个是另一个的换位。只有 hammoire 是一个向量列表,不确定这是否是您最终需要的。