如何在 SQL 中制作对数直方图?
How do I make a logarithmic histogram in SQL?
我的table:
val
1
2
3
4
5
6
10
15
想要的结果:
bin | qty
1 | 1
2 | 2
4 | 3
8 | 3
这意味着有 inclusive/exclusive 个范围,
- 1个值在1-2之间,
- 2 个值在 2-4 之间,
- 3 个值在 4-8 之间,
- 3 个值在 8-16 之间。
在这种情况下,您的 bin 大小(以对数表示)为 2。
如果您想使用其他 bin 大小,请替换下面脚本中的 2s。
select
pow(2, floor(ln(val) / ln(2))) as bin,
count(bin) as qty
from
mytable
group by
bin;
说明
首先,我们以 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
然后我们将这个:
val | floor(ln(val) / ln(2))
1 | 0
2 | 1
3 | 1
4 | 2
5 | 2
最后,我们使用 2 的幂将这些底值转换为对数 bin 值。
val | pow(2, floor(ln(val) / ln(2)))
1 | 1
2 | 2
3 | 2
4 | 4
5 | 4
剩下的就是简单地按对数箱分组并计数。
注意事项
没有战俘
如果您的 RDBMS 不支持 pow(x, y)
,您可以使用 exp(y * ln(x))
。然后表达式变为:
exp(floor(ln(val) / ln(2)) * ln(2))
零
log(0) 未定义。在我测试的 RDBMS 中,它 returns null.
如果您的 table 的值为 0,您很可能希望将它们分到 0 和 1 之间。为此,您可以用 ifnull(..., 0) 包裹整个表达式,例如所以:
ifnull(pow(2, floor(ln(val) / ln(2))), 0)
否定
负数的对数未定义...但您可能希望将它们像 [0 到 -1)、[-1 到 -2]、[-2 到 -4)、[-4 到-8), 等等
如果您的数据库有负值,您可以通过首先在您的值中使用 abs
来实现分箱,然后最后通过将结果乘以 val/abs(val)
来恢复其原始信号。然后你的表情变成:
pow(2, floor(ln(abs(val)) / ln(2))) * val/abs(val)
负数和零
如果您的数据库既有负值也有零值,您应该用 ifnull
包裹其他所有内容。否则,val/abs(val)
部分会让您除以零,重新引入空值。
ifnull(pow(2, floor(ln(abs(val)) / ln(2))) * val/abs(val), 0)
我的table:
val
1
2
3
4
5
6
10
15
想要的结果:
bin | qty
1 | 1
2 | 2
4 | 3
8 | 3
这意味着有 inclusive/exclusive 个范围,
- 1个值在1-2之间,
- 2 个值在 2-4 之间,
- 3 个值在 4-8 之间,
- 3 个值在 8-16 之间。
在这种情况下,您的 bin 大小(以对数表示)为 2。
如果您想使用其他 bin 大小,请替换下面脚本中的 2s。
select
pow(2, floor(ln(val) / ln(2))) as bin,
count(bin) as qty
from
mytable
group by
bin;
说明
首先,我们以 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
然后我们将这个:
val | floor(ln(val) / ln(2))
1 | 0
2 | 1
3 | 1
4 | 2
5 | 2
最后,我们使用 2 的幂将这些底值转换为对数 bin 值。
val | pow(2, floor(ln(val) / ln(2)))
1 | 1
2 | 2
3 | 2
4 | 4
5 | 4
剩下的就是简单地按对数箱分组并计数。
注意事项
没有战俘
如果您的 RDBMS 不支持 pow(x, y)
,您可以使用 exp(y * ln(x))
。然后表达式变为:
exp(floor(ln(val) / ln(2)) * ln(2))
零
log(0) 未定义。在我测试的 RDBMS 中,它 returns null.
如果您的 table 的值为 0,您很可能希望将它们分到 0 和 1 之间。为此,您可以用 ifnull(..., 0) 包裹整个表达式,例如所以:
ifnull(pow(2, floor(ln(val) / ln(2))), 0)
否定
负数的对数未定义...但您可能希望将它们像 [0 到 -1)、[-1 到 -2]、[-2 到 -4)、[-4 到-8), 等等
如果您的数据库有负值,您可以通过首先在您的值中使用 abs
来实现分箱,然后最后通过将结果乘以 val/abs(val)
来恢复其原始信号。然后你的表情变成:
pow(2, floor(ln(abs(val)) / ln(2))) * val/abs(val)
负数和零
如果您的数据库既有负值也有零值,您应该用 ifnull
包裹其他所有内容。否则,val/abs(val)
部分会让您除以零,重新引入空值。
ifnull(pow(2, floor(ln(abs(val)) / ln(2))) * val/abs(val), 0)