【问题标题】:solving a problem with map reduce解决 map reduce 的问题
【发布时间】:2020-12-01 02:04:31
【问题描述】:

我想在 ruby​​ 中模拟我对像 hadoop 这样的系统的 mapreduce 函数的实现,以验证这个想法至少是否有效。

我有以下问题。我有两个元素列表:

List1
3 - A
4 - B
5 - C
7 - D
8 - F

List2
2 - A
8 - B
6 - C
9 - D
4 - E

我需要构建一个通用列表,其中包含与两个列表中通用字母相关的数字的总和:

commonList
5 - A
12 - B
11 - C
16 - D

我想用mapreduce 操作制作一个ruby 脚本来解决这个问题。我不确定如何解决这个问题,或者在 ruby​​ 脚本中模拟这个问题要遵循什么程序。

任何帮助表示赞赏。

【问题讨论】:

    标签: ruby mapreduce


    【解决方案1】:

    使用 irb (ruby-1.9.2-p180):

    list = [ {a:2, b:1, d:3}, {a:3, b:2, c:3}, {a:4, b:1, c:3} ]
     => [{:a=>2, :b=>1, :d=>3}, {:a=>3, :b=>2, :c=>3}, {:a=>4, :b=>1, :c=>3}] 
    
    Hash[list.map(&:keys).inject(&:&).map{|key| [key,list.map{|arr| arr[key]}.inject(&:+)]}]
     => {:a=>9, :b=>4} 
    

    此解决方案适用于多个数组 (2+),它找到公共键并将它们相加,返回结果哈希

    查找公共密钥(收集密钥并查找公共部分):

    list.map(&:keys).inject(&:&)
    

    查找键的总和(通过键选择值并将它们求和):

    list.map{|arr| arr[key]}.inject(&:+)
    

    从数组对[[:a,9], [:b,4]]构建哈希:

    results = [[:a,9], [:b,4]]
    Hash[ results ]
    

    我喜欢用红宝石做这个衬里!

    【讨论】:

      【解决方案2】:

      您可以尝试考虑MapReduce wikipedia 文章中给出的元素:

      • 输入阅读器 - 在您的情况下,这可能是从您的输入哈希中对 [key, value] 对的方法调用。
      • 一个 Map 函数 - 您已经有了用来处理数据的键,因此您的 map 工作人员只会返回它作为输入获得的 [key, value]
      • 分区函数 - 一种基于键分配 reduce 工作人员的方法。在您的情况下,它可能只是 key.hash % REDUCER_COUNT
      • 比较功能 - 我认为这不适用于您的情况,因为您不需要以任何特定顺序处理值。
      • Reduce 函数 - 将给出 [key, list] 对,列表是与键关联的值列表。如果列表长度超过一个元素,它将返回 list 的总和(因为您只希望处理两个输入哈希中出现的元素)。
      • 输出编写器 - 在您的示例中可能是普通哈希。

      here's我(过度)简化了上述实现。

      【讨论】:

      • 我正在尝试您的解决方案,但我收到以下行错误:[key, list.inject(&:+)]。我收到此错误:“TypeError:错误的参数类型符号(预期的 Proc)”
      • 您可能使用的是较旧的 Ruby。请改用list.inject{|acc,i| acc+i}
      【解决方案3】:

      假设我们已经实现了所有其他与 map-reduce 相关的功能(输入读取器、输出写入器、全局排序......),这些将是 mapreduce 的:

      def map(input)
        input.each do |count, letter|
          yield [letter, count]
        end
      end
      
      def reduce(letter, partial_counts)
        result = if partial_counts.size == 2
          partial_counts[0] + partial_counts[1]
        end
      
        yield result
      end
      

      map 函数将yield 一对(letter, count),稍后进行分组。然后对于从maps reduce 收到的每个letter 将收到一个数组,其中包含由map 为该letter 产生的每个计数。由于您希望仅当字母出现在两个哈希上时才产生,所以我们需要 counts 出现在 partial_counts 上两次以使用它来计算最后的总和。 reduce 函数可以通过多种方式实现。我试图让它尽可能简单易懂,尽管它的实现非常适合这个问题。

      使用这些mapreduce 实现将返回键和值反转的最后一个哈希,这更有意义,因为可能有多个具有相同计数的字母。如果它也反转键和值,输入会更好。这样一来,map 就像生成每对 (letter, count) 一样简单:

      def map(input)
        input.each do |letter, count|
          yield [letter, count]
        end
      end
      

      def map(input)
        input.each do |i|
          yield i
        end
      end
      

      【讨论】:

        【解决方案4】:
        list_1 = ["3 - A", "4 - B", "5 - C", "7 - D", "8 - F"]
        
        list_2 = ["2 - A", "8 - B", "6 - C", "9 - D", "4 - E"]
        
        (list_1 + list_2).map do |str|
          # change array of strings to array in the form of [[name, value], ...]
          str  =~ /(\d+) - (.*)/ && [$2, $1.to_i]
        end.reduce({}) do |memo, obj|
          # use a temporary Hash to sum up the values;
          # the value is an array in the form of [value_counter, iteration_counter]
          prev = memo[obj.first] || [0, 0]
          memo[obj.first] = [prev.first + obj.last, prev.last + 1]
          memo
        end.map do |key, value|
          # convert to array in original format or
          # nil, if occurred only once
          value.last > 1 ? "#{key} - #{value.first}" : nil
        end.compact
        
        => ["A - 5", "B - 12", "C - 11", "D - 16"]
        

        此代码使用 Ruby 的 mapreduce 方法,但直接在 Hash 上执行所有这些操作会更加优雅。

        【讨论】:

          猜你喜欢
          • 2011-02-03
          • 1970-01-01
          • 2023-03-28
          • 1970-01-01
          • 1970-01-01
          • 2011-03-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多