我正在尝试线性插值Array[Option[Long]]
。例如:
val example1 = Array(Some(20l), None, Some(60l))
val example2 = Array(Some(20l), None, None, Some(80l))
val example3 = Array(Some(20l), None, None, Some(80l), Some(90l), Some(100l))
val example4 = Array(Some(20l), None, None, Some(80l), None, Some(82l))
我期待:
val example1Interpolated = Array(20l, 40l, 60l)
val example2Interpolated = Array(20l, 40l, 60l, 80l)
val example3Interpolated = Array(20l, 40l, 60l, 80l, 90l, 100l)
val example4Interpolated = Array(20l, 40l, 60l, 80l, 81l, 82l)
集合中的元素之间没有关系(例如example4
)。然而,这些值是单调递增的。
对于那些熟悉Python的人,我正在寻找以下Scala等价物:
def interpolate(input_):
nans = np.isnan(input_)
get_index = lambda z: z.nonzero()[0]
input_[nans] = np.interp(get_index(nans), get_index(~nans), input_[~nans])
return input_
适用于:
interpolate(np.array([20, np.nan, 60]))
interpolate(np.array([20, np.nan, np.nan, 80]))
interpolate(np.array([20, np.nan, np.nan, 80, np.nan, 82]))
的产率:
array([ 20., 40., 60.])
array([ 20., 40., 60., 80.])
array([ 20., 40., 60., 80., 81., 82.])
答案 0 :(得分:3)
只要列表中至少有一个元素None
,即使存在前导Some(_)
,此函数也会起作用。它在Integral
类型中也是通用的。 (如果需要,可以在Fractional
种类型中使其通用。)
def interpolate[T](list: Iterable[Option[T]])(implicit num: Integral[T]) = {
import num._
val prevs = list.zipWithIndex.scanLeft(Option.empty[(T, Int)]) {
case (prev, (cur, i)) => cur.map((_, i)).orElse(prev)
}
val nexts = list.zipWithIndex.scanRight(Option.empty[(T, Int)]) {
case ((cur, i), next) => cur.map((_, i)).orElse(next)
}
prevs.tail.zip(nexts).zipWithIndex.map {
case ((Some((prev, i)), Some((next, j))), k) =>
if (i == j) prev else prev + (next - prev) * fromInt(k - i) / fromInt(j - i)
case ((Some((prev, _)), _), _) => prev
case ((_, Some((next, _))), _) => next
}
}
这会构建prevs
,跟踪最左侧的Some(_)
及其索引,以及nexts
,它与右侧相同。然后,并行迭代prevs
和nexts
,它会根据左,右和索引生成插值。如果缺少左侧或右侧,只需从另一侧填写。
答案 1 :(得分:2)
我不熟悉numpy,但我认为这可以处理所有已证实的案例。它假定将定义列表中的第一个和最后一个元素(如果不是这样,则必须重新执行fillNones
函数)。
def interpolate(list: List[Option[Long]]) = {
// Creates a new list that will be used to replace a sequence of Nones
def fillNones(noneCount: Int, min: Long, max: Long): List[Long] = {
val stepSize = (max - min) / (noneCount + 1)
(1 to noneCount).toList.map(i => i * stepSize + min)
}
// We will recursively traverse the list
def recursive(done: List[Long], todo: List[Option[Long]]): List[Long] = {
todo match {
// If todo is empty, we are done
case Nil => done
// If the head of todo is Some(Long), then add it to the list of things that are done and move on
case Some(l) :: tail => recursive(done :+ l, tail)
// If the head wasn't Some(Long), then we have to figure out how many Nones are in a row, and replace them
case todo =>
// Find out how many Nones are in a row
val noneCount = todo.takeWhile(_.isEmpty).length
// Split the todo so we can get what is remaining
val remaining = todo.splitAt(noneCount)._2
// Create a new list to replace the sequence of Nones
val filled = fillNones(noneCount, done.last, remaining.head.get)
// Add our new filled list to done, and continue on
recursive(done ++ filled, remaining)
}
}
recursive(List.empty, list)
}
测试:
val example1 = List(Some(20l), None, Some(60l))
println(interpolate(example1))
// Prints: List(20, 40, 60)
val example2 = List(Some(20l), None, None, Some(80l))
println(interpolate(example2))
// Prints: List(20, 40, 60, 80)
val example3 = List(Some(20l), None, None, Some(80l), Some(90l), Some(100l))
println(interpolate(example3))
// Prints: List(20, 40, 60, 80, 90, 100)
val example4 = List(Some(20l), None, None, Some(80l), None, Some(82l))
println(interpolate(example4))
// Prints: List(20, 40, 60, 80, 81, 82)