假设我有一个整数的RDD,如果过滤器的窗口是3,如何应用中值滤波器?
我查看的Spark中的所有map和filter方法,一次只处理一个元素。但是要在窗口中找到中位数,我们想要同时知道窗口中所有元素的值。
我是Spark的新手,非常感谢任何帮助。
答案 0 :(得分:0)
我假设您需要应用此Wikipedia页面中描述的中值过滤器。 可以这样做,但在Spark中做效率不高。
基本上,您需要先与x_n
:RDD[X_N] => RDD[List(X_N -1, X_N, X_N +1)]
的邻居建立RDD。这是我的代码,用于计算维基百科中的相同示例。抱歉,它在Scala中,而不是Java中,但我认为这个想法应该是明确的。
val nums = List[Int](2, 80, 6, 3)
val rdd = sc.parallelize(nums, 2)
def median3(x: List[(Int, Int)]): Int = {
// here we discard the index of x and sort by its value
val x2 = x.map(_._1).sorted
x2(1)
}
val filtered = rdd.zipWithIndex.flatMap{
t =>
val (x_n, n) = t
// First, duplicate data: x_n should appear in windows of its neighbours
List(
(n - 1, (x_n, -1)), // the last value is kept for illustration
(n, (x_n, 0)),
(n + 1, (x_n, 1))
)
// grouping by n we will get (x_n-1, x_n, x_n +1)
// flatMap to discard corner cases
}.groupByKey().flatMap{
r =>
val (i, x) = r
(i, x.toList) match {
case (_, x) if x.size == 1 => None // corner cases: n=-1 and n=4
// first element and last element need special treatment
case (i, x) if i == 0 => Some((i, median3(List(x.head) ++ x)))
case (i, x) if x.size == 2 => Some((i, median3(x ++ List(x.reverse.head))))
// filtering itself
case (_, x) => Some((i, median3(x)))
}
}.sortByKey().map(_._2).collect()
// filtered: Array[Int] = Array(2, 6, 6, 3)
此代码效率低下有两个原因:
.groupByKey
和.sortByKey
),这是Spark中最昂贵的操作如果您的数据适合放在一台机器上,最好选择pandas。
答案 1 :(得分:0)
我发现Spark API中的窗口函数和UDAF是实现中值过滤的好方法。
注册UDAF:
sqlContext.udf().register("medianFilter", medianFilterUDAF);
通过窗口调用UDAF:
Column col1 = org.apache.spark.sql.functions.callUDF("medianFilter", intDF.col("column1")).over(Window.orderBy("columnA", "columnB").rowsBetween(-windowSize, windowSize));
medianFilterUDAF
是自定义UDAF的对象,它在窗口上执行元素的中值过滤。你必须覆盖UDAF类的方法即。 dataType()
,evaluate(Row buffer)
,initialize(MutableAggregationBuffer buffer)
,update(MutableAggregationBuffer buffer, Row row)
,merge(MutableAggregationBuffer buffer1, Row buffer2)
,inputSchema()
,bufferSchema()
和deterministic()
。
因此,对于中值过滤,您必须对缓冲区的元素进行排序,并在evaluate
方法中返回中间元素。