如何按性别分组并按群组加入?

时间:2017-04-16 21:06:08

标签: scala apache-spark apache-spark-sql

我尝试过多种方法来解决以下问题:

 Gender, Age, Value
 1,      20,  21
 2,      23   22
 1,      26,  23
 2,      29,  24

   Male_Age, Male_Value, Female_Age,  Female_Value
     20          21         23           22
     26          23         29           24

我需要做的是按性别分组,而不是使用像(sum,count,avg)这样的聚合,我需要创建List [age]和List [value]。这应该是可能的,因为我正在使用允许功能操作的数据集。

如果男性和女性的行数不相同,则应使用nulls填充列。

我尝试过的一种方法是使用其他数据框的列创建一个新的数据框,如下所示:

  

DF   .select(male.select(" sex")。where(' sex === 1).col(" sex"),
  female.select(" sex")。where(' sex === 2).col(" sex"))

然而,这奇怪地产生如下输出:

sex,    sex,
 1,       1
 2,       2
 1,       1
 2,       2

我无法看到这是怎么可能的。

我也尝试过使用数据透视表,但是它迫使我按照以下方式聚合:

  

df.withColumn(" sex2",df.col(" sex"))
  .groupBy("性别&#34)
  .pivot(" sex2&#34)    .agg(       总和('值&#39)。如("平均&#34),
      stddev(' value).as(" std.dev"))   .show()

|sex|    1.0_mean|   1.0_std. dev|   2.0_mean|    2.0_std. dev|
|1.0|0.4926065526|   1.8110632697|           |                |
|2.0|            |               |0.951250372|1.75060275400785|

以下代码完成了我在Oracle SQL中所需的代码,因此我认为它应该可以在Spark SQL中使用...

drop table mytable

CREATE TABLE mytable 
( gender number(10) NOT NULL,
  age number(10) NOT NULL,   
  value number(10) );

 insert into mytable values (1,20,21); 
 insert into mytable values(2,23,22); 
 insert into mytable values (1,26,23); 
 insert into mytable values (2,29,24); 
 insert into mytable values (1,30,25);

 select * from mytable;


SELECT A.VALUE AS MALE, 
       B.VALUE AS FEMALE 
FROM 
(select value, rownum RN from mytable where gender = 1) A 
FULL OUTER JOIN 
(select value, rownum RN from mytable where gender = 2) B
ON A.RN = B.RN

enter image description here

2 个答案:

答案 0 :(得分:1)

以下内容应该会给你结果。

val df = Seq(
  (1,      20,  21),
  (2,      23,   22),
  (1,      26,  23),
  (2,      29,  24)
).toDF("Gender", "Age", "Value")

scala> df.show
+------+---+-----+
|Gender|Age|Value|
+------+---+-----+
|     1| 20|   21|
|     2| 23|   22|
|     1| 26|   23|
|     2| 29|   24|
+------+---+-----+

// Gender 1 = Male
// Gender 2 = Female

import org.apache.spark.sql.expressions.Window
val byGender = Window.partitionBy("gender").orderBy("gender")

val males = df
  .filter("gender = 1")
  .select($"age" as "male_age",
          $"value" as "male_value",
          row_number() over byGender as "RN")

scala> males.show
+--------+----------+---+
|male_age|male_value| RN|
+--------+----------+---+
|      20|        21|  1|
|      26|        23|  2|
+--------+----------+---+

val females = df
  .filter("gender = 2")
  .select($"age" as "female_age",
          $"value" as "female_value",
          row_number() over byGender as "RN")

scala> females.show
+----------+------------+---+
|female_age|female_value| RN|
+----------+------------+---+
|        23|          22|  1|
|        29|          24|  2|
+----------+------------+---+

scala> males.join(females, Seq("RN"), "outer").show
+---+--------+----------+----------+------------+
| RN|male_age|male_value|female_age|female_value|
+---+--------+----------+----------+------------+
|  1|      20|        21|        23|          22|
|  2|      26|        23|        29|          24|
+---+--------+----------+----------+------------+

答案 1 :(得分:0)

如果DataFrame名为df,其中包含genderagevalue列,您可以这样做:

df.groupBy($"gender")
  .agg(collect_list($"age"), collect_list($"value")).rdd.map { row =>
     val ages: Seq[Int] = row.getSeq(1)
     val values: Seq[Int] = row.getSeq(2)
     (row.getInt(0), ages.head, ages.last, values.head, values.last)
  }.toDF("gender", "male_age", "female_age", "male_value", "female_value")

这会使用非常有用的Spark collect_list library中的functions聚合函数来聚合您想要的值。 (如您所见,还有一个collect_set。)

之后,我不知道有任何更高级别的DataFrame函数可以将这些列式数组扩展为各自的列,因此我回到了我们的低级RDD API祖先用过。我只是将所有内容扩展为Tuple,然后将其重新转换为DataFrame。上述评论者提到了我没有提到过的案件;使用headOptiontailOption等函数可能会有用。但这应该足以让你感动。