从数据库生成直方图

Dón*_*nal 5 java sql language-agnostic groovy histogram

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

  • 图表中最多应有N个柱(间隔)
  • 每个条的宽度(x轴范围)应该是均匀的,每个条的高度应该反映该间隔中的值的数量.
  • 条形的端点应以圆数出现.我理解这是一个相当模糊的要求,但希望下面的例子将说明我的意思
  • 间隔应该是连续的,例如,下一个间隔应该从前一个间隔开始
  • 理想情况下,应该可以使用单个查询检索数据
  • 计数(y轴值)为0的间隔是可以的
  • 如果第一个间隔的下限小于最小值value并且/或者最后一个间隔的上限大于最大值,则可以value

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

+------------+
| value      |
+------------+
|     -49.2  |
|     -28.2  |
|      13.3  |
|      23.3  |
|      51.4  |
|      77.9  |
+------------+
Run Code Online (Sandbox Code Playgroud)

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

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

计算间隔宽度

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

SELECT min(value), max(value), count(*) FROM my_table
Run Code Online (Sandbox Code Playgroud)

然后将结果传递给此(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
}
Run Code Online (Sandbox Code Playgroud)

获取频率分布

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

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

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

缺点

虽然这种方法在数据集相对均匀分布时工作得相当好,但在不是这种情况时,会产生宽度不同或不连续的区间.此外,当数据集的范围小(例如,1和4之间的所有值)和N大(例如30)时,产生的间隔的数量趋于远小于N.

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

inj*_*eer 0

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

获取最小 val 和条形大小:

select min( lng ) as min, ( max( lng ) - min( lng ) ) / ? as interval from address
Run Code Online (Sandbox Code Playgroud)

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

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

的输出N = 30如下:

ix  count
0   31
18  10149
20  36185
21  5443
24  1
29  3
Run Code Online (Sandbox Code Playgroud)

为了填补空白,你需要一句绝妙的俏皮话:

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

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

华泰