私は、ベクトル化されたものすべてを使用およびstepfun
組み合わせて、ベクトル化されたソリューションを提供できると思います。少しねじれた/複雑なロジックですが、すべての努力に値するものです。pmin
pmax
stepfun
+ pmin
+を使用する利点pmax
:
- 非常に高速です(以下のベンチマークを参照)
- 行列のサイズに制限されません (Jonathan のコードを実行中に巨大なベクトルのエラーを参照してください)
まず、このアイデアはJonathan Chang's
投稿から着想を得ています。ここでの小さな違いは、違いではなくインデックスが必要なことです。また、すべての値が正であると仮定します(入力から)。これを負の入力を持つベクトルに拡張することもできますが、必要に応じてその作業を任せます。コードとベンチマークに進む前に、背後にある考え方について説明させてください。runif
stepfun
2 つのベクトルx
( に相当v[,1]
) とy
( に相当) があるとしt[,1]
ます。次に、この方法でを並べ替えy
て作成しstepfun
ます。sorted y
y_sort <- sort(y)
step <- stepfun(y_sort, 0:length(y))
これはどのように私たちを助けますか?クエリstep(a)
を実行すると、 の最大値のインデックスが得られy_sort
ます < a
。これが浸透するまでに時間がかかる場合があります。つまり、値はとのa
間の位置にあります。ここで、最初に把握しなければならないことは、これら 2 つの値のどちらが に最も近いかということです。これは、これらのインデックスに対応する のインデックスとおよび の値を抽出し、かどうかを尋ねることによって実現されます。false の場合はインデックスであり、逆の場合も同様です。次に、 fromから元のインデックスを取得します。これは、オプションinを使用して、対応する並べ替えられたインデックスを取得することで実現できます。step(a)
step(a) + 1
sorted y (y_sort)
a
step(a)
step(a)+1
y_sort
abs(a-y_sort[step(a)]) > abs(a - y_sort[step(a)+1])
step(a)
y
y_sort
index.return = TRUE
sort
この方法に従うのは非常に複雑かもしれないことに同意します。ただし、コードを確認して段階的に実行し、上記のテキストを使用してそれに従ってください (必要な場合)。最良の部分はa
、ベクトルにすることができるため、非常に高速です! それではコードに進みます。
# vectorised solution using stepfun
vectorise_fun1 <- function(x, y) {
y_sort <- sort(abs(y), index.return = TRUE)
y_sval <- y_sort$x
y_sidx <- y_sort$ix
# stepfun
step_fun <- stepfun(y_sval, 0:length(y))
ix1 <- pmax(1, step_fun(x))
ix2 <- pmin(length(y), 1 + ix1)
iy <- abs(x - y_sval[ix1]) > abs(x - y_sval[ix2])
# construct output
res <- rep(0, length(x))
res[iy] <- y_sidx[ix2[iy]]
res[!iy] <- y_sidx[ix1[!iy]]
res
}
# obtaining result
out_arun <- vectorise_fun1(v[,1], t[,1])
# (or) v[,2] <- vectorise_fun1(v[,1], t[,1])
# Are the results identical?
# Matthew's solution
vectorise_fun2 <- function(x, y) {
res <- Vectorize(function(r) which.min(abs(x[r] - y)))(seq(length(x)))
}
out_matthew <- vectorise_fun2(v[,1], t[,1])
# Jonathan's solution
vectorise_fun3 <- function(x, y) {
V <- matrix(rep.int(x, length(y)), ncol = length(y))
TT <- matrix(rep.int(y, length(x)), ncol = length(y), byrow = T)
max.col(-abs(V-TT))
}
out_jonathan <- vectorise_fun3(v[,1], t[,1])
# Are the results identical?
> all(out_arun == out_matthew)
[1] TRUE
> all(out_arun == out_jonathan)
[1] TRUE
それで、ポイントは何ですか?すべての結果は同一であり、 の関数stepfun
は巨大で追跡が困難です。大きなベクトルを考えてみましょう。
x <- runif(1e4)
y <- runif(1e3)
それでは、ベンチマークを行って利点を見てみましょう。
require(rbenchmark)
> benchmark( out_arun <- vectorise_fun1(x,y),
out_matthew <- vectorise_fun2(x,y),
out_jonathan <- vectorise_fun3(x,y),
replications=1, order = "elapsed")
# test replications elapsed relative user.self
# 1 out_arun <- vectorise_fun1(x, y) 1 0.004 1.00 0.005
# 2 out_matthew <- vectorise_fun2(x, y) 1 0.221 55.25 0.169
# 3 out_jonathan <- vectorise_fun3(x, y) 1 1.381 345.25 0.873
# Are the results identical?
> all(out_arun == out_matthew)
[1] TRUE
> all(out_arun == out_jonathan)
[1] TRUE
したがって、使用step_fun
すると、最小で 55 回、最大で 345 回高速になります。では、さらに大きなベクトルを見てみましょう。
x <- runif(1e5)
y <- runif(1e4)
require(rbenchmark)
> benchmark( out_arun <- vectorise_fun1(x,y),
out_matthew <- vectorise_fun2(x,y),
replications=1, order = "elapsed")
# test replications elapsed relative user.self
# 1 out_arun <- vectorise_fun1(x, y) 1 0.052 1.000 0.043
# 2 out_matthew <- vectorise_fun2(x, y) 1 16.668 320.538 11.849
ジョナサンの関数は割り当てエラーを引き起こしました:
Error in rep.int(x, length(y)) :
cannot allocate vector of length 1000000000
そしてスピードアップはこちらで320倍。