如何在 SQL 中制作对数直方图?

gbe*_*ger 3 sql logarithm histogram

我的表:

val
1
2
3
4
5
6
10
15
Run Code Online (Sandbox Code Playgroud)

预期结果:

bin | qty 
1   | 1
2   | 2
4   | 3
8   | 3
Run Code Online (Sandbox Code Playgroud)

这意味着有包含/不包含范围,

  • 1-2 之间的 1 个值,
  • 2-4 之间的 2 个值,
  • 4-8 之间的 3 个值,
  • 8-16 之间的 3 个值。

gbe*_*ger 6

在这种情况下,您的 bin 大小(以对数形式表示)为 2。

如果您希望使用其他 bin 大小,请替换以下脚本中的 2s。

select 
    pow(2, floor(ln(val) / ln(2))) as bin,
    count(bin) as qty
from 
    mytable
group by
    bin;
Run Code Online (Sandbox Code Playgroud)

解释

首先,我们以 2 为基数记录您的值。log(val, 2)可能在某些 RDBMS 中工作,但如果不是,请记住log(val, 2) = ln(val) / ln(2).

val | ln(val) / ln(2)
1   | 0
2   | 1
3   | 1.58496250072
4   | 2
5   | 2.32192809489
Run Code Online (Sandbox Code Playgroud)

然后我们把这个:

val | floor(ln(val) / ln(2))
1   | 0
2   | 1
3   | 1
4   | 2
5   | 2
Run Code Online (Sandbox Code Playgroud)

最后,我们使用 2 的幂将这些下限值转换为对数 bin 值。

val | pow(2, floor(ln(val) / ln(2)))
1   | 1
2   | 2
3   | 2
4   | 4
5   | 4
Run Code Online (Sandbox Code Playgroud)

其余的只是按对数箱和计数进行分组。

注意事项

没有战俘

如果您的 RDBMS 不支持pow(x, y),您可以使用exp(y * ln(x)). 然后表达式变为:

    exp(floor(ln(val) / ln(2)) * ln(2))
Run Code Online (Sandbox Code Playgroud)

log(0) 未定义。在我测试的 RDBMS 中,它返回 null。

如果您的表的值为 0,您很可能希望将它们放在 0 和 1 之间。为此,您可以使用 ifnull(..., 0) 包装整个表达式,如下所示:

    ifnull(pow(2, floor(ln(val) / ln(2))), 0)
Run Code Online (Sandbox Code Playgroud)

消极的

负数的对数未定义...但您可能希望将它们分类为 [0 到 -1)、[-1 到 -2)、[-2 到 -4)、[-4 到 -8) 等” .

如果您的数据库具有负值,您可以通过首先abs在您的值中使用来实现该分箱,然后最后通过将结果乘以 来恢复其原始信号val/abs(val)。然后你的表达变成:

pow(2, floor(ln(abs(val)) / ln(2))) * val/abs(val)
Run Code Online (Sandbox Code Playgroud)

负数和零

如果您的数据库同时具有负值和零值,则应将ifnull其他所有内容都包装起来。否则,该val/abs(val)部分会使您除以零,重新引入空值。

ifnull(pow(2, floor(ln(abs(val)) / ln(2))) * val/abs(val), 0)
Run Code Online (Sandbox Code Playgroud)