在单个mapreduce中产生最大值和最小值

时间:2017-10-28 15:54:10

标签: python hadoop mapreduce mrjob

我刚开始使用MRJob库在Python中编写MapReduce程序。

视频教程中的一个示例是通过location_id找到最高温度。继编写之后,通过location_id查找最低温度的另一个程序也很简单。

我想知道,有没有办法在一个mapreduce程序中通过location_id产生最高和最低温度?以下是我的观点:

from mrjob.job import MRJob

'''Sample Data
ITE00100554,18000101,TMAX,-75,,,E,
ITE00100554,18000101,TMIN,-148,,,E,
GM000010962,18000101,PRCP,0,,,E,
EZE00100082,18000101,TMAX,-86,,,E,
EZE00100082,18000101,TMIN,-135,,,E,
ITE00100554,18000102,TMAX,-60,,I,E,
ITE00100554,18000102,TMIN,-125,,,E,
GM000010962,18000102,PRCP,0,,,E,
EZE00100082,18000102,TMAX,-44,,,E, 

Output I am expecting to see:
ITE00100554  32.3  20.2
EZE00100082  34.4  19.6
'''

class MaxMinTemperature(MRJob):
    def mapper(self, _, line):
        location, datetime, measure, temperature, w, x, y, z = line.split(',')
        temperature = float(temperature)/10
        if measure == 'TMAX' or measure == 'TMIN':
            yield location, temperature

    def reducer(self, location, temperatures):
        yield location, max(temperatures), min(temperatures)


if __name__ == '__main__':
    MaxMinTemperature.run()

我收到以下错误:

File "MaxMinTemperature.py", line 12, in reducer
yield location, max(temperatures), min(temperatures)
ValueError: min() arg is an empty sequence

这可能吗?

感谢您的协助。

希夫

2 个答案:

答案 0 :(得分:4)

你在reducer中有两个问题:

  1. 如果检查温度参数的类型,您将看到它是一个生成器。生成器只能遍历一次,因此您无法将同一生成器传递给'min'和'max'函数。正确的解决方案是手动遍历它。错误的解决方案 - 将其转换为列表 - 可能会在足够大的输入上导致内存不足错误,因为列表将所有元素保存在内存中而生成器不会。

  2. reducer的结果必须是两元素元组。因此,您需要将最小和最高温度组合在另一个元组中。

  3. 完整的工作解决方案:

    class MaxMinTemperature(MRJob):
        def mapper(self, _, line):
            location, datetime, measure, temperature, w, x, y, z = line.split(',')
            temperature = float(temperature)/10
            if measure in ('TMAX', 'TMIN'):
                yield location, temperature
    
        def reducer(self, location, temperatures):
            min_temp = next(temperatures)
            max_temp = min_temp
            for item in temperatures:
                min_temp = min(item, min_temp)
                max_temp = max(item, max_temp)
            yield location, (min_temp, max_temp)
    

答案 1 :(得分:0)

问题在于temperatures方法中的reducergenerator

为了更好地理解,让我们创建一个简单的生成器并查看其行为:

def my_gen(an_iterable):
    for item in an_iterable:
        yield item

my_generator = my_gen([1,2,3,4,5])
print(type(my_generator)) # <class 'generator'>

这种对象的功能之一是,一旦用尽,就无法重复使用它:

print(list(my_generator)) # [1, 2, 3, 4, 5]
print(list(my_generator)) # []

因此,max()min()的顺序执行会导致错误:

my_generator = my_gen([1,2,3,4,5])

print(max(my_generator)) # 5
print(min(my_generator)) # ValueError: min() arg is an empty sequence

所以,你不能在max()min()内置函数中使用相同的生成器,因为在第二次使用时,生成器将耗尽。

相反,您可以:

1)将生成器转换为列表并使用它:

my_generator = my_gen([1,2,3,4,5])
my_list = list(my_generator)

print(max(my_list)) # 5
print(min(my_list)) # 1 

2)或在1 for-loop中提取生成器的最小值和最大值:

my_generator = my_gen([1,2,3,4,5])

from functools import reduce
val_max, val_min = reduce(lambda x,y: (max(y, x[0]), min(y, x[1])), my_generator, (float('-inf'), float('inf'))) 

print(val_max, val_min) # 5 1

所以,以下编辑reducer

def reducer(self, location, temperatures):
    tempr_list = list(temperatures)
    yield location, max(tempr_list), min(tempr_list)

应该修正错误。