How to speed up matplotlib scatterpie chart? - python

Is there any way to speed up matplotlib scatter pie chart? I am currently using these draw_pie function and plotting around 3k points. It takes more than 30 minutes to plot all these points. Thats way slower than R's function which takes rougly 5min.
def draw_pie(dist,
xpos,
ypos,
size,
ax=None):
if ax is None:
fig, ax = plt.subplots(figsize=(10,8))
# for incremental pie slices
cumsum = np.cumsum(dist)
cumsum = cumsum/ cumsum[-1]
pie = [0] + cumsum.tolist()
for r1, r2 in zip(pie[:-1], pie[1:]):
angles = np.linspace(2 * np.pi * r1, 2 * np.pi * r2)
x = [0] + np.cos(angles).tolist()
y = [0] + np.sin(angles).tolist()
xy = np.column_stack([x, y])
ax.scatter([xpos], [ypos], marker=xy, s=size)
return ax

Related

Create subplots on the same page

How do I subplots multiple graphs? so it will look like the picture.
the image is how the plots should look
Im able to plot individule but i cant figure out
A = 1 # Wave amplitude in meters
T = 10 # Time Period in secs
pi = 3.14 # Value of pi
n_w = 10 # Number of waves
wavelength = 156 # Wavelength in meters
k = (2 * pi) / wavelength
w = (2 * pi) / T
def wave_elevation(x,t):
return A * np.cos((k * x) - (w * t))
t_list = np.array([0,0.25,0.5,0.75,1.0])*T
for t in t_list:
wave_ele_Val = []
for i in np.linspace(0,wavelength*n_w,1560):
wave_ele_Val.append(wave_elevation(i,t))
fig, ax = plt.subplots(figsize=(15, 5))
plt.plot(np.linspace(0,wavelength*n_w,1560),wave_ele_Val,'r')
plt.title("Wave Elevation-Space Variations # " + str(t) + "* Time Periods")
plt.xlabel("x (m)")
plt.ylabel("\u03B7")
plt.grid()
plt.show()
Try declaring figures outside loop.
import math
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
A = 1 # Wave amplitude in meters
T = 10 # Time Period in secs
pi = 3.14 # Value of pi
n_w = 10 # Number of waves
wavelength = 156 # Wavelength in meters
k = (2 * pi) / wavelength
w = (2 * pi) / T
def wave_elevation(x,t):
return A * np.cos((k * x) - (w * t))
x_list = np.array([0,0.25,0.5,0.75,1.0])*wavelength
fig, ax = plt.subplots(len(x_list), figsize=(15, 5))
for i, x in enumerate(x_list):
wave_ele_Val = []
for j in np.linspace(0,T*n_w,1000):
wave_ele_Val.append(wave_elevation(x, j))
ax[i].plot(np.linspace(0,T*n_w,1000),wave_ele_Val)
ax[i].grid()
plt.show()
all the configuration is in the link
You need to specify how many subplots you want:
matplotlib.pyplot.subplots(nrows=1, ncols=1, *, sharex=False, sharey=False, squeeze=True, subplot_kw=None, gridspec_kw=None, **fig_kw)
In your case this would be plt.subplots(5)
The plt.plot() needs to be changed to ax[i].plot(x,y)
Where i is your number of subplot.
Also shift the plt.show() outside of the loop, so it is called at the end and not in between.
This is one example, how you can do it:
x_list = np.array([0,0.25,0.5,0.75,1.0])*wavelength
fig, ax = plt.subplots(5, figsize=(15, 5))
for j,x in enumerate(x_list):
wave_ele_Val = []
for i in np.linspace(0,T*n_w,1000):
wave_ele_Val.append(wave_elevation(x, i))
ax[j].plot(np.linspace(0,T*n_w,1000),wave_ele_Val)
ax[j].grid()
ax[j].set_title("Wave Elevation-Time Variations # " + str(x) + "WaveLengths")
ax[j].set_ylabel("\u03B7")
fig.tight_layout()
plt.xlabel("Time (s)")
plt.show()
This gives me:

How to plot a donut chart around a point on a scatterplot?

