【问题标题】:How to find an item in array which has the most occurrences [duplicate]如何在数组中找到出现次数最多的项目[重复]
【发布时间】:2010-09-29 13:21:10
【问题描述】:

如何在数组中找到出现次数最多的项目?

[1, 1, 1, 2, 3].mode
=> 1

['cat', 'dog', 'snake', 'dog'].mode
=> dog

【问题讨论】:

  • 你问的是所谓的“模式”。
  • 方法 "mode" 在 Rails 4 中不起作用 :( 我在这里找到了答案 => stackoverflow.com/questions/8921999/… :)
  • @Romans8.38-39 - 谢谢,我以为我快疯了。似乎是另一个必须扔掉以破坏工作代码的有用东西。有人需要分叉 Rails 并放回所有好的部分。

标签: ruby arrays


【解决方案1】:

首先构建一个散列,将数组中的每个值映射到它的频率……

arr = [1, 1, 1, 2, 3]

freq = arr.inject(Hash.new(0)) { |h,v| h[v] += 1; h }
#=> {1=>3, 2=>1, 3=>1}

...然后使用频率表找到频率最高的元素:

arr.max_by { |v| freq[v] }
#=> 1

【讨论】:

  • 也可以是freq.max_by { |_, v| v }.first作为最后一行。
  • 如果你喜欢在线,你也可以arr.each_with_object(Hash.new(0)) { |v, h| h[v] += 1 }.max_by(&:last)
  • 有很多方法可以做到这一点^v^to_count = [[5, 6], [1, 2], [3, 4], [7, 8], [1, 2]]counter1 = to_count.each_with_object(Hash.new(0)){|it, acc| acc[it] += 1}counter2 = Hash.new(0).tap{|h| to_count.each{|it| h[it] += 1}}counter3 = to_count.group_by(&:itself).map{|it, its| [it, its.length]}.to_hcounter4 = to_count.inject(Hash.new(0)) { |h,v| h[v] += 1; h }counter4 = to_count.inject(Hash.new(0)) { |h,v| h[v] += 1; h }p counter1.max_by(&:last).firstp counter2.max_by(&:last).firstp to_count.max_by{|v| counter3[v]}@9876543334p to_count.max_by{|v| counter4[v]}987654334@9876543334@986 /跨度>
  • 嗯............
  • 有很多方法可以做到这一点,但最快的是使用堆栈。见app.codility.com/programmers/lessons/8-leader/dominator
【解决方案2】:
idx = {}
[2,2,1,3,1].each { |i| idx.include?(i) ? idx[i] += 1 : idx[i] = 1}

这只是一个简单的索引器。您可以将 [2,2,1..] 数组替换为任何类型的基于符号/字符串的标识符,这不适用于对象,您需要引入更多复杂性,但这很简单。

重读你的问题,这个解决方案有点过度设计,因为它会返回所有出现的索引,而不仅仅是出现最多的那个。

