Pyspark SQL:在数据透视表中保留仅包含空值的条目

时间:2018-01-27 17:19:18

标签: sql apache-spark pyspark pivot

我正在尝试在PySpark SQL数据框架上创建一个数据透视表,它不会删除空值。我的输入表具有以下结构:

data

我使用spark 2.1在Python 2下运行IBM Data Science Experience云中的所有内容。

在pandas数据帧上执行此操作时,“dropna = false”参数会为我提供我想要的结果。

table= pd.pivot_table(ratings,columns=['movieId'],index=[ 'monthyear','userId'], values='rating', dropna=False)

作为输出,我得到以下内容:

Pandas pivot result

在PySpark SQL中我现在使用以下命令:

ratings_pivot = spark_df.groupBy('monthyear','userId').pivot('movieId').sum("rating").show()

作为输出,我得到以下内容:

PySpark pivot result

如您所见,所有仅显示空值的条目都不会显示。是否有可能在SQL中使用类似dropna = false的东西?由于这是非常具体的,我在互联网上找不到任何相关内容。

我刚刚提取了一个小数据集进行复制:

df = spark.createDataFrame([("1", 30, 2.5,200912), ("1", 32, 3.0,200912), ("2", 40, 4.0,201002), ("3", 45, 2.5,200002)], ("userID", "movieID", "rating", "monthyear"))

df.show()

+------+-------+------+---------+
|userID|movieID|rating|monthyear|
+------+-------+------+---------+
|     1|     30|   2.5|   200912|
|     1|     32|   3.0|   200912|
|     2|     40|   4.0|   201002|
|     3|     45|   2.5|   200002|
+------+-------+------+---------+

如果我现在运行数据透视查询,我会得到以下结果:

df.groupBy("monthyear","UserID").pivot("movieID").sum("rating").show()

+---------+------+----+----+----+----+
|monthyear|UserID|  30|  32|  40|  45|
+---------+------+----+----+----+----+
|   201002|     2|null|null| 4.0|null|
|   200912|     1| 2.5| 3.0|null|null|
|   200002|     3|null|null|null| 2.5|
+---------+------+----+----+----+----+

我现在想要的是,结果如下所示:

+---------+------+----+----+----+----+
|monthyear|UserID|  30|  32|  40|  45|
+---------+------+----+----+----+----+
|   201002|     2|null|null| 4.0|null|
|   200912|     2|null|null|null|null|
|   200002|     2|null|null|null|null|
|   200912|     1| 2.5| 3.0|null|null|
|   200002|     1|null|null|null|null|
|   201002|     1|null|null|null|null|
|   200002|     3|null|null|null| 2.5|
|   200912|     3|null|null|null|null|
|   201002|     3|null|null|null|null|
+---------+------+----+----+----+----+

2 个答案:

答案 0 :(得分:2)

对于行和列,Spark确实为所有null值保留条目:

Spark 2.1

Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /__ / .__/\_,_/_/ /_/\_\   version 2.1.1
      /_/

Using Python version 3.6.4 (default, Dec 21 2017 21:42:08)
SparkSession available as 'spark'.

In [1]: df = spark.createDataFrame([("a", 1, 4), ("a", 2, 2), ("b", 3, None), (None, 4, None)], ("x", "y", "z"))

In [2]: df.groupBy("x").pivot("y").sum("z").show()
+----+----+----+----+----+                                                      
|   x|   1|   2|   3|   4|
+----+----+----+----+----+
|null|null|null|null|null|
|   b|null|null|null|null|
|   a|   4|   2|null|null|
+----+----+----+----+----+

Spark 2.2

Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /__ / .__/\_,_/_/ /_/\_\   version 2.2.1
      /_/

Using Python version 3.6.4 (default, Dec 21 2017 21:42:08)
SparkSession available as 'spark'.

In [1]: df = spark.createDataFrame([("a", 1, 4), ("a", 2, 2), ("b", 3, None), (None, 4, None)], ("x", "y", "z"))

In [2]: df.groupBy("x").pivot("y").sum("z").show()
+----+----+----+----+----+                                                      
|   x|   1|   2|   3|   4|
+----+----+----+----+----+
|null|null|null|null|null|
|   b|null|null|null|null|
|   a|   4|   2|null|null|
+----+----+----+----+----+

Spark 2.3

Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /__ / .__/\_,_/_/ /_/\_\   version 2.3.0
      /_/

Using Python version 3.6.4 (default, Dec 21 2017 21:42:08)
SparkSession available as 'spark'.

In [1]: df = spark.createDataFrame([("a", 1, 4), ("a", 2, 2), ("b", 3, None), (None, 4, None)], ("x", "y", "z"))

In [2]: df.groupBy("x").pivot("y").sum("z").show()
+----+----+----+----+----+                                                      
|   x|   1|   2|   3|   4|
+----+----+----+----+----+
|null|null|null|null|null|
|   b|null|null|null|null|
|   a|   4|   2|null|null|
+----+----+----+----+----+

答案 1 :(得分:2)

Spark提供这样的东西,因为它不会扩展。仅pivot就足够昂贵了。它可以使用外连接手动完成:

n = 20 # Adjust value depending on the data

wide = (df
    # Get unique months
    .select("monthyear")
    .distinct()
    .coalesce(n)  # Coalesce to avoid partition number "explosion"
    # Same as above for UserID and get Cartesian product
    .crossJoin(df.select("UserID").distinct().coalesce(n))
    # Join with pivoted data
    .join(
        df.groupBy("monthyear", "UserID")
            .pivot("movieID")
            .sum("rating"),
        ["monthyear", "UserID"], 
        "leftouter"))
wide.show()

# +---------+------+----+----+----+----+
# |monthyear|UserID|  30|  32|  40|  45|
# +---------+------+----+----+----+----+
# |   201002|     3|null|null|null|null|
# |   201002|     2|null|null| 4.0|null|
# |   200002|     1|null|null|null|null|
# |   200912|     1| 2.5| 3.0|null|null|
# |   200002|     3|null|null|null| 2.5|
# |   200912|     2|null|null|null|null|
# |   200912|     3|null|null|null|null|
# |   201002|     1|null|null|null|null|
# |   200002|     2|null|null|null|null|
# +---------+------+----+----+----+----+