How to format a polar contour plot in matplotlib - python

I have some sample code to make a polar contour plot:
import numpy as np
import matplotlib.pyplot as plt
azimuths = np.radians(np.linspace(0, 180, 90))
zeniths = np.arange(50, 5050, 50)
r, theta = np.meshgrid(zeniths, azimuths)
values = 90.0+5.0*np.random.random((len(azimuths), len(zeniths)))
fig, ax = plt.subplots(subplot_kw=dict(projection='polar'))
ax.set_theta_zero_location("W")
pp = plt.contourf(theta, r, values, label='tmp')
cbar = plt.colorbar(pp, orientation='vertical')
cbar.ax.set_ylabel('scale label')
plt.show()
which gives me something like:
...but I would like something more like this:
...with space in the middle, and only showing 0 to 180 degrees. Does anyone know of a convenient way to do this?

I'm not sure how convenient this is, but here's a hackable solution (taken from here):
import numpy as np
import mpl_toolkits.axisartist.floating_axes as floating_axes
from matplotlib.projections import PolarAxes
from mpl_toolkits.axisartist.grid_finder import FixedLocator, MaxNLocator, \
DictFormatter
import matplotlib.pyplot as plt
tr = PolarAxes.PolarTransform()
degree_ticks = lambda d: (d*np.pi/180, "%d$^\\circ$"%(360-d))
angle_ticks = map(degree_ticks, np.linspace(180, 360, 5))
grid_locator1 = FixedLocator([v for v, s in angle_ticks])
tick_formatter1 = DictFormatter(dict(angle_ticks))
tick_formatter2 = DictFormatter(dict(zip(np.linspace(1000, 6000, 6),
map(str, np.linspace(0, 5000, 6)))))
grid_locator2 = MaxNLocator(5)
gh = floating_axes.GridHelperCurveLinear(tr,
extremes=(2*np.pi, np.pi, 1000, 6000),
grid_locator1=grid_locator1,
grid_locator2=grid_locator2,
tick_formatter1=tick_formatter1,
tick_formatter2=tick_formatter2)
fig = plt.figure()
ax = floating_axes.FloatingSubplot(fig, 111, grid_helper=gh)
fig.add_subplot(ax)
azimuths = np.radians(np.linspace(180, 360, 90)) # added 180 degrees
zeniths = np.arange(1050, 6050, 50) # added 1000
r, theta = np.meshgrid(zeniths, azimuths)
values = 90.0+5.0*np.random.random((len(azimuths), len(zeniths)))
aux_ax = ax.get_aux_axes(tr)
aux_ax.patch = ax.patch
ax.patch.zorder = 0.9
aux_ax.contourf(theta, r, values) # use aux_ax instead of ax
plt.show()
Note that (in order to get the space near the origin), you'll need to shift all your data points by 1000 in the radius direction and by pi in the theta direction (to get the lower hemisphere).
This yields:

Related

Plotting a line between 2-D point to its corresponding value in 3-D

I am trying to plot a dashed line in a 3-D Matplotlib plot. I would like to get a dashed line between each (x_pt, y_pt) to its corresponding z_pt.
from mpl_toolkits import mplot3d
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
import matplotlib
matplotlib.rcParams['mathtext.fontset'] = 'cm'
matplotlib.rcParams['axes.labelsize'] = 13
def z_function(x, y):
a = 1
b = 5.1/(4*np.pi**2)
c = 5/np.pi
r = 6
s = 10
t = 1/(8*np.pi)
return a*(y - b*x**2 + c*x - r)**2 + s*(1 - t)*np.cos(x) + s
x = np.linspace(-5, 10, 100)
y = np.linspace(0, 15, 100)
indexes = np.random.randint(0, 100, 5)
x_pt = x[indexes]
y_pt = y[indexes]
z_pt = z_function(x_pt, y_pt)
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.scatter(x_pt, y_pt, color='k', marker='x', depthshade=False)
ax.scatter(x_pt, y_pt, z_pt, color='k', marker='^', depthshade=False)
ax.set_xticks([-5, 0, 5, 10])
ax.set_yticks([0, 5, 10, 15])
ax.set_zticks([100, 200, 300])
ax.view_init(30, -120)
ax.set_xlabel(r'$x_1$')
ax.set_ylabel(r'$x_2$')
ax.zaxis.set_rotate_label(False)
ax.set_zlabel(r'$f(x)$', rotation=0)
ax.w_xaxis.pane.fill = False
ax.w_yaxis.pane.fill = False
ax.w_zaxis.pane.fill = False
plt.show()
Can anyone help me with this?
If I understand your problem correctly, you need to connect the point (x,y,0) to (x,y,z) like so:
for x_,y_,z_ in zip(x_pt, y_pt, z_pt):
ax.plot([x_,x_],[y_,y_],[0,z_], '--', c='grey')
It should be as simple as:
ax.plot(x_pt, y_pt, zs=z_pt, color='blue', marker='--', depthshade=False)
alternatively using:
ax.plot3D(x_pt, y_pt, z_pt, marker='--')
UPDATE:
You will need to create extra dummy coordinates for each point on the x-y axis, like so:
import numpy as np
n = 10 # number of points in the line
for i in len(x_pt):
x_range = np.linspace(0, x_pt[i], n)
y_range = np.linspace(0, y_pt[i], n)
ax.plot3D(x_range, y_range, [z_pt[i]]*n, marker='--')
NOTE: Untested

