我有这个数据帧path_df:
path_df.show()
+---------------+-------------+----+
|FromComponentID|ToComponentID|Cost|
+---------------+-------------+----+
| 160| 163|27.0|
| 160| 183|27.0|
| 161| 162|22.0|
| 161| 170|31.0|
| 162| 161|22.0|
| 162| 167|24.0|
| 163| 160|27.0|
| 163| 164|27.0|
| 164| 163|27.0|
| 164| 165|35.0|
| 165| 164|35.0|
| 165| 166|33.0|
| 166| 165|33.0|
| 166| 167|31.0|
| 167| 162|24.0|
| 167| 166|31.0|
| 167| 168|27.0|
| 168| 167|27.0|
| 168| 169|23.0|
| 169| 168|23.0|
+---------------+-------------+----+
only showing top 20 rows
由此,我想制作一个词典,如下:
{FromComponentID:{ToComponentID:Cost}}
对于我目前的数据,它将是:
{160 : {163 : 27,
183 : 27},
161 : {162 : 22,
170 : 31},
162 : {161 : 22
167 : 24},
...
167 : {162 : 24,
166 : 31,
168 : 27}
168 : {167 : 27,
169 : 23},
169 : {168 : 23}
}
我可以只使用PySpark吗?或者也许最好提取我的数据并直接用python处理它们。
答案 0 :(得分:5)
您可以使用数据框转换和udfs完成所有这些操作。唯一有点烦人的是,因为你在技术上有两种不同类型的字典(一种是key = integer和value = dictionary,另一种是key = integer value = float),你必须定义两个具有不同数据类型的udfs。这是一种可行的方法:
from pyspark.sql.functions import udf,collect_list,create_map
from pyspark.sql.types import MapType,IntegerType,FloatType
data = [[160,163,27.0],[160,183,27.0],[161,162,22.0],
[161,170,31.0],[162,161,22.0],[162,167,24.0],
[163,160,27.0],[163,164,27.0],[164,163,27.0],
[164,165,35.0],[165,164,35.0],[165,166,33.0],
[166,165,33.0],[166,167,31.0],[167,162,24.0],
[167,166,31.0],[167,168,27.0],[168,167,27.0],
[168,169,23.0],[169,168,23.0]]
cols = ['FromComponentID','ToComponentID','Cost']
df = spark.createDataFrame(data,cols)
combineMap = udf(lambda maps: {key:f[key] for f in maps for key in f},
MapType(IntegerType(),FloatType()))
combineDeepMap = udf(lambda maps: {key:f[key] for f in maps for key in f},
MapType(IntegerType(),MapType(IntegerType(),FloatType())))
mapdf = df.groupBy('FromComponentID')\
.agg(collect_list(create_map('ToComponentID','Cost')).alias('maps'))\
.agg(combineDeepMap(collect_list(create_map('FromComponentID',combineMap('maps')))))
result_dict = mapdf.collect()[0][0]
对于大型数据集,这应该比需要将数据收集到单个节点的解决方案提供一些性能提升。但由于火花仍然需要序列化udf,因此与基于rdd的解决方案相比,它不会获得巨大的收益。
更新
rdd解决方案更紧凑,但在我看来,它并不是那么干净。这是因为pyspark不容易将大字典存储为rdds。解决方案是将其存储为元组的分布式列表,然后在将其收集到单个节点时将其转换为字典。这是一个可能的解决方案:
maprdd = df.rdd.groupBy(lambda x:x[0]).map(lambda x:(x[0],{y[1]:y[2] for y in x[1]}))
result_dict = dict(maprdd.collect())
同样,这应该比单个节点上的纯python实现提供性能提升,并且它可能与数据帧实现没有什么不同,但我的期望是数据帧版本将更高性能。
答案 1 :(得分:0)
你可以试试这种方式
df_prod = spark.read.csv('/path/to/sample.csv',inferSchema=True,header=True)
rdd = df_prod.rdd.map(lambda x: {x['FromComponentID']:{x['ToComponentID']:x['Cost']}})
rdd.collect()
答案 2 :(得分:0)
我知道的最简单的方法是下面的(但有Pandas依赖):
path_df.toPandas().set_index('FromComponentID').T.to_dict('list')