ลดรายการคำ นับสิ่งอันดับจนถึงคีย์รวม

ฉันกำลังพยายามใช้ตัวอย่างการนับจำนวนคำ 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) ...)
]

คล้ายกับตัวอย่างการนับจำนวนคำที่นี่ ฉันต้องการที่จะกรองคำที่มีจำนวนต่ำกว่าเกณฑ์สำหรับบางคนออก ขอบคุณ!


person scmz    schedule 29.09.2017    source แหล่งที่มา


คำตอบ (2)


คีย์ที่คุณพยายามลดข้ามคือ (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 อยู่อาจง่ายกว่า

person Kerrick Staley    schedule 29.09.2017
comment
ข้อมูลของฉันมีโครงสร้างแตกต่างออกไปเล็กน้อยแต่สิ่งนี้ช่วยให้ฉันเข้าใจวิธีแก้ไข ข้อมูลเริ่มต้นของฉันดูเหมือน [Row(key=u'VI', item=u'word1 word2 word3'), ...] และฉันได้สร้างฟังก์ชันที่โทเค็นรายการและส่งคืน [((name, token), 1) for token in tokens] จากนั้นฉันก็ใช้ฟังก์ชันกับข้อมูลของฉันด้วย flatMap เพื่อให้ได้โครงสร้างที่คุณแนะนำ - person scmz; 04.10.2017

เพื่อความสมบูรณ์ นี่คือวิธีที่ฉันแก้ไขคำถามแต่ละส่วน:

ถาม 1: รวมจำนวนคำตามคีย์บางคีย์

import re

def restructure_data(name_and_freetext):
    name = name_and_freetext[0]
    tokens = re.sub('[&|/|\d{4}|\.|\,|\:|\-|\(|\)|\+|\$|\!]', ' ', 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()
person scmz    schedule 04.10.2017