将clojure.lang.LazySeq转换为org.apache.spark.api.java.JavaRDD类型

将clojure.lang.LazySeq转换为org.apache.spark.api.java.JavaRDD类型,java,clojure,apache-spark,clojure-java-interop,flambo,Java,Clojure,Apache Spark,Clojure Java Interop,Flambo,我在clojure中开发了一个函数,用于从最后一个非空值填充一个空列,我假设这是可行的,给定 (:require [flambo.api :as f]) (defn replicate-val [ rdd input ] (let [{:keys [ col ]} input result (reductions (fn [a b] (if (empty? (nth b col))

我在clojure中开发了一个函数,用于从最后一个非空值填充一个空列,我假设这是可行的,给定

(:require [flambo.api :as f])

(defn replicate-val
  [ rdd input ]
  (let [{:keys [ col ]} input
    result (reductions (fn [a b]
                         (if (empty? (nth b col))
                           (assoc b col (nth a col))
                           b)) rdd )]
(println "Result type is: "(type result))))
得到这个:

;=> "Result type is:  clojure.lang.LazySeq"
问题是如何使用flambo(spark包装器)将其转换回JavaRDD类型

我尝试了
(f/map result#(.toJavaRDD%))
let
表单中尝试转换为
JavaRDD
类型

我犯了这个错误

"No matching method found: map for class clojure.lang.LazySeq"
这是预期的,因为结果的类型为
clojure.lang.LazySeq

问题是如何进行这种转换,或者如何重构代码以适应这种情况

以下是一个示例输入rdd:

(type rdd) ;=> "org.apache.spark.api.java.JavaRDD"
但看起来:

[["04" "2" "3"] ["04" "" "5"] ["5" "16" ""] ["07" "" "36"] ["07" "" "34"] ["07" "25" "34"]]
所需输出为:

[["04" "2" "3"] ["04" "2" "5"] ["5" "16" ""] ["07" "16" "36"] ["07" "16" "34"] ["07" "25" "34"]]

谢谢。

首先,RDD是不可移植的(不要实现
ISeq
),因此您不能使用
缩减
。忽略访问以前记录的整个想法是相当棘手的。首先,您不能直接从另一个分区访问值。此外,只有不需要洗牌的转换才能保持顺序

这里最简单的方法是使用具有明确顺序的数据帧和窗口函数,但据我所知,Flambo没有实现所需的方法。使用原始SQL或访问Java/Scala API始终是可能的,但如果您想避免这种情况,可以尝试使用以下管道

首先,让我们使用每个分区的最后一个值创建广播变量:

(需要“[flambo.broadcast:as bd]”)
(导入org.apache.spark.TaskContext)
(每个零件最后一个def(f/fn[it]
(让[context(TaskContext/get)xs(迭代器seq it)]
[[(.partitionId上下文)(最后一个xs)]))
(def上次VAL bd
(屋宇署/广播事务主任)
(进入{}(->rdd(f/map分区每部分最后一个)(f/collect()()))
下一步是实际作业的某个助手:

(定义填充对[col]
(fn[x](让[[ab]x](如果(空)(第n个b列))(关联b列(第n个a列))b)))
(def加注对
(f/fn[it](让[part id(.partitionId(TaskContext/get));;获取partion id
xs(迭代器seq it);;将输入转换为seq
prev(如果(零?零件id);;查找上一个元素
(第一个X)((bd/值最后一个VAL bd)零件id))
;创建成对的序列(上一个,当前)
成对(分区2-1(cons-prev-xs))
和以前一样
{:键[col]}输入
准备映射函数
映射器(填充对列)]
(地图映射器对)
最后,您可以使用
填充对
映射分区:

(->rdd(f/map分区填充对)(f/collect))
这里隐藏的一个假设是分区的顺序遵循值的顺序。在一般情况下,它可能是,也可能不是,但如果没有明确的顺序,它可能是你能得到的最好的

另一种方法是
zipWithIndex
,交换值的顺序,并使用偏移量执行联接

(需要“[flambo.tuple:as-tp]”)
(def rdd idx(f/map to pair(.zipWithIndex rdd)#(.swap%))
(def rdd idx偏移量
(f/映射到rdd idx对
(fn[t](let[p(f/untuplet)](tp/元组(dec)(第一个p))(第二个p(()())))
(f/map(f/values(.rightOuterJoin rdd idx offset rdd idx))f/untuple)
接下来,您可以使用与前面类似的方法进行映射

编辑

速记。问题是缺少引用透明性,并且您正在利用给定实现的附带属性,而不是契约。
map
语义中没有任何东西要求按给定顺序处理元素。如果内部实现发生更改,它可能不再有效。使用Clojure

(defn foo [x] (let [aa @a] (swap! a (fn [&args] x)) aa))

(def a (atom 0))
(map foo (range 1 20))
与之相比:

(def a (atom 0))
(pmap foo (range 1 20))

@Jyd我添加了一个关于原子的简短评论。我想你会发现它很有用。我真的很感谢你在这方面所做的努力,我不确定在clojure开发中使用可变数据结构,正如我建议的那样,如果我改变数据结构,只是试图避免使用可变数据,那就没有出路了,尤其是现在我正处于clojure开发的早期阶段。或者您有什么建议?快速跟进,我注意到
[“5”“16”“][07”“36”“][07”“34”“][/code>将产生
[“5”“16”“][07”“16”“36”“][07”“34”“][/code>而不是
[“5”“16”“][07”“16”“36”“][07”“16”“34”“][/code>,正如预期的那样,当然,如果我们再次重复这个过程来填补空白,我们可以支持任意查找,但这需要一些努力。您应该收集last not empty以进行广播,而不是last元素。然后,您必须更正它以处理所有空列的分区(将上一个分区的最后一个非空值移位)。最后,您可以使用递归函数代替partition+map,该函数将用最后一个非空值填充列。