我有一些数据,我有语言和相对单位大小。我想产生一个气泡图,然后将其导出到PGF。我得到了我的大部分代码从这个答案Making a non-overlapping bubble chart in Matplotlib (circle packing),但我有问题,我的文本退出圆边界:
我该怎么做呢?增加所有内容的规模(我认为这很容易),或者确保气泡的大小总是大于里面的文本(根据数据序列,气泡之间仍然是成比例的)。我认为这很难做到,但我真的不需要。
相关编码:
#!/usr/bin/env python3
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
# create 10 circles with different radii
r = np.random.randint(5,15, size=10)
mapping = [("English", 25),
("French", 13),
("Spanish", 32),
("Thai", 10),
("Vientamese", 13),
("Chinese", 20),
("Jamaican", 8),
("Scottish", 3),
("Irish", 12),
("American", 5),
("Romanian", 3),
("Dutch", 2)]
class C():
def __init__(self,r):
self.colors = list(mcolors.XKCD_COLORS)
self.N = len(r)
self.labels = [item[0] for item in r]
self.x = np.ones((self.N,3))
self.x[:,2] = [item[1] for item in r]
maxstep = 2*self.x[:,2].max()
length = np.ceil(np.sqrt(self.N))
grid = np.arange(0,length*maxstep,maxstep)
gx,gy = np.meshgrid(grid,grid)
self.x[:,0] = gx.flatten()[:self.N]
self.x[:,1] = gy.flatten()[:self.N]
self.x[:,:2] = self.x[:,:2] - np.mean(self.x[:,:2], axis=0)
self.step = self.x[:,2].min()
self.p = lambda x,y: np.sum((x**2+y**2)**2)
self.E = self.energy()
self.iter = 1.
def minimize(self):
while self.iter < 1000*self.N:
for i in range(self.N):
rand = np.random.randn(2)*self.step/self.iter
self.x[i,:2] += rand
e = self.energy()
if (e < self.E and self.isvalid(i)):
self.E = e
self.iter = 1.
else:
self.x[i,:2] -= rand
self.iter += 1.
def energy(self):
return self.p(self.x[:,0], self.x[:,1])
def distance(self,x1,x2):
return np.sqrt((x1[0]-x2[0])**2+(x1[1]-x2[1])**2)-x1[2]-x2[2]
def isvalid(self, i):
for j in range(self.N):
if i!=j:
if self.distance(self.x[i,:], self.x[j,:]) < 0:
return False
return True
def scale(self, size):
"""Scales up the plot"""
self.x = self.x*size
def plot(self, ax):
for i in range(self.N):
circ = plt.Circle(self.x[i,:2],self.x[i,2], color=mcolors.XKCD_COLORS[self.colors[i]])
ax.add_patch(circ)
ax.text(self.x[i][0],self.x[i][1], self.labels[i], horizontalalignment='center', size='medium', color='black', weight='semibold')
c = C(mapping)
fig, ax = plt.subplots(subplot_kw=dict(aspect="equal"))
ax.axis("off")
c.minimize()
c.plot(ax)
ax.relim()
ax.autoscale_view()
plt.show()
字符串
1条答案
按热度按时间ruyhziif1#
我认为你概述的这两种方法在很大程度上是等效的。在这两种情况下,你必须根据圆圈的大小来确定文本框的大小。为matplotlib文本对象获取精确的边界框是一件棘手的事情,因为渲染文本对象是由后端完成的,而不是matplotlib本身。所以你必须渲染文本对象,获取其边界框,计算当前边界和所需边界之间的比率,移除文本对象,最后重新渲染按先前计算的比率重新缩放的文本。并且由于边界框计算以及因此重新缩放对于非常小和非常大的文本对象是非常不准确的,你实际上必须重复这个过程几次(下面我做了两次,这是最低限度的)。
关于圆圈的位置,我也冒昧地用适当的最小化来代替你在能量景观中的随机漫步,它更快,我认为结果更好。
的数据
字符串