我有一个CSV,其中字段是特定格式的日期时间。我无法直接在我的Dataframe中导入它,因为它需要是一个时间戳。所以我将其导入为字符串并将其转换为Timestamp
,如此
import java.sql.Timestamp
import java.text.SimpleDateFormat
import java.util.Date
import org.apache.spark.sql.Row
def getTimestamp(x:Any) : Timestamp = {
val format = new SimpleDateFormat("MM/dd/yyyy' 'HH:mm:ss")
if (x.toString() == "")
return null
else {
val d = format.parse(x.toString());
val t = new Timestamp(d.getTime());
return t
}
}
def convert(row : Row) : Row = {
val d1 = getTimestamp(row(3))
return Row(row(0),row(1),row(2),d1)
}
使用Dataframe API或spark-sql有更好,更简洁的方法吗?上述方法需要创建RDD并再次为Dataframe提供架构。
答案 0 :(得分:45)
Spark> = 2.2
从2.2开始,您可以直接提供格式字符串:
import org.apache.spark.sql.functions.to_timestamp
val ts = to_timestamp($"dts", "MM/dd/yyyy HH:mm:ss")
df.withColumn("ts", ts).show(2, false)
// +---+-------------------+-------------------+
// |id |dts |ts |
// +---+-------------------+-------------------+
// |1 |05/26/2016 01:01:01|2016-05-26 01:01:01|
// |2 |#$@#@# |null |
// +---+-------------------+-------------------+
Spark> = 1.6,< 2.2 强>
您可以使用Spark 1.5中引入的日期处理功能。假设您有以下数据:
val df = Seq((1L, "05/26/2016 01:01:01"), (2L, "#$@#@#")).toDF("id", "dts")
您可以使用unix_timestamp
来解析字符串并将其转换为时间戳
import org.apache.spark.sql.functions.unix_timestamp
val ts = unix_timestamp($"dts", "MM/dd/yyyy HH:mm:ss").cast("timestamp")
df.withColumn("ts", ts).show(2, false)
// +---+-------------------+---------------------+
// |id |dts |ts |
// +---+-------------------+---------------------+
// |1 |05/26/2016 01:01:01|2016-05-26 01:01:01.0|
// |2 |#$@#@# |null |
// +---+-------------------+---------------------+
正如您所看到的,它涵盖了解析和错误处理。格式字符串应与Java SimpleDateFormat
兼容。
Spark> = 1.5,< 1.6
你必须使用这样的东西:
unix_timestamp($"dts", "MM/dd/yyyy HH:mm:ss").cast("double").cast("timestamp")
或
(unix_timestamp($"dts", "MM/dd/yyyy HH:mm:ss") * 1000).cast("timestamp")
归因于SPARK-11724。
Spark< 1.5 强>
您应该能够将这些内容与expr
和HiveContext
一起使用。
答案 1 :(得分:6)
我还没有使用Spark SQL,但我认为这将是更惯用的scala(null使用不被认为是一种好习惯):
def getTimestamp(s: String) : Option[Timestamp] = s match {
case "" => None
case _ => {
val format = new SimpleDateFormat("MM/dd/yyyy' 'HH:mm:ss")
Try(new Timestamp(format.parse(s).getTime)) match {
case Success(t) => Some(t)
case Failure(_) => None
}
}
}
请注意我假设您事先知道Row
元素类型(如果您从csv文件中读取它们,它们都是String
),这就是我使用String
之类的正确类型的原因而不是Any
(一切都是Any
的子类型。)
它还取决于您希望如何处理解析异常。在这种情况下,如果发生解析异常,则只返回None
。
您可以继续使用它:
rows.map(row => Row(row(0),row(1),row(2), getTimestamp(row(3))
答案 2 :(得分:1)
我的数据集中有ISO8601时间戳,我需要将其转换为“yyyy-MM-dd”格式。这就是我所做的:
In [8]:
df < 95
Out[8]:
2015-09-26 2015-09-27 2015-09-28 2015-09-29
0001_Durgacomplex_NBSNL True False True True
0002_Shivanagar_area_Bdr False False True False
0003_Old_city_Bidar False True False False
0004_Bidar_Mw_Station True False False False
0005_Bidri_colony False True False False
您可以在spark SQL查询中使用UDF。
答案 3 :(得分:1)
火花版本:2.4.4
scala> import org.apache.spark.sql.types.TimestampType
import org.apache.spark.sql.types.TimestampType
scala> val df = Seq("2019-04-01 08:28:00").toDF("ts")
df: org.apache.spark.sql.DataFrame = [ts: string]
scala> val df_mod = df.select($"ts".cast(TimestampType))
df_mod: org.apache.spark.sql.DataFrame = [ts: timestamp]
scala> df_mod.printSchema()
root
|-- ts: timestamp (nullable = true)
答案 4 :(得分:0)
我想将您编写的getTimeStamp
方法移到rdd的mapPartitions中,并在迭代器中的行中重用GenericMutableRow:
val strRdd = sc.textFile("hdfs://path/to/cvs-file")
val rowRdd: RDD[Row] = strRdd.map(_.split('\t')).mapPartitions { iter =>
new Iterator[Row] {
val row = new GenericMutableRow(4)
var current: Array[String] = _
def hasNext = iter.hasNext
def next() = {
current = iter.next()
row(0) = current(0)
row(1) = current(1)
row(2) = current(2)
val ts = getTimestamp(current(3))
if(ts != null) {
row.update(3, ts)
} else {
row.setNullAt(3)
}
row
}
}
}
您仍应使用架构生成DataFrame
val df = sqlContext.createDataFrame(rowRdd, tableSchema)
在迭代器实现中使用GenericMutableRow可以在Aggregate Operator,InMemoryColumnarTableScan,ParquetTableOperations等中找到。
答案 5 :(得分:0)
我会使用https://github.com/databricks/spark-csv
这将为您推断时间戳。
import com.databricks.spark.csv._
val rdd: RDD[String] = sc.textFile("csvfile.csv")
val df : DataFrame = new CsvParser().withDelimiter('|')
.withInferSchema(true)
.withParseMode("DROPMALFORMED")
.csvRdd(sqlContext, rdd)
答案 6 :(得分:0)
我在to_timestamp遇到了一些问题,它返回一个空字符串。经过大量的试验和错误,我能够通过将其转换为时间戳,然后再转换为字符串来解决它。我希望这对遇到相同问题的其他人有所帮助:
df.columns.intersect(cols).foldLeft(df)((newDf, col) => {
val conversionFunc = to_timestamp(newDf(col).cast("timestamp"), "MM/dd/yyyy HH:mm:ss").cast("string")
newDf.withColumn(col, conversionFunc)
})