spark groupByKey 循环数组 行转列

本文详细介绍如何使用Apache Spark的groupByKey方法将行数据转换为列数据,以实现数据聚合和重组。通过具体示例,展示了从读取CSV文件,数据预处理,到最终保存结果的全过程。

使用spark-shell 通过groupByKey方法将行专列。

一、需求

在HDFS或本地目录有一份text格式数据(数据内容为英文逗号分隔,字段内容为uid,value,key),现在要求将这些数据按照uid分组,最后保存的格式为uid,uidValue|key1,value1|key2,value2|… 。具体参见下方:

//输入数据
cat /home/hadoop/data/source.csv
8888880674736793701,1003422322c6c937c63af250637fb07e89012,a_zmxy_bindid
8888880674736793701,82a5d0ff83cdd46f8aed902a02105e9f,id_no
8888880674736793701,098dcdb655a715d3816ee8d4854f5db9,regist_mobile
8888880674736793701,c4de399c388097b9a3909bb94cfd51a1,travel_emerg_mobile
8888880674736793702,82a5d0ff83cdd46f8aed902a02105e93,id_no
8888880674736793702,1003422322c6c937c63af250637fb07e89013,b_zmxy_bindid
8888880674736793702,098dcdb655a715d3816ee8d4854f5db3,regist_mobile
8888880674736793702,c4de399c388097b9a3909bb94cfd51a3,travel_emerg_mobile
8888880674736793704,1003422322c6c937c63af250637fb07e89014,a_zmxy_bindid
8888880674736793704,82a5d0ff83cdd46f8aed902a02105e94,id_no
8888880674736793704,098dcdb655a715d3816ee8d4854f5db4,regist_mobile
8888880674736793704,c4de399c388097b9a3909bb94cfd51a4,travel_emerg_mobile
8888880674736793704,c4de399c388097b9a3909bb94cfd51h4,home_emerg_mobile
8888880674736793965,6fab6c3dd609da82acc4b4a48713bdff,regist_mobile
8888880674736793965,c397e3055a5a60566d4a2670979ee0b4,wx_id
8888880674736793965,ws0gnv-ec:01:ee:38:c4:fa,work_wifimac
8888880674736793965,196985b4d3922813b15fe9790c689b1e15dc6,a_zmxy_bindid
8888880674736793965,f4f954dbe1be7bb14643041099a0ed2d,id_no
8888880674736793965,863264039353592,travel_imei
8888880674736793965,ws0gnv-ec:01:ee:38:c4:fa,rest_wifimac
8888880674736793965,7hfL8qWJQGmlH4H-W2Jgbg,travel_oid

//期待输出数据
8888880674736793965,uid|6fab6c3dd609da82acc4b4a48713bdff,regist_mobile|c397e3055a5a60566d4a2670979ee0b4,wx_id|ws0gnv-ec:01:ee:38:c4:fa,work_wifimac|196985b4d3922813b15fe9790c689b1e15dc6,a_zmxy_bindid|f4f954dbe1be7bb14643041099a0ed2d,id_no|863264039353592,travel_imei|ws0gnv-ec:01:ee:38:c4:fa,rest_wifimac|7hfL8qWJQGmlH4H-W2Jgbg,travel_oid
8888880674736793702,uid|1003422322c6c937c63af250637fb07e89013,a_zmxy_bindid|82a5d0ff83cdd46f8aed902a02105e93,id_no|1003422322c6c937c63af250637fb07e89013,b_zmxy_bindid|098dcdb655a715d3816ee8d4854f5db3,regist_mobile|c4de399c388097b9a3909bb94cfd51a3,travel_emerg_mobile
8888880674736793704,uid|1003422322c6c937c63af250637fb07e89014,a_zmxy_bindid|82a5d0ff83cdd46f8aed902a02105e94,id_no|1003422322c6c937c63af250637fb07e89014,b_zmxy_bindid|098dcdb655a715d3816ee8d4854f5db4,regist_mobile|c4de399c388097b9a3909bb94cfd51a4,travel_emerg_mobile|c4de399c388097b9a3909bb94cfd51h4,home_emerg_mobile
8888880674736793701,uid|1003422322c6c937c63af250637fb07e89012,a_zmxy_bindid|82a5d0ff83cdd46f8aed902a02105e9f,id_no|1003422322c6c937c63af250637fb07e89012,b_zmxy_bindid|098dcdb655a715d3816ee8d4854f5db9,regist_mobile|c4de399c388097b9a3909bb94cfd51a1,travel_emerg_mobile
二、spark-shell scala 实现
#原始本地数据 /home/hadoop/data/source.csv
$SPARK_HOME/bin/spark-shell
//spark 列转行
//(uid),(uid,value,key)
scala> val lines = sc.textFile("/home/hadoop/data/source.csv").map(_.split(",")).keyBy(a => a(0)).cache()
scala> lines.collect
#打印的内容省略,后续不在强调

#将map中的value数组类型变成字符串类型,用英文逗号链接
scala> var rdd1= lines.mapValues(v =>v.mkString(","))
rdd1: org.apache.spark.rdd.RDD[(String, String)] = MapPartitionsRDD[39] at mapValues at <console>:28

scala> rdd1.collect

#groupByKey(uid) 按照key group by
scala> val rdd2 = rdd1.groupByKey();
rdd2: org.apache.spark.rdd.RDD[(String, Iterable[String])] = ShuffledRDD[40] at groupByKey at <console>:30

scala> rdd2.collect

