我有一个Spark工作,可以将一些数据处理成几个单独的数据帧。我将这些数据帧存储在一个列表中,即dataframes []。最后,我想将这些数据帧组合成一个分层格式,然后用avro编写输出。 avro架构是这样的:
{
"name": "mydata",
"type": "record",
"fields": [
{"name": "data", "type": {
"type": "array", "items": {
"name": "actualData", "type": "record", "fields": [
{"name": "metadata1", "type": "int"},
{"name": "metadata2", "type": "string"},
{"name": "dataframe", "type": {
"type": "array", "items": {
"name": "dataframeRecord", "type": "record", "fields": [
{"name": "field1", "type": "int"},
{"name": "field2", "type": "int"},
{"name": "field3", "type": ["string", "null"]}]
}
}
}]
}
}
}
]
}
可以推断,每个数据帧都有三个字段,field1,field2和field3,我想在avro文件中将其作为一个数组写入。还有一些与每个数据帧相关的元数据。
我目前的做法是,一旦处理完这些数据,就将数据帧写入S3,然后使用单独的程序从S3中提取这些数据,使用avro库编写avro文件,然后将其上传到S3试。
但是,随着数据量的增长,这变得非常缓慢。我已经查看了databricks库直接编写avro文件,但我不知道如何将数据帧组合在内存中,或者数据库库如何确定我使用的模式。
在Spark中有没有惯用的方法呢?
P.S。我在Python中使用EMR和Spark 2.0.0。
答案 0 :(得分:0)
如果架构相同,并且您只想将所有记录放入同一个DataFrame中,则可以使用DataFrame unionAll方法。
http://spark.apache.org/docs/1.6.3/api/python/pyspark.sql.html#pyspark.sql.DataFrame.unionAll
此函数将采用一个数据帧并将其附加到另一个数据帧。问题是它假设两列之间的列顺序相同,因此您可能需要做一些工作才能使它们排成一行,并为任何缺失的列创建空列。这是我用来安全地合并多个数据帧的python函数
def union_multiple_dataframes(iterable_list_df):
input_dfs = list(iterable_list_df)
# First figure out all the field names
field_types = {}
for df in input_dfs:
for field in df.schema.fields:
# Check for type mismatch
if field in field_types:
if field.dataType != field_types[field.name]:
raise ValueError("Mismatched data types when unioning dataframes for field: {}".format(field))
else:
field_types[field.name] = field.dataType
# First add in empty fields so all df's have the same schema
fields = set(field_types.keys())
for i, df in enumerate(input_dfs):
missing = fields - set(df.schema.names)
for field in missing:
df = df.withColumn(field, F.lit(None))
input_dfs[i] = df
# Finally put all the df's columns in the same order, and do the actual union
sorted_dfs = [df.select(*sorted(fields)) for df in iterable_list_df]
return reduce(lambda x, y: x.unionAll(y), sorted_dfs)
示例用法如下:
input_dfs = [do_something(..) for x in y]
combined_df = union_multiple_dataframes(input_dfs)
combined_df.write.format("com.databricks.spark.avro").save("s3://my-bucket/path")
答案 1 :(得分:0)
我找到了一个特定于PySpark的解决方案:
对于每个数据框,我使用.collect()来获取行列表。对于每个Row对象,我调用asDict()来获取字典。从那里,我能够用一个简单的循环构建一个字典列表。一旦我有了这个词典列表,数据退出Spark并进入纯Python领域,并且“更容易”处理(但效率较低)。
或者,如果我选择Scala而不是Python,我可能已经能够将数据帧转换为数据集,这似乎提供了一些方法来执行我需要的操作,但这完全是另一个故事。