Changing subplot size for displaying ellipse correctly

I'm trying to plot the confidence ellipse for uniformly distributed points. When plotting the ellipse and the scatter plot using Matplotlib, I find that a portion of the ellipse is clipped by the subplot. I tried implementing other solutions suggested here on SO, given here, here, here and here. but am not able to correct the displayed plot.
How do I change the size of this subplot in order to correctly and completely display the ellipse?
Code for generating ellipse: multidimensional confidence intervals
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse
def eigsorted(cov):
vals, vecs = np.linalg.eigh(cov)
order = vals.argsort()[::-1]
return vals[order], vecs[:,order]
nstd = 2
fig = plt.figure(1, figsize=(12, 5))
#ax = plt.subplots(111)
ax = fig.add_subplot(111, aspect='equal')
test1 = np.random.uniform(40, 60, 1000)
test2 = np.random.uniform(100, 120, 1000)
cov = np.cov(test1, test2)
vals, vecs = eigsorted(cov)
theta = np.degrees(np.arctan2(*vecs[:,0][::-1]))
w, h = 2 * nstd * np.sqrt(vals)
ell = Ellipse(xy=(np.mean(test1), np.mean(test2)),
width=w, height=h,
angle=theta, color='black')
ell.set_facecolor('none')
ax.add_artist(ell)
plt.scatter(test1, test2)
plt.show()
As #ImportanceOfBeingEarnest rightly commented, a patch needed to be added for displaying the ellipse correctly. Updating the code below for future reference of others.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse
def eigsorted(cov):
vals, vecs = np.linalg.eigh(cov)
order = vals.argsort()[::-1]
return vals[order], vecs[:,order]
nstd = 2
fig = plt.figure(1, figsize=(12, 5))
#ax = plt.subplots(111)
ax = fig.add_subplot(111, aspect='equal')
test1 = np.random.uniform(40, 60, 1000)
test2 = np.random.uniform(100, 120, 1000)
cov = np.cov(test1, test2)
vals, vecs = eigsorted(cov)
theta = np.degrees(np.arctan2(*vecs[:,0][::-1]))
w, h = 2 * nstd * np.sqrt(vals)
ell = Ellipse(xy=(np.mean(test1), np.mean(test2)),
width=w, height=h,
angle=theta, color='black')
ell.set_facecolor('none')
#Corrected code for displaying ellipse correctly
ax.add_patch(ell)
plt.scatter(test1, test2)
plt.show()

Difficulty plotting a two dimensional lognorm surface

here is the code im using and I've also attached the output. I'd like to plot a two dimensional lognorm function as a 3d surface, the above code is supposed to do this however the output results in the entire plane being skewed rather than just the z values. any help or suggestions would be greatly appreciated.
dx = 90 - (-90)
dy = 90 - (-90)
c = [dx + dx/2.0, dy+dy/2.0]
z = np.zeros((400, 400))
x = np.linspace(-90, 90, 400)
y = x.copy()
for i in range(len(x)):
for j in range(len(y)):
p =[x[i], y[j]]
d = math.sqrt((p[0]-c[0])**2 + (p[1]-c[1])**2)
t = d
z[i][j] = lognorm.pdf(t, 1.2)
fig = plt.figure()
ax = fig.add_subplot(111, projection = '3d')
ax.plot_surface(x,y, z, cmap = 'viridis')
plt.show()
output of the provided code
ideally I'd like for it to look something like this.
this is the image here
I think you wanted to plot a 3D surface and here is an example:
#!/usr/bin/python3
# 2018/10/25 14:44 (+0800)
# Plot a 3D surface
from scipy.stats import norm, lognorm
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
xy = np.linspace(-5, 5, 400)
xx, yy = np.meshgrid(xy)
t = np.sqrt(xx**2 + yy**2)
zz = lognorm.pdf(t, 1.2)
fig = plt.figure()
ax = fig.add_subplot(111, projection = '3d')
ax.plot_surface(xx,yy, zz, cmap = 'viridis')
plt.show()

