在Python中创建自定义Spark RDD

时间:2015-07-12 13:07:33

标签: python apache-spark pyspark rdd

是否可以在Python中扩展Spark的RDD以添加自定义运算符?如果不可能,那么如何为扩展RDD的类包装Scala代码,例如: http://blog.madhukaraphatak.com/extending-spark-api/

编辑:我正在尝试创建一个新的RDD,比如PersonRDD并在PersonRDD上添加一组新的运算符,例如。 PersonRDD.computeMedianIncome()。根据下面的链接,在Python中执行此操作并非易事。但是,由于它是一个旧线程,我想知道是否有任何新的更新。如果没有,我想用Scala来做,但我不知道如何使用Py4J从Python调用该类(mail-archives.us.apache.org/mod_mbox/spark-user/201308.mbox/...)

非常感谢任何建议或帮助。

曼迪

2 个答案:

答案 0 :(得分:4)

在分布式环境中计算精确中位数需要花费一些精力,所以假设你想要像RDD中的所有值那样平方。让我们调用这个方法squares并假设它应该如下工作:

assert rdd.squares().collect() == rdd.map(lambda x: x * x).collect()

1。修改pyspark.RDD定义:

from pyspark import RDD

def squares(self):
    return self.map(lambda x: x * x)

RDD.squares = squares
rdd = sc.parallelize([1, 2, 3])
assert rdd.squares().collect() == [1, 4, 9]

注意:如果修改类定义,则每个实例都可以访问squares

2。创建RDD子类:

class RDDWithSquares(RDD):
    def squares(self):
        return self.map(lambda x: x * x)

rdd = sc.parallelize([1, 2, 3])
rdd.__class__ = RDDWithSquares # WARNING: see a comment below

分配一个类是一个肮脏的黑客,所以在实践中你应该以适当的方式创建一个RDD(参见例如context.parallelize实现)。

3。将方法添加到实例

import types

rdd = sc.parallelize([1, 2, 3])
# Reusing squares function defined above
rdd.squares = types.MethodType(squares, rdd)

声明

首先,我没有对这些问题进行过长时间的测试,以确保没有任何隐藏的问题。

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

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

答案 1 :(得分:0)

我有一个类似的问题,虽然我还没有在我的扩展版本上测试普通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