确定 Ruby 中嵌套数组的中值元素?

Determine median element of a nested array in Ruby?

我需要 Ruby 中的中位数计算方法,它也适用于嵌套数组,类似于 "uniq" 和 "sort_by":对于我可以通过块定义的那些,应考虑嵌套数组值的数量。

class Array
   def median
      . . .
   end
end

puts [[1,3],[2,5],[3,-4]].median{|z,w| z}

=> [2,5]

puts [[1,3],[2,5],[3,-4]].median{|z,w| w}

=> [1,3]

我确定我应该以某种方式处理 "yield",但我不知道该怎么做。

flatten() 是你的朋友。它将嵌套数组(或任何可枚举数组)折叠成单个数组。然后,计算中位数变得微不足道:

class Array
  def median
    array = self.flatten.sort
    if array.size % 2 == 1
      array[array.size / 2]
    else
      mid = array.size / 2
      (array[mid] + array[mid-1]) / 2.0
    end
  end

  def mean
    self.flatten.reduce(:+) / self.flatten.size.to_f  
  end
end

这让您可以:

irb> a
=> [[1, 3], [2, 5], [3, -4]]
irb> a.median
=> 2.5
irb> c
=> [[1, 2, 3, 6], [4, 5, [100]]]
irb> c.median
=> 4
irb> c.mean
=> 17.285714285714285

为了提高性能,您可能需要计算 self.flatten 一次,然后对该数组执行两个算术运算。但是,除非您使用大量数据,否则这可能无关紧要,希望 Ruby 会为您优化它。但老实说,我不会担心性能。

[已编辑,@kiddorails 指出我混淆了中位数和均值!谢谢,孩子!]

由于中位数需要排序,您可以委派给 sort_by 并处理结果:

class Array
  def median(&block)
    block = :itself unless block_given?

    sorted = sort_by(&block)
    if length.odd?
      sorted[sorted.length / 2]
    else
      sorted[sorted.length / 2 - 1, 2]
    end
  end
end

样本运行:

[13, 23, 11, 16, 15, 10, 26].median # => 15
# hyperbole showing the block is used on single elements
count = 0; [13, 23, 11, 16, 15, 10, 26].median { |a| count += 1 } # => 16
# even length data set
# usually you'd average these, but that becomes trickier with nested arrays
[14, 13, 23, 11, 16, 15, 10, 26].median # =>  [14, 15]

# your examples:
[[1,3], [2,5], [3,-4]].median { |z,_| z} # => [2, 5]
[[1,3], [2,5], [3,-4]].median { |_,w| w } # => [1, 3]

# added [6, -6] to your examples:
[[1,3], [2,5], [3,-4], [6, -6]].median { |z,_| z } # => [[2, 5], [3, -4]]
[[1,3], [2,5], [3,-4], [6, -6]].median { |_,w| w } # => [[3, -4], [1, 3]]

您没有指定偶数长度数组应该发生什么。对于数学中位数(如果我没记错的话)你会平均两个最中心的元素,但随之而来的问题是 2 个不同数组的平均值是什么样的。这采用了返回中心元素的简单(对我们而言)方法,调用者必须决定如何处理它们。 (如果它不是嵌套在里面的另一个数组,如果它是一个人的列表,并且你想要姓氏的中位数怎么办,例如)

我假设数组的中位数定义如下。对于包含奇数个元素的数组 a,中位数是 [m],其中 ma 的一个元素,其中 e <=> m 是非正数a.size/2 其他元素 ee <=> m 对于剩余的 a.size/2 其他元素 e 是非负的。对于具有偶数个元素的数组,中位数为 [m, n],其中 mna 的元素,其中 m <=> n 为非正数, e <=> m 对于 a.size/2-1 个其他元素是非正数 e 并且 e <=> n 对于剩余的 a.size/2-1 个其他元素是非负数 e.

class Array
  def median
    min_by(1+self.size/2, &:itself).pop(self.size.odd? ? 1 : 2)
  end
end

[2, 4, 5, 3, 1].median
  #=> [3]
[2, 6, 4, 5, 3, 1].median
  #=> [3, 4]
[3, 6, 4, 5, 3, 1].median
  #=> [3, 4]
['hamster', 'dog', 'fish', 'cat'].median
  #=> ["dog", "fish"]
[[1, 3], [2, 5], [3, -4]].median
  #=> [[2, 5]]
[[2,6], [3,-4], [1,3], [2,5]].median
  # => [[2, 5], [2, 6]]
arr = [[[3,1], 1], [[4], 2], [[2,1], 4, 1], [[3,1], 0], [[1,2,3], 5]]
arr.median
  #=> [[[3, 1], 0]]

最后一个例子

arr.sort
  #=> [[[1, 2, 3], 5], [[2, 1], 4, 1], [[3, 1], 0], [[3, 1], 1], [[4], 2]]

参见 Enumerable#min_by。可选参数在 Ruby v2.1.

中引入