按 x 比例缩放 matplotlib.pyplot.Axes.scatter 标记大小
Scale matplotlib.pyplot.Axes.scatter markersize by x-scale
我想根据 x/y-axis 上的点数缩放 matplotlib.pyplot.Axes.scatter
图的 markersize
。
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
值。
目前,运行 以上生成以下情节...
...并更改 vmax = 41
的值
两个图中的 markersize
保留为默认值,即 markersize=6
。
我的问题是,如何计算 markersize
值,以便 marker
触及每个单元格的边缘? (每个单元格最多有一个数据点。)
使用圈子
一个简单的选择是用半径为 0.5 的 Circles
组成的 PatchCollection
替换散点图。
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)
使用带有数据单位大小标记的散点图
如果需要散点图,替代方法是将标记大小更新为数据单位。
这里最简单的解决方案是先绘制图形一次,然后获取轴大小并从中计算以点为单位的标记大小。
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()
有关如何使用散点标记大小的一些背景信息,请参见例如this answer。上述解决方案的缺点是将标记大小固定为图的大小和状态。如果轴限制发生变化或图被缩放,散点图将再次具有错误的大小。
因此,以下解决方案将更通用。
这有点复杂,与 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()
(由于 this issue,我更新了代码以使用计时器重绘 canvas)
我想根据 x/y-axis 上的点数缩放 matplotlib.pyplot.Axes.scatter
图的 markersize
。
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
值。
目前,运行 以上生成以下情节...
...并更改 vmax = 41
的值
两个图中的 markersize
保留为默认值,即 markersize=6
。
我的问题是,如何计算 markersize
值,以便 marker
触及每个单元格的边缘? (每个单元格最多有一个数据点。)
使用圈子
一个简单的选择是用半径为 0.5 的 Circles
组成的 PatchCollection
替换散点图。
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)
使用带有数据单位大小标记的散点图
如果需要散点图,替代方法是将标记大小更新为数据单位。
这里最简单的解决方案是先绘制图形一次,然后获取轴大小并从中计算以点为单位的标记大小。
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()
有关如何使用散点标记大小的一些背景信息,请参见例如this answer。上述解决方案的缺点是将标记大小固定为图的大小和状态。如果轴限制发生变化或图被缩放,散点图将再次具有错误的大小。
因此,以下解决方案将更通用。 这有点复杂,与 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()
(由于 this issue,我更新了代码以使用计时器重绘 canvas)