web-dev-qa-db-ja.com

CSVをSparkタイムスタンプと日付タイプのデータフレームに読み込む

Spark 1.6のCDHです。

この架空のCSVをApacheにインポートしようとしていますSpark DataFrame:

$ hadoop fs -cat test.csv
a,b,c,2016-09-09,a,2016-11-11 09:09:09.0,a
a,b,c,2016-09-10,a,2016-11-11 09:09:10.0,a

databricks-csv jarを使用しています。

val textData = sqlContext.read
    .format("com.databricks.spark.csv")
    .option("header", "false")
    .option("delimiter", ",")
    .option("dateFormat", "yyyy-MM-dd HH:mm:ss")
    .option("inferSchema", "true")
    .option("nullValue", "null")
    .load("test.csv")

InferSchemaを使用して、結果のDataFrameのスキーマを作成します。 printSchema()関数は、上記のコードに対して次の出力を提供します。

scala> textData.printSchema()
root
 |-- C0: string (nullable = true)
 |-- C1: string (nullable = true)
 |-- C2: string (nullable = true)
 |-- C3: string (nullable = true)
 |-- C4: string (nullable = true)
 |-- C5: timestamp (nullable = true)
 |-- C6: string (nullable = true)

scala> textData.show()
+---+---+---+----------+---+--------------------+---+
| C0| C1| C2|        C3| C4|                  C5| C6|
+---+---+---+----------+---+--------------------+---+
|  a|  b|  c|2016-09-09|  a|2016-11-11 09:09:...|  a|
|  a|  b|  c|2016-09-10|  a|2016-11-11 09:09:...|  a|
+---+---+---+----------+---+--------------------+---+

C3列には String タイプがあります。 C3に date 型を持たせたい。それを日付タイプにするには、次のコードを試してみました。

val textData = sqlContext.read.format("com.databricks.spark.csv")
    .option("header", "false")
    .option("delimiter", ",")
    .option("dateFormat", "yyyy-MM-dd")
    .option("inferSchema", "true")
    .option("nullValue", "null")
    .load("test.csv")

scala> textData.printSchema
root
 |-- C0: string (nullable = true)
 |-- C1: string (nullable = true)
 |-- C2: string (nullable = true)
 |-- C3: timestamp (nullable = true)
 |-- C4: string (nullable = true)
 |-- C5: timestamp (nullable = true)
 |-- C6: string (nullable = true)

scala> textData.show()
+---+---+---+--------------------+---+--------------------+---+
| C0| C1| C2|                  C3| C4|                  C5| C6|
+---+---+---+--------------------+---+--------------------+---+
|  a|  b|  c|2016-09-09 00:00:...|  a|2016-11-11 00:00:...|  a|
|  a|  b|  c|2016-09-10 00:00:...|  a|2016-11-11 00:00:...|  a|
+---+---+---+--------------------+---+--------------------+---+

このコードと最初のブロックの唯一の違いは、 dateFormat オプション行です( "yyyy-MM-dd" の代わりに "yyyy-MM-dd HH:mm:ss" ). timestamps (C3はまだ日付ではないので、C3とC5の両方を取得します。 )。ただし、C5の場合、HH :: mm:ssの部分は無視され、データではゼロとして表示されます。

理想的には、C3を日付タイプにし、C5をタイムスタンプタイプにし、そのHH:mm:ss部分を無視しないようにする必要があります。私の解決策は今このように見えます。私は自分のDBからデータを並列にプルしてcsvを作成します。私はすべての日付をタイムスタンプとしてプルすることを確認します(理想的ではありません)。したがって、テストcsvは次のようになります。

$ hadoop fs -cat new-test.csv
a,b,c,2016-09-09 00:00:00,a,2016-11-11 09:09:09.0,a
a,b,c,2016-09-10 00:00:00,a,2016-11-11 09:09:10.0,a

これは私の最終的な作業コードです:

val textData = sqlContext.read.format("com.databricks.spark.csv")
    .option("header", "false")
    .option("delimiter", ",")
    .option("dateFormat", "yyyy-MM-dd HH:mm:ss")
    .schema(finalSchema)
    .option("nullValue", "null")
    .load("new-test.csv")

ここでは、dateFormatで完全なタイムスタンプ形式( "yyyy-MM-dd HH:mm:ss" ))を使用しています。 finalSchemaインスタンスを手動で作成します。c3は日付、C5はタイムスタンプタイプ(Spark sqlタイプ)です。これらのスキーマを適用するには、schema()関数を使用します。出力は次のようになります。

scala> finalSchema
res4: org.Apache.spark.sql.types.StructType = StructType(StructField(C0,StringType,true), StructField(C1,StringType,true), StructField(C2,StringType,true), StructField(C3,DateType,true), StructField(C4,StringType,true), StructField(C5,TimestampType,true), StructField(C6,StringType,true))

scala> textData.printSchema()
root
 |-- C0: string (nullable = true)
 |-- C1: string (nullable = true)
 |-- C2: string (nullable = true)
 |-- C3: date (nullable = true)
 |-- C4: string (nullable = true)
 |-- C5: timestamp (nullable = true)
 |-- C6: string (nullable = true)


scala> textData.show()
+---+---+---+----------+---+--------------------+---+
| C0| C1| C2|        C3| C4|                  C5| C6|
+---+---+---+----------+---+--------------------+---+
|  a|  b|  c|2016-09-09|  a|2016-11-11 09:09:...|  a|
|  a|  b|  c|2016-09-10|  a|2016-11-11 09:09:...|  a|
+---+---+---+----------+---+--------------------+---+

csvファイル(日付とタイムスタンプの両方のタイプをspark dataframe?

関連リンク:
http://spark.Apache.org/docs/latest/sql-programming-guide.html#manually-specifying-options
https://github.com/databricks/spark-csv

19
Mihir Shinde

重要なケースの推論オプションを使用すると、おそらく期待される結果が返されません。 InferSchema.scala で確認できるように:

if (field == null || field.isEmpty || field == nullValue) {
  typeSoFar
} else {
  typeSoFar match {
    case NullType => tryParseInteger(field)
    case IntegerType => tryParseInteger(field)
    case LongType => tryParseLong(field)
    case DoubleType => tryParseDouble(field)
    case TimestampType => tryParseTimestamp(field)
    case BooleanType => tryParseBoolean(field)
    case StringType => StringType
    case other: DataType =>
      throw new UnsupportedOperationException(s"Unexpected data type $other")

各列を日付タイプではなくタイムスタンプタイプとのみ照合しようとするため、この場合の「すぐに使用できるソリューション」は不可能です。しかし、私の経験では、「より簡単な」解決策は、スキーマを 必要なタイプ で直接定義することであり、推論オプションが、データ全体ではなく、RDDにのみ一致するタイプを設定することを回避します。最終的なスキーマは効率的なソリューションです。

3
Jader Martins

エレガントではありませんが、次のようにタイムスタンプから日付に変換できます(最後の行を確認してください)。

val textData = sqlContext.read.format("com.databricks.spark.csv")
    .option("header", "false")
    .option("delimiter", ",")
    .option("dateFormat", "yyyy-MM-dd")
    .option("inferSchema", "true")
    .option("nullValue", "null")
    .load("test.csv")
    .withColumn("C4", expr("""to_date(C4)"""))
0
Carlos Verdes