在Python类中注册spark SQL用户定义函数

时间:2015-10-01 18:02:19

标签: python apache-spark apache-spark-sql

我正在通过Python API使用Spark进行一些数据处理。这是我正在使用的课程的简化部分:

class data_processor(object):
    def __init__(self,filepath):
        self.config = Config() # this loads some config options from file
        self.type_conversions = {int:IntegerType,str:StringType}
        self.load_data(filepath)
        self.format_age()

    def load_data(self,filepath,delim='\x01'):
        cols = [...] # list of column names 
        types = [int, str, str, ... ] # list of column types
        user_data = sc.textFile(filepath,use_unicode=False).map(lambda row: [types[i](val) for i,val in enumerate(row.strip().split(delim))])
        fields = StructType([StructField(field_name,self.type_conversions[field_type]()) for field_name,field_type in zip(cols,types)])
        self.user_data = user_data.toDF(fields)
        self.user_data.registerTempTable('data')

    def format_age(self):
        age_range = self.config.age_range # tuple of (age_min, age_max)
        age_bins = self.config.age_bins # list of bin boundaries
        def _format_age(age):
            if age<age_range[0] or age>age_range[1]:
                return None
            else:
                return  np.digitize([age],age_bins)[0]
        sqlContext.udf.register('format_age', lambda x: _format_age(x), IntegerType())

现在,如果我使用data=data_processor(filepath)实例化该类,我可以对数据帧进行查询。例如,这可以起作用:

sqlContext.sql("select * from data limit 10").take(1)

但我显然没有正确设置udf。例如,如果我尝试,

sqlContext.sql("select age, format_age(age) from data limit 10").take(1)

我收到错误:

Py4JJavaError: An error occurred while calling z:org.apache.spark.api.python.PythonRDD.collectAndServe.

(使用长堆栈跟踪,典型的Spark,这里包含的时间太长了。)

那么,我到底做错了什么?在这样的方法中定义UDF的正确方法是什么(最好是作为类方法)。我知道Spark不喜欢传递类对象,因此format_age的嵌套结构(受this question启发)。

想法?

1 个答案:

答案 0 :(得分:1)

答案简短而简单。您不能将NumPy数据类型用作Spark SQL中标准Python类型的替代品。当您将返回类型声明为np.digitize时,返回的numpy.int64类型为int而不是IntegerType

您需要做的就是转换从_format_age返回的值:

def _format_age(age):
    ...
    return  int(np.digitize(...))