从数据库生成直方图

generate histogram from database

提问人:Dónal 提问时间:7/30/2014 最后编辑:Dónal 更新时间:7/30/2014 访问量:744

问:

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

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

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

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

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

但是,我正在努力提出一个适用于任何 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 小得多。

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

java sql groovy 语言不可知直 方图

评论

0赞 7/30/2014
+1 想出了一件非常罕见的事情:一个关于 Stack Overflow 的好问题。我希望我能:(回答这个问题
0赞 Dónal 7/30/2014
@MikeW我希望你也能:)我开始怀疑这是否更像是一个统计/数学问题,而不是一个编程问题

答:

0赞 injecteer 7/30/2014 #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赞 Dónal 7/30/2014
据推测,这并不能满足柱线(区间)的上限和下限为整数的要求?此外,调整每个条形宽度的时髦单行似乎意味着它们不会有统一的宽度?
0赞 injecteer 7/30/2014
“整数”的要求非常棘手,并迫使您具有不均匀的间隔。不,单行只是用 0 或一些默认值填充缺失的条形
0赞 injecteer 7/30/2014
要实现“整数”要求,第 0 根柱线必须在最小值之前开始,第 N 根柱线在最大值之后结束
0赞 Dónal 7/30/2014
如果第 0 根柱线在最小值之前开始和/或最后一根柱线在最大值之后结束,那没关系 - 我会将此信息添加到我的帖子中
0赞 injecteer 7/30/2014
顺便说一句,“整数”应该像 1234 还是 1200 或 1000?在后一种情况下,您可以使用 log10() 技巧