Reputation: 698
I want to plot colored pie charts at specific positions without distorting their circular aspect ratio. I'm using Wedge patches because I could not find a better solution. Here is the code
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import patches, collections
fig, axes = plt.subplots()
for i in range(20):
x = np.random.uniform(low=0, high=1, size=10).cumsum()
axes.scatter(x=x, y=np.repeat(i, x.shape[0]), c='gray', s=1)
pies = []
N = 4
cmap = plt.cm.get_cmap("hsv", N + 1)
colors = list(map(cmap, range(N)))
print(colors)
for i in range(2, 2 + N):
thetas = np.linspace(0, 360, num=i)
assert len(thetas) - 1 <= len(colors)
for theta1, theta2, c in zip(thetas[:-1], thetas[1:], colors):
wedge = patches.Wedge((i, i), r=i / 10, theta1=theta1, theta2=theta2,
color=c)
pies.append(wedge)
axes.add_collection(collections.PatchCollection(pies,
match_original=True))
plt.show()
How to preserve the aspect ratio of pie charts? Setting axes.set_aspect("equal")
is NOT an option because it squeezes the plot completely when I have more data points.
I've been looking at how to draw circles and preserve the aspect ratio but the solution cannot be adopted here - I'm plotting Wedges/pie charts, not Circles.
I also looked at matplotlib transforms but couldn't find the answer there either.
Upvotes: 0
Views: 556
Reputation: 1208
I tried the same thing, and matplotlib really doesn't try to make this easy for you, but I found a solution that you should be able to use.
You need to separate the centers from the wedges and add them to the PatchCollection as offsets. Then you can apply different transforms to the offsets (transOffset) and shape (transform).
Notice that I have changed the r-value (radius). This value is no longer in data coordinates, so it should always be the same size, regardless of how much you zoom, but it is too small to be visible at i/10
.
from matplotlib import patches, collections, transforms
offsets = []
for i in range(2, 2 + N):
thetas = np.linspace(0, 360, num=i)
assert len(thetas) - 1 <= len(colors)
for theta1, theta2, c in zip(thetas[:-1], thetas[1:], colors):
wedge = patches.Wedge((0, 0), r=10, theta1=theta1, theta2=theta2,
color=c)
offsets.append((i, i))
pies.append(wedge)
coll = collections.PatchCollection(
pies, match_original=True, offsets=offsets,
transform=transforms.IdentityTransform(),
transOffset=axes.transData
)
Upvotes: 1
Reputation: 773
It works fine for me when I set set_aspect('equal')
:
Image is narrowed because y-range is longer than x-range I think.
If you'd set y_lim between 0 and a number lower than y_max, you'd see it better:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import patches, collections
fig, axes = plt.subplots()
for i in range(20):
x = np.random.uniform(low=0, high=1, size=10).cumsum()
axes.scatter(x=x, y=np.repeat(i, x.shape[0]), c='gray', s=1)
pies = []
N = 4
cmap = plt.cm.get_cmap("hsv", N + 1)
colors = list(map(cmap, range(N)))
print(colors)
for i in range(2, 2 + N):
thetas = np.linspace(0, 360, num=i)
assert len(thetas) - 1 <= len(colors)
for theta1, theta2, c in zip(thetas[:-1], thetas[1:], colors):
wedge = patches.Wedge((i, i), r=i / 10, theta1=theta1, theta2=theta2,
color=c)
pies.append(wedge)
axes.add_collection(collections.PatchCollection(pies,
match_original=True))
axes.set_aspect('equal')
axes.set_ylim(0,7.5)
plt.show()
Upvotes: 0