使用Spark轻松做数据透视(Pivot)

本文深入解析了Spark中的Pivot算子,从1.6版本引入至今,Pivot在2.4版本得到了增强,极大简化了数据处理流程。文章通过实例演示了如何使用Pivot进行数据透视,包括设置分组、pivot列及聚合方式,并对比了不同设置下的输出结果,同时介绍了Spark SQL中的Pivot语法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

spark从1.6开始引入,到现在2.4版本,pivot算子有了进一步增强,这使得后续无论是交给pandas继续做处理,还是交给R继续分析,都简化了不少。大家无论在使用pandas、numpy或是R的时候,首先会做的就是处理数据,尤其是将列表,转成成合适的形状。


列表

在说透视表之前,我们先看看,什么是列表,在传统观念上,列表的每一行代表一条记录,而每一列代表一个属性。


+-------+-------+-----+

|   date|project|value|

+-------+-------+-----+

|2018-01|     p1|  100|

|2018-01|     p2|  200|

|2018-01|     p3|  300|

|2018-02|     p1| 1000|

|2018-02|     p2| 2000|

|2018-03|     px|  999|

+-------+-------+-----+


举个简单的例子,如上表,一条记录可能代表某个项目,在某个年月创造的价值。而在这个表里面,某一列,就代表一个属性,比如date代表日期,project代表项目名称。而这里每一行,代表一条独立,完整的记录,一条与另外一条记录,没有直接的关系。


这种结构,也是一般关系型数据库的数据结构。


透视表

透视表没有一个明确的定义,一般是观念上是指,为了方便进行数据分析,而对数据进行一定的重排,方便后续分析,计算等操作。透视表每一个元素及其对应的“坐标”一起形成一条完整的记录。


+-------+------+------+-----+-----+

|   date|    p1|    p2|   p3|   px|

+-------+------+------+-----+-----+

|2018-01| 100.0| 200.0|300.0|  0.0|

|2018-02|1000.0|2000.0|  0.0|  0.0|

|2018-03|   0.0|   0.0|  0.0|999.0|

+-------+------+------+-----+-----+


上面的表,是将列表进行重排后的透视表,其第一行和第一列可以理解成索引,而在表中根据索引可以确定一条唯一的值,他们一起组成一条相当于列表里的数据。


通过一般的定义,我们能看出,透视表主要用于分析,所以,一般的场景我们都会先对数据进行聚合,以后再对数据分析,这样也更有意义。就好像,将话费清单,做成透视表,尽管逻辑上没有任何问题,但是结果是可能比现在的清单列表更难查阅。


PS:一些可以借鉴的名词,目前维基百科并没有收录,也只能权且理解一下吧

640?wx_fmt=png


建模拟数据


先来模拟个数据吧,按照前面的例子,建个csv,这里多加了一列s2,是为了做多透视列的,

date,project,value,s2
2018-01,p1,100,12
2018-01,p2,200,33
2018-01,p3,300,44
2018-02,p1,1000,22
2018-02,p2,2000,41
2018-03,px,999,22



spark API


我们先来看下DEMO程序

SparkConf sparkConf = new SparkConf().setAppName("JavaWordCount").setMaster("local");
SparkContext sc = SparkContext.getOrCreate(sparkConf);
SparkSession ss = new SparkSession(sc);
Dataset<Row> ds = ss.read()
//csv分隔符
.option("sep", ",")
//是否包含header
.option("header", "true")
//加载csv路径
.csv("E:\\devlop\\workspace\\sparkdemo\\src\\main\\java\\com\\dafei1288\\spark\\data1.csv");
Dataset<Row> r =
//设置分组
ds.groupBy(col("date"))
       //设置pivot
       .pivot("project")
       //设置聚合
       .agg(sum("value"));
r.show();


在加载csv的时候,我们设置了分隔符,以及读取表头。

对加载后的dataset只需要进行3步设置


  1. groupBy 设置分组列

  2. pivot 设置pivot列

  3. agg 设置聚合方式,可以是求和、平均等聚合函数


我们得到的输出结果如下:


+-------+------+------+-----+-----+

|   date|    p1|    p2|   p3|   px|

+-------+------+------+-----+-----+

|2018-03|  null|  null| null|999.0|

|2018-02|1000.0|2000.0| null| null|

|2018-01| 100.0| 200.0|300.0| null|

+-------+------+------+-----+-----+


请注意,这里和sql有些区别,就是groupBy的时候,不需要将project列写入了,如果写入成了

groupBy(col("date"),col("project"))

那么结果就是这样了

+-------+-------+------+------+-----+-----+

|   date|project|    p1|    p2|   p3|   px|

+-------+-------+------+------+-----+-----+

|2018-01|     p3|  null|  null|300.0| null|

|2018-01|     p2|  null| 200.0| null| null|

|2018-01|     p1| 100.0|  null| null| null|

|2018-03|     px|  null|  null| null|999.0|

|2018-02|     p1|1000.0|  null| null| null|

|2018-02|     p2|  null|2000.0| null| null|

+-------+-------+------+------+-----+-----+


sparkSQL 


SparkConf sparkConf = new SparkConf().setAppName("JavaWordCount").setMaster("local");
SparkContext sc = SparkContext.getOrCreate(sparkConf);
SparkSession ss = new SparkSession(sc);
Dataset<Row> ds = ss.read() .option("sep", ",")
.option("header", "true").csv("E:\\devlop\\workspace\\sparkdemo\\src\\main\\java\\com\\dafei1288\\spark\\data1.csv");
ds.registerTempTable("f");
Dataset<Row> r = ds.sqlContext().sql(
"select * from (
   select date,project as p,sum(value) as ss from f group by date,project
  )
 pivot (  
     sum(ss)
     for p in ( 'p1','p2','p3','px' )  
  )
  order by date");
