查找Ruby数组的模式



我试图找到一个数组的模式。Mode =出现频率最高的元素

我知道#enumerable有很多技巧,但我还没有学到。我正在做的练习假设我可以在不理解enumerable的情况下解决这个问题。

我已经写出了我的比赛计划,但我在第二部分卡住了。我不确定是否可以将散列键与数组进行比较,如果找到,则增加值。
def mode(array)    
  # Push array elements to hash.  Hash should overwrite dup keys.
  myhash = {}
  array.each do |x|
      myhash[x] = 0
  end
  # compare Hash keys to Array.  When found, push +=1 to hash's value.    
  if myhash[k] == array[x]
    myhash[k] += 1
  end
  # Sort hash by value
  # Grab the highest hash value
  # Return key(s) per the highest hash value
  # rejoice!
end
test = [1, 2, 3, 3, 3, 4, 5, 6, 6, 6]
mode(test) # => 3, 6 (because they each appear 3 times)

您可以使用默认初始值创建散列:

myhash = Hash.new(0)

然后增加特定的出现次数:

myhash["foo"] += 1
myhash["bar"] += 7
myhash["bar"] += 3
p myhash    # {"foo"=>1, "bar"=>10}

有了这样的理解,如果你替换你的初始哈希声明,然后在array.each迭代器中进行自增,你实际上就完成了。

myhash.sort_by{|key,value| value}[-1] 

给出哈希值排序集合中的最后一个条目,这应该是您的模式。请注意,可能有多个模式,因此您可以在value部分保持不变的情况下向后迭代以确定所有模式。

有很多很多方法可以做到这一点。以下是一些。

# 1

array = [3,1,4,5,4,3]
a = array.uniq                   #=> [3, 1, 4, 5]
         .map {|e| [e, array.count(e)]}
                                 #=> [[3, 2], [1, 1], [4, 2], [5, 1]]
         .sort_by {|_,cnt| -cnt} #=> [[3, 2], [4, 2], [1, 1], [5, 1]]
a.take_while {|_,cnt| cnt == a.first.last}
                                 #=> [[3, 2], [4, 2]]
 .map(&:first)                   #=> [3, 4]

# 2

array.sort                       #=> [1, 3, 3, 4, 4, 5]
     .chunk {|e| e}
       #<Enumerator: #<Enumerator::Generator:0x000001021820b0>:each>
     .map { |e,a| [e, a.size] }  #=> [[1, 1], [3, 2], [4, 2], [5, 1]]
     .sort_by { |_,cnt| -cnt }   #=> [[4, 2], [3, 2], [1, 1], [5, 1]]
     .chunk(&:last)
       #<Enumerator: #<Enumerator::Generator:0x00000103037e70>:each>
     .first                      #=> [2, [[4, 2], [3, 2]]]
     .last                       #=> [[4, 2], [3, 2]]
     .map(&:first)               #=> [4, 3]

# 3

h = array.each_with_object({}) { |e,h|
      (h[e] || 0) += 1 }         #=> {3=>2, 1=>1, 4=>2, 5=>1}
max_cnt = h.values.max           #=> 2
h.select { |_,cnt| cnt == max_cnt }.keys
                                 #=> [3, 4]

# 4

a = array.group_by { |e| e }     #=> {3=>[3, 3], 1=>[1], 4=>[4, 4], 5=>[5]}
         .map {|e,ees| [e,ees.size]}
                                 #=> [[3, 2], [1, 1], [4, 2], [5, 1]]
max = a.max_by(&:last)           #=> [3, 2]
       .last                     #=> 2
a.select {|_,cnt| cnt == max}.map(&:first)
                                 #=> [3, 4]

在您的方法中,您首先初始化了一个散列,其中包含从数组的唯一值中获取的键,并将相关值全部设置为零。例如,数组[1,2,2,3]将创建散列{1: 0, 2: 0, 3: 0}

之后,您计划对数组中每个值的实例进行计数,方法是将哈希中关联键的值每个实例加1。因此,在数组中找到数字1之后,散列看起来像这样:{1: 1, 2: 0, 3: 0}。显然,您需要对数组中的每个值都这样做,因此考虑到您的方法和当前的理解水平,我建议再次循环遍历数组:

array.each do |x|
  myhash[x] += 1
end

正如你所看到的,我们不需要检查myhash[k] == array[x],因为我们已经为数组中的每个数字创建了一个键:值对。

然而,虽然这种方法可以工作,但效率不是很高:我们必须循环遍历数组两次。第一次将所有键:值对初始化为某种默认值(在本例中为零),第二次计算每个数字的频率。

由于每个键的默认值为0,因此可以通过使用不同的哈希构造函数来消除初始化默认值的需要。如果我们访问一个不存在的键,myhash = {}将返回nil,但如果我们访问一个不存在的键,myhash = Hash.new(0)将返回0(注意,如果需要,您可以提供任何其他值或变量)。

通过提供默认值0,我们可以完全摆脱第一个循环。当第二个循环发现一个不存在的键时,它将使用提供的默认值并自动初始化它。

def mode(array)
    array.group_by{ |e| e }.group_by{ |k, v| v.size }.max.pop.map{ |e| e.shift }
end

使用simple_stats gem:

test = [1, 2, 3, 3, 3, 4, 5, 6, 6, 6]
test.modes #=> [3, 6]

如果是未排序数组,则按降序排序

array = array.sort!

然后使用排序数组创建一个默认值为0的散列,数组中的每个元素作为键,出现次数作为值

hash = Hash.new(0)
array.each {|i| hash[i] +=1 }

如果按值(出现次数)降序排序,则mode将是第一个元素

mode = hash.sort_by{|key, value| -value}.first[0]

最新更新