我希望使用Python的Matplotlib添加一个楔形来概述一组极性数据。由于未知原因,我尝试使用Wedge修补程序艺术家失败。我希望了解这些未知因素,或者寻找补丁艺术家方法的替代方法。
主要问题是Wedge补丁未按我期望的那样显示。给定我的代码,我希望它以一定角度定向,并且半径范围约为〜0.05。这将其放在以下扇区图中: 1
但是此楔形物的尺寸和位置与我期望的不同。在查看缩小的图时,它也会移动: 2
楔形具有大约正确的角度范围(约25-27度),但它以错误的半径(应为〜0.4)开始,并且是错误的宽度(应为〜0.05)。为什么会这样,又如何绘制具有这些所需尺寸的楔子?
我已经查看并改编了类似问题的代码(例如,参见Python: Add a Ring Sector or a Wedge to a Polar Plot)。
这是我的主要代码的改编版,其中包含示例数据。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Wedge
##Enter data
thetaRad = np.array([0.455, 0.456, 0.455, 0.456, 0.46 , 0.459, 0.461, 0.461, 0.453,
0.459, 0.46 , 0.46 , 0.46 , 0.451, 0.46 , 0.457, 0.45 , 0.451,
0.45 , 0.45 , 0.451, 0.452, 0.461, 0.459, 0.451, 0.455, 0.454,
0.457, 0.459, 0.451, 0.46 , 0.453, 0.46 , 0.452, 0.452, 0.45 ,
0.453, 0.452, 0.452, 0.456, 0.45 , 0.458, 0.461, 0.457, 0.45 ,
0.453, 0.459, 0.459, 0.455, 0.456, 0.457, 0.457, 0.454, 0.453,
0.455, 0.456, 0.459, 0.455, 0.453, 0.455, 0.454, 0.459, 0.457,
0.454, 0.46 , 0.458, 0.459, 0.457, 0.451, 0.45 , 0.455, 0.461,
0.455, 0.458, 0.456, 0.449, 0.459, 0.453, 0.458, 0.457, 0.456,
0.45 , 0.459, 0.458, 0.453, 0.452, 0.459, 0.454, 0.455, 0.452,
0.453, 0.451, 0.453, 0.461, 0.452, 0.458, 0.449, 0.461, 0.459,
0.452, 0.458, 0.455, 0.452, 0.451, 0.457, 0.457, 0.457, 0.457,
0.456, 0.456, 0.451, 0.451, 0.452, 0.459, 0.45 , 0.453, 0.45 ,
0.449, 0.453, 0.455, 0.457])
Zs = np.array([0.052, 0.052, 0.057, 0.058, 0.058, 0.058, 0.058, 0.058, 0.059,
0.059, 0.059, 0.059, 0.06 , 0.06 , 0.06 , 0.06 , 0.064, 0.134,
0.134, 0.134, 0.134, 0.135, 0.135, 0.135, 0.135, 0.135, 0.135,
0.135, 0.135, 0.135, 0.135, 0.135, 0.135, 0.136, 0.136, 0.136,
0.136, 0.136, 0.136, 0.137, 0.309, 0.311, 0.32 , 0.328, 0.352,
0.379, 0.381, 0.381, 0.382, 0.382, 0.383, 0.383, 0.386, 0.387,
0.39 , 0.392, 0.392, 0.392, 0.392, 0.393, 0.393, 0.394, 0.394,
0.394, 0.394, 0.394, 0.394, 0.395, 0.395, 0.396, 0.422, 0.426,
0.48 , 0.482, 0.483, 0.483, 0.484, 0.487, 0.487, 0.489, 0.489,
0.49 , 0.49 , 0.491, 0.491, 0.491, 0.491, 0.492, 0.492, 0.496,
0.497, 0.498, 0.5 , 0.505, 0.764, 0.767, 0.771, 0.771, 0.777,
0.833, 0.844, 0.855, 0.858, 0.863, 0.866, 0.868, 0.869, 0.87 ,
0.871, 0.872, 0.875, 0.994, 0.995, 0.996, 1.002, 1.004, 1.01 ,
1.01 , 1.011, 1.475, 1.667])
maxZ = 0.55
minZ = 0.28
##Prepare plot
fig = plt.figure()
color = 'k'
m = 'o'
size = 1
ax = fig.add_subplot(111, projection='polar')
plt.scatter(thetaRad,Zs, c=color, marker=m, s = size)
ax.set_rmax(maxZ)
ax.set_rmin(minZ)
#set theta limits to be scaled from the dataset
minTheta = 0.95*min(thetaRad)
maxTheta = 1.05*max(thetaRad)
#uncomment these for the partial sector plot:
#ax.set_thetamin(np.rad2deg(minTheta))
#ax.set_thetamax(np.rad2deg(maxTheta))
#ax.set_rorigin(-minZ)
ticks = np.linspace(minTheta, maxTheta, 4)
ax.set_xticks(ticks)
##Add a wedge
#define the wedge's width and range
window = np.array([0.35,0.40])
dTheta = np.deg2rad(0.5)
wedgeRange = [minTheta+dTheta, maxTheta-dTheta]
wedgeRange = np.rad2deg(wedgeRange)
r = window[1]
width = window[1]-window[0]
width = width
#apparently, plt's polar plot is silently centered at (0.5,0.5) instead of the
#origin, so set this:
center = (0.5,0.5)
wedge = Wedge(center, r, wedgeRange[0],wedgeRange[1],width=width, transform=ax.transAxes, linestyle='--', fill=False, color='red')
ax.add_artist(wedge)
答案 0 :(得分:1)
事实证明,这比我最初预想的要复杂得多。这里的主要问题是给Wedge
的坐标和角度是轴坐标,而真正需要的是数据坐标中的楔形。尤其是角度很难正确设置。
我发现的解决方案是将楔形的角点转换为轴坐标,然后使用这些点通过线性代数计算楔形的中心,半径和角度。可能有一种方法可以直接使用数据坐标进行此操作,但至少可以这样做。我在matplotlib transformation tutorial和其他一些答案中找到了帮助:
为了使解决方案更容易解释,我在示例中更改了楔形坐标,并为在计算中使用的几何点添加了一些带编号的注释。这是代码:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Wedge
def perp( a ) :
##from https://stackoverflow.com/a/3252222/2454357
b = np.empty_like(a)
b[0] = -a[1]
b[1] = a[0]
return b
def seq_intersect(a1,a2, b1,b2) :
##from https://stackoverflow.com/a/3252222/2454357
da = a2-a1
db = b2-b1
dp = a1-b1
dap = perp(da)
denom = np.dot( dap, db)
num = np.dot( dap, dp )
return (num / denom.astype(float))*db + b1
def angle(a1, a2, b1, b2):
##from https://stackoverflow.com/a/16544330/2454357
x1, y1 = a2-a1
x2, y2 = b2-b1
dot = x1*x2 + y1*y2 # dot product between [x1, y1] and [x2, y2]
det = x1*y2 - y1*x2 # determinant
return np.arctan2(det, dot) # atan2(y, x) or atan2(sin, cos)
def draw_wedge(
ax, r_min = 0.3, r_max = 0.5, t_min = np.pi/4, t_max = 3*np.pi/4
):
##some data
R = np.random.rand(100)*(r_max-r_min)+r_min
T = np.random.rand(100)*(t_max-t_min)+t_min
ax.scatter(T,R)
##compute the corner points of the wedge:
axtmin = 0
rs = np.array([r_min, r_max, r_min, r_max, r_min, r_max])
ts = np.array([axtmin, axtmin, t_min, t_min, t_max, t_max])
##display them in a scatter plot
ax.scatter(ts, rs, color='r', marker='x', lw=5)
##from https://matplotlib.org/users/transforms_tutorial.html
trans = ax.transData + ax.transAxes.inverted()
##convert to figure cordinates, for a starter
xax, yax = trans.transform([(t,r) for t,r in zip(ts, rs)]).T
for i,(x,y) in enumerate(zip(xax, yax)):
ax.annotate(
str(i), (x,y), xytext = (x+0.1, y), xycoords = 'axes fraction',
arrowprops = dict(
width=2,
),
)
##compute the angles of the wedge:
tstart = np.rad2deg(angle(*np.array((xax[[0,1,2,3]],yax[[0,1,2,3]])).T))
tend = np.rad2deg(angle(*np.array((xax[[0,1,4,5]],yax[[0,1,4,5]])).T))
##the center is where the two wedge sides cross (maybe outside the axes)
center=seq_intersect(*np.array((xax[[2,3,4,5]],yax[[2,3,4,5]])).T)
##compute the inner and outer radii of the wedge:
rinner = np.sqrt((xax[1]-center[0])**2+(yax[1]-center[1])**2)
router = np.sqrt((xax[2]-center[0])**2+(yax[2]-center[1])**2)
wedge = Wedge(center,
router, tstart, tend,
width=router-rinner,
#0.6,tstart,tend,0.3,
transform=ax.transAxes, linestyle='--', lw=3,
fill=False, color='red')
ax.add_artist(wedge)
fig = plt.figure(figsize=(8,4))
ax1 = fig.add_subplot(121, projection='polar')
ax2 = fig.add_subplot(122, projection='polar')
##reducing the displayed theta and r ranges in second axes:
ax2.set_thetamin(10)
ax2.set_thetamax(40)
## ax.set_rmax() does not work as one would expect -- use ax.set_ylim() instead
## from https://stackoverflow.com/a/9231553/2454357
ax2.set_ylim([0.2,0.8])
ax2.set_rorigin(-0.2)
#from https://stackoverflow.com/a/41823326/2454357
fig.canvas.draw()
draw_wedge(ax1)
draw_wedge(ax2, t_min=np.deg2rad(15), t_max=np.deg2rad(30))
plt.show()
及其产生的图像:
说明:
在代码中,我定义了6个几何点:楔形的4个角和theta=0
线上的两个点,它们对应于楔形的内半径和外半径。然后,使用转换ax.transData+ax.transAxes.inverted()
将这些点从数据转换为轴坐标。现在,在轴坐标中,我使用这些点来计算楔形的中心(楔形左右两侧的交点;点2、3、4和5)以及theta=0
线之间的角度和楔子的侧面(分别指向0、1、2、3和0、1、4、5)。可以将两个半径计算为楔形中心与点2和3之间的欧几里得距离。利用这些数字,最终可以构造楔形。
请注意,此解决方案对所有图形和轴的操作都不可靠。特别是在添加楔形之后 更改轴限制或纵横比会放错位置。可以调整图的大小并进行测试。希望这会有所帮助。
旧答案:
这有点可笑,但显然radius参数与轴数据无关。您可以通过添加一个半径为0.5
的楔形来进行检查,将其与center=(0.5,0.5)
一起生成一个跨越整个数据范围的楔形。您可以定义一个函数来将楔形半径从数据坐标转换为以下坐标:
def transform_radius(r, rmin, rmax):
return (r-rmin)/(rmax-rmin)*0.5
此处rmin
和rmax
分别是轴的最小半径和最大半径。另一个问题是如何绘制局部楔形。根据文档:
如果给出宽度,则从内半径 r-width 到外半径 r 绘制部分楔形。
因此,在您的情况下,您传递给Wedge
的半径应该是外部半径,而不是内部半径。放在一起,这应该正确显示楔形:
r_inner = transform_radius(r, minZ, maxZ)
r_outer = transform_radius(r+width, minZ, maxZ)
wedge = Wedge(
center,
r_outer,
wedgeRange[0],wedgeRange[1],
width=r_outer-r_inner,
transform=ax.transAxes, linestyle='--',
fill=False, color='red'
)
ax.add_artist(wedge)
如果我误解了一些东西,请告诉我。