Uh oh!
There was an error while loading. Please reload this page.
[SPARK-8992] [SQL] Add pivot to dataframe api - #7841
Conversation
rxin
commented
Aug 1, 2015
Jenkins, ok to test. |
aray
commented
Aug 3, 2015
@rxin it looks like Jenkins forgot about building this. Can you help trigger the build again? |
SparkQA
commented
Aug 3, 2015
Test build #1319 has finished for PR 7841 at commit
|
rxin
commented
Aug 12, 2015
@aray FYI this didn't make it into the 1.5 release (was submitted too close to the feature freeze deadline), but we will try to include it in Spark 1.6. |
Conflicts: sql/core/src/test/scala/org/apache/spark/sql/TestData.scala
JoshRosen
commented
Oct 20, 2015
@rxin, do you want to revisit this now for 1.6? |
courseSales.groupBy($"year").pivot($"course", "dotNET", "Java").agg(sum($"earnings")) Also, fixed master merge.
Conflicts: sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala
aray
commented
Oct 23, 2015
@rxin and @JoshRosen, this is ready for review now. |
rxin
commented
Oct 23, 2015
@aray Thanks a lot for updating this. To help api design, can you take a look at other frameworks and see what their signatures look like? |
aray
commented
Oct 23, 2015
@rxin here is my summary of other frameworks API's I'm going to use an example dataset form the pandas doc for all the examples (as df)
This APIscala> df.groupBy("A", "B").pivot("C", "small", "large").sum("D").show
+---+---+-----+-----+|A|B|small|large|+---+---+-----+-----+|foo|two|6|null||bar|two|6|7||foo|one|1|4||bar|one|5|4|+---+---+-----+-----+
scala> df.groupBy("A", "B").pivot("C", "small", "large").agg(sum("D"), avg("D")).show
+---+---+------------+------------+------------+------------+|A|B|small sum(D)|small avg(D)|large sum(D)|large avg(D)|+---+---+------------+------------+------------+------------+|foo|two|6|3.0|null|null||bar|two|6|6.0|7|7.0||foo|one|1|1.0|4|2.0||bar|one|5|5.0|4|4.0|+---+---+------------+------------+------------+------------+
scala> df.pivot(Seq($"A", $"B"), $"C", Seq("small", "large"), sum($"D")).show
+---+---+-----+-----+|A|B|small|large|+---+---+-----+-----+|foo|two|6|null||bar|two|6|7||foo|one|1|4||bar|one|5|4|+---+---+-----+-----+We require a list of values for the pivot column as we are required to know the output columns of the operator ahead of time. Pandas and reshape2 do not require this but the comparable SQL operators do. We also allow multiple aggregations which not all implementations allow. pandasThe comparable metod for pandas is Example >>>pivot_table(df, values='D', index=['A', 'B'], columns=['C'], aggfunc=np.sum)
smalllargefooone14two6NaNbarone54two67Pandas also allows multiple aggregations: >>>pivot_table(df, values='D', index=['A', 'B'], columns=['C'], aggfunc=[np.sum, np.average])
sumaverageClargesmalllargesmallABbarone4545two7676fooone4121twoNaN6NaN3References
See also: reshape2 (R)The comparable method for reshape2 is > dcast(df, A+B~C, sum)
UsingDasvaluecolumn:usevalue.vartooverride.ABlargesmall1barone452bartwo763fooone414footwo06Note that by default cast fills with the value from applying fun.aggregate to 0 length vector References
See also: MS SQL ServerSELECT*FROM df
pivot (sum(D) for C in ([small], [large])) phttp://sqlfiddle.com/#!3/cf887/3/0 References Oracle 11gSELECT*FROM df
pivot (sum(D) for C in ('small', 'large')) phttp://sqlfiddle.com/#!4/29bc5/3/0 Oracle also allows multiple aggregations and with similar output to this api SELECT*FROM df
pivot (sum(D) as sum, avg(D) as avg for C in ('small', 'large')) phttp://sqlfiddle.com/#!4/29bc5/5/0 References
Let me know if I can do anything else to help this along. Also would you mind adding me to the jenkins whitelist so I can test it? |
marmbrus
commented
Oct 23, 2015
ok to test |
SparkQA
commented
Oct 23, 2015
Test build #44249 has finished for PR 7841 at commit
|
rxin
commented
Oct 23, 2015
I like your 2nd interface more (group by and then pivot), since it is easier to get that working for both Java and Scala. We can implement a simpler interface for Python/R that's closer to existing frameworks. How hard would it be to not require the values? |
aray
commented
Oct 24, 2015
@rxin, Not requiring the values would necessitate doing a separate query for the distinct values of the column before the pivot query. It looks like at least some DF operations (eg, drop) would need the result so even if we made Pivot.output lazy we would be running an unexpected job. If a user really didn't want to specify the values, they can explicitly do the query: df.groupBy("A", "B").pivot("C", df.select("C").distinct.collect.map(_.getString(0)):_*).sum("D")Needing to know the output columns of an operator for analysis/planning is probably why the other SQL implementations require the values also (technically Oracle supports omitting it but only in XML mode where you essentially just get one column). |
Merge branch 'master' of https://github.com/apache/spark into sql-pivot Conflicts: sql/core/src/main/scala/org/apache/spark/sql/GroupedData.scala
SparkQA
commented
Oct 30, 2015
Test build #44643 has finished for PR 7841 at commit
|
rxin
commented
Nov 6, 2015
@aray sorry was away for spark summit - back now and will get to this today. |
Merge branch 'master' of https://github.com/apache/spark into sql-pivot Conflicts: sql/core/src/main/scala/org/apache/spark/sql/GroupedData.scala
SparkQA
commented
Nov 8, 2015
Test build #45316 has finished for PR 7841 at commit
|
SparkQA
commented
Nov 9, 2015
Test build #45366 has finished for PR 7841 at commit
|
rxin
commented
Nov 9, 2015
@aray I talked to a few more people about this. Most like the 2nd API more (groupBy.pivot.agg). I think it'd also be better to remove the requirement to specify values, e.g. just take in a column without the values. So it looks like courseSales.groupBy($"year").pivot($"course").agg(sum($"earnings"))Can you update the pull request? Thanks. |
rxin
commented
Nov 9, 2015
BTW we can also later add a variant that allows users to specify values directly, in order to avoid materializing the intermediate data. |
…ot provided. Add unit tests for this scenario.
aray
commented
Nov 9, 2015
@rxin Updated, the values are now optional. |
yhuai
commented
Nov 11, 2015
@aray This is very cool! Here are a few things I'd like to discuss.
|
SparkQA
commented
Nov 11, 2015
Test build #45645 has finished for PR 7841 at commit
|
- Use Literal's for the pivot column values instead of strings. - Change seperator when using multiple aggregates to `_` instead of space. - Some additional unit testing
aray
commented
Nov 11, 2015
@yhuai RE your questions (3 was already addressed above):
The argument for not requiring values I think is convenience and also similarity to other non-sql tools mentioned above. The negative is performance, but since we give them the option to specify I don't think that is a problem.
I initially used strings as the type since that is the common usage scenario. But I agree that using Literal's is the better solution and will avoid casts which could hurt performance. For convenience I kept the second method (changed to I really appreciate the review. Let me know if I can do anything else to help! |
There was a problem hiding this comment.
Seems we still need to check the number of children and make sure we have a single child?
There was a problem hiding this comment.
It should now work fine with aggregate functions that have multiple children as long as they ignore updates when all values are null. For example Corr should work since it only updates its aggregation buffer if both its arguments are non null.
SparkQA
commented
Nov 11, 2015
Test build #45659 has finished for PR 7841 at commit
|
…o prevent unintended OOM errors.
aray
commented
Nov 11, 2015
@yhuai I think this addresses everything we discussed, let me know if I missed anything or if there is anything else I can do. Again, thanks for the code review. |
yhuai
commented
Nov 11, 2015
LGTM pending jenkins. |
SparkQA
commented
Nov 12, 2015
Test build #45673 has finished for PR 7841 at commit
|
yhuai
commented
Nov 12, 2015
Thanks! Merging to master and branch 1.6. |
This adds a pivot method to the dataframe api.
Following the lead of cube and rollup this adds a Pivot operator that is translated into an Aggregate by the analyzer.
Currently the syntax is like:
~~courseSales.pivot(Seq($"year"), $"course", Seq("dotNET", "Java"), sum($"earnings"))~~
~~Would we be interested in the following syntax also/alternatively? and~~
courseSales.groupBy($"year").pivot($"course", "dotNET", "Java").agg(sum($"earnings"))
//or
courseSales.groupBy($"year").pivot($"course").agg(sum($"earnings"))
Later we can add it to `SQLParser`, but as Hive doesn't support it we cant add it there, right?
~~Also what would be the suggested Java friendly method signature for this?~~
Author: Andrew Ray <ray.andrew@gmail.com>
Closes#7841 from aray/sql-pivot.
(cherry picked from commit b8ff688)
Signed-off-by: Yin Huai <yhuai@databricks.com>rxin
commented
Nov 12, 2015
@aray do you want to submit a pull request for python api too? |
aray
commented
Nov 12, 2015
@rxin sure I'll put together a PR for the python API tonight |
This adds a pivot method to the dataframe api.
Following the lead of cube and rollup this adds a Pivot operator that is translated into an Aggregate by the analyzer.
Currently the syntax is like:
~~courseSales.pivot(Seq($"year"), $"course", Seq("dotNET", "Java"), sum($"earnings"))~~
~~Would we be interested in the following syntax also/alternatively? and~~
courseSales.groupBy($"year").pivot($"course", "dotNET", "Java").agg(sum($"earnings"))
//or
courseSales.groupBy($"year").pivot($"course").agg(sum($"earnings"))
Later we can add it to `SQLParser`, but as Hive doesn't support it we cant add it there, right?
~~Also what would be the suggested Java friendly method signature for this?~~
Author: Andrew Ray <ray.andrew@gmail.com>
Closesapache#7841 from aray/sql-pivot.rxin
commented
Jun 9, 2016
@aray this pull request was highlighted in http://www.slideshare.net/databricks/deep-dive-into-catalyst-apache-spark-20s-optimizer |
pushpam002
commented
Aug 18, 2020
thank you |
This adds a pivot method to the dataframe api.
Following the lead of cube and rollup this adds a Pivot operator that is translated into an Aggregate by the analyzer.
Currently the syntax is like:
courseSales.pivot(Seq($"year"), $ "course", Seq("dotNET", "Java"), sum($"earnings"))Would we be interested in the following syntax also/alternatively? andLater we can add it to
SQLParser, but as Hive doesn't support it we cant add it there, right?Also what would be the suggested Java friendly method signature for this?