Ruby Percentile计算以匹配Excel公式(需要重构)

时间:2009-11-16 20:03:56

标签: ruby excel math statistics

我用Ruby编写了两个简单的计算方法,它们与Microsoft Excel计算给定数据集的上下四分位数的方式相匹配 - 这与普遍接受的方法(惊喜)不同。

我的问题是 - 这些方法可以在多大程度上以及如何最好地重构以达到最大干燥度?

# Return an upper quartile value on the same basis as Microsoft Excel (Freund+Perles method)
  def excel_upper_quartile(array)
      return nil if array.empty?
      sorted_array = array.sort
      u = (0.25*(3*sorted_array.length+1))
      if (u-u.truncate).is_a?(Integer)
        return sorted_array[(u-u.truncate)-1]
      else
        sample = sorted_array[u.truncate.abs-1]
        sample1 = sorted_array[(u.truncate.abs)]
        return sample+((sample1-sample)*(u-u.truncate))
      end
  end


  # Return a lower quartile value on the same basis as Microsoft Excel (Freund+Perles method)
  def excel_lower_quartile(array)
      return nil if array.empty?
      sorted_array = array.sort
      u = (0.25*(sorted_array.length+3))
      if (u-u.truncate).is_a?(Integer)
        return sorted_array[(u-u.truncate)-1]
      else
        sample = sorted_array[u.truncate.abs-1]
        sample1 = sorted_array[(u.truncate.abs)]
        return sample+((sample1-sample)*(u-u.truncate))
      end
  end

2 个答案:

答案 0 :(得分:4)

我将首先概括一下并提供一种方法来处理这两个实例。

def excel_quartile(array, quartile)
  # Returns nil if array is empty and covers the case of array.length == 1
  return array.first if array.length <= 1
  sorted = array.sort
  # The 4th quartile is always the last element in the sorted list.
  return sorted.last if quartile == 4
  # Source: http://mathworld.wolfram.com/Quartile.html
  quartile_position = 0.25 * (quartile*sorted.length + 4 - quartile)
  quartile_int = quartile_position.to_i
  lower = sorted[quartile_int - 1]
  upper = sorted[quartile_int]
  lower + (upper - lower) * (quartile_position - quartile_int)
end

然后你可以制作方便的方法:

def excel_lower_quartile(array)
  excel_quartile(array, 1)
end

def excel_upper_quartile(array)
  excel_quartile(array, 3)
end

注意:excel_quartile方法符合quartile in { 1, 2, 3, 4}的预期。还有别的,我保证失败。

更新

我使用的公式没有在我引用的网站上明确给出,但它是Freund和Perles计算四分位数的方法的抽象。

进一步更新:

原始代码中存在错误,但您永远不应该遇到它:u - u.trunc始终在[0.0,1.0]区间内,因此唯一一个类似整数的时间是{{1} }。但是,u - u.trunc = 0仍然是Float的一个实例,只要(u - u.trunc)是Float,所以你的代码永远不会发生错误计算的索引。顺便说一句,如果u - u.trunc是一个整数,你的方法将返回数组的最后一个元素。

答案 1 :(得分:2)

有些人可能不同意重构,但这是我如何处理它:

def excel_quartile(extreme,array)      
  return nil if array.empty?
  sorted_array = array.sort
  u = case extreme
  when :upper then 3 * sorted_array.length + 1
  when :lower then sorted_array.length + 3
  else raise "ArgumentError"
  end
  u *= 0.25
  if (u-u.truncate).is_a?(Integer)
    return sorted_array[(u-u.truncate)-1]
  else
    sample = sorted_array[u.truncate.abs-1]
    sample1 = sorted_array[(u.truncate.abs)]
    return sample+((sample1-sample)*(u-u.truncate))
  end
end

def excel_upper_quartile(array)
  excel_quartile(:upper, array)
end

def excel_lower_quartile(array)
  excel_quartile(:lower, array)
end