【问题标题】:Quantizing an array so that a subset of quantized values is still consistently quantized量化一个数组,以便量化值的子集仍然一致量化
【发布时间】:2015-04-03 00:29:23
【问题描述】:

给定一个ints 的数组,我想量化每个值,使量化值的总和为 100。每个量化值也应该是一个整数。这在整个数组被量化时有效,但是当量化值的子集相加时,它不会相对于其余值保持量化。

例如,数值 44, 40, 7, 2, 0, 0 被量化为 47, 43, 8, 2, 0, 0(其和为 100)。如果取最后 4 个量化值,总和为 53,这与第一个值一致(即 47 + 53 = 100)。

但是对于值 78、7、7、1、0、0,最后 4 个量化值(8、8、1、0、0)的总和是 17。第一个量化值是 84,当添加时到 17 不等于 100。很明显,这是由于四舍五入造成的。有没有办法调整舍入以使子集仍然一致?

这是 Ruby 代码:

class Quantize
  def initialize(array)
    @array = array.map { |a| a.to_i }
  end

  def values
    @array.map { |a| quantize(a) }
  end

  def sub_total(i, j)
    @array[i..j].map { |a| quantize(a) }.reduce(:+)
  end

  private

  def quantize(val)
    (val * 100.0 / total).round(0)
  end

  def total
    @array.reduce(:+)
  end
end

以及(失败的)测试:

require 'quantize'

describe Quantize do
  context 'first example' do
    let(:subject) { described_class.new([44, 40, 7, 2, 0, 0]) }

    context '#values' do
      it 'quantizes array to add up to 100' do
        expect(subject.values).to eq([47, 43, 8, 2, 0, 0])
      end
    end

    context '#sub_total' do
      it 'adds a subset of array' do
        expect(subject.sub_total(1, 5)).to eq(53)
      end
    end
  end

  context 'second example' do
    let(:subject) { described_class.new([78, 7, 7, 1, 0, 0]) }

    context '#values' do
      it 'quantizes array to add up to 100' do
        expect(subject.values).to eq([84, 8, 8, 1, 0, 0])
      end
    end

    context '#sub_total' do
      it 'adds a subset of array' do
        expect(subject.sub_total(1, 5)).to eq(16)
      end
    end
  end
end

【问题讨论】:

  • 运行你的代码,你的第二个例子的量化——正如你在测试用例中所说的——是 [84, 8, 8, 1, 0, 0]。这增加了 101 - 而不是 100,如您所愿。此外,正如您在问题中所说,最后 5 位数字的总和是 17,而不是 15。因此,存在比您在问题中描述的更深层次的问题。也就是说,四舍五入是有缺陷的,句号。这与子集无关。
  • @M.AnthonyAiello 你是对的。差异是由于编辑不当造成的。我会解决的。
  • @ReedGLaw - 编辑有帮助 - 但问题仍然比子集更根本。第二个例子加起来不是 100 — 它加起来是 101。所以 1)你的算法一开始就没有做你想做的事,更不用说子集了; 2)您的测试用例不会暴露错误。对于后者,我建议对量化值求和以确保它们等于 100,而不是指定量化必须是什么。
  • @M.AnthonyAiello 再一次你是对的。查看第二个示例的未舍入结果 (83.87097, 7.52688, 7.52688, 1.07527, 0.0, 0.0),我发现测试的预期结果并不明显。是否应该将 7.52688 值之一向上舍入为 8,而将另一个向下舍入为 7?我应该更详细地描述实际问题。原始值代表临床试验中的患者。数组的六个元素各自代表一个不良事件等级(从 0 表示无不良事件到 5 表示死亡)。这些值应以百分比形式显示在表格中,以使它们看起来一致。
  • (接上一条评论)...所以在第二个示例中,78 名患者为 0 级(无不良事件),7 名患者为 1 级,7 名患者为 2 级,1 名患者为 3 级。共有93(78+7+7+1)名患者。对于最终表格,显示的是百分比而不是患者人数。百分比应保持一致,以便它们加起来为 100。

标签: ruby rounding quantization


【解决方案1】:

正如问题的 cmets 中所述,量化例程无法正确执行:第二个示例 [78, 7, 7, 1, 0, 0] 被量化为 [84, 8, 8, 1, 0, 0] — 它加到 101 而不是加到 100。

这是一种可以产生正确结果的方法:

def quantize(array, value)
  quantized = array.map(&:to_i)
  total = array.reduce(:+)
  remainder = value - total

  index = 0

  if remainder > 0
    while remainder > 0 
      quantized[index] += 1
      remainder -= 1
      index = (index + 1) % quantized.length
    end
  else
    while remainder < 0 
      quantized[index] -= 1
      remainder += 1
      index = (index + 1) % quantized.length
    end
  end

  quantized
end

如问题中所述,这可以解决您的问题。麻烦的结果变成[80, 8, 8, 2, 1, 1],加100,保持你描述的子集关系。当然,该解决方案可以提高性能——但它的优点是工作起来简单易懂。

【讨论】:

  • 阅读代码似乎会循环遍历数组,从每个值中加或减 1,直到总数等于 value。这个解决方案的问题在于,如果您阅读了我在问题 cmets 中对问题域的描述,您会发现似乎有 1% 的患者已经死亡 (quantized[4]),而实际上没有 (@987654327 @) 已经死了。此外,quantized[0] 是 80,但这与 83.87097 的实际百分比值相差甚远。如果必须伪造某些值,我宁愿是 7.52688 (quantized[1..2])。这意味着一个向上取整到 8,另一个向下取整到 7。
  • 确实如此。但问题的原始参数并没有规定这些限制。在您稍后发表评论之前,我什至不知道您需要缩放。正如最初所说,这确实解决了问题。
  • 我认为可以对其进行调整以使其正常工作。我所要做的就是跳过等于 0 的数组元素。我会试一试。
  • 我应该一直使用“缩放”这个词。我的错误是不知道如何称呼这个问题。量化用于将一组连续的值(如信号)限制为相对较小的离散集。我只想以不同的比例表示一组数字。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-13
  • 1970-01-01
相关资源
最近更新 更多