1. 程式人生 > >對sparkDataFrame 多列進行多個函式操作

對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 在這裡插入圖片描述 method2 在這裡插入圖片描述 可以看到我們可以向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:_*)

注意最後一行程式碼的傳參方式