火花枢轴没有聚合

时间:2016-11-22 22:29:24

标签: apache-spark apache-spark-sql

https://databricks.com/blog/2016/02/09/reshaping-data-with-pivot-in-apache-spark.html很好地解释了一个支点如何用于火花。

在我的python代码中,我使用没有聚合的pandas但重置索引并加入:

pd.pivot_table(data=dfCountries, index=['A'], columns=['B'])
countryToMerge.index.name = 'ISO'
df.merge(countryToMerge['value'].reset_index(), on='ISO', how='inner')

这如何在火花中发挥作用?

我尝试手动分组和加入,如:

val grouped = countryKPI.groupBy("A").pivot("B")
df.join(grouped, df.col("ISO") === grouped.col("ISO")).show

但这不起作用。 reset_index如何适应spark /如何以火花原生方式实现?

修改

python代码的最小示例:

import pandas as pd
from datetime import datetime, timedelta
import numpy as np
dates = pd.DataFrame([(datetime(2016, 1, 1) + timedelta(i)).strftime('%Y-%m-%d') for i in range(10)], columns=["dates"])
isos = pd.DataFrame(["ABC", "POL", "ABC", "POL","ABC", "POL","ABC", "POL","ABC", "POL"], columns=['ISO'])
dates['ISO'] = isos.ISO
dates['ISO'] = dates['ISO'].astype("category")
countryKPI = pd.DataFrame({'country_id3':['ABC','POL','ABC','POL'],
                       'indicator_id':['a','a','b','b'],
                       'value':[7,8,9,7]})
countryToMerge = pd.pivot_table(data=countryKPI, index=['country_id3'], columns=['indicator_id'])
countryToMerge.index.name = 'ISO'
print(dates.merge(countryToMerge['value'].reset_index(), on='ISO', how='inner'))

  dates  ISO  a  b
0  2016-01-01  ABC  7  9
1  2016-01-03  ABC  7  9
2  2016-01-05  ABC  7  9
3  2016-01-07  ABC  7  9
4  2016-01-09  ABC  7  9
5  2016-01-02  POL  8  7
6  2016-01-04  POL  8  7
7  2016-01-06  POL  8  7
8  2016-01-08  POL  8  7
9  2016-01-10  POL  8  7

跟随scala / spark

val dates = Seq(("2016-01-01", "ABC"),
    ("2016-01-02", "ABC"),
    ("2016-01-03", "POL"),
    ("2016-01-04", "ABC"),
    ("2016-01-05", "POL"),
    ("2016-01-06", "ABC"),
    ("2016-01-07", "POL"),
    ("2016-01-08", "ABC"),
    ("2016-01-09", "POL"),
    ("2016-01-10", "ABC")
  ).toDF("dates", "ISO")
    .withColumn("dates", 'dates.cast("Date"))

  dates.show
  dates.printSchema

  val countryKPI = Seq(("ABC", "a", 7),
    ("ABC", "b", 8),
    ("POL", "a", 9),
    ("POL", "b", 7)
  ).toDF("country_id3", "indicator_id", "value")

  countryKPI.show
  countryKPI.printSchema

val grouped = countryKPI.groupBy("country_id3").pivot("indicator_id")

4 个答案:

答案 0 :(得分:2)

以下代码片段似乎有效 - 但我不确定avg的汇总是否正确 - 尽管"拟合数字"是输出。

countryKPI.groupBy("country_id3").pivot("indicator_id").avg("value").show

我不确定这是否效率低下"与仅重用值(因为我不想聚合)相比,获得更大量的数据(平均)。

答案 1 :(得分:0)

没有一个很好的方法来进行数据透视而不在Spark中进行汇总,基本上,它假定您只需要使用OneHotEncoder来实现该功能,但是缺少人类可以理解的简单数据透视。我发现最好的方法是:

val pivot = countryKPI
  .groupBy("country_id3", "value")
  .pivot("indicator_id", Seq("a", "b"))
  .agg(first(col("indicator_id")))

pivot.show
+-----------+-----+----+----+
|country_id3|value|   a|   b|
+-----------+-----+----+----+
|        ABC|    8|null|   b|
|        POL|    9|   a|null|
|        POL|    7|null|   b|
|        ABC|    7|   a|null|
+-----------+-----+----+----+

但是,如果(country_id3, value)在数据集中没有区别,那么您将折叠行,并可能从数据透视列中获取一个毫无意义的first()值。

另一种方法是将id列添加到数据集,在该新id上分组,旋转所需的列,然后再联接回原始数据集。这是一个示例:

val countryWithId = countryKPI.withColumn("id", monotonically_increasing_id)
val pivotted = countryWithId
.groupBy("id")
.pivot("indicator_id")
.agg(first(col("indicator_id")))

val pivot2 = countryWithId.join(pivotted, Seq("id")).drop("id") //.drop("indicator_id")

pivot2.show
+-----------+------------+-----+----+----+
|country_id3|indicator_id|value|   a|   b|
+-----------+------------+-----+----+----+
|        ABC|           a|    7|   a|null|
|        ABC|           b|    8|null|   b|
|        POL|           a|    9|   a|null|
|        POL|           b|    7|null|   b|
+-----------+------------+-----+----+----+

在这种情况下,您仍然具有原始的数据透视列,但是如果愿意,也可以.drop()

答案 2 :(得分:0)

这对我有用-不是因为我支持Georg Heiler关于使用“平均值”进行聚合的主张。为了在不进行汇总的情况下应用数据透视表,您只需指定 groupBy 字词,并使其尽可能地详细。如果满足此条件,则可以使用任何聚合项(平均,最小,最大等)。

  countryKPI.groupBy("country_id3").pivot("indicator_id").agg(avg("value").alias("value_term"))

答案 3 :(得分:0)

在pyspark中,您可以使用以下内容:

类似于上述@Derek Kaknes; 创建一个唯一的id列,然后使用sum或其他agg函数进行汇总。确保您分组的列包括新创建的id_column。

from pyspark.sql.functions import monotonically_increasing_id

df = df.withColumn("id_column", monotonically_increasing_id())
groupby_columns = ["id_column"] + your_desired_columns
df = df.groupBy(groupby_columns).pivot(pivot_column).sum(value_column)