基于条件映射pyspark数据帧列中字典中的值

基于条件映射pyspark数据帧列中字典中的值,pyspark,Pyspark,我有下面的数据框,我想从字典映射列 data = [ ('web', '0'), ('web', '1'), ('web', '2'), ('twitter', '0'), ('twitter', '1'), ('facebook', '0'), ('facebook', '1'), ('facebook','2') ] data = (spark.createDataFrame(data, ['channel','type'])) 我有下

我有下面的数据框,我想从字典映射列

data = [
  ('web', '0'),   
  ('web', '1'),
  ('web', '2'),
    ('twitter', '0'),
    ('twitter', '1'),
  ('facebook', '0'),
  ('facebook', '1'),
      ('facebook','2')
]
data = (spark.createDataFrame(data, ['channel','type']))
我有下面的字典,我希望字典中的值基于列“channel”的值进行映射

conf = {'channel_type':
        {'web': {'0': 'website', '1': 'news', '2': 'blogs'},
 'twitter': {'0': 'tweet', '1': 'retweet'},
 'facebook': {'0': 'post',
  '1': 'feed_post',
  '2': 'comment',
  '3': 'shared_post'},
 'you_tube': {'0': 'comment'},
 'instagram': {'0': 'video', '1': 'media', '2': 'comment'},
 'reddit': {'0': 'reddit_post', '1': 'reddit_comment'},
 'linkedin': {'0': 'linkedin_articles',
  '1': 'linkedin_rich',
  '2': 'linkedin_comments'}}
       }
我已经尝试了下面的代码,但没有成功

mapping_expr_twitter = f.create_map([lit(x) for x in chain(*conf['channel_type']['twitter'].items())])

mapped_cols = (data.withColumn('channel_type', f.when(f.col('channel')=='twitter',
                                                      mapping_expr_twitter.getItem(f.col("type")))
                              .otherwsie(None))
              )

最终结果应该是,如果列“channel”有web,那么列“type”有0,那么通道类型应该是“website”

是否可以稍微改变字典的结构,必须使用简单的python代码才能实现。然后,您可以从中创建一个数据帧并进行连接。否则,将需要成本高昂的udf。由于这个表很小,您也可以进行广播连接。(此处未显示)

结果是:

data_res.show()
+-------+----+--------+
|channel|type|  result|
+-------+----+--------+
|    web|   1|    news|
|twitter|   0|   tweet|
|    web|   0|webbsite|
|twitter|   1| retweet|
+-------+----+--------+

一种方法是加入dataframe,但我不建议这样做,因为仅仅加入map将是一项繁重的操作

另一种方法是使用UDF,这也是最不推荐的,因为UDF是blackbox,不能由catalyst optimizer优化,但解决方案仍然如下所示

map_func = f.udf(lambda channel,typ : conf['channel_type'].get(channel,channel).get(typ,typ))
data.withColumn('ChannelType',map_func(f.col('channel'),f.col('type'))).show()

+--------+----+-----------+
| channel|type|ChannelType|
+--------+----+-----------+
|     web|   0|    website|
|     web|   1|       news|
|     web|   2|      blogs|
| twitter|   0|      tweet|
| twitter|   1|    retweet|
|facebook|   0|       post|
|facebook|   1|  feed_post|
|facebook|   2|    comment|
+--------+----+-----------+
另一种方法是使用create_map,这在本例中是最首选的

from itertools import chain
conf_mapper = f.create_map([f.lit(i) for i in chain(*{k+x:y for k,v in conf['channel_type'].items() for x,y in v.items()}.items())])
data.withColumn('ChannelType',conf_mapper[f.concat('channel','type')]).show()

+--------+----+-----------+
| channel|type|ChannelType|
+--------+----+-----------+
|     web|   0|    website|
|     web|   1|       news|
|     web|   2|      blogs|
| twitter|   0|      tweet|
| twitter|   1|    retweet|
|facebook|   0|       post|
|facebook|   1|  feed_post|
|facebook|   2|    comment|
+--------+----+-----------+

from itertools import chain
conf_mapper = f.create_map([f.lit(i) for i in chain(*{k+x:y for k,v in conf['channel_type'].items() for x,y in v.items()}.items())])
data.withColumn('ChannelType',conf_mapper[f.concat('channel','type')]).show()

+--------+----+-----------+
| channel|type|ChannelType|
+--------+----+-----------+
|     web|   0|    website|
|     web|   1|       news|
|     web|   2|      blogs|
| twitter|   0|      tweet|
| twitter|   1|    retweet|
|facebook|   0|       post|
|facebook|   1|  feed_post|
|facebook|   2|    comment|
+--------+----+-----------+