Kurangi daftar kata, hitung tupel hingga kunci agregat

Saya mencoba mengambil contoh jumlah kata Spark dan menggabungkan jumlah kata berdasarkan beberapa nilai lain (misalnya, kata dan jumlah menurut orang dengan orang "VI" atau "MO" dalam kasus di bawah)

Saya memiliki rdd yang merupakan daftar tupel yang nilainya adalah daftar tupel:

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

Yang memberi saya:

[(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),
   ...
 )]

Saya ingin sesuatu seperti:

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

Mirip dengan contoh jumlah kata di sini, Saya ingin dapat menyaring kata-kata dengan jumlah di bawah ambang batas tertentu untuk beberapa orang. Terima kasih!


person scmz    schedule 29.09.2017    source sumber


Jawaban (2)


Kunci yang Anda coba kurangi adalah (name, word) pasangan, bukan hanya nama. Jadi, Anda perlu melakukan langkah .map untuk memperbaiki data Anda:

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)

Ini seharusnya memberi Anda

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

Untuk membuatnya menjadi format yang persis sama dengan yang Anda sebutkan, Anda dapat melakukan:

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)

Namun sebenarnya mungkin lebih mudah untuk bekerja dengan data dalam format datar yang menggunakan counts_by_name_word.

person Kerrick Staley    schedule 29.09.2017
comment
Struktur data saya sedikit berbeda, tetapi ini membantu saya memahami cara memperbaikinya. Data awal saya tampak seperti [Row(key=u'VI', item=u'word1 word2 word3'), ...] dan saya membuat fungsi yang memberi token pada item dan mengembalikan [((name, token), 1) for token in tokens]. Dari sana saya menerapkan fungsi tersebut ke data saya dengan flatMap untuk mendapatkan struktur yang Anda sarankan. - person scmz; 04.10.2017

Untuk kelengkapannya, inilah cara saya menyelesaikan setiap bagian pertanyaan:

Pertanyaan 1: Gabungkan jumlah kata menurut beberapa kunci

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)

Pertanyaan 2: Filter kata-kata dengan jumlah di bawah ambang batas tertentu:

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

Bonus: Urutkan kata dari yang paling sering ke yang paling jarang

# 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