Мэппер и Редуктор с итераторами и генераторами - Hadoop Python
Primary tabs
Давайте немного усовершенствуем код мэппера и редуктора отсюда:
1) mapper.py
#!/usr/bin/env python
"""Более сложный Mapper, использующий Итераторы и генераторы Питона"""
import sys
# эта функция вернёт своебразный "массив массивов"
def read_input(file):
for line in file:
# разбивает строку на слова
yield line.split() # возвращает генератор
"""Точка входа в мэппер с разделителем в виде таба в качестве разделителя по-умолчанию."""
def main(separator='\t'):
# Передаём в качестве "файла" стандартный поток ввода
data = read_input(sys.stdin)
for words in data:
# Пишем результаты в стандартный поток вывода
# what we output here will be the input for the Выход мэппера будет входом редуктора
# Reduce step, i.e. the input for reducer.py
#
# отделяем слово табом от формального
# ("формального" так как сумма ещё не подсчитана - это будет сделано в редукторе)
# числа вхождений равного 1
for word in words:
print '%s%s%d' % (word, separator, 1)
if __name__ == "__main__":
main()2) reducer.py
#!/usr/bin/env python
from itertools import groupby
from operator import itemgetter
import sys
# читаем выход мэппера
# и разбиваем каждую пару на ключ-значение
# по знаку табуляции
def read_mapper_output(file, separator='\t'):
for line in file:
yield line.rstrip().split(separator, 1)
def main(separator='\t'):
# будем читать из стандартного потока ввода
data = read_mapper_output(sys.stdin, separator=separator)
# groupby groups multiple word-count pairs by word,
# and creates an iterator that returns consecutive keys and their group:
# current_word - string containing a word (the key)
# group - iterator yielding all ["<current_word>", "<count>"] items
# с помощью groupby группирует пары "слово-число вхождений"
# по значению слова и создаёт итератор
#
#
#
for current_word, group in groupby(data, itemgetter(0)):
try:
total_count = sum(int(count) for current_word, count in group)
print "%s%s%d" % (current_word, separator, total_count)
except ValueError:
# count was not a number, so silently discard this item
pass
if __name__ == "__main__":
main()
- Log in to post comments
- 23163 reads
humanmashine
Mon, 02/10/2014 - 22:34
Permalink
Немного прокомментирую reducer
Этот код:
Возвращает генератор, который при каждой итерации будет возвращать список из двух строк полученных расзделением строки. считанной из файла, оттталкиваясь от разделителя (сепаратора). Как я понял из предыдущих примеров, выглядить будет так: ['foo', '1'].
Это выражение вернёт генератор, который будет при каждой итерации возвращать кортедж, первое значение - это сгруппированное значение в коллекции без повторений, тоесть если у нас такая коллекция: [['foo', '1'], ['foo', '1'], ['foo', '1']], то результат будет (['foo', '1'], [['foo', '1'], ['foo', '1'], ['foo', '1']]), причём жирный список, это утрированное представление значений генератора, по настоящему вместо жирного списка будет генератор и чтобы получить эти значения, надо его "проитерировать" (скажем применить функцияю list()).
Слудея вышесказанному, следующий цикл:
Будет перебирать все слова в data, но уже без повторений, а сами повторения можно выбрать из group - так как это генератор, из которого можно достать ранее рассмотренные "жирные" значения. Собственно в цикле мы с помощью генератора списков:
который формирует список чисел, находим сумму элементов списка, используя встроенную функцию sum().
vedro-compota
Tue, 02/11/2014 - 11:29
Permalink
м?)
м?)
_____________
матфак вгу и остальная классика =)
vedro-compota
Tue, 02/11/2014 - 11:29
Permalink
будет при каждой итерации
то есть первое значение этого кортежа - это тоже кортеж (пара - ['foo', '1'])
тогда получается, что в current_word попадёт не слово ....
выходит что кортеж такой: ('foo', [['foo', '1'], ['foo', '1'], ['foo', '1']]) ?
_____________
матфак вгу и остальная классика =)
humanmashine
Tue, 02/11/2014 - 12:52
Permalink
Да )))
Да, ошибся. Первое значение - это ключ группировки, в нашем случае - слово.
vedro-compota
Wed, 02/12/2014 - 15:37
Permalink
ок) в любом случае - ответ
ок) в любом случае - ответ был отличный (в конце я пропишу комментарии в первом сообщений)
ещё уточнение:
у нас эта строка лихо пишет значения сразу в две переменные.
а может ли сразу в три, четыре и т.д.?
_____________
матфак вгу и остальная классика =)
humanmashine
Wed, 02/12/2014 - 20:10
Permalink
Да, можно и три и четыре и сто-пятьсот
Этот вопрос можно выделить в отдельную тему. Тему присваивания. Дело в том, что в Python можно присваивать элементы картежа (или другой коллекции, скажем списка) отдельным переменным (Упаковка и распаковка). Это учень удобно для передачи и получений значений при работе с функциями.
Тут лучше пример:
a, b = ("trolala", "btrolb") # в итоге a = "trolala", соответственно b = "btrolb" a, b, c = [1, 2, 5] print(c) # выведет 5 def fun_func(): a = "so" b = 4 res = str(b) + a return res, a, b # возвращает кортеж из трёх элементов a, b, c = fun_func() # a = "so", b = 4, c = "4so" print(c) # выведет "4so" sm = fun_func() print(sm) # выведет кортеж ("so", 4, "4so")vedro-compota
Thu, 02/13/2014 - 14:38
Permalink
В отдельную тему вынес.
В отдельную тему вынес. правильно ли я понимаю, что если попытаться присвоить результат из fun_func() двум переменным. то будет брошена ошибка?
типа:
def fun_func(): a = "so" b = 4 res = str(b) + a return res, a, b # возвращает кортеж из трёх элементов sm, b = fun_func() # несогласованность числа значений??наверное, это можно сразу как примечание написать там.
_____________
матфак вгу и остальная классика =)
humanmashine
Thu, 02/13/2014 - 15:13
Permalink
Правильно)))
Правильно)))