【讨论】:

    【解决方案3】:

    虽然我喜欢 grep 解决方案的优雅并提醒(或教给我)我忘记(或完全忽略)的 Enumerable 中的方法,但它很慢,很慢,很慢。我 100% 同意创建 Array#mode 方法是一个好主意,但是 - 这是 Ruby,我们不需要作用于数组的函数库,我们可以创建一个 mixin,将必要的函数添加到 Array 类本身。

    但是inject(Hash) 替代方案使用了一种我们也并不真正需要的排序:我们只想要出现次数最多的值。

    这两种解决方案都没有解决模式可能不止一个值的可能性。也许这不是问题中的问题(无法判断)。不过,我想我想知道是否有平局,无论如何,我认为我们可以在性能上有所提高。

    require 'benchmark'
    
    class Array
      def mode1
        sort_by {|i| grep(i).length }.last
      end
      def mode2
        freq = inject(Hash.new(0)) { |h,v| h[v] += 1; h }
        sort_by { |v| freq[v] }.last    
      end
      def mode3
        freq = inject(Hash.new(0)) { |h,v| h[v] += 1; h }
        max = freq.values.max                   # we're only interested in the key(s) with the highest frequency
        freq.select { |k, f| f == max }         # extract the keys that have the max frequency
      end
    end
    
    arr = Array.new(1_000) { |i| rand(100) }    # something to test with
    
    Benchmark.bm(30) do |r|
      res = {}
      (1..3).each do |i|
        m = "mode#{i}"
        r.report(m) do
          100.times do
            res[m] = arr.send(m).inspect
          end
        end
      end
      res.each { |k, v| puts "%10s = %s" % [k, v] }
    end
    

    下面是示例运行的输出:

                                    user     system      total        real
    mode1                          34.375000   0.000000  34.375000 ( 34.393000)
    mode2                           0.359000   0.000000   0.359000 (  0.359000)
    mode3                           0.219000   0.000000   0.219000 (  0.219000)
         mode1 = 41
         mode2 = 41
         mode3 = [[41, 17], [80, 17], [72, 17]]
    

    “优化”模式 3 花费了前一个记录保持者的 60% 的时间。还要注意多个频率最高的条目。


    几个月后,我注意到Nilesh's answer,它提供了这个:

    def mode4
      group_by{|i| i}.max{|x,y| x[1].length <=> y[1].length}[0]
    end
    

    它不适用于开箱即用的 1.8.6,因为该版本没有 Array#group_by。对于 Rails 开发人员,ActiveSupport 有它,尽管它似乎比上面的mode3 慢 2-3%。但是,使用(出色的)backports gem 可以产生 10-12% 的增益,并提供一大堆 1.8.7 和 1.9 的附加功能。

    以上内容仅适用于 1.8.6 - 并且主要仅适用于安装在 Windows 上的情况。既然我已经安装了它,下面是您从 IronRuby 1.0(在 .NET 4.0 上)获得的内容:

    ==========================   IronRuby   =====================================
    (iterations bumped to **1000**)    user     system      total        real
    mode1 (I didn't bother :-))
    mode2                           4.265625   0.046875   4.312500 (  4.203151)
    mode3                           0.828125   0.000000   0.828125 (  0.781255)
    mode4                           1.203125   0.000000   1.203125 (  1.062507)
    

    因此,如果性能非常关键,请在您的 Ruby 版本和操作系统上对选项进行基准测试。 YMMV.

    【讨论】:

      【解决方案4】:

      我找到了一个更快的方法。试试这个:

        class Array
          def mode4
            group_by{|i| i}.max{|x,y| x[1].length <=> y[1].length}[0]
          end
        end
      

      基准输出:

                                          user     system      total        real
      mode1                          24.340000   0.070000  24.410000 ( 24.526991)
      mode2                           0.200000   0.000000   0.200000 (  0.195348)
      mode3                           0.120000   0.000000   0.120000 (  0.118200)
      mode4                           0.050000   0.010000   0.060000 (  0.056315)
           mode1 = 76
           mode2 = 76
           mode3 = [[76, 18]]
           mode4 = 76
      

      【讨论】:

        【解决方案5】:

        这是这个问题的副本 “Ruby - Unique elements in Array”。

        这是该问题的解决方案:

        group_by { |n| n }.values.max_by(&:size).first
        

        那个版本似乎比 Nilesh C 的答案还要快。这是我用来对其进行基准测试的代码(OS X 10.6 Core 2 2.4GHz MB)。

        感谢 Mike Woodhouse 的(原始)基准测试代码:

        class Array
           def mode1
             group_by { |n| n }.values.max_by(&:size).first
           end
           def mode2
             freq = inject(Hash.new(0)) { |h,v| h[v] += 1; h }
             max = freq.values.max                   # we're only interested in the key(s) with the highest frequency
             freq.select { |k, f| f == max }         # extract the keys that have the max frequency
           end
        end
        
        arr = Array.new(1_0000) { |i| rand(100000) }    # something to test with
        
        Benchmark.bm(30) do |r|
            (1..2).each do |i| r.report("mode#{i}") { 100.times do arr.send("mode#{i}").inspect; end }; end
        end
        

        以下是基准测试的结果:

                                        user     system      total        real
        mode1                           1.830000   0.010000   1.840000 (  1.876642)
        mode2                           2.280000   0.010000   2.290000 (  2.382117)
         mode1 = 70099
         mode2 = [[70099, 3], [70102, 3], [51694, 3], [49685, 3], [38410, 3], [90815, 3], [30551, 3], [34720, 3], [58373, 3]]
        

        如您所见,此版本的速度提高了约 20%,但需要注意的是忽略了关系。我也喜欢简洁,我个人按原样使用它,而不用到处乱打补丁。 :)

        【讨论】:

          【解决方案6】:

          这是另一个版本,它确实为您提供了一种模式应有的关系:

          def mode
            group_by {|x| x}.group_by {|k,v| v.size}.sort.last.last.map(&:first)
          end
          

          换句话说,对值进行分组,然后按值的数量对这些 kv 对进行分组,然后对 那些 kv 对进行排序,取最后一个(最高)大小组,然后展开它的值.我喜欢group_by

          【讨论】:

          • 这是唯一一个单行的答案。 +1
          【解决方案7】:

          如果你想避免学习#inject(你不应该这样做......)

          words = ['cat', 'dog', 'snake', 'dog']
          count = Hash.new(0)
          
          words.each {|word| count[word] += 1}
          count.sort_by { |k,v| v }.last
          

          但如果我以前读过这个答案,现在我对#inject 和人一无所知,你需要了解#inject。

          【讨论】:

            【解决方案8】:
            def mode(array)
            
                count = []  # Number of times element is repeated in array
                output = [] 
                array.compact!
                unique = array.uniq
                j=0
            
                unique.each do |i|
                    count[j] = array.count(i)
                    j+=1
                end
                k=0
                count.each do |i|
                    output[k] = unique[k] if i == count.max
                    k+=1
                end  
            
                return output.compact.inspect
            end
            
            p mode([3,3,4,5]) #=> [3]
            
            p mode([1,2,3]) #=> [1,2,3]
            
            p mode([0,0,0,0,0,1,2,3,3,3,3,3]) #=> [0,3]
            
            p mode([-1,-1,nil,nil,nil,0]) #=> [-1]
            
            p mode([-2,-2,3,4,5,6,7,8,9,10,1000]) #=> [-2]
            

            【讨论】:

              【解决方案9】:
              arr = [ 1, 3, 44, 3 ]
              most_frequent_item = arr.uniq.max_by{ |i| arr.count( i ) }
              puts most_frequent_item
              #=> 3
              

              甚至不需要考虑频率映射。

              【讨论】:

              • 虽然这是 O(n*n),但接受的答案是 O(n)
              【解决方案10】:
              array.max_by { |i| array.count(i) }
              

              【讨论】:

              • 不是只提供代码答案,而是添加关于您的答案如何解决问题的说明将有助于您的读者学习。
              • 谢谢,你帮了我!您的代码仅返回最常见的值。我对其进行了一些修改,以首先返回按最大值排序的所有值:array.sort_by { |u| array.count(u) }.reverse
              • 不过这是O(n^2)
              • @YtvwlD 优化不是 OP 问题的一部分。如果您有更优化的方法,为什么不直接发布而不是重复其他人的答案?
              【解决方案11】:

              Ruby 版本 >= 2.7 将具有 Enumerable#tally

              统计集合。返回一个哈希值,其中键是元素 并且值是集合中元素的数量 对应键。

              所以,你可以这样做

              [1, 1, 1, 2, 3].tally
              # => {1=>3, 2=>1, 3=>1} 
              

              【讨论】:

              • 红宝石的新东西!
              猜你喜欢
              • 1970-01-01
              • 2021-07-30
              • 2011-10-22
              • 2019-04-02
              • 1970-01-01
              • 1970-01-01
              • 2020-12-03
              • 1970-01-01
              • 2018-04-11
              相关资源
              最近更新 更多