SPARK:根据多行条件设置列值

时间:2016-04-07 03:52:32

标签: apache-spark dataframe apache-spark-sql

我有一个以下格式的数据框:

+----+---+-----+------+-----+------+
|AGEF|SEX|F0_34|F35_44|M0_34|M35_44|
+----+---+-----+------+-----+------+
|  30|  0|    0|     0|    0|     0|
|  94|  1|    0|     0|    0|     0|
|  94|  0|    0|     0|    0|     0|
|  94|  0|    0|     0|    0|     0|
|  94|  1|    0|     0|    0|     0|
|  44|  0|    0|     0|    0|     0|
|  66|  0|    0|     0|    0|     0|
|  66|  0|    0|     0|    0|     0|
|  74|  0|    0|     0|    0|     0|
|  74|  0|    0|     0|    0|     0|
|  29|  0|    0|     0|    0|     0|

现在根据列AGEF和SEX的值,我需要为相应的列名指定1。每个列名都是自我解释,如同其他情况一样,F0_34在0到34岁之间是女性。

预期输出

+----+---+-----+------+-----+------+
|AGEF|SEX|F0_34|F35_44|M0_34|M35_44|
+----+---+-----+------+-----+------+
|  30|  0|    1|     0|    0|     0|
|  94|  1|    0|     0|    0|     0|
|  94|  0|    0|     0|    0|     0|
|  94|  0|    0|     0|    0|     0|
|  94|  1|    0|     0|    0|     0|
|  44|  0|    0|     1|    0|     0|
|  66|  0|    0|     0|    0|     0|
|  66|  0|    0|     0|    0|     0|
|  74|  0|    0|     0|    0|     0|
|  74|  0|    0|     0|    0|     0|
|  29|  0|    1|     0|    0|     0|

提前致谢!!!

2 个答案:

答案 0 :(得分:5)

通常,最有效的方法是直接在SQL表达式上运行。例如:

def categorize(ageRanges: Seq[(Int, Int)], sexValues: Seq[(Int, String)]) = for {
  (ageL, ageH) <- ageRanges
  (sexV, sexL) <- sexValues
} yield ($"SEX" === sexL && $"AGEF".between(ageL, ageH)).alias(
  s"$sexL-$ageL-$ageH"
)

df.select(
  $"*" +: categorize(Seq((0, 34), (35, 44)), Seq((0, "F"), (1, "M"))): _*
)

答案 1 :(得分:4)

最简单的方法是创建一个带有5个参数的UDF(例如:actual_age,actual_sex,target_sex,target_min_age,target_max_age)并返回1或0.这样的事情:

val ageRanger = udf[Int,Int,Int,Int,Int,Int]((age: Int, sex: Int, targetSex: Int, targetMinAge: Int, targetMaxAge: Int) => { 
  if (age >= targetMinAge && age <= targetMaxAge && sex == targetSex) 1 else 0
})

然后,如果您有DataFrame

val df = Seq((30,0),(94,1),(94,0),(44,0)).toDF("AGEF", "SEX")
// +----+---+
// |AGEF|SEX|
// +----+---+
// |  30|  0|
// |  94|  1|
// |  94|  0|
// |  44|  0|
// +----+---+

df.withColumn("F0_34", ageRanger($"AGEF", $"SEX", lit(0), lit(0), lit(34)))
  .withColumn("F35_44", ageRanger($"AGEF", $"SEX", lit(0), lit(35), lit(44)))
  .show
// +----+---+-----+------+
// |AGEF|SEX|F0_34|F35_44|
// +----+---+-----+------+
// |  30|  0|    1|     0|
// |  94|  1|    0|     0|
// |  94|  0|    0|     0|
// |  44|  0|    0|     1|
// +----+---+-----+------+

请注意,您必须将值UDF传递给Columns,因此我使用lit(...)来包含硬编码值的Int值。可能有一种更为流畅的方式,但这样做很好。