Spark:保存由“虚拟”列分区的DataFrame

时间:2016-02-16 16:07:51

标签: apache-spark dataframe pyspark apache-spark-sql partitioning

我正在使用PySpark执行经典ETL作业(加载数据集,处理它,保存它)并希望将我的Dataframe保存为由“虚拟”列分区的文件/目录;我的意思是“虚拟”是我有一个列Timestamp是一个包含ISO 8601编码日期的字符串,我想按年/月/日分区;但我实际上并没有DataFrame中的Year,Month或Day列;我有这个时间戳,但我可以从中导出这些列,但我不希望我的resultat项目将这些列中的一列序列化。

将DataFrame保存到磁盘所产生的文件结构应如下所示:

/ 
    year=2016/
        month=01/
            day=01/
                part-****.gz

有没有办法用Spark / Pyspark做我想做的事情?

1 个答案:

答案 0 :(得分:22)

用于分区的列不包含在序列化数据本身中。例如,如果您创建DataFrame,请执行以下操作:

df = sc.parallelize([
    (1, "foo", 2.0, "2016-02-16"),
    (2, "bar", 3.0, "2016-02-16")
]).toDF(["id", "x", "y", "date"])

并写如下:

import tempfile
from pyspark.sql.functions import col, dayofmonth, month, year
outdir = tempfile.mktemp()

dt = col("date").cast("date")
fname = [(year, "year"), (month, "month"), (dayofmonth, "day")]
exprs = [col("*")] + [f(dt).alias(name) for f, name in fname]

(df
    .select(*exprs)
    .write
    .partitionBy(*(name for _, name in fname))
    .format("json")
    .save(outdir))

单个文件不包含分区列:

import os

(sqlContext.read
    .json(os.path.join(outdir, "year=2016/month=2/day=16/"))
    .printSchema())

## root
##  |-- date: string (nullable = true)
##  |-- id: long (nullable = true)
##  |-- x: string (nullable = true)
##  |-- y: double (nullable = true)

分区数据仅存储在目录结构中,不会复制到序列化文件中。只有在读取完整或部分目录树时才会附加它:

sqlContext.read.json(outdir).printSchema()

## root
##  |-- date: string (nullable = true)
##  |-- id: long (nullable = true)
##  |-- x: string (nullable = true)
##  |-- y: double (nullable = true)
##  |-- year: integer (nullable = true)
##  |-- month: integer (nullable = true)
##  |-- day: integer (nullable = true)

sqlContext.read.json(os.path.join(outdir, "year=2016/month=2/")).printSchema()

## root
##  |-- date: string (nullable = true)
##  |-- id: long (nullable = true)
##  |-- x: string (nullable = true)
##  |-- y: double (nullable = true)
##  |-- day: integer (nullable = true)