r.na().fill(0).show();


可以看到,这里我们将读取的csv注册成了表f,使用spark sql语句,这里和oracle的透视语句类似

pivot语法: pivot( 聚合列  for  待转换列 in (列值) )     

其语法还是比较简单的。


为了展示数据好看一点,我特意使用语句

r.na().fill(0)

将空值`null`替换成了0。


+-------+------+------+-----+-----+

|   date|    p1|    p2|   p3|   px|

+-------+------+------+-----+-----+

|2018-01| 100.0| 200.0|300.0|  0.0|

|2018-02|1000.0|2000.0|  0.0|  0.0|

|2018-03|   0.0|   0.0|  0.0|999.0|

+-------+------+------+-----+-----+



多聚合列


上文提到了,多做了一列,就是为了这个DEMO准备的,使用如下SparkSQL语句,设置多聚合列透视表

select * from (
   select date,project as p,sum(value) as ss,sum(s2) as ss2 from f group by date,project
)
pivot (  
     sum(ss),sum(ss2)  
    for p in ( 'p1','p2','p3','px' )
)
order by date


这里为例方便看,我就截图了

640?wx_fmt=png



为了防止OOM的情况,spark对pivot的数据量进行了限制,其可以通过spark.sql.pivotMaxValues 来进行修改,默认值为10000,这里是指piovt后的列数。


好了,关于spark pivot就介绍到这了,其实这里与矩阵的行列转换类似,pivot对应的也有unpivot,下次我们再聊。


参考资料:

https://stackoverflow.com/questions/30244910/how-to-pivot-dataframe

https://databricks.com/session/pivoting-data-with-sparksql


<think>好的,我现在需要帮助用户解决在Spark SQL中使用PIVOT操作的问题。用户希望得到一个示例以及相关文档的指引。首先,我需要回忆一下Spark SQL中PIVOT的用法,确保自己理解正确。 首先,PIVOT操作用于将行数据转换为列,这在数据透视或生成交叉表时非常有用。Spark SQL确实支持PIVOT,但可能语法上与某些传统SQL有所不同。我需要确认正确的语法结构,比如是否需要使用特定的函数或关键字。 接下来,用户需要示例。我应该构造一个简单的数据集,比如销售数据,包含年份、产品类别和销售额。然后展示如何通过PIVOT将产品类别作为列,年份作为行,聚合销售额。这样用户能直观地看到数据如何转换。 然后,关于文档引用,我需要找到官方文档中关于PIVOT的部分。记得Spark SQL的文档通常可以在Apache官网上找到,或者Databricks的文档也可能有详细说明。需要确保提供的链接或引用是准确的,避免用户找不到资源。 另外,用户可能在使用PIVOT时遇到常见问题,比如性能问题或者处理大量动态列的情况。这时候可能需要提到优化方法,比如提前确定列名,或者使用动态生成SQL语句的技巧。不过根据用户当前的问题,可能不需要深入讨论这些,但如果有相关问题生成的话,可以作为后续问题提出。 还需要检查示例中的代码是否正确。比如,使用SELECT语句后的字段顺序,PIVOT子句的位置是否正确,聚合函数是否正确应用。例如,正确的语法应该是将PIVOT放在FROM之后,指定聚合函数、透视列和要转换的值。 最后,确保回答符合用户的要求:结构清晰,分步骤说明,使用中文,并且正确应用LaTeX格式。比如,在提到列名或函数时使用$...$包裹,独立公式用$$...$$,但在这个问题中可能不需要数学公式,但如果有涉及计算的例子,需要注意格式。 总结步骤:解释PIVOT的作用,提供示例数据集和代码,说明代码各部分的作用,指出文档来源,最后生成相关问题。同时检查是否有遗漏,如语法细节或常见错误提示,确保用户能顺利应用示例并进一步查阅文档。</think>在 Spark SQL 中,`PIVOT` 操作用于将行数据转换为列,常用于生成交叉表或数据透视。以下是一个示例及说明: ### 示例:统计不同年份的销售额按产品类别展开 假设有表 `sales`,包含字段 `year`(年份)、`category`(产品类别)和 `amount`(销售额): ```sql -- 创建示例数据 CREATE TEMPORARY VIEW sales AS SELECT 2020 AS year, 'A' AS category, 100 AS amount UNION ALL SELECT 2020, 'B', 200 UNION ALL SELECT 2021, 'A', 150 UNION ALL SELECT 2021, 'B', 250; -- 使用 PIVOT 按年份横向展示不同类别的销售额 SELECT * FROM sales PIVOT ( SUM(amount) AS total -- 聚合函数 FOR category IN ('A', 'B') -- 待转换的列值 ); ``` #### 输出结果: | year | A_total | B_total | |------|---------|---------| | 2020 | 100 | 200 | | 2021 | 150 | 250 | ### 语法说明 1. **`PIVOT` 子句位置**:需在 `FROM` 后直接使用,而非 `SELECT` 中。 2. **聚合函数**:必须指定(如 `SUM`、`AVG`),用于合并同一组的多个值。 3. **列值限定**:`FOR ... IN` 需明确列出待转换的列值(如 `'A', 'B'`)。若列值动态变化,需提前提取或通过编程生成 SQL 语句。 ### 官方文档参考 - Spark SQL PIVOT 文档:[Apache Spark SQL Pivoting](https://spark.apache.org/docs/latest/sql-ref-syntax-qry-select-pivot.html) [^1] - 进阶用法示例可参考 Databricks 指南:[Databricks Pivot Guide](https://docs.databricks.com/sql/language-manual/sql-ref-syntax-qry-select-pivot.html) [^2]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

麒思妙想

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值