Python Matplotlib - imshow but with hexagons

Code is:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import colors
example_data = np.random.randint(4, size=(40,44))
cmap = colors.ListedColormap(['black', 'green', 'red', 'blue'])
bounds = [0,1,2,3,4]
norm = colors.BoundaryNorm(bounds, cmap.N)
img = plt.imshow(example_data, interpolation = 'nearest', origin = 'lower',
cmap = cmap, norm = norm)
Which gets me roughly what I want. What I am looking for is if there is a way to get the shape of each tile to be hexagonal rather than square? I think imshow might not be the way to do it but if there is a way you can change the default tile it would be good.
Thanks.
Here is a solution using patches:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import colors
import matplotlib.patches as mpatches
from matplotlib.collections import PatchCollection
nx = 40
ny = 44
example_data = np.random.randint(4, size=(nx,ny))
cmap = colors.ListedColormap(['black', 'green', 'red', 'blue'])
bounds = [0,1,2,3,4]
norm = colors.BoundaryNorm(bounds, cmap.N)
x = np.linspace(0, 1, nx)
y = np.linspace(0, 1, ny)
X, Y = np.meshgrid(x, y)
dx = np.diff(x)[0]
dy = np.diff(y)[0]
ds = np.sqrt(dx**2 + dy**2)
patches = []
for i in x:
for n, j in enumerate(y):
if n%2:
polygon = mpatches.RegularPolygon([i-dx/2., j], 6, 0.6*dx)
else:
polygon = mpatches.RegularPolygon([i, j], 6, 0.6*dx)
patches.append(polygon)
collection = PatchCollection(patches, cmap=cmap, norm=norm, alpha=1.0)
fig, ax = plt.subplots(1,1)
ax.add_collection(collection)
collection.set_array(example_data.ravel())
plt.show()
which looks like this,
Previous solution, it doesn't tessellate nicely and the hexagons are poorly shaped but you could use a scatter plot with coloured hexagons,
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import colors
nx = 40
ny = 44
example_data = np.random.randint(4, size=(nx,ny))
cmap = colors.ListedColormap(['black', 'green', 'red', 'blue'])
bounds = [0,1,2,3,4]
norm = colors.BoundaryNorm(bounds, cmap.N)
x = np.linspace(0, 1, nx)
y = np.linspace(0, 1, ny)
X, Y = np.meshgrid(x, y)
img = plt.scatter(X.ravel(),Y.ravel(),c=example_data.ravel(), cmap=cmap, norm=norm, s=360, marker=(6, 0), alpha=0.4)
plt.colorbar(img)
plt.show()
which looks like,

Custom scale for radial contour plot in matplotlib