I have a scatterplot with a few points which I can plot easily enough. I want to add a donut chart around each of the points to indicate which classes make up the point. I saw the example of nested donut charts but I want to make a scatter/donut plot for multiple points.
This is the code I have so far for making the scatterplot and the donut chart. It will plot all 3 data points and one donut chart for the first point.
import numpy as np
import matplotlib.pyplot as plt
# Fixing random state for reproducibility
np.random.seed(19680801)
## Scatter
# create three data points with three random class makeups
N = 3
N_class = 5
x = np.random.rand(N)
y = np.random.rand(N)
vals = [np.random.randint(2, size=N_class) for _ in range(N)]
plt.scatter(x, y, s=500)
plt.show()
## Donut plot
# Create 5 equal sized wedges
size_of_groups = np.ones(5)
# Create a pieplot
plt.pie(size_of_groups, colors=["grey" if val == 0 else "red" for val in vals[0]])
#plt.show()
# add a circle at the center
my_circle=plt.Circle( (0,0), 0.7, color='white')
p = plt.gcf()
p.gca().add_artist(my_circle)
plt.show()
Something similar to this for each point (disregarding the pie chart center, just a scatter point)
Adapting the Scatter plot with pie chart markers example, one can just add a white marker in the middle to make the pies become donuts.
import numpy as np
import matplotlib.pyplot as plt
# first define the ratios
r1 = 0.2 # 20%
r2 = r1 + 0.4 # 40%
# define some sizes of the scatter marker
sizes = np.array([60, 80, 120])*4
center_sizes = sizes/3.
# calculate the points of the first pie marker
#
# these are just the origin (0,0) +
# some points on a circle cos,sin
x = [0] + np.cos(np.linspace(0, 2 * np.pi * r1, 10)).tolist()
y = [0] + np.sin(np.linspace(0, 2 * np.pi * r1, 10)).tolist()
xy1 = np.column_stack([x, y])
s1 = np.abs(xy1).max()
x = [0] + np.cos(np.linspace(2 * np.pi * r1, 2 * np.pi * r2, 10)).tolist()
y = [0] + np.sin(np.linspace(2 * np.pi * r1, 2 * np.pi * r2, 10)).tolist()
xy2 = np.column_stack([x, y])
s2 = np.abs(xy2).max()
x = [0] + np.cos(np.linspace(2 * np.pi * r2, 2 * np.pi, 10)).tolist()
y = [0] + np.sin(np.linspace(2 * np.pi * r2, 2 * np.pi, 10)).tolist()
xy3 = np.column_stack([x, y])
s3 = np.abs(xy3).max()
fig, ax = plt.subplots()
ax.scatter(range(3), range(3), marker=xy1,
s=s1 ** 2 * sizes, facecolor='indigo')
ax.scatter(range(3), range(3), marker=xy2,
s=s2 ** 2 * sizes, facecolor='gold')
ax.scatter(range(3), range(3), marker=xy3,
s=s3 ** 2 * sizes, facecolor='crimson')
# centers
ax.scatter(range(3), range(3), s=center_sizes, marker="o", color="w")
plt.show()
If instead a real pie chart is desired, you may use the arguments center and radius to position several pies on the axes.
import matplotlib.pyplot as plt
# first define the ratios
r1 = 0.2 # 20%
r2 = r1 + 0.4 # 40%
x = list(range(3))
y = list(range(3))
fig, ax = plt.subplots()
for xi,yi in zip(x,y):
ax.pie([r1,r2,r2], colors=['indigo', "gold", 'crimson'],
center=(xi, yi), radius=0.2+xi/4,
wedgeprops=dict(width=(0.2+xi/4)/2), frame=True)
ax.autoscale()
plt.show()

Zoom in on polar plot

I'd like to create an inset within my fig which is a zoom in on part of my polar plot.
I've tried various different methods but can't seem to crack the correct way to do using matplotlib. My code to create the plot (from my pandas dataframe) is below. I've also included the plot that it produces.
def plot_polar_chart_new(n, start, df, sales, title):
HSV_tuples = [(x * 1.0 / n, 0.5, 0.5) for x in range(n)]
RGB_tuples = map(lambda x: colorsys.hsv_to_rgb(*x), HSV_tuples)
RGB_normalised = [tuple(n / max(t) for n in t) for t in RGB_tuples]
figsize=(15, 15)
fig = mpl.pyplot.figure(figsize=figsize)
ax = fig.add_subplot(1,1,1, polar=True)
start = 0
prev_count = 0
for i, salesperson in enumerate(sales):
count, division = (df[salesperson], df.index)
ax.bar((division - start) * 2 * np.pi / N, height=count, width=2 * np.pi / N, color=RGB_normalised[i], bottom=prev_count, label=salesperson)
prev_count += count
ax.set_xticks(np.linspace(0, 2 * np.pi, N, endpoint=False))
ax.set_xticklabels(range(start, N + start),fontsize=20)
ax.yaxis.set_tick_params(labelsize=20)
ax.set_theta_direction(-1)
ax.set_theta_offset(np.pi / 2.0)
ax.set_title(title, y=1.1, fontsize=20)
ax.legend(bbox_to_anchor=(0.9, 1.1), loc=2)
mpl.pyplot.show()
I'd like to create a plot inset which zooms in on part of the plot between 17 and 02.
Please help!
Thanks

