线性与对数刻度

8 投票
3 回答
5606 浏览
提问于 2025-04-17 08:45

假设有一条长度为 X 像素的线,比如:

0-------|---V---|-------|-------|-------max

如果 0 <= V <= max,那么在普通的线性比例下,V 的位置可以用 X/max*V 像素来表示。

那么在对数比例下,我该如何计算像素位置呢?而从这个像素位置又如何得到 V 的值呢?

  1. 这不是作业
  2. 我想知道背后的数学原理(请不要说“用 FOO-plotlib”这种话)
  3. 我喜欢 Python

对数比例的效果是“放大”比例的左侧。那么,是否可以对右侧做同样的事情呢?

[更新]

谢谢你们的数学讲解!

最后我没有使用对数。我只是用一组值的平均值作为比例的中心。这个控制用于选择一组值的分界百分位数,这些值将用于绘制一个 分级图

如果用户选择对称比例(红色标记=平均值,绿色标记=中心,深度表示某个值出现的次数): enter image description here

而不对称比例则更容易进行细微调整: enter image description here

3 个回答

2

这个可以很容易地扩展到其他功能上。我的空间度量是用字符来表示的,而不是用像素(这就是为什么最大值是字符(或像素))。
只适用于正值。

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。不过,真是的!这太棒了!:-)

3
           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 时,对数是没有定义的)。

14

假设你有一个任意的值 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

要“放大”方程的“右侧”,你只需要交换方程,所以从 VX 时进行指数运算,而反向时则取对数。原则上是这样的。不过,你还需要处理 X 可能为 0 的情况:

logmax = log(Xmax + 1, b)
X = b ** (logmax * (V - Vmin) / (Vmax - Vmin)) - 1
V = (Vmax - Vmin) * log(X + 1, b) / logmax + Vmin

撰写回答