减少单词列表,将元组计数到聚合键

Posted

技术标签:

【中文标题】减少单词列表,将元组计数到聚合键【英文标题】:Reduce list of word, count tuples up to aggregate key 【发布时间】:2017-09-29 16:27:13 【问题描述】:

我正在尝试使用 Spark 字数统计示例并按其他值汇总字数(例如,在下面的情况下,人是“VI”或“MO”的人的字数和计数)

我有一个 rdd,它是一个元组列表,其值为元组列表:

from operator import add
reduced_tokens = tokenized.reduceByKey(add)
reduced_tokens.take(2)

这给了我:

[(u'VI', [(u'word1', 1), (u'word2', 1), (u'word3', 1)]),
 (u'MO',
  [(u'word4', 1),
   (u'word4', 1),
   (u'word5', 1),
   (u'word8', 1),
   (u'word10', 1),
   (u'word1', 1),
   (u'word4', 1),
   (u'word6', 1),
   (u'word9', 1),
   ...
 )]

我想要类似的东西:

[
 ('VI', 
    [(u'word1', 1), (u'word2', 1), (u'word3', 1)],
 ('MO', 
    [(u'word4', 58), (u'word8', 2), (u'word9', 23) ...)
]

类似于word count example here,我希望能够过滤掉某个人的计数低于某个阈值的单词。谢谢!

【问题讨论】:

【参考方案1】:

您尝试减少的键是 (name, word) 对,而不仅仅是名称。因此,您需要执行.map 步骤来修复您的数据:

def key_by_name_word(record):
  name, (word, count) = record
  return (name, word), count

tokenized_by_name_word = tokenized.map(key_by_name_word)
counts_by_name_word = tokenized_by_name_word.reduce(add)

这应该给你

[
  (('VI', 'word1'), 1),
  (('VI', 'word2'), 1),
  (('VI', 'word3'), 1),
  (('MO', 'word4'), 58),
  ...
]

要将其转换为您提到的完全相同的格式,您可以这样做:

def key_by_name(record):
  # this is the inverse of key_by_name_word
  (name, word), count = record
  return name, (word, count)

output = counts_by_name_word.map(key_by_name).reduceByKey(add)

但实际上使用counts_by_name_word 所在的平面格式的数据可能更容易。

【讨论】:

我的数据结构略有不同,但这有助于我了解如何修复它。我的初始数据看起来像[Row(key=u'VI', item=u'word1 word2 word3'), ...],我创建了一个函数来标记项目并返回[((name, token), 1) for token in tokens]。从那里我使用 flatMap 将函数应用于我的数据以获得您建议的结构。【参考方案2】:

为了完整起见,以下是我解决问题各个部分的方法:

问题 1:按某个键汇总字数

import re

def restructure_data(name_and_freetext):
    name = name_and_freetext[0]
    tokens = re.sub('[&|/|\d4|\.|\,|\:|\-|\(|\)|\+|\$|\!]', ' ', name_and_freetext[1]).split()
    return [((name, token), 1) for token in tokens]

filtered_data = data.filter((data.flag==1)).select('name', 'item')
tokenized = filtered_data.rdd.flatMap(restructure_data)

问题 2:过滤掉计数低于某个阈值的单词:

from operator import add

# keep words which have counts >= 5
counts_by_state_word = tokenized.reduceByKey(add).filter(lambda x: x[1] >= 5)

# map filtered word counts into a list by key so we can sort them
restruct = counts_by_name_word.map(lambda x: (x[0][0], [(x[0][1], x[1])]))

奖励:将单词从最频繁到最不频繁排序

# sort the word counts from most frequent to least frequent words
output = restruct.reduceByKey(add).map(lambda x: (x[0], sorted(x[1], key=lambda y: y[1], reverse=True))).collect()

【讨论】:

以上是关于减少单词列表,将元组计数到聚合键的主要内容,如果未能解决你的问题,请参考以下文章

将元组列表列表转换为 dict 未按预期工作

将元组列表合并到字典中

使用 Pyspark 从单词列表的行条目创建元组并使用 RDD 计数

如何将元组列表从 python 发送和解包到 qml?

将元组列表转换为 Pandas 系列

将元组列表转换为字典