在Scala中插入数组

时间:2017-04-26 20:21:07

标签: scala collections linear-interpolation

我正在尝试线性插值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.])

2 个答案:

答案 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,它与右侧相同。然后,并行迭代prevsnexts,它会根据左,右和索引生成插值。如果缺少左侧或右侧,只需从另一侧填写。

答案 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)