继续上一篇学习spark
本次将通过一个实际场景来综合学习下spark如何实现hive中的列转行和行转列(关于hive的)列转行和行转列介绍
问题描述
假设我们有这样的交易记录,如下:
@6259656412068037 @822039695680011 7230 366.0 2015-06-18 1624 4030 0 @00400606 2015-06-18 16:24:28 S22 03 48224030 01059999 625965 02 01 1 12 156 00 N 宁德市普丽缇莎美容养生会所 2015-06
@4895928106642641 @822039695680011 7230 0.01 2015-06-18 1639 4030 0 @00400606 2015-06-18 16:39:33 S22 03 48224030 01059999 489592 02 01 1 01 156 00 N 宁德市普丽缇莎美容养生会所 2015-06
@6221552326194389 @822039695680011 7230 213.0 2015-05-03 1710 4030 0 @00400606 2015-05-03 17:10:50 S22 03 48224030 05105840 622155 02 01 1 12 156 00 N 宁德市普丽缇莎美容养生会所 2015-05
@3568398832346622 @822039695680011 7230 235.0 2015-05-03 1447 4030 0 @00400606 2015-05-03 14:47:08 S22 03 48224030 63030000 356839 02 01 1 04 156 00 N 宁德市普丽缇莎美容养生会所 2015-05
@6259651283230624 @822039695680011 7230 231.0 2015-05-03 1606 4030 0 @00400606 2015-05-03 16:06:16 S22 03 48224030 01059999 625965 02 01 1 12 156 00 N 宁德市普丽缇莎美容养生会所 2015-05
字段用\t分割,表示的含义依次是:卡号、商户号、商户类型、交易金额、交易日期、xx、xx、xx、机器id、交易时间戳、xx、xx、xx、xx、xx、xx、xx、xx、xx、xx、xx、xx、xx、xx、交易月份,其中xx是表示我们并不关注的字段
现在需要我们计算的指标描述如下:
观测月中,提取该PID下的流水,按天计算其两次交易间的刷卡间隔,以秒为单位。最后将这些间隔求平均后,按天分别输出例如:某PID在13年9月2日有3笔交易,时间戳为10:00:00,10:00:30,10:00:40,则其间隔为30,10,平均为20。以此类推,统计出该PID下当月有交易的日期下的所有平均间隔,枚举输出
问题分析
直接贴代码边解释边分析
val trans = sc.textFile("/<span style="font-family: Arial, Helvetica, sans-serif;">xxx</span><span style="font-family: Arial, Helvetica, sans-serif;">/yangka/trans/yangka_sample_trans")</span>
该代码读取出所有该数据
val totalTrans = trans.map { tran =>
val field = tran.split("\t")
val pid = field(8)
val ymd = field(4)
val time = field(9)
(pid + "_" + ymd, time)
}.combineByKey(
x => (sdf.parse(x).getTime / 1000).toString,
(x: String, y: String) => x + "," + (sdf.parse(y).getTime / 1000).toString,
(x: String, y: String) => x + "," + y
).map { line =>
val dates = line._2.split(",").sorted
if (dates.length <= 1) {
(line._1, "0")
} else {
var values: String = ""
for (i <- 0 to dates.length - 1) {
if (i != dates.length - 1) {
val seconds = dates(i + 1).toLong - dates(i).toLong
values += seconds.toString + ","
}
}
val tmp = values.substring(0, values.length - 1).split(",")
(line._1, (tmp.foldLeft(0)(_ + _.toInt).toDouble / tmp.length).toString)
}
}
该代码块稍微复杂,解释如下:
1-6行是做了一个简单的map,取出我们有用的字段,最后的输出格式为( @00400606_2015-06-18, 2015-06-18 16:24:28)
7-10行用了一个函数combineByKey,我们先看下combineByKey如何使用 ,见 http://www.iteblog.com/archives/1291,最后我们得到这样的格式( @00400606_2015-06-18, "13000000,1321312,41241231"),KV的value是一堆用逗号隔开的时间戳,我们提到的行转列就是这么实现了
剩下的代码对一堆时间戳进行排序,然后用scala的foldLeft计算平均数
至此问题解决了,至于列转行其实很简单,用map就可以了,不多说了
总结
总结下最近的spark学习,由于自己之前是做hadoop、hive、hbase开发的,所以拿到手的问题自然会先想到用hive怎么做,然后再按照步骤用spark来实现,这也是一种不错的解决方法,总结而言scala集合操作、spark的常用transform算子要比较熟悉。