如何将Spark数据帧中的任意数量的列从Timestamps转换为Longs?

时间:2017-08-18 01:42:37

标签: scala apache-spark apache-spark-sql user-defined-functions

我在Scala中写这个并使用Spark 1.6,并且没有选择切换到更新的版本。我试图合并两个数据帧,一个从Hadoop集群上的Avro文件中拉出来,另一个从Teradata数据库中取出。我可以很好地读取它们,并且两者都保证以相同的顺序具有相同的列名,但是当我尝试使用

合并它们时
 data1.unionAll(data2)

我遇到了错误,因为Avro会将时间戳转换为long,因此两者的数据类型不匹配这些字段。这个过程将重复几次,我知道表中总会有至少一个时间戳字段,但可能会有更多,我不会总是知道他们的名字,所以我试图做一般方法这会将任意数量的列从timestamp转换为long。这就是我到目前为止所做的:

def transformTimestamps(df: DataFrame): DataFrame = {
    val convert_timestamp_udf = udf((time:Timestamp) => time.getTime())
    df.dtypes.foreach { f => 
        val fName = f._1
        val fType = f._2
        if (fType == "TimestampType:) {
            println("Found timestamp col: " + fName)
            df.withColumn(fName, convert_timestamp_udf(df.col(fName)))
            df.printSchema()
        }
    }
   return df
}

使用打印输出我可以告诉该方法只能正确识别时间戳列,但.withColumn转换不起作用。在下一行中打印架构不会显示更新的列。此外,我还尝试为转换后的值创建一个全新的列,并且它也没有添加到df中。有谁能发现为什么这不起作用?

3 个答案:

答案 0 :(得分:2)

以下行只是transformation

df.withColumn(fName, convert_timestamp_udf(df.col(fName)))

在执行dataframe之前不会反映在原始action上。分配将作为一个动作,因此您可以创建一个临时dataframe并在循环中分配给它

   def transformTimestamps(df: DataFrame): DataFrame = {
      val convert_timestamp_udf = udf((time:Timestamp) => time.getTime())
      var tempDF = df
      df.schema.map(f => {
        val fName = f.name
        val fType = f.dataType
        if (fType.toString == "TimestampType") {
          println("Found timestamp col: " + fName)
          tempDF = tempDF.withColumn(fName, convert_timestamp_udf(df.col(fName)))
          tempDF.printSchema()
        }
      })
      return tempDF
    }

我希望答案很有帮助

答案 1 :(得分:1)

避免使用可变var的一种方法是,您可以通过汇总TimestampType的列列表并通过foldLeft与您的转化UDF一起浏览列表来执行类型转换:

import java.sql.Timestamp

val df = Seq(
  (1, Timestamp.valueOf("2016-05-01 11:30:00"), "a", Timestamp.valueOf("2017-06-01 07:00:30")),
  (2, Timestamp.valueOf("2016-06-01 12:30:00"), "b", Timestamp.valueOf("2017-07-01 08:00:30")),
  (3, Timestamp.valueOf("2016-07-01 13:30:00"), "c", Timestamp.valueOf("2017-08-01 09:00:30"))
).toDF("id", "date1", "status", "date2")

val convert_timestamp_udf = udf( (time: Timestamp) => time.getTime() )

// Assemble all columns filtered with type TimestampType
val tsColumns = df.dtypes.filter(x => x._2 == "TimestampType")

// Create new dataframe by converting all Timestamps to Longs via foldLeft
val dfNew = tsColumns.foldLeft( df )(
  (acc, x) => acc.withColumn(x._1, convert_timestamp_udf(df(x._1)))
)

dfNew.show
+---+-------------+------+-------------+
| id|        date1|status|        date2|
+---+-------------+------+-------------+
|  1|1462127400000|     a|1496325630000|
|  2|1464809400000|     b|1498921230000|
|  3|1467405000000|     c|1501603230000|
+---+-------------+------+-------------+

答案 2 :(得分:0)

    val index = ss.sparkContext.parallelize( Seq((1,"2017-5-5"),
  (2,"2017-5-5"),
  (3,"2017-5-5"),
  (4,"2017-5-5"),
  (5,"2017-5-5"))).toDF("ID", "time")

val convert_timestamp_udf = udf((time:Timestamp) => time.getTime())

val newDF = index.withColumn("time", convert_timestamp_udf($"time"))
newDF.show