条宽相等的对数多序列图

Logarithmic multi-sequenz plot with equal bar widths

我有类似的东西

import matplotlib.pyplot as plt
import numpy as np

a=[0.05, 0.1, 0.2, 1, 2, 3]
plt.hist((a*2, a*3), bins=[0, 0.1, 1, 10])
plt.gca().set_xscale("symlog", linthreshx=0.1)
plt.show()

这给了我以下情节:

可以看出,条形宽度不相等。在线性部分(从 0 到 0.1),一切都找到了,但在此之后,条形宽度仍然是线性刻度,而轴是对数刻度,给我之间的条形和空间不均匀的宽度(刻度是不在酒吧的中间)。

有什么办法可以纠正这个问题吗?

如果您对数据集一个接一个地绘制的绘图没问题,则可以使用 histtype='stepfilled'。当然,您需要谨慎选择具有 alpha 值的颜色,这样您的所有数据仍然可以看到...

a = [0.05, 0.1, 0.2, 1, 2, 3] * 2
b = [0.05, 0.05, 0.05, 0.15, 0.15, 2]
colors = [(0.2, 0.2, 0.9, 0.5), (0.9, 0.2, 0.2, 0.5)]  # RGBA tuples
plt.hist((a, b), bins=[0, 0.1, 1, 10], histtype='stepfilled', color=colors)
plt.gca().set_xscale("symlog", linthreshx=0.1)
plt.show()

为了更好地说明,我稍微更改了您的数据。这给了我:

出于某种原因,重叠颜色似乎出错了(matplotlib 1.3.1 与 Python 3.4.0;这是一个错误吗?),但这可能是 solution/alternative 你的问题.

好的,我发现了真正的问题:当您使用这些 bin-edge 设置创建直方图时,直方图会创建大小相等的条形,并且 non-log 上的 outside-spacing 相等]规模。

为了演示,这里是问题情节的 zoomed-in 版本,但比例为 non-log:

注意前两个条形如何以 (0 + 0.1) / 2 = 0.05 为中心,边缘处的间隙为 0.1 / 10 = 0.01,而接下来的两个条形以 (0.1 + 1.0) 为中心/ 2 = 0.55,两边的间距为 1.1 / 10 = 0.11。

当转换为对数刻度时,条宽和边宽都会被折腾得很大。由于你有一个从 0 到 0.1 的线性比例,之后 事情变成 log-scale。

除了手动完成所有操作外,我不知道有什么办法可以解决这个问题。我使用 bin-edges 的几何平均值来计算条形边缘和条形宽度应该是多少。请注意,这段代码仅适用于两个数据集。如果你有更多的数据集,你需要有一些函数来适当地用几何级数填充 bin-edges。

import numpy as np
import matplotlib.pyplot as plt

def geometric_means(a):
    """Return pairwise geometric means of adjacent elements."""
    return np.sqrt(a[1:] * a[:-1])

a = [0.05, 0.1, 0.2, 1, 2, 3] * 2
b = [0.05, 0.1, 0.2, 1, 2, 3] * 3

# Find frequencies
bins = np.array([0, 0.1, 1, 10])
a_hist = np.histogram(a, bins=bins)[0]
b_hist = np.histogram(b, bins=bins)[0]

# Find log-scale mid-points for bar-edges
mid_vals = np.hstack((np.array([0.05,]), geometric_means(bins[1:])))

# Compute bar left-edges, and bar widths
a_x = np.empty(mid_vals.size * 2)
a_x = bins[:-1]
a_widths = mid_vals - bins[:-1]

b_x = np.empty(mid_vals.size * 2)
b_x = mid_vals
b_widths = bins[1:] - mid_vals

plt.bar(a_x, a_hist, width=a_widths, color='b')
plt.bar(b_x, b_hist, width=b_widths, color='g')

plt.gca().set_xscale("symlog", linthreshx=0.1)
plt.show()

最终结果:

抱歉,条形之间整齐的缝隙消失了。同样,这可以通过进行适当的几何插值来解决,以便在 log-scale.

上一切都是线性的

受到的启发,我提出了以下解决方案:

import matplotlib.pyplot as plt
import numpy as np

d=[0.05, 0.1, 0.2, 1, 2, 3]


def LogHistPlot(data, bins):
    totalWidth=0.8
    colors=("b", "r", "g")
    for i, d in enumerate(data):
        heights = np.histogram(d, bins)[0]
        width=1/len(data)*totalWidth
        left=np.array(range(len(heights))) + i*width

        plt.bar(left, heights, width, color=colors[i], label=i)
        plt.xticks(range(len(bins)), bins)
    plt.legend(loc='best')

LogHistPlot((d*2, d*3, d*4), [0, 0.1, 1, 10])

plt.show()

产生这个情节:

基本思路是删除 plt.hist 函数,用 numpy 计算直方图并用 plt.bar 绘制。然后,您可以轻松地使用线性 x 轴,这使得条形宽度计算变得微不足道。最后,刻度被 bin 边缘替换,从而产生对数刻度。而且您甚至不必再处理符号日志 linear/logarithmic 拙劣的东西了。

以防万一有人偶然发现这个问题: 这个解决方案看起来更像它应该的样子