Scala RDD - 根据标准放宽数据聚合

时间:2018-01-09 08:49:07

标签: scala apache-spark rdd

我有这种格式的数据:

RDD[(String, String, String), Int)]

我可以像这样代表它

|------|------|------------|----------------|
|(Month|Gender|Nationality)|NumberOfCustomer|
|------|------|------------|----------------|
| 1    |  M   |    FRA     |      8         |
| 1    |  F   |    FRA     |      2         |
| 1    |      |    FRA     |      2         |
| 1    |  M   |            |      7         |
| 1    |  F   |            |      2         |
| 1    |  M   |    USA     |      3         |
| 1    |  F   |    USA     |      4         |
| 1    |      |    USA     |      13        |
|------|------|------------|----------------|

由于某些限制条件,当我的客户数量少于 10 时,我无法显示数据。 因此,我需要通过放宽一些标准来聚合数据(国籍然后性别)。

例如,由于Month 1 and the Gender M and the Nationality FRA的客户不足(少于10个),我需要将数据连接到其他国籍(未知)。 在处理数据之后,我应该有这样的事情:

|------|------|------------|----------------|
|(Month|Gender|Nationality)|NumberOfCustomer|
|------|------|------------|----------------|
| 1    |  M   |  Other     |      15        |
|------|------|------------|----------------|

Month 1, Gender F and Nationality FRA相同,然后美国等等。 结果应该是:

|------|------|------------|----------------|
|(Month|Gender|Nationality)|NumberOfCustomer|
|------|------|------------|----------------|
| 1    |      |    FRA     |      2         |
| 1    |  M   |    Other   |      18        |
| 1    |  F   |    Other   |      8         |
| 1    |      |    USA     |      13        |
|------|------|------------|----------------|

之后,Month 1, Gender Unknown and Nationality FRA仍然没有足够的客户。 所以我需要将Month 1, Gender Unknown and the Nationality连接到最少的客户(这里是美国) 结果:

|------|------|------------|----------------|
|(Month|Gender|Nationality)|NumberOfCustomer|
|------|------|------------|----------------|
| 1    |  M   |    Other   |      18        |
| 1    |  F   |    Other   |      8         |
| 1    |      |    USA     |      15        |
|------|------|------------|----------------|

之后,Month 1, Gender F and Nationality Other仍然没有足够的客户。

我需要保留Month 1 - Gender Unknown - USA Nationality(因为有超过10个客户) 但是我需要将国籍标准删除到Month 1 - Gender F - Other Nationality,因为其中只有8个客户。

最终的最终结果应该是

|------|------|------------|----------------|
|(Month|Gender|Nationality)|NumberOfCustomer|
|------|------|------------|----------------|
| 1    |Other |    Other   |      26        |
| 1    |      |    USA     |      15        |
|------|------|------------|----------------|

我的问题是如何在Apache Spark中使用Scala RDD尽可能高效地实现这一目标? (有两个以上的标准可以放松,例如国籍,然后是性别,然后是年龄,然后是体重等等,总是按照相同的顺序)

编辑 :在评论中添加的代码和数据

获取我的RDD [(String,String,String),Int)]:

val reducedByKey = myRDD.map(x =>
  (
    (
      x.month,
      x.gender,
      x.nationality
    ), 1
  )
).reduceByKey(_+_)

一些数据:

((1,M,FRA),8)
((1,F,FRA),4)
((1,,FRA),46)
((1,M,ENG),13)
((1,F,ENG),40)
((1,M,USA),1)
((1,F,USA),4)
((1,,USA),3)
((2,M,FRA),4)
((2,F,FRA),1)
((2,M,USA),10)
((2,F,USA),4)
((2,,USA),60)

2 个答案:

答案 0 :(得分:1)

Gender列小于10 时,您的问题可归纳为NationalityOther列更改为NumberOfCustomer

因此,如果您知道将rdd转换为dataframe如下

+-----+------+-----------+----------------+
|Month|Gender|Nationality|NumberOfCustomer|
+-----+------+-----------+----------------+
|    1|     M|        FRA|               8|
|    1|     F|        FRA|               4|
|    1|      |        FRA|              46|
|    1|     M|        ENG|              13|
|    1|     F|        ENG|              40|
|    1|     M|        USA|               1|
|    1|     F|        USA|               4|
|    1|      |        USA|               3|
|    2|     M|        FRA|               4|
|    2|     F|        FRA|               1|
|    2|     M|        USA|              10|
|    2|     F|        USA|               4|
|    2|      |        USA|              60|
+-----+------+-----------+----------------+

您可以使用我在上面解释的逻辑

import org.apache.spark.sql.functions._
df.withColumn("Gender", when($"NumberOfCustomer" < 10, lit("Other")).otherwise($"Gender"))
  .withColumn("Nationality", when($"NumberOfCustomer" < 10, lit("Other")).otherwise($"Nationality"))
  .groupBy("Month","Gender","Nationality").agg(sum("NumberOfCustomer").as("NumberOfCustomer"))
  .show()

你应该有你想要的结果

答案 1 :(得分:0)

您可以将数据转换为DataFrame

val df = rdd.toDF.select(
  $"_1._1" as "month", $"_1._2" as "gender", $"_1._3" as "nationality", 
  $"_2" as "number_of_customers"
).na.replace(Seq("gender", "nationality"), Map("" -> "other"))

并使用过滤器应用多维数据集:

val aggregates = df
  .cube($"month", $"gender", $"nationality")
  .agg(sum("number_of_customers") as "number_of_customers")
  .where($"number_of_customers" >= 10)

这会产生许多集合:

+-----+------+-----------+-------------------+
|month|gender|nationality|number_of_customers|
+-----+------+-----------+-------------------+
| null|  null|        USA|                 20|
|    1| other|       null|                 15|
|    1| other|        USA|                 13|
|    1|  null|       null|                 41|
| null| other|        USA|                 13|
|    1|     M|       null|                 18|
| null|     M|       null|                 18|
| null|  null|        FRA|                 12|
| null|  null|       null|                 41|
| null| other|       null|                 15|
|    1|  null|        FRA|                 12|
|    1|  null|        USA|                 20|
+-----+------+-----------+-------------------+

您可以稍后过滤此结果,例如查找最大的,非重叠的级别集合,并且聚合结果应该足够小,以便在本地集合上使用标准集合操作。

例如:

val sets = df
   .select(df.columns.map(c => concat_ws("_", lit(c), col(c))): _*)
    .collect.map(row => row.toSeq.collect { case s: String => s }.toSet )

sets.filter(s => !sets.contains(s subsetOf _)).foreach(println)
// Set(month_1, gender_M, nationality_FRA, number_of_customers_8)
// Set(month_1, gender_F, nationality_FRA, number_of_customers_2)
// Set(month_1, gender_other, nationality_FRA, number_of_customers_2)
// Set(month_1, gender_M, nationality_other, number_of_customers_7)
// Set(month_1, gender_F, nationality_other, number_of_customers_2)
// Set(month_1, gender_M, nationality_USA, number_of_customers_3)
// Set(month_1, gender_F, nationality_USA, number_of_customers_4)
// Set(month_1, gender_other, nationality_USA, number_of_customers_13)