使用toDF()函数从PySpark中的RDD转换为Dataframe时的奇怪行为

时间:2018-10-31 05:33:17

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

我是Spark的新手。当我使用toDF()函数将RDD转换为数据帧时,似乎可以像我之前编写的map()一样计算所有转换函数。我想知道PySpark中的toDF()是转换还是动作。

我创建了一个简单的RDD并使用一个简单的函数输出其值(仅用于测试),并在map()之后使用toDF()。结果似乎部分运行了map中的函数。当我显示数据帧的结果时,toDF()就像转换一样,然后再次输出结果。

>>> a = sc.parallelize([(1,),(2,),(3,)])
>>> def f(x):
...     print(x[0])
...     return (x[0] + 1, )
...
>>> b = a.map(f).toDF(["id"])
2
1
>>> b = a.map(f).toDF(["id"]).show()
2
1
1
2
3
+---+
| id|
+---+
|  2|
|  3|
|  4|
+---+

有人可以告诉我为什么PySpark中的toDF()函数既充当动作又充当变换?非常感谢。

PS:在我的案例中,toDF就像转换一样。

1 个答案:

答案 0 :(得分:1)

这并不奇怪。由于您未提供架构,因此Spark必须根据数据进行推断。如果RDD是输入,它将调用SparkSession._createFromRDD,然后调用SparkSession._inferSchema,如果缺少samplingRatio,则调用will evaluate up to 100 row

first = rdd.first()
if not first:
    raise ValueError("The first row in RDD is empty, "
                     "can not infer schema")
if type(first) is dict:
    warnings.warn("Using RDD of dict to inferSchema is deprecated. "
                  "Use pyspark.sql.Row instead")


if samplingRatio is None:
    schema = _infer_schema(first, names=names)
    if _has_nulltype(schema):
        for row in rdd.take(100)[1:]:
            schema = _merge_type(schema, _infer_schema(row, names=names))
            if not _has_nulltype(schema):
                break
        else:
            raise ValueError("Some of types cannot be determined by the "
                             "first 100 rows, please try again with sampling")

现在,剩下的唯一难题就是为什么它不能准确评估一条记录。毕竟,您的情况first不为空并且不包含None

这是因为first是通过take实现的,并不保证将评估确切的项目数。如果第一个分区不能产生所需数量的项目,它将反复增加要扫描的分区数量。请检查the implementation了解详情。

如果要避免这种情况,则应使用createDataFrame并以DDL字符串形式提供模式:

spark.createDataFrame(a.map(f), "val: integer")

或等效的StructType

您不会在Scala副本中找到任何类似的行为,因为它没有在toDF中使用架构推断。它要么从Encoder(使用Scala反射获取)中检索相应的模式,要么根本不允许转换。最接近的类似行为是对诸如CSV or JSON之类的输入源进行推断:

spark.read.json(Seq("""{"foo": "bar"}""").toDS.map(x => { println(x); x }))