Python/Matplotlib - How to plot a vector sum on top of a circular histogram?

I am trying to plot both a circular histogram and a vector (overlapping) on the same polar plot, but cannot get the vector to show up.
Basically, my data set consists of the times at which unitary events occur during a repeating cycle. This data is in the array "all_phases", which is just a list of degree values for each of these events. I want to show (1) the overall distribution of events w/ a circular histogram (bins corresponding to degree ranges) and (2) a vector sum as a measure of the coherence of all of these values (treating each event as a unit vector).
I can plot either one of these things individually on the subplot titled "histo", but when I try to plot both, only the histogram shows up. I have tried playing with the z-indexes of both objects to no use. The code is:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
import math
array = np.array
all_phases = [array([-38.24240218]), array([-120.51570738]), array([-23.70224663]),
array([114.9540152]), array([ 2.94523445]), array([-2.16112692]), array([-18.72274284]),
array([13.2292216]), array([-95.5659992]), array([15.69046269]), array([ 51.12022047]),
array([-89.10567276]), array([ 41.77283949]), array([-9.92584921]), array([-7.59680678]),
array([166.71824996]), array([-178.94642752]), array([-23.75819463]), array([38.69481261]),
array([-52.26651244]), array([-57.40976514]), array([33.68226762]), array([-122.1818295]),
array([ 10.17007425]), array([-38.03726335]),array([44.9504975]), array([ 134.63972923]),
array([ 63.02516932]),array([-106.54049292]), array([-25.6527599])]
number_bins = 60
bin_size = 360/number_bins
cluster_num = 1
counts, theta = np.histogram(all_phases, np.arange(-180, 180 + bin_size, bin_size), density=True)
theta = theta[:-1]+ bin_size/2.
theta = theta * np.pi / 180
a_deg = map(lambda x: np.ndarray.item(x), all_phases)
a_rad = map(lambda x: math.radians(x), a_deg)
a_cos = map(lambda x: math.cos(x), a_rad)
a_sin = map(lambda x: math.sin(x), a_rad)
uv_x = sum(a_cos)/len(a_cos)
uv_y = sum(a_sin)/len(a_sin)
uv_radius = np.sqrt((uv_x*uv_x) + (uv_y*uv_y))
uv_phase = np.angle(complex(uv_x, uv_y))
"""
plot histogram and vector sum
"""
fig = plt.figure()
ax1 = fig.add_axes([0.1, 0.16, 0.05, 0.56])
histo = fig.add_subplot(111, polar=True)
histo.yaxis.set_ticks(())
histo.arrow(0,0,0.11, 1, head_width=.01, zorder=2)
plt.suptitle("Phase distribution for Neuron #" + str(cluster_num), fontsize=15, y=.94)
plt.subplots_adjust(bottom=0.12, right=0.95, top=0.78, wspace=0.4)
width = (2*np.pi) / number_bins
bars = histo.bar(theta, counts, width = width, bottom=0.002)
for r, bar in zip(counts, bars):
bar.set_facecolor(plt.cm.jet(r / max(counts)))
bar.set_alpha(0.7)
bar.set_zorder(1)
norm = matplotlib.colors.Normalize(vmin (counts.min())*len(all_phases)*bin_size, vmax=(counts.max())*len(all_phases)*bin_size)
cb1 = matplotlib.colorbar.ColorbarBase(ax1, cmap=plt.cm.jet,
orientation='vertical', norm=norm, alpha=0.4,
ticks=np.arange(0, (counts.max())*len(all_phases)*bin_size)+1, )
cb1.ax.tick_params(labelsize=9)
cb1.solids.set_rasterized(True)
cb1.set_label("# spikes")
cb1.ax.yaxis.set_label_position('left')
plt.show()
cluster_num = cluster_num + 1
vs_radius and vs_phase are the parameters for the vector sum arrow I want to put on the polar plot, which I end up calling w/ histo.arrow().
My suspicion is that it might have something to do with trying to put two things on a subplot object?
Any help or thoughts would be very much appreciated!!
The problem is that the FancyArrow that is used by Axes.arrow() does not play well with polar plots.
Instead, you could use the annotate() function to draw a simple arrow that works better in the case of polar plots.
for example:
# Compute pie slices
N = 20
theta = np.linspace(0.0, 2 * np.pi, N, endpoint=False)
radii = 10 * np.random.rand(N)
width = np.pi / 4 * np.random.rand(N)
ax = plt.subplot(111, projection='polar')
bars = ax.bar(theta, radii, width=width, bottom=0.0)
# Use custom colors and opacity
for r, bar in zip(radii, bars):
bar.set_facecolor(plt.cm.viridis(r / 10.))
bar.set_alpha(0.5)
v_angle = 0.275*np.pi
v_length = 4
ax.annotate('',xy=(v_angle, v_length), xytext=(v_angle,0), xycoords='data', arrowprops=dict(width=5, color='red'))
plt.show()
As a general rule, when you deal with polar plot, you have to work just as if you were working with a linear plot. That is to say, you shouldn't try to draw your arrow from (0,0) but rather from (uv_phase, 0)
fig, ax = plt.subplots()
bars = ax.bar(theta, radii, width=width, bottom=0.0)
# Use custom colors and opacity
for r, bar in zip(radii, bars):
bar.set_facecolor(plt.cm.viridis(r / 10.))
bar.set_alpha(0.5)
ax.annotate('',xy=(v_angle, v_length), xytext=(v_angle,0), xycoords='data', arrowprops=dict(width=5, color='red'))

