Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/apache-spark/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python PySpark:从数据帧创建dict的dict?_Python_Apache Spark_Pyspark - Fatal编程技术网

Python PySpark:从数据帧创建dict的dict?

Python PySpark:从数据帧创建dict的dict?,python,apache-spark,pyspark,Python,Apache Spark,Pyspark,我有以下格式的数据,这些数据是从配置单元获取到数据帧中的: date, stock, price 1388534400, GOOG, 50 1388534400, FB, 60 1388534400, MSFT, 55 1388620800, GOOG, 52 1388620800, FB, 61 1388620800, MSFT, 55 其中date是当天午夜的纪元,我们有10年左右的数据(8亿行以上)。 我想买一本字典如下: { 'GOOG': { '1388534400': 50, '1

我有以下格式的数据,这些数据是从配置单元获取到数据帧中的:

date, stock, price
1388534400, GOOG, 50
1388534400, FB, 60
1388534400, MSFT, 55
1388620800, GOOG, 52
1388620800, FB, 61
1388620800, MSFT, 55
其中date是当天午夜的纪元,我们有10年左右的数据(8亿行以上)。 我想买一本字典如下:

{
'GOOG':
{
'1388534400': 50,
'1388620800': 52
}

'FB':
{
'1388534400': 60,
'1388620800': 61
}
}
一种天真的方法是获得一个独特股票的列表,然后通过只过滤每个股票的那些行来获得数据帧的子集,但这似乎过于天真,效率极低。
这能在Spark中轻松完成吗?我目前使用PyHive在本机Python中运行,但由于数据量太大,我宁愿在集群/Spark上运行。

我使用的是
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}}

在spark 2.4中,当聚合每个股票的值时,可以使用
map\u from\u array
来构建日期值映射。那么使用股票代码作为一个键就成了一个使用问题。本例使用Python3.4中的
ChainMap
构建您所描述的最终dict结构

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}}}

虽然“values”列名可能不在字典结构中,但我希望这说明了您可以做什么。

如果需要该数据结构,您必须在某个时候将所有数据收集到本地内存中。当然你可以在spark中进行聚合,但是你仍然会失去使用spark的好处。据我所知,它将与使用PyHive解决方案相同(或非常类似)。也许这是一个?很可能是一个XY问题。以下是我试图实现的要点——我有过去5年中每30分钟获取的数据点的值,可能是每只股票87k点。我需要指定的格式-
{symbol:{epoch1:value1,epoch2:value2…}
,因为我有一个例程,它搜索缺少的数据点并插入空值,返回排序后的json文件中的所有内容,最终将其输入可视化门户。我想我也可以将验证例程移动到Spark,也许可以选择将数据写入指定格式的文件?这样写操作就可以并行进行(对多个文件),而且比收集数据效率高得多。@pault问题的更多细节是这样的--我这样做是因为有可能丢失需要处理的数据谢谢,但是我在导入过程中得到了
ImportError:cannotimportname create\u map
。我使用的是Spark 1.6.0/Python 2.7