[英]PySpark: create dict of dicts from dataframe?
我有以下格式的数据,这些数据是从Hive获取到数据帧中的:
date, stock, price
1388534400, GOOG, 50
1388534400, FB, 60
1388534400, MSFT, 55
1388620800, GOOG, 52
1388620800, FB, 61
1388620800, MSFT, 55
日期是当天午夜的纪元,而我们的数据可以追溯到10年前(8亿多行)。 我想得到一个字典,如下所示:
{
'GOOG':
{
'1388534400': 50,
'1388620800': 52
}
'FB':
{
'1388534400': 60,
'1388620800': 61
}
}
一种幼稚的方法是获取唯一股票列表,然后通过仅过滤出每种股票的那些行来获取数据框的子集,但这似乎过于幼稚且效率极低。 可以在Spark中轻松完成吗? 目前,我已经可以使用PyHive在本地Python中运行它,但是由于数据量巨大,我宁愿在集群/ Spark上完成此操作。
在map_from_arrays
2.4中,当汇总每只股票的价值时,可以使用map_from_arrays
来构建日期-价值映射。 然后,只需使用create_map
即可将股票代号用作键。 本示例使用python 3.4中的ChainMap
构建最终的字典结构,如您所述。
import json
from collections import ChainMap
from pyspark.sql import SparkSession
from pyspark.sql.functions import *
spark = SparkSession \
.builder \
.appName("example") \
.getOrCreate()
df = spark.createDataFrame([
(1388534400, "GOOG", 50),
(1388534400, "FB", 60),
(1388534400, "MSFT", 55),
(1388620800, "GOOG", 52),
(1388620800, "FB", 61),
(1388620800, "MSFT", 55)]
).toDF("date", "stock", "price")
out = df.groupBy("stock") \
.agg(
map_from_arrays(
collect_list("date"), collect_list("price")).alias("values")) \
.select(create_map("stock", "values").alias("values")) \
.rdd.flatMap(lambda x: x) \
.collect()
print(json.dumps(dict(ChainMap(*out)), indent=4, separators=(',', ': '), sort_keys=True))
这使:
{
"FB": {
"1388534400": 60,
"1388620800": 61
},
"GOOG": {
"1388534400": 50,
"1388620800": 52
},
"MSFT": {
"1388534400": 55,
"1388620800": 55
}
}
但是 ,正如您所说的,您可能有很多数据实际上并不希望在内存中创建此字典,因此,最好将其拆分并将相同的字典结构写入不同分区的文件中会更好。
为此,我们将日期截短到给定的月份,然后为每个月和每个股票编写单独的文件:
out = df.groupBy(trunc(expr("CAST(date as TIMESTAMP)"), "month").alias("month"), df["stock"]) \
.agg(
map_from_arrays(
collect_list("date"), collect_list("price")).alias("values")) \
.select("month", "stock", create_map("stock", "values").alias("values"))
out.write.partitionBy("month", "stock").format("json").save("out/prices")
这为您提供了如下结构:
out
└── prices
├── _SUCCESS
└── month=2014-01-01
├── stock=FB
│ └── part-00093-3741bdc2-345a-488e-82da-53bb586cd23b.c000.json
├── stock=GOOG
│ └── part-00014-3741bdc2-345a-488e-82da-53bb586cd23b.c000.json
└── stock=MSFT
└── part-00152-3741bdc2-345a-488e-82da-53bb586cd23b.c000.json
MSFT文件如下所示:
{"values":{"MSFT":{"1388534400":55,"1388620800":55}}}
尽管“值”列名称可能不在字典结构中,但我希望这可以说明您可以执行的操作。
我正在使用Spark 2.3.1
这是PySpark
版本-
from pyspark.sql.functions import udf,collect_list,create_map
from pyspark.sql.types import MapType,IntegerType,StringType
myValues = [('1388534400', 'GOOG', 50), ('1388534400', 'FB', 60), ('1388534400', 'MSFT', 55), ('1388620800', 'GOOG', 52),
('1388620800', 'FB', 61), ('1388620800', 'MSFT', 55)]
df = sqlContext.createDataFrame(myValues,['date','stock','price'])
df.show()
+----------+-----+-----+
| date|stock|price|
+----------+-----+-----+
|1388534400| GOOG| 50|
|1388534400| FB| 60|
|1388534400| MSFT| 55|
|1388620800| GOOG| 52|
|1388620800| FB| 61|
|1388620800| MSFT| 55|
+----------+-----+-----+
combineMap = udf(lambda maps: {key:f[key] for f in maps for key in f},
MapType(StringType(),IntegerType()))
combineDeepMap = udf(lambda maps: {key:f[key] for f in maps for key in f},
MapType(StringType(),MapType(StringType(),IntegerType())))
mapdf = df.groupBy('stock')\
.agg(collect_list(create_map('date','price')).alias('maps'))\
.agg(combineDeepMap(collect_list(create_map('stock',combineMap('maps')))))
new_dict= mapdf.collect()[0][0]
print(new_dict)
{u'GOOG': {u'1388620800': 52, u'1388534400': 50}, u'FB': {u'1388620800': 61, u'1388534400': 60}, u'MSFT': {u'1388620800': 55, u'1388534400': 55}}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.