【问题标题】:generate histogram from database从数据库生成直方图
【发布时间】:2014-09-21 18:23:08
【问题描述】:

在 MySQL 数据库中,有一个带有单个数字 value 列的表。我想将这些值的分布绘制为具有以下要求的条形图/直方图:

  • 图表中最多应有 N 个柱(区间)
  • 每个条形的宽度(x 轴范围)应一致,每个条形的高度应反映此区间中值的数量。
  • 条形的端点应以整数出现。我知道这是一个相当模糊的要求,但希望以下示例能够说明我的意思
  • 间隔应该是连续的,例如下一个间隔应该从前一个结束的地方开始
  • 理想情况下,应该可以通过单个查询检索数据
  • 间隔计数(y 轴值)为 0 是可以的
  • 如果第一个区间的下限小于最小值value 和/或最后一个区间的上限大于最大值value,则可以

示例

如果 N = 3 并且表包含以下数据

+------------+
| value      |
+------------+
|     -49.2  |
|     -28.2  |
|      13.3  |
|      23.3  |
|      51.4  |
|      77.9  |
+------------+

检查时很容易看出间隔{-50..0, 0..50, 50..100} 满足此数据集和 N 值的要求。

但是,我正在努力想出一个适用于任何 N 值和任何数据集的通用解决方案。到目前为止,这是我尝试过的:

计算间隔宽度

通过以下查询获取最大和最小value

SELECT min(value), max(value), count(*) FROM my_table

然后将结果传递给这个(Groovy/Java)方法来计算每个区间的宽度

// intervalCount is what I've referred to as "N"
static Integer getRoundedIntervalSize(Double min, Double max, Integer intervalCount) {
    Number intervalSize = Math.ceil((max - min) / intervalCount)

    Integer roundingScale = Math.log10(intervalSize) - 1
    Number roundNearest = 10 ** roundingScale

    // round up the interval size to the nearest roundNearest
    Number intervalDelta = roundNearest - (intervalSize % roundNearest)
    intervalSize + intervalDelta
}

获取频率分布

然后我使用以下查询(将getRoundedIntervalSize 返回的值替换为:groupSize)来获取每个区间中的值的数量

SELECT      floor(value / :groupSize) * :groupSize  as groupLowerLimit,
            count(*) as groupCount
FROM        my_table
GROUP BY    groupLowerLimit
ORDER BY    groupLowerLimit ASC

这将返回每个区间的下限和每个区间中的值的数量,这就是我构建频率分布所需的全部内容。

不足之处

虽然这种方法在数据集分布相对均匀时效果很好,但如果不是这种情况,它会导致间隔具有不同的宽度或不连续。此外,当数据集的范围较小(例如,1 到 4 之间的所有值)且 N 较大(例如 30)时,生成的区间数往往远小于 N。

有没有更好的方法来解决这个问题,满足上述要求?

【问题讨论】:

  • +1 提出了这个非常罕见的事情:Stack Overflow 上的一个好问题。我希望我能回答它:(
  • @MikeW 我希望你也可以 :) 我开始怀疑这是否更像是一个统计/数学问题而不是编程问题

标签: java sql groovy language-agnostic histogram


【解决方案1】:

我尝试了几个查询(我使用了一个带有一些坐标的表格)

获取最小值和条形尺寸:

select min( lng ) as min, ( max( lng ) - min( lng ) ) / ? as interval from address

获取分布。这些条从 0 到 N 编号,当然可能有间隙

select floor( abs( lng - :min ) / :interval ) as ix, count(*) from address group by ix order by ix

N = 30 的输出如下:

ix  count
0   31
18  10149
20  36185
21  5443
24  1
29  3

要填补空白,您需要一个时髦的单线:

def metrics = run1stQuery(..., n )
def results = run2ndQuery(.., metrics )
def fullResults = (0..n).collect{ results[ it ] ?: metrics.min } // adjust the missing value

因此,您应该获取任何 N 和最小/最大值的频率组。

HTH

【讨论】:

  • 大概这不能处理条形(间隔)的上限和下限为整数的要求?另外,调整每个条的宽度的常规单线似乎意味着它们不会有统一的宽度?
  • “整数”的要求真的很棘手,并迫使你有不均匀的间隔。不,单行只是用 0 或一些默认值填充缺失的条
  • 要实现“整数”要求,您的第 0 个柱必须在最小值之前开始,第 N 个柱必须在最大值之后结束
  • 如果第 0 条在最小值之前开始和/或最后一条在最大值之后结束,这没关系 - 我会将此信息添加到我的帖子中
  • 顺便说一句,“整数”应该是 1234 还是 1200 或 1000?在后一种情况下,您可以使用 log10() 技巧
猜你喜欢
  • 2010-10-03
  • 2012-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-24
  • 1970-01-01
相关资源
最近更新 更多