0

我正在尝试将以下 Matlab 逻辑索引模式转换为 Scala 代码:

% x is an [Nx1] array of Int32
% y is an [Nx1] array of Int32
% myExpensiveFunction() processes batches of unique x.

ux = unique(x);
z = nan(size(x));
for i = 1:length(ux)
  idx = x == ux(i);
  z(idx) = myExpensiveFuntion(x(idx), y(idx));
end

假设我正在使用val x: Array[Int]Scala。做这个的最好方式是什么?

编辑:为了澄清,我希望一次处理一批(x,y),按唯一x分组,并返回一个结果(z),其顺序对应于初始输入。我愿意对 x 进行排序,但最终需要回到原始的未排序顺序。我的主要要求是以清晰且合理有效的方式处理所有索引/映射/排序。

4

3 回答 3

1

这在 Scala 中大部分都非常简单。唯一有点不同寻常的是唯一x索引。在 Scala 中,您可以使用 `groupBy' 来做到这一点。由于这是一个非常重索引的方法,所以我将放弃并一直使用索引:

val z = Array.fill(x.length)(Double.NaN)
x.indices.groupBy(i => x(i)).foreach{ case (xi, is) =>
  is.foreach(i => z(i) = myExpensiveFunction(xi, y(i)))
}
z

假设你可以忍受缺乏向量的情况myExpensiveFunction。如果不,

val z = Array.fill(x.length)(Double.NaN)
x.indices.groupBy(i => x(i)).foreach{ case (xi, is) =>
  val xs = Array.fill(is.length)(xi)
  val ys = is.map(i => y(i)).toArray
  val zs = myExpensiveFunction(xs, ys)
  is.foreach(i => z(i) = zs(i))
}
z

这不是在 Scala 中进行计算的最自然的方法,也不是最有效的方法,但是如果您的昂贵函数很昂贵,您就不会关心效率,这是我能得出的最接近直译的方法。

(将您的 matlab 算法翻译成几乎所有其他东西都需要一定程度的痛苦或重新思考,因为 matlab 中的“自然”计算与大多数其他语言中的计算不同。)

于 2013-02-28T16:28:23.323 回答
0

重要的是要让Matlabunique正确。一个简单的解决方案是使用 aSet来确定唯一值:

val occurringValues = x.toSet

occurringValues.foreach{ value =>
  val indices = x.indices.filter(i => x(i) == value)
  for (i <- indices) {
    z(i) = myExpensiveFunction(x(i), y(i))
  }
}

注意:我假设可以更改myExpensiveFunction为按元素操作...

于 2013-02-28T16:31:23.653 回答
0
scala> def process(xs: Array[Int], ys: Array[Int], f: (Seq[Int], Seq[Int]) => Double): Array[Double] = {
     |   val ux = xs.distinct
     |   val zs = Array.fill(xs.size)(Double.NaN)
     |   for(x <- ux) {
     |     val idx = xs.indices.filter{ i => xs(i) == x }
     |     val res = f(idx.map(xs), idx.map(ys))
     |     idx foreach { i => zs(i) = res }
     |   }
     |   zs
     | }
process: (xs: Array[Int], ys: Array[Int], f: (Seq[Int], Seq[Int]) => Double)Array[Double]

scala> val xs = Array(1,2,1,2,3)
xs: Array[Int] = Array(1, 2, 1, 2, 3)

scala> val ys = Array(1,2,3,4,5)
ys: Array[Int] = Array(1, 2, 3, 4, 5)

scala> val f = (a: Seq[Int], b: Seq[Int]) => a.sum/b.sum.toDouble
f: (Seq[Int], Seq[Int]) => Double = <function2>

scala> process(xs, ys, f)
res0: Array[Double] = Array(0.5, 0.6666666666666666, 0.5, 0.6666666666666666, 0.6)
于 2013-02-28T17:11:56.003 回答