【问题标题】:Transform dataset with empty data for dates使用空数据转换数据集以获取日期
【发布时间】:2018-10-25 12:31:10
【问题描述】:

我有一个包含日期、帐户 ID 和值的数据集。我想将数据集转换为一个新数据集,如果 accountid 在特定日期不存在,则在该日期添加一个值为 0 的 accountid。这可能吗

    val df = sc.parallelize(Seq(("2018-01-01", 100.5,"id1"),
  ("2018-01-02", 120.6,"id1"),
  ("2018-01-03", 450.2,"id2")
  )).toDF("date", "val","accountid")
    +----------+-----+---------+
|      date|  val|accountid|
+----------+-----+---------+
|2018-01-01|100.5|      id1|
|2018-01-02|120.6|      id1|
|2018-01-03|450.2|      id2|
+----------+-----+---------+

我想把这个数据集转换成这种格式

+----------+-----+---------+
|      date|  val|accountid|
+----------+-----+---------+
|2018-01-01|100.5|      id1|
|2018-01-01|  0.0|      id2|
|2018-01-02|120.6|      id1|
|2018-01-02|  0.0|      id2|
|2018-01-03|450.2|      id2|
|2018-01-03|0.0  |      id1|
+----------+-----+---------+

【问题讨论】:

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


    【解决方案1】:

    您只需使用udf 函数即可满足您的要求。

    但在此之前,您必须获取完整集accountids 并将其广播以用于udf 函数。

    udf 函数的返回数组将被分解最终选择列

    import org.apache.spark.sql.functions._
    val idList = df.select(collect_set("accountid")).first().getAs[Seq[String]](0)
    
    val broadCastedIdList = sc.broadcast(idList)
    
    def populateUdf = udf((date: String, value: Double, accountid: String)=> Array(accounts(date, value, accountid)) ++ broadCastedIdList.value.filterNot(_ == accountid).map(accounts(date, 0.0, _)))
    
    df.select(populateUdf(col("date"), col("val"), col("accountid")).as("struct"))
        .withColumn("struct", explode(col("struct")))
        .select(col("struct.date"), col("struct.value").as("val"), col("struct.accountid"))
      .show(false)
    

    当然你需要case class

    case class accounts(date:String, value:Double, accountid:String)
    

    这应该给你

    +----------+-----+---------+
    |date      |val  |accountid|
    +----------+-----+---------+
    |2018-01-01|100.5|id1      |
    |2018-01-01|0.0  |id2      |
    |2018-01-02|120.6|id1      |
    |2018-01-02|0.0  |id2      |
    |2018-01-03|450.2|id2      |
    |2018-01-03|0.0  |id1      |
    +----------+-----+---------+
    

    注意:在 case 类中使用 value 关键字,因为保留的标识符名称不能用作变量名

    【讨论】:

      【解决方案2】:

      您可以创建参考

      import org.apache.spark.sql.functions._
      import org.apache.spark.sql.Row
      
      val Row(minTs: Long, maxTs: Long) = df
        .select(to_date($"date").cast("timestamp").cast("bigint") as "date")
        .select(min($"date"), max($"date")).first
      
      val by =  60 * 60 * 24
      
      val ref = spark
        .range(minTs, maxTs + by, by)
        .select($"id".cast("timestamp").cast("date").cast("string").as("date"))
        .crossJoin(df.select("accountid").distinct)
      

      与输入数据的外连接:

      ref.join(df, Seq("date", "accountid"), "leftouter").na.fill(0.0).show
      // +----------+---------+-----+      
      // |      date|accountid|  val|
      // +----------+---------+-----+
      // |2018-01-03|      id1|  0.0|
      // |2018-01-01|      id1|100.5|
      // |2018-01-02|      id2|  0.0|
      // |2018-01-02|      id1|120.6|
      // |2018-01-03|      id2|450.2|
      // |2018-01-01|      id2|  0.0|
      // +----------+---------+-----+
      

      this sparklyr answeruser6910411 采用的概念。

      【讨论】:

      • 太棒了!有用。一个问题是交叉连接功能是否性能密集,假设原始数据集是否包含 1M 记录?
      • 由于洗牌和从稀疏到密集的转换,整个过程的成本很高,但您无法真正避免获得所需结果的成本。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-02-23
      • 2017-12-18
      • 1970-01-01
      • 1970-01-01
      • 2022-06-23
      • 2015-05-15
      • 1970-01-01
      相关资源
      最近更新 更多