如何在Spark 2.1上更新pyspark数据帧元数据?

时间:2017-10-10 13:18:51

标签: apache-spark pyspark apache-spark-sql spark-dataframe pyspark-sql

我遇到了SparkML的OneHotEncoder问题,因为它读取数据帧元数据以确定它应为其创建的稀疏矢量对象分配的值范围。

更具体地说,我正在使用包含0到23之间所有单个值的训练集对“小时”字段进行编码。

现在我使用管道的“transform”方法对单行数据帧进行评分。

不幸的是,这导致了OneHotEncoder

的不同编码的稀疏矢量对象

(24,[5],[1.0])vs.(11,[10],[1.0])

我已经记录了这个here,但这被认为是重复的。所以在这个thread中有一个解决方案,用于更新数据帧的元数据以反映“小时”字段的实际范围:

from pyspark.sql.functions import col

meta = {"ml_attr": {
    "vals": [str(x) for x in range(6)],   # Provide a set of levels
    "type": "nominal", 
    "name": "class"}}

loaded.transform(
    df.withColumn("class", col("class").alias("class", metadata=meta)) )

不幸的是我收到了这个错误:

  

TypeError:alias()得到了一个意外的关键字参数'metadata'

1 个答案:

答案 0 :(得分:3)

在PySpark 2.1中,alias方法没有参数metadatadocs) - Spark 2.2中的became available;尽管如此,仍然可以修改PySpark中的列元数据< 2.2,感谢令人难以置信的Spark Gotchas,由@eliasah@zero323维护:

import json

from pyspark import SparkContext
from pyspark.sql import Column
from pyspark.sql.functions import col

spark.version
# u'2.1.1'

df = sc.parallelize((
        (0, "x", 2.0),
        (1, "y", 3.0),
        (2, "x", -1.0)
        )).toDF(["label", "x1", "x2"])

df.show()
# +-----+---+----+ 
# |label| x1|  x2|
# +-----+---+----+
# |    0|  x| 2.0|
# |    1|  y| 3.0|
# |    2|  x|-1.0|
# +-----+---+----+

假设我们希望强制我们的label数据在0到5之间的可能性,尽管在我们的数据框中介于0和2之间,我们应该如何修改列元数据:

def withMeta(self, alias, meta):
    sc = SparkContext._active_spark_context
    jmeta = sc._gateway.jvm.org.apache.spark.sql.types.Metadata
    return Column(getattr(self._jc, "as")(alias, jmeta.fromJson(json.dumps(meta))))

Column.withMeta = withMeta

# new metadata:
meta = {"ml_attr": {"name": "label_with_meta",
                    "type": "nominal",
                    "vals": [str(x) for x in range(6)]}}

df_with_meta = df.withColumn("label_with_meta", col("label").withMeta("", meta))

通过zero323向this answer致敬!