I think you need 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
From your code:
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
Note that the function (`-` in this example) is called once, with long vectors that cover all pairs of each vector. For example:
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
So if your real-world problem has a more complex function (perhaps preferring working a single pair of data at a time) then you might need to think about how to vectorize it properly, such as with mapply.
Somebody asked about performance.
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
I should note that only hammoire's is exactly like your outcome: mine is a matrix; akrun1 is a single column; akrun2 is like mine but one is a tranposition of the other. Only hammoire's is a list of vectors, not sure if that's ultimately what you need.