在Python中创建自定义Spark RDD

在Python中创建自定义Spark RDD,python,apache-spark,pyspark,rdd,Python,Apache Spark,Pyspark,Rdd,可以在Python中扩展Spark的RDD以添加自定义运算符吗?如果不可能,那么如何为扩展RDD的类包装Scala代码,例如: 编辑:我正在尝试创建一个新的RDD,比如PersonRDD,并在PersonRDD上添加一组新操作符,例如PersonRDD.ComputeMediaInCome()。根据下面的链接,在Python中这样做并不简单。然而,由于这是一个旧的线程,我想知道是否有任何新的更新。如果不是,我想使用Scala来实现,但我不知道如何使用Py4J(mail archives.us.

可以在Python中扩展Spark的RDD以添加自定义运算符吗?如果不可能,那么如何为扩展RDD的类包装Scala代码,例如:

编辑:我正在尝试创建一个新的RDD,比如PersonRDD,并在PersonRDD上添加一组新操作符,例如PersonRDD.ComputeMediaInCome()。根据下面的链接,在Python中这样做并不简单。然而,由于这是一个旧的线程,我想知道是否有任何新的更新。如果不是,我想使用Scala来实现,但我不知道如何使用Py4J(mail archives.us.apache.org/mod_mbox/spark user/201308.mbox/…)从Python调用该类

任何建议或帮助都将不胜感激


Mandy

在分布式环境中计算精确的中值需要一些努力,所以假设您希望RDD中的值都是平方。让我们调用此方法
squares
,并假设其工作原理如下:

assert rdd.squares().collect() == rdd.map(lambda x: x * x).collect()
1.修改
pyspark.RDD
定义: 注意:如果修改类定义,则每个实例都可以访问
方块

2.创建RDD子类: 分配一个类是一个棘手的问题,所以在实践中,您应该以适当的方式创建RDD(参见示例实现)

3.将方法添加到实例 免责声明 首先,我还没有对这些进行足够长的测试,以确保没有隐藏的问题

此外,我认为这真的不值得大惊小怪。如果没有静态类型检查,很难找到任何好处,您可以使用函数、curry和更干净的方式获得类似的结果

from toolz import pipe
pipe(
    sc.parallelize([1, 2, 3]),
    squares,
    lambda rdd: rdd.collect())

我有一个类似的问题,虽然到目前为止我还没有在扩展版本上测试正常RDD的全部功能,但它仍能正常工作。这确实需要一些工作,我不确定这是否是最好的解决方案,但我所做的只是扩展RDD类,通过将返回新RDD的方法传递到新类的构造函数中并向类添加方法,重新实现这些方法。以下是代码的简短部分:

from pyspark.rdd import RDD, PipelinedRDD

class CustomRDD(RDD):
    def __init__(self, rdd, first=True):
        if first:
            rdd = custom_parser(rdd)
        self._jrdd = rdd._jrdd
        self.is_cached = rdd.is_cached
        self.is_checkpointed = rdd.is_checkpointed
        self.ctx = rdd.ctx
        self._jrdd_deserializer = rdd._jrdd_deserializer
        self._id = rdd._id
        self.partitioner = rdd.partitioner

    def mapPartitionsWithIndex(self, f, preservesPartition=False):
        return CustomRDD(PipelinedRDD(self, f, preservesPartition), False)

    def union(self, other):
        return WebtrendsRDD(super(WebtrendsRDD, self).union(other), False)

    def custom_method(self):
        return CustomRDD(self.filter(lambda x: x.has_property()), False)

mapPartitionsWithIndex方法由许多其他RDD功能调用,因此涵盖了很多内容,但是还有许多其他方法,您必须使用自己的构造函数来包装,以像我使用union时一样,不断获取自己的CustomRDD。

谢谢@zero323。我希望能够像Scala或Java一样干净地继承表单RDD,而不是破解解决方案。解决方案1不起作用,因为用户可以调用错误类型的新操作员。2不适用于RDD的子类,例如:newAPIHadoopFile,但可以用于我。。。再次感谢您抽出时间提出解决方案。嗯,您必须记住几件事。Python中的类型规则与Scala有很大不同,Python RDD并没有按类型参数化。从Scala的角度来看,每个Python RDD看起来都像
RDD[Any]
。因此,您有责任只调用适用的方法。类似于
sc.parallelize(range(3)).groupByKey()
显然没有意义,在执行转换时会失败,但在类型级别上没有任何错误。与Scala不同,您可以在运行时修改现有类。没有隐式转换地狱,我们知道“显式优于隐式”。如果添加一个方法,类型仍然没有问题。它唯一说明的是,根据实例的状态,调用此方法可能是有效的。从概念的角度来看,它可能是最接近Scala隐式方法的方法。尽管如此,我认为管道函数调用更安全、更具python风格,如果想在Spark之上创建DSL,也同样有效。这完全有道理。你显然比我有更多的经验,所以我也会考虑管道的例子。
import types

rdd = sc.parallelize([1, 2, 3])
# Reusing squares function defined above
rdd.squares = types.MethodType(squares, rdd)
from toolz import pipe
pipe(
    sc.parallelize([1, 2, 3]),
    squares,
    lambda rdd: rdd.collect())
from pyspark.rdd import RDD, PipelinedRDD

class CustomRDD(RDD):
    def __init__(self, rdd, first=True):
        if first:
            rdd = custom_parser(rdd)
        self._jrdd = rdd._jrdd
        self.is_cached = rdd.is_cached
        self.is_checkpointed = rdd.is_checkpointed
        self.ctx = rdd.ctx
        self._jrdd_deserializer = rdd._jrdd_deserializer
        self._id = rdd._id
        self.partitioner = rdd.partitioner

    def mapPartitionsWithIndex(self, f, preservesPartition=False):
        return CustomRDD(PipelinedRDD(self, f, preservesPartition), False)

    def union(self, other):
        return WebtrendsRDD(super(WebtrendsRDD, self).union(other), False)

    def custom_method(self):
        return CustomRDD(self.filter(lambda x: x.has_property()), False)