线性与对数刻度
假设有一条长度为 X
像素的线,比如:
0-------|---V---|-------|-------|-------max
如果 0 <= V <= max
,那么在普通的线性比例下,V
的位置可以用 X/max*V
像素来表示。
那么在对数比例下,我该如何计算像素位置呢?而从这个像素位置又如何得到 V
的值呢?
- 这不是作业
- 我想知道背后的数学原理(请不要说“用 FOO-plotlib”这种话)
- 我喜欢 Python
对数比例的效果是“放大”比例的左侧。那么,是否可以对右侧做同样的事情呢?
[更新]
谢谢你们的数学讲解!
最后我没有使用对数。我只是用一组值的平均值作为比例的中心。这个控制用于选择一组值的分界百分位数,这些值将用于绘制一个 分级图。
如果用户选择对称比例(红色标记=平均值,绿色标记=中心,深度表示某个值出现的次数):
而不对称比例则更容易进行细微调整:
3 个回答
这个可以很容易地扩展到其他功能上。我的空间度量是用字符来表示的,而不是用像素(这就是为什么最大值是字符(或像素))。
只适用于正值。
import math
def scale(myval, mode='lin'):
steps = 7
chars = max = 10 * steps
if mode=='log':
val = 10 * math.log10(myval)
else:
val = myval
coord = []
count = 0
not_yet = True
for i in range(steps):
for j in range(10):
count += 1
if val <= count and not_yet:
coord.append('V')
not_yet = False
pos = count
elif j==9:
coord.append('|')
else:
coord.append('*')
graph = ''.join(coord)
text = 'graph %s\n\n%s\nvalue = %5.1f rel.pos. = %5.2f\n'
print text % (mode, graph, myval, chars * pos/max)
scale(50, 'lin')
scale(50, 'log')
希望上面的内容不会被认为是FOO-plotlib。不过,真是的!这太棒了!:-)
Linear Logarithmic
Forward pos = V * X/max pos = log(V) * X/log(max)
Reverse V = pos * max/X V = B^( pos * log(max)/X )
(B 是对数的底数)
显然,你必须确保 V 大于等于 1(当 V=1 时,对应的位置 pos=0;当 V 在 0 到 1 之间时,对应的值是从负无穷到 0;而当 V 小于 0 时,对数是没有定义的)。
假设你有一个任意的值 V
,并且你知道这个值的范围是 0 到 Vmax
之间。你想计算一个像素的 x 坐标,叫它 X
,而你的“屏幕”上 x 坐标的范围是从 0 到 Xmax
。按照你说的“正常”方式,你可以这样做:
X = Xmax * V / Vmax
V = Vmax * X / Xmax
我喜欢把这个过程想象成,首先把这个值归一化到 0 到 1 之间,方法是计算 V / Vmax
,然后再把这个值乘以最大值,得到一个 0 到最大值之间的值。
如果你想用对数的方法来做同样的事情,你需要为 V
选择一个不同的下限。如果 V
小于等于 0,你就会遇到 ValueError
错误。所以我们假设 0 < Vmin
<= V
<= Vmax
。接下来,你需要确定使用哪种对数,因为对数有无数种。常见的有三种,分别是以 2、e 和 10 为底的对数,这样得到的 x 轴看起来是这样的:
------|------|------|------|---- ------|------|------|------|----
2^-1 2^0 2^1 2^2 == 0.5 1 2 4
------|------|------|------|---- ------|------|------|------|----
e^-1 e^0 e^1 e^2 == 0.4 1 2.7 7.4
------|------|------|------|---- ------|------|------|------|----
10^-1 10^0 10^1 10^2 == 0.1 1 10 100
原则上,如果我们能从左边的表达式中得到指数,我们就可以使用和上面相同的原理,得到一个 0 到 Xmax
之间的值,这就是对数的用处。假设你使用的是底数 b
,你可以用这些表达式进行相互转换:
from math import log
logmax = log(Vmax / Vmin, b)
X = Xmax * log(V / Vmin, b) / logmax
V = Vmin * b ** (logmax * X / Xmax)
这几乎是相同的思路,只是你需要先确保 log(somevalue, b)
会给你一个非负值。你可以通过在 log
函数内部除以 Vmin
来实现。接下来,你可以除以这个表达式能产生的最大值,当然就是 log(Vmax / Vmin, b)
,这样你就会得到一个 0 到 1 之间的值,和之前一样。
另一种方法是先归一化 (X / Xmax
),然后再缩放回最大值 (* logmax
),以符合反函数的预期最大值。顺便说一下,反函数是将 b
进行指数运算。如果 X
是 0,那么 b ** (logmax * X / Xmax)
将等于 1,因此为了得到正确的下限,我们需要乘以 Vmin
。换句话说,由于我们在反向操作时首先做的是除以 Vmin
,所以现在我们最后一步需要乘以 Vmin
。
要“放大”方程的“右侧”,你只需要交换方程,所以从 V
到 X
时进行指数运算,而反向时则取对数。原则上是这样的。不过,你还需要处理 X
可能为 0 的情况:
logmax = log(Xmax + 1, b)
X = b ** (logmax * (V - Vmin) / (Vmax - Vmin)) - 1
V = (Vmax - Vmin) * log(X + 1, b) / logmax + Vmin