通过x-scale缩放matplotlib.pyplot.Axes.scatter标记大小 [英] Scale matplotlib.pyplot.Axes.scatter markersize by x-scale

查看:280
本文介绍了通过x-scale缩放matplotlib.pyplot.Axes.scatter标记大小的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想根据x/y轴上的点数来缩放matplotlib.pyplot.Axes.scatter图的markersize.

I would like to scale the markersize of matplotlib.pyplot.Axes.scatter plot based on the number of points on the x/y-axis.

import matplotlib.pyplot as plt
import numpy as np

vmin = 1
vmax = 11

x = np.random.randint(vmin, vmax, 5)
y = np.random.randint(vmin, vmax, 5)

fig, ax = plt.subplots()
for v in np.arange(vmin, vmax):
    ax.axvline(v - 0.5)
    ax.axvline(v + 0.5)
    ax.axhline(v - 0.5)
    ax.axhline(v + 0.5)

ax.set_xlim(vmin - 0.5, vmax + 0.5)
ax.set_ylim(vmin - 0.5, vmax + 0.5)
ax.scatter(x, y)

ax.set_aspect(1)
plt.show()

ax始终使用相同的纵横比,并且两个轴具有相同的lim值.

ax is always using an equal aspect ratio and both axes have the same lim values.

当前,运行上述命令将生成以下图...

Currently, running the above generates the following plot ...

...并更改vmax = 41的值

...and changing the value of vmax = 41

两个图中的markersize保留默认值,即markersize=6.

The markersize in both plots is left to the default, i.e. markersize=6.

我的问题是,如何计算markersize值,以使marker接触每个单元格的边缘? (每个单元格最多有一个数据点.)

My question is, how could I compute the markersize value so the markers touch the edges of each cell? (Each cell has a maximum of one data point.)

推荐答案

使用圆圈

一个简单的选择是用由半径为0.5的Circles组成的PatchCollection代替散点.

Using Circles

An easy option is to replace the scatter by a PatchCollection consisting of Circles of radius 0.5.

circles = [plt.Circle((xi,yi), radius=0.5, linewidth=0) for xi,yi in zip(x,y)]
c = matplotlib.collections.PatchCollection(circles)
ax.add_collection(c)

如果需要散点图,则替代方法是将标记大小更新为数据单位.

The alternative, if a scatter plot is desired, would be to update the markersize to be in data units.

这里的简单解决方案是先绘制一次图形,然后获取轴尺寸并从中计算出标记尺寸(以磅为单位).

The easy solution here would be to first draw the figure once, then take the axes size and calculate the markersize in points from it.

import matplotlib.pyplot as plt
import numpy as np

vmin = 1
vmax = 11

x = np.random.randint(vmin, vmax, 5)
y = np.random.randint(vmin, vmax, 5)

fig, ax = plt.subplots(dpi=141)
for v in np.arange(vmin, vmax):
    ax.axvline(v - 0.5)
    ax.axvline(v + 0.5)
    ax.axhline(v - 0.5)
    ax.axhline(v + 0.5)

ax.set_xlim(vmin - 0.5, vmax + 0.5)
ax.set_ylim(vmin - 0.5, vmax + 0.5)

ax.set_aspect(1)
fig.canvas.draw()
s = ((ax.get_window_extent().width  / (vmax-vmin+1.) * 72./fig.dpi) ** 2)

ax.scatter(x, y, s = s, linewidth=0)

plt.show()

有关如何使用散点标记大小的背景知识,请参见例如此答案.上述解决方案的缺点是将标记大小固定为绘图的大小和状态.万一轴的极限发生变化或图被缩放,散布图的大小将再次错误.

For some background on how markersize of scatters is used, see e.g. this answer. The drawback of the above solution is that is fixes the marker size to the size and state of the plot. In case the axes limits would change or the plot is zoomed, the scatter plot would again have the wrong sizing.

因此,以下解决方案将更通用. 这有点涉及,并且与绘制宽度以数据单位表示的行.

Hence the following solution would be more generic. This is a little involved and would work similarly as Plotting a line with width in data units.

import matplotlib.pyplot as plt
import numpy as np

vmin = 1
vmax = 32

x = np.random.randint(vmin, vmax, 5)
y = np.random.randint(vmin, vmax, 5)

fig, ax = plt.subplots()
for v in np.arange(vmin, vmax):
    ax.axvline(v - 0.5)
    ax.axvline(v + 0.5)
    ax.axhline(v - 0.5)
    ax.axhline(v + 0.5)

ax.set_xlim(vmin - 0.5, vmax + 0.5)
ax.set_ylim(vmin - 0.5, vmax + 0.5)

class scatter():
    def __init__(self,x,y,ax,size=1,**kwargs):
        self.n = len(x)
        self.ax = ax
        self.ax.figure.canvas.draw()
        self.size_data=size
        self.size = size
        self.sc = ax.scatter(x,y,s=self.size,**kwargs)
        self._resize()
        self.cid = ax.figure.canvas.mpl_connect('draw_event', self._resize)

    def _resize(self,event=None):
        ppd=72./self.ax.figure.dpi
        trans = self.ax.transData.transform
        s =  ((trans((1,self.size_data))-trans((0,0)))*ppd)[1]
        if s != self.size:
            self.sc.set_sizes(s**2*np.ones(self.n))
            self.size = s
            self._redraw_later()

    def _redraw_later(self):
        self.timer = self.ax.figure.canvas.new_timer(interval=10)
        self.timer.single_shot = True
        self.timer.add_callback(lambda : self.ax.figure.canvas.draw_idle())
        self.timer.start()


sc = scatter(x,y,ax, linewidth=0)

ax.set_aspect(1)
plt.show()

(由于此问题,我更新了代码以使用计时器重画画布 a>)

这篇关于通过x-scale缩放matplotlib.pyplot.Axes.scatter标记大小的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