Contour density plot in matplotlib using polar coordinates

From a set of angle (theta) and radius (r) I drew a scatter plot using matplotlib:
fig = plt.figure()
ax = plt.subplot(111, polar=True)
ax.scatter(theta, r, color='None', edgecolor='red')
ax.set_rmax(1)
plt.savefig("polar.eps",bbox_inches='tight')
Which gave me this figure
I now want to draw the density contour map on top of that, so I tried:
fig = plt.figure()
ax = plt.subplot(111, polar=True)
H, theta_edges, r_edges = np.histogram2d(theta, r)
cax = ax.contourf(theta_edges[:-1], r_edges[:-1], H, 10, cmap=plt.cm.Spectral)
ax.set_rmax(1)
plt.savefig("polar.eps",bbox_inches='tight')
Which gave me the following results that is obviously not what I wanted to do.
What am I doing wrong ?
I think that the solution to your problem is to define the bins arrays for your histogram (for instance a linspaced array between 0 and 2pi for theta and between 0 and 1 for r). This can be done with the bins or range arguments of function numpy.histogram
I you do so, make sure that the theta values are all between 0 and 2pi by plotting theta % (2 * pi) instead of theta.
Finally, you may choose to plot the middle of the bin edges instead of the left side of the bins as done in your example (use 0.5 * (r_edges[1:] + r_edges[:-1]) instead of r_edges[:-1])
below is a suggestion of code
import matplotlib.pyplot as plt
import numpy as np
#create the data
r1 = .2 + .2 * np.random.randn(200)
theta1 = 0. + np.pi / 7. * np.random.randn(len(r1))
r2 = .8 + .2 * np.random.randn(300)
theta2 = .75 * np.pi + np.pi / 7. * np.random.randn(len(r2))
r = np.concatenate((r1, r2))
theta = np.concatenate((theta1, theta2))
fig = plt.figure()
ax = plt.subplot(111, polar=True)
#define the bin spaces
r_bins = np.linspace(0., 1., 12)
N_theta = 36
d_theta = 2. * np.pi / (N_theta + 1.)
theta_bins = np.linspace(-d_theta / 2., 2. * np.pi + d_theta / 2., N_theta)
H, theta_edges, r_edges = np.histogram2d(theta % (2. * np.pi), r, bins = (theta_bins, r_bins))
#plot data in the middle of the bins
r_mid = .5 * (r_edges[:-1] + r_edges[1:])
theta_mid = .5 * (theta_edges[:-1] + theta_edges[1:])
cax = ax.contourf(theta_mid, r_mid, H.T, 10, cmap=plt.cm.Spectral)
ax.scatter(theta, r, color='k', marker='+')
ax.set_rmax(1)
plt.show()
which should result as

Categories

Resources