根据条件在pyspark数据框列中映射字典中的值

时间:2020-07-01 06:20:15

标签: pyspark

我具有以下数据框,我希望从字典中映射该列。

data = [
  ('web', '0'),   
  ('web', '1'),
  ('web', '2'),
    ('twitter', '0'),
    ('twitter', '1'),
  ('facebook', '0'),
  ('facebook', '1'),
      ('facebook','2')
]
data = (spark.createDataFrame(data, ['channel','type']))

我有下面的字典,我想根据“通道”列的值映射字典中的值

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

最终结果应该是,如果“通道”列具有web,那么列“类型具有0”,那么通道类型应该是“网站”

2 个答案:

答案 0 :(得分:1)

一种方法是通过联接数据框,但我不建议这样做,因为仅联接到地图将是一项繁重的操作

其他方法是使用UDF,这也是最不推荐使用的UDF,因为UDF是黑匣子,无法通过催化剂优化程序进行优化,但是解决方案仍然如此

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

答案 1 :(得分:0)

是否可以以稍微不同的方式重构字典,必须使用简单的python代码才能实现。然后,您可以从中创建一个数据框并加入。否则,将需要昂贵的udf。由于此表很小,因此您也可以进行广播联接。(此处未显示)

data = [
  ('web', '0'),   
  ('web', '1'),  
    ('twitter', '0'),
    ('twitter', '1')     
]
data_df = (sqlContext.createDataFrame(data, ['channel','type']))
# Changed dictionary structure
conf = [{"channel": 'web', 'type': 0, 'result': 'webbsite'},
            {"channel": 'web', 'type': 1, 'result': 'news'},
            {"channel": 'twitter', 'type': 0, 'result': 'tweet'},
            {"channel": 'twitter', 'type': 1, 'result': 'retweet'}]
 
conf_df = sqlContext.createDataFrame(data_sub)

data_res = data_df.join(conf_df,on=['channel','type'],how='left')

结果:

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