Matplotlib - 动态绘图高度,水平条总是1px高度

时间:2017-03-01 16:08:19

标签: python matplotlib plot

我使用MatPlotLib动态生成水平条形图。它在大多数情况下都能很好地工作,直到人们试图绘制大量的数据点。 MatPlotLib试图将所有条形图压入图中,它们开始消失。

理想的解决方案是生成绘图,使每个水平条的高度为一个像素,每个条形图分隔1px。然后,得到的绘图图像的总高度将取决于条的数量。但由于MatPlotLib中的所有东西都是相对的,我真的陷入了如何做到这一点。任何帮助将不胜感激!

2 个答案:

答案 0 :(得分:3)

一种选择是使用条形作为像素生成图像。

import matplotlib.pyplot as plt
import numpy as np

dpi = 100
N = 100 # numbner of bars (approx. half the number of pixels)
w = 200 #width of plot in pixels
sp = 3  # spacing within axes in pixels
bp = 50; lp = 70 # bottom, left pixel spacing
bottom=float(bp)/(2*N+2*sp+2*bp)
top = 1.-bottom
left=float(lp)/(w+2*lp)
right=1.-left
figheight = (2*N+2*sp)/float(dpi)/(1-(1-top)-bottom) #inch
figwidth = w/float(dpi)/(1-(1-right)-left)

# this is the input array to plot
inp = np.random.rand(N)+0.16

ar = np.zeros((2*N+2*sp,w))
ninp = np.round(inp/float(inp.max())*w).astype(np.int)
for n in range(N):
    ar[2*n+sp, 0: ninp[n]] = np.ones(ninp[n])

fig, ax=plt.subplots(figsize=(figwidth, figheight), dpi=dpi)
plt.subplots_adjust(left=left, bottom=bottom, right=right, top=top)
plt.setp(ax.spines.values(), linewidth=0.5)

ext = [0,inp.max(), N-0.5+(sp+0.5)/2., -(sp+0.5)/2.]
ax.imshow(ar, extent=ext, interpolation="none", cmap="gray_r", origin="upper", aspect="auto")
ax.set_xlim((0,inp.max()*1.1))

ax.set_ylabel("item")
ax.set_xlabel("length")
plt.savefig(__file__+".png", dpi=dpi)
plt.show()

enter image description here

这适用于dpi的任何设置 请注意,ticklabels可能看起来有点偏离,这是来自matplotlib的不准确;我不知道如何克服。

答案 1 :(得分:1)

此示例显示如何绘制1像素宽度的线:

yinch = 2

fig, ax = plt.subplots(figsize=(3,yinch), facecolor='w')
fig.subplots_adjust(left=0, right=1, bottom=0, top=1)

ypixels = int(yinch*fig.get_dpi())

for i in range(ypixels):

    if i % 2 == 0:
        c = 'k'
    else:
        c = 'w'

    ax.plot([0,np.random.rand()], [i,i], color=c, linewidth=72./fig.get_dpi())

ax.set_ylim(0,ypixels)
ax.axis('off')

结果如下(放大200%):

enter image description here

编辑:

使用不同的dpi不是问题,但是使用plot()变得不太有用,因为您无法指定线宽单位。您可以计算所需的线宽,但我认为在这种情况下使用barh()会更清楚。

在上面的示例中,我只是禁用了轴来聚焦在1px条上,如果删除它可以正常绘制。围绕它的间距不是问题,因为Matplotlib没有绑定到图的0-1范围,但是您想要将bbox_inches='tight'添加到您的savefig以包括正常0-1范围之外的艺术家。如果您在轴内花费大量时间进行“精确”绘图,我认为更容易拉伸轴以跨越整个图形尺寸。您当然采用不同的方法,但这需要您还以英寸计算轴尺寸。这两个角度都可以工作,这取决于您的确切情况,这可能更方便。

另请注意,旧版本的Matplotlib(< 2.0?)具有不同的默认figure.dpisavefig.dpi。您可以通过在dpi=fig.get_dpi()语句中添加savefig来避免这种情况。升级的众多原因之一。 ;)

yinch = 2
dpi = 128

fig, ax = plt.subplots(figsize=(3,yinch), facecolor='w', dpi=dpi)
fig.subplots_adjust(left=0, right=1, bottom=0, top=1)

ypixels = int(yinch*fig.get_dpi())

for i in range(ypixels):

    if i % 2 == 0:
        c = '#aa0000'
    else:
        c = 'w'
    ax.barh(i,np.random.rand(), height=1, color=c)

ax.set_title('DPI %i' % dpi)
ax.set_ylim(0,ypixels)

fig.savefig('mypic.png', bbox_inches='tight')

enter image description here