I have a sample script to generate a polar contour plot in matplotlib:
import os
import math
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.axisartist.floating_axes as floating_axes
from matplotlib.projections import PolarAxes
from mpl_toolkits.axisartist.grid_finder import FixedLocator, MaxNLocator, DictFormatter
import random
# ------------------------------------ #
def setup_arc_radial_axes(fig, rect, angle_ticks, radius_ticks, min_rad, max_rad):
tr = PolarAxes.PolarTransform()
pi = np.pi
grid_locator1 = FixedLocator([v for v, s in angle_ticks])
tick_formatter1 = DictFormatter(dict(angle_ticks))
grid_locator2 = FixedLocator([a for a, b in radius_ticks])
tick_formatter2 = DictFormatter(dict(radius_ticks))
grid_helper = floating_axes.GridHelperCurveLinear(tr,
extremes=((370.0*(pi/180.0)), (170.0*(pi/180.0)), max_rad, min_rad),
grid_locator1=grid_locator1,
grid_locator2=grid_locator2,
tick_formatter1=tick_formatter1,
tick_formatter2=tick_formatter2,
)
ax1 = floating_axes.FloatingSubplot(fig, rect, grid_helper=grid_helper)
fig.add_subplot(ax1)
ax1.grid(True)
# create a parasite axes whose transData in RA, cz
aux_ax = ax1.get_aux_axes(tr)
aux_ax.patch = ax1.patch
ax1.patch.zorder=0.9
#ax1.axis["left"].set_ticklabel_direction("+")
return ax1, aux_ax
# ------------------------------------ #
# write angle values to the plotting array
angles = []
for mic_num in range(38):
angle = float(mic_num)*(180.0/36.0)*(math.pi/180.0)+math.pi
angles.append(angle)
# ------------------------------------ #
### these are merely the ticks that appear on the plot axis
### these don't actually get plotted
angle_ticks = range(0,190,10)
angle_ticks_rads = [a*math.pi/180.0 for a in angle_ticks]
angle_ticks_rads_plus_offset = [a+math.pi for a in angle_ticks_rads]
angle_ticks_for_plot = []
for i in range(len(angle_ticks)):
angle_ticks_for_plot.append((angle_ticks_rads_plus_offset[i],r"$"+str(angle_ticks[i])+"$"))
# ------------------------------------ #
scale = 1.0
aspect = 1.50
height = 8.0
fig = plt.figure(1, figsize=(height*aspect*scale, height*scale))
fig.subplots_adjust(wspace=0.3, left=0.05, right=0.95, top=0.84)
fig.subplots_adjust()
plot_real_min = 30.0
plot_real_max = 100.0
plot_fake_min = 0.0
plot_fake_max = 5000.0
rad_tick_increment = 500.0
radius_ticks = []
for i in range(int(plot_fake_min),int(plot_fake_max)+int(rad_tick_increment),int(rad_tick_increment)):
plot_fake_val = ((i-plot_fake_min)/(plot_fake_max-plot_fake_min))*(plot_real_max-plot_real_min)+plot_real_min
radius_ticks.append((plot_fake_val, r"$"+str(i)+"$"))
ax2, aux_ax2 = setup_arc_radial_axes(fig, 111, angle_ticks_for_plot, radius_ticks, plot_real_min, plot_real_max)
azimuths = np.radians(np.linspace(0, 180, 91))
azimuths_adjusted = [ (x + math.pi) for x in azimuths ]
zeniths = np.arange(0, 5050, 50)
zeniths_adjusted = [((x-plot_fake_min)/(plot_fake_max-plot_fake_min))*(plot_real_max-plot_real_min)+plot_real_min for x in zeniths]
r, theta = np.meshgrid(zeniths_adjusted, azimuths_adjusted)
values = 90.0+5.0*np.random.random((len(azimuths), len(zeniths)))
aux_ax2.contourf(theta, r, values)
cbar = plt.colorbar(aux_ax2.contourf(theta, r, values), orientation='vertical')
cbar.ax.set_ylabel('Contour Value [Unit]', fontsize = 16)
plt.suptitle('Plot Title ', fontsize = 24, weight="bold")
plt.legend(loc=3,prop={'size':20})
plt.xlabel('Angle [deg]', fontsize=20, weight="bold")
plt.ylabel('Frequency [Hz]', fontsize=20, weight="bold")
# plt.show()
plt.savefig('test.png', dpi=100)
plt.close()
This script will generate a plot that looks something like:
My question is how can I plot with an alternate color bar scale? Is it possible to define a custom scale?
Something like a blue-white-red scale where deltas around a central value can easily be shown would be the best, something like:
You can create a custom scale, but matplotlib already has what you want. All you have to do is add an argument to contourf:
aux_ax2.contourf(theta, r, values, cmap = 'bwr')
If you don't like bwr, coolwarm and seismic are also blue to red. If you need to reverse the scale, just add _r to the colormap name. You can find more colormaps here: http://matplotlib.org/examples/color/colormaps_reference.html
I can't run your code, but I think you could solve your problem this way:
from matplotlib import pyplot as plt
import matplotlib as mpl
f = plt.figure(figsize=(5,10))
ax = f.add_axes([0.01, 0.01, 0.4, 0.95])
#here we create custom colors
cmap = mpl.colors.LinearSegmentedColormap.from_list(name='Some Data',colors=['b', 'w','w', 'r'])
cb = mpl.colorbar.ColorbarBase(ax, cmap=cmap, orientation='vertical')
cb.set_label('Some Data')
plt.show()
And if linear way is not what you are looking for here is some other types:
http://matplotlib.org/api/colors_api.html#module-matplotlib.colors

Categories

Resources