对sparkDataFrame 多列进行多个函数操作

最近做机器学习项目的特征工程遇到问题,就是对spark的dataframe 进行处理时,要对某个feature(也就是列)进行多个函数操作,场景如下:
数据的schema如下,先groupBy(“user_id”),然后对分组后的每个feature 求一些统计特征比如max min avg等等

root
 |-- user_id: string (nullable = true)
 |-- month: string (nullable = true)
 |-- id_1: string (nullable = true)
 |-- id_2: string (nullable = true)
 |-- id_3: string (nullable = true)
 |-- id_4: string (nullable = true)
 |-- id_5: string (nullable = true)
 |-- id_6: string (nullable = true)
 |-- id_7: string (nullable = true)
 |-- id_8: string (nullable = true)
 |-- id_9: string (nullable = true)
 |-- id_10: string (nullable = true)
 |-- id_11: string (nullable = true)
 |-- id_12: string (nullable = true)

一般的写法如下

spark.read.parquet("xxxxx")
	.groupBy("user_id")
	.agg(
	  max($"id_1").alias("max_id_1"), min($"id_1").alias("min_id_2"),avg($"id_1").alias("avg_id_1"),
	  ....
     max($"id_12").alias("max_id_12"), min($"id_12").alias("min_id_12"),avg($"id_12").alias("avg_id_12"),    
      )

这样的写法虽然可以,但是不够科学,如果有一万个feature就要写一万行这样的代码,这样方法体里面就超过了64KB,这样最终程序连编译都过不了。

查看spark dataset 的agg API如下

method1
对sparkDataFrame 多列进行多个函数操作_第1张图片
method2
对sparkDataFrame 多列进行多个函数操作_第2张图片
可以看到我们可以向agg里面传入一个map,键为feature值为对应的操作,比如max操作对应的就是字符串“max”
,或者向agg里面传入二元组(key,feature),传入map的坏处是map只能存一个key,但元组可以存多个key
,这两个方法可以可以解决输入重复类似代码的问题,但是新成的列会有默认的列名(怎么重命名知道的麻烦再评论区留言),在摸索中发现了下面的方法,在agg中传入一个操作的Seq就行了,比如传入max($“feature”).alias(“xx”)这样的,代码如下:

 def gen_expr(feature_array: Array[String]): Seq[Column] ={
      val expr = feature_array.toSeq.filter(x=>x!="user_id" && x!="month").flatMap(x=>
        Seq(min(toDouble(col(x))).alias(x+"_min"),max(toDouble(col(x))).alias(x+"_max"),avg(toDouble(col(x))).alias(x+"_avg"),stddev_pop(toDouble(col(x))).alias(x+"_std_pop"),stddev_samp(toDouble(col(x))).alias(x+"_std_sample"),sum(countzero(toDouble(col(x)))).alias(x+"_nullcount"))
      )
      expr
    }
    val expr1 = gen_expr(app_details_1_compete_data.columns)
    val new_app_details_1_compete_data   = app_details_1_compete_data.groupBy("user_id").agg(expr1.head,expr1.tail:_*)

注意最后一行代码的传参方式

你可能感兴趣的:(Spark)