#将分组后的数据改成可识别的形式,打印结果
rdd2.map{                                                      /* 分组,最重要的就是这,同类的数据分组到一起,后面只需要计算V了 */
    case (k, v) =>
      var vt = ""                                                     /* 定义vt存数据的变量,恩,这很java,一般scala中很少见到var */
      v.foreach {                                                         /* 遍历需要计算的V  */
        x =>
        val i = x.indexOf(",")
        vt ++= x.substring(i+1) + "|"
      } 
    val s=vt.dropRight(1) /*删除最后一个字符串即|*/ 
    s"$k,uid|$s"                                                      /* 拼字符串,返回数据 */
  }
  .foreach(println)

val rdd3 = rdd2.map{                                                      /* 分组,最重要的就是这,同类的数据分组到一起,后面只需要计算V了 */
    case (k, v) =>
      var vt = ""                                                     /* 定义vt存数据的变量,恩,这很java,一般scala中很少见到var */
      v.foreach {                                                         /* 遍历需要计算的V  */
        x =>
        val i = x.indexOf(",")
        vt ++= x.substring(i+1) + "|"
      } 
    val s=vt.dropRight(1) /*删除最后一个字符串即|*/ 
    s"$k,uid|$s"                                                      /* 拼字符串,返回数据 */
  }
  
scala> rdd3.collect

#保存结果到本地
scala> rdd3.saveAsTextFile("/home/hadoop/data/adjacent_list.csv")
[hadoop@bigdata-k-01 adjacent_list.csv]$ pwd
/home/hadoop/janusgraph-data/adjacent_list.csv
三、验证结果
[hadoop@bigdata-k-01 adjacent_list.csv]$ pwd
/home/hadoop/data/adjacent_list.csv
[hadoop@bigdata-k-01 adjacent_list.csv]$ ll
总用量 8
-rw-r--r-- 1 hadoop hadoop 912 3月  23 18:06 part-00000
-rw-r--r-- 1 hadoop hadoop 263 3月  23 18:06 part-00001
-rw-r--r-- 1 hadoop hadoop   0 3月  23 18:06 _SUCCESS

[hadoop@bigdata-k-01 adjacent_list.csv]$ cat *
8888880674736793965,uid|6fab6c3dd609da82acc4b4a48713bdff,regist_mobile|c397e3055a5a60566d4a2670979ee0b4,wx_id|ws0gnv-ec:01:ee:38:c4:fa,work_wifimac|196985b4d3922813b15fe9790c689b1e15dc6,a_zmxy_bindid|f4f954dbe1be7bb14643041099a0ed2d,id_no|863264039353592,travel_imei|ws0gnv-ec:01:ee:38:c4:fa,rest_wifimac|7hfL8qWJQGmlH4H-W2Jgbg,travel_oid
8888880674736793702,uid|1003422322c6c937c63af250637fb07e89013,a_zmxy_bindid|82a5d0ff83cdd46f8aed902a02105e93,id_no|1003422322c6c937c63af250637fb07e89013,b_zmxy_bindid|098dcdb655a715d3816ee8d4854f5db3,regist_mobile|c4de399c388097b9a3909bb94cfd51a3,travel_emerg_mobile
8888880674736793704,uid|1003422322c6c937c63af250637fb07e89014,a_zmxy_bindid|82a5d0ff83cdd46f8aed902a02105e94,id_no|1003422322c6c937c63af250637fb07e89014,b_zmxy_bindid|098dcdb655a715d3816ee8d4854f5db4,regist_mobile|c4de399c388097b9a3909bb94cfd51a4,travel_emerg_mobile|c4de399c388097b9a3909bb94cfd51h4,home_emerg_mobile
8888880674736793701,uid|1003422322c6c937c63af250637fb07e89012,a_zmxy_bindid|82a5d0ff83cdd46f8aed902a02105e9f,id_no|1003422322c6c937c63af250637fb07e89012,b_zmxy_bindid|098dcdb655a715d3816ee8d4854f5db9,regist_mobile|c4de399c388097b9a3909bb94cfd51a1,travel_emerg_mobile

Spark实现行转列可以使用PIVOT函数,它是Spark SQL中的一个聚合函数,用于将一的值转换为多。它将行数据中的某一作为名,将该对应的值作为新的的值,并将其他的值保持不变,完成行转列操作[^1]。 以下通过具体案例展示使用Spark SQL DSL与Spark SQL两种方案实现行转列: ### 案例数据 以`city.csv`文件的数据为例,文件内容如下: ```plaintext city,yearm,count 北京,202001,1000 北京,202004,1023 北京,202007,1980 北京,202010,1098 北京,202101,988 北京,202104,976 北京,202107,1098 北京,202110,1221 上海,202001,1222 上海,202004,800 上海,202007,908 上海,202010,1009 上海,202101,709 上海,202104,799 上海,202107,980 上海,202110,897 ``` ### 代码实现 ```scala import org.apache.spark.sql.SparkSession object RowToColumnExample { def main(args: Array[String]): Unit = { // 创建执行环境 val session: SparkSession = SparkSession.builder() .appName(this.getClass.getSimpleName) .master("local[2]").getOrCreate() // 加载数据文件,注册DF val piovtCsvDF = session.read.format("csv") .option("header", true) .load("/Users/zhoulei/Documents/workspaces/zholei-core/ToolsLibrary/src/main/resources/city.csv") // 使用PIVOT进行行转列操作 val pivotedDF = piovtCsvDF.groupBy("city") .pivot("yearm") .agg("count" -> "sum") pivotedDF.show() } } ``` 上述代码中,首先创建了一个`SparkSession`对象,用于与Spark进行交互。然后加载了`city.csv`文件并将其注册为一个DataFrame。接着使用`groupBy`方法按`city`进行分组,使用`pivot`方法将`yearm`的值转换名,最后使用`agg`方法对`count`进行聚合操作,这里使用`sum`函数求和。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值