Python plot data as a function of angle in a ring - python

How would I have to proceed to obtain the following plot in Python :
For each angle I have a given value and I would like to plot it in a ring, any ideas ?

Something along these lines might work for you
import matplotlib.pyplot as plt
from matplotlib.patches import Wedge
import numpy as np
theta = np.linspace(0, 360, 100)
fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(111, frameon=False)
for i in range(len(theta)-1):
ax.add_artist(
Wedge((0, 0), 1, theta[i], theta[i+1], width=0.2, color=str(np.random.rand()))
)
ax.set_xlim((-2,2))
ax.set_ylim((-2,2))
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
fig.show()

An alternative approach would be to create a pcolormesh inside a set of polar axes:
from matplotlib import pyplot as plt
import numpy as np
def polar_heat(values, thetas=None, radii=None, ax=None, fraction=0.3,
**kwargs):
values = np.atleast_2d(values)
if thetas is None:
thetas = np.linspace(0, 2*np.pi, values.shape[1]).reshape(1, -1)
if radii is None:
radii = np.linspace(0, 1, values.shape[0] + 1).reshape(-1, 1)
if ax is None:
fig, ax = plt.subplots(1, 1, subplot_kw={'polar':True})
mesh = ax.pcolormesh(thetas, radii, values, **kwargs)
radrange = radii.ptp()
ax.set_rlim(radrange * (1 - 1. / fraction), radrange)
ax.set_axis_off()
return mesh
For example:
thetas = np.linspace(0, 2*np.pi, 180)
values = np.sin(6 * thetas)
polar_heat(values, thetas, fraction=0.3)
You could easily have multiple nested rings:
values2 = np.vstack([np.sin(3 * thetas), np.cos(6 * thetas)])
polar_heat(values2, fraction=0.6)

You may want to use pie function from matplotlib.pyplot.
You can plot a standard pie chart and place a white circle in the center then, so that it looks like a donut diagram.
See this tutorial for an example of what I'm talking about.
You can also experiment with Vega (format for visualization), namely with Vincent library for Python. See examples with pie/donut charts here.

Related

How do I create curved bins in matplotlib polar 2D histogram?

I am plotting a polar 2d histogram in Python 3.7 using matplotlib and the following code (adapted from this answer to another question):
import numpy as np
import matplotlib.pyplot as plt
# input data
azimut = np.random.rand(3000)*2*np.pi
radius = np.random.rayleigh(9, size=3000)
# binning
rbins = np.linspace(0, radius.max(), 10)
abins = np.linspace(0, 2*np.pi, 10)
# histogram
hist, _, _ = np.histogram2d(azimut, radius, bins=(abins, rbins))
A, R = np.meshgrid(abins, rbins)
# plot
fig, ax = plt.subplots(subplot_kw=dict(projection="polar"))
pc = ax.pcolormesh(A, R, hist.T, cmap='inferno')
fig.colorbar(pc)
plt.show()
To produce the following plot:
Due to the larger bin sizes, the polar projection is appearing more like a polygon rather than a circle.
Is there any way to plot this so that the bins appear curved rather than straight? I.E. so that the plot is always circular, regardless of the bin size and doesn't become polygon-like when bins are larger?
A matplotlib solution would be preferable, but others are welcome.
Thanks very much for any help.
To get a rounded look, the mesh can be subdivided into more angles. Note that np.linspace(0, 2 * np.pi, 10) creates 9 bins (and 10 boundaries). For the subdivided mesh you need e.g. 90 bins, so 91 boundaries. The histogram values need to be repeated by the same factor.
The code below uses a different colormap for debugging purposes. An optional grid highlights the original boundaries.
import numpy as np
import matplotlib.pyplot as plt
# input data
azimut = np.random.rand(3000) * 2 * np.pi
radius = np.random.rayleigh(9, size=3000)
# binning
rbins = np.linspace(0, radius.max(), 7)
abins = np.linspace(0, 2 * np.pi, 10)
subdivs = 10
abins2 = np.linspace(0, 2 * np.pi, (len(abins) - 1) * subdivs + 1)
# histogram
hist, _, _ = np.histogram2d(azimut, radius, bins=(abins, rbins))
A1, R1 = np.meshgrid(abins, rbins)
A2, R2 = np.meshgrid(abins2, rbins)
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(10, 4), subplot_kw=dict(projection="polar"))
# plot with original mesh
pc1 = ax1.pcolormesh(A1, R1, hist.T, cmap='hsv')
ax1.tick_params(axis='y', labelcolor='white')
ax1.set_xticks(abins[:-1])
fig.colorbar(pc1, ax=ax1)
# plot with subdivided mesh
pc2 = ax2.pcolormesh(A2, R2, np.repeat(hist.T, subdivs, axis=1), cmap='hsv')
ax2.tick_params(axis='y', labelcolor='white')
ax2.set_xticks(abins[:-1])
ax2.set_yticks(rbins, minor=True)
ax2.grid(axis='x', color='white')
ax2.grid(axis='y', which='minor', color='white')
fig.colorbar(pc2, ax=ax2)
plt.tight_layout()
plt.show()

Rotate matplotlib colourmap

The ProPlot Python package adds additional features to the Matplotlib library, including colourmap manipulations. One feature that is particularly attractive to me is the ability to rotate/shift colourmaps. To give you an example:
import proplot as pplot
import matplotlib.pyplot as plt
import numpy as np
state = np.random.RandomState(51423)
data = state.rand(30, 30).cumsum(axis=1)
fig, axes = plt.subplots(ncols=3, figsize=(9, 4))
fig.patch.set_facecolor("white")
axes[0].pcolormesh(data, cmap="Blues")
axes[0].set_title("Blues")
axes[1].pcolormesh(data, cmap="Blues_r")
axes[1].set_title("Reversed Blues")
axes[2].pcolormesh(data, cmap="Blues_s")
axes[2].set_title("Rotated Blues")
plt.tight_layout()
plt.show()
In the third column, you see the 180° rotated version of Blues. Currently ProPlot suffers from a bug that doesn't allow the user to revert the plotting style to Matplotlib's default style, so I was wondering if there was an easy way to rotate a colourmap in Matplotlib without resorting to ProPlot. I always found cmap manipulations in Matplotlib a bit arcane, so any help would be much appreciated.
If what you are trying to do is shift the colormaps, this can be done (relatively) easily:
def shift_cmap(cmap, frac):
"""Shifts a colormap by a certain fraction.
Keyword arguments:
cmap -- the colormap to be shifted. Can be a colormap name or a Colormap object
frac -- the fraction of the colorbar by which to shift (must be between 0 and 1)
"""
N=256
if isinstance(cmap, str):
cmap = plt.get_cmap(cmap)
n = cmap.name
x = np.linspace(0,1,N)
out = np.roll(x, int(N*frac))
new_cmap = matplotlib.colors.LinearSegmentedColormap.from_list(f'{n}_s', cmap(out))
return new_cmap
demonstration:
x = np.linspace(0,1,100)
x = np.vstack([x,x])
cmap1 = plt.get_cmap('Blues')
cmap2 = shift_cmap(cmap1, 0.25)
fig, (ax1, ax2) = plt.subplots(2,1)
ax1.imshow(x, aspect='auto', cmap=cmap1)
ax2.imshow(x, aspect='auto', cmap=cmap2)
To reverse a ListedColormap, there is a built-in reversed() but for the intended rotation, we have to create our own function.
#fake data generation
import numpy as np
np.random.seed(123)
#numpy array containing x, y, and color
arr = np.random.random(30).reshape(3, 10)
from matplotlib import pyplot as plt
from matplotlib.colors import ListedColormap
def rotate_cm(co_map, deg=180):
#define a function where the colormap is rotated by a certain degree
#180° shifts by 50%, 360° no change
n = co_map.N
#if rotating in the opposite direction feels more intuitive, reverse the sign here
deg = -deg%360
if deg < 0:
deg += 360
cutpoint = n * deg // 360
new_col_arr = [co_map(i) for i in range(cutpoint, n)] + [co_map(i) for i in range(cutpoint)]
return ListedColormap(new_col_arr)
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(21,7))
#any listed colormap
my_cm = plt.cm.get_cmap("inferno")
#normal color map
cb1 = ax1.scatter(*arr[:2,:], c=arr[2,:], cmap=my_cm, marker="o")
plt.colorbar(cb1, ax=ax1)
ax1.set_title("regular colormap")
#reversed colormap
cb2 = ax2.scatter(*arr[:2,:], c=arr[2,:], cmap=my_cm.reversed(), marker="o")
plt.colorbar(cb2, ax=ax2)
ax2.set_title("reversed colormap")
#rotated colormap
cb3 = ax3.scatter(*arr[:2,:], c=arr[2,:], cmap=rotate_cm(my_cm, 90), marker="o")
#you can also combine the rotation with reversed()
#cb3 = ax3.scatter(*arr[:2,:], c=arr[2,:], cmap=rotate_cm(my_cm, 90).reversed(), marker="o")
plt.colorbar(cb3, ax=ax3)
ax3.set_title("colormap rotated by 90°")
plt.show()
Sample output:

Plotting Circular contour lines in matplotlib

I am trying to circular contour lines around an array of random values of radius. The result should be a bunch of concentric circles with different radius. However I am not too sure how to plot the theta so that for each radius, all values of theta is plotted to form a line.
import random
import numpy as np
r= sort(np.array([ random.random()*5 for i in arange(100) ]))
len(r)
theta = [t for t in linspace(0,2*pi,100)]
ax = plt.subplot(111, polar=True)
ax.plot(theta, r, 'o',color='r', linewidth=3)
ax.set_rmax(2.0)
ax.grid(True)
Thank you.
Here is a one-line addition that I think does what you want:
import random
import numpy as np
import matplotlib.pyplot as plt
r= np.sort(np.array([ random.random()*5 for i in np.arange(100) ]))
len(r)
theta = [t for t in np.linspace(0,2*np.pi,100)]
ax = plt.subplot(111, polar=True)
ax.plot(theta, r, 'o',color='r', linewidth=3)
ax.set_rmax(2.0)
ax.grid(True)
[ax.plot(theta, rcirc*np.ones(100)) for rcirc in r.max()*np.random.rand(5)]
plt.show()
A quick-and-dirty way to do it would be to use np.linspace to effectively draw a polygon (as I think you were attempting to do):
import numpy as np
from matplotlib import pyplot as plt
# some random radii
r = np.random.rand(10)
# 1000 angles linearly spaced between 0 and 2pi
t = np.linspace(0, 2 * np.pi, 1000)
# broadcast r against t to make each a (1000, 10) array
r, t = np.broadcast_arrays(r[None, :], t[:, None])
# plot the lines
fig, ax = plt.subplots(1, 1, subplot_kw={'polar':True})
ax.plot(t, r, '-')
I'm sure there must be a more elegant way to do this, though.

plot a donut with fill or fill_between use pyplot in matplotlib

I want to plot a donut and my script is
import numpy as np
import matplotlib.pyplot as plt
pi,sin,cos = np.pi,np.sin,np.cos
r1 = 1
r2 = 2
theta = np.linspace(0,2*pi,36)
x1 = r1*cos(theta)
y1 = r1*sin(theta)
x2 = r2*cos(theta)
y2 = r2*sin(theta)
How to get a donut with red filled area ?
You can traverse the boundaries of the area in closed curve, and use fill method to fill the area inside this closed area:
import numpy as np
import matplotlib.pyplot as plt
n, radii = 50, [.7, .95]
theta = np.linspace(0, 2*np.pi, n, endpoint=True)
xs = np.outer(radii, np.cos(theta))
ys = np.outer(radii, np.sin(theta))
# in order to have a closed area, the circles
# should be traversed in opposite directions
xs[1,:] = xs[1,::-1]
ys[1,:] = ys[1,::-1]
ax = plt.subplot(111, aspect='equal')
ax.fill(np.ravel(xs), np.ravel(ys), edgecolor='#348ABD')
plt.show()
This can easily be applied to any shape, for example, a pentagon inside or outside of a circle:
You can do this by plotting the top and bottom halves separately:
import numpy as np
import matplotlib.pyplot as plt
inner = 5.
outer = 10.
x = np.linspace(-outer, outer, 1000, endpoint=True)
yO = outer*np.sin(np.arccos(x/outer)) # x-axis values -> outer circle
yI = inner*np.sin(np.arccos(x/inner)) # x-axis values -> inner circle (with nan's beyond circle)
yI[np.isnan(yI)] = 0. # yI now looks like a boulder hat, meeting yO at the outer points
ax = plt.subplot(111)
ax.fill_between(x, yI, yO, color="red")
ax.fill_between(x, -yO, -yI, color="red")
plt.show()
Or you can use polar coordinates, though whether this is beneficial depends on the broader context:
import numpy as np
import matplotlib.pyplot as plt
theta = np.linspace(0., 2.*np.pi, 80, endpoint=True)
ax = plt.subplot(111, polar=True)
ax.fill_between(theta, 5., 10., color="red")
plt.show()
It's a bit of a hack but the following works:
import numpy as np
import matplotlib.pyplot as plt
pi,sin,cos = np.pi,np.sin,np.cos
r1 = 1
r2 = 2
theta = np.linspace(0,2*pi,36)
x1 = r1*cos(theta)
y1 = r1*sin(theta)
x2 = r2*cos(theta)
y2 = r2*sin(theta)
fig, ax = plt.subplots()
ax.fill_between(x2, -y2, y2, color='red')
ax.fill_between(x1, y1, -y1, color='white')
plt.show()
It plots the whole area of your donut in red and then plots the central "hole" in white.
The answer given by tom10 is ten ;)
But if you want to define the circle (donut) origin is simple, just add the position x,y in the x, yI, yO and -yO and -yI, like this:
...
pos = [4,2]
ax.fill_between(x+pos[0], yI+pos[1], yO+pos[1], color=color)
ax.fill_between(x+pos[0], -yO+pos[1], -yI+pos[1], color=color)
...
REF Example: https://pastebin.com/8Ew4Vthb

Shade 'cells' in polar plot with matplotlib

I've got a bunch of regularly distributed points (θ = n*π/6, r=1...8), each having a value in [0, 1]. I can plot them with their values in matplotlib using
polar(thetas, rs, c=values)
But rather then having just a meagre little dot I'd like to shade the corresponding 'cell' (ie. everything until halfway to the adjacent points) with the colour corresponding to the point's value:
(Note that here my values are just [0, .5, 1], in really they will be everything between 0 and 1. Is there any straight-forward way of realising this (or something close enough) with matplotlib? Maybe it's easier to think about it as a 2D-histogram?
This can be done quite nicely by treating it as a polar stacked barchart:
import matplotlib.pyplot as plt
import numpy as np
from random import choice
fig = plt.figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True)
for i in xrange(12*8):
color = choice(['navy','maroon','lightgreen'])
ax.bar(i * 2 * np.pi / 12, 1, width=2 * np.pi / 12, bottom=i / 12,
color=color, edgecolor = color)
plt.ylim(0,10)
ax.set_yticks([])
plt.show()
Produces:
Sure! Just use pcolormesh on a polar axes.
E.g.
import matplotlib.pyplot as plt
import numpy as np
# Generate some data...
# Note that all of these are _2D_ arrays, so that we can use meshgrid
# You'll need to "grid" your data to use pcolormesh if it's un-ordered points
theta, r = np.mgrid[0:2*np.pi:20j, 0:1:10j]
z = np.random.random(theta.size).reshape(theta.shape)
fig, (ax1, ax2) = plt.subplots(ncols=2, subplot_kw=dict(projection='polar'))
ax1.scatter(theta.flatten(), r.flatten(), c=z.flatten())
ax1.set_title('Scattered Points')
ax2.pcolormesh(theta, r, z)
ax2.set_title('Cells')
for ax in [ax1, ax2]:
ax.set_ylim([0, 1])
ax.set_yticklabels([])
plt.show()
If your data isn't already on a regular grid, then you'll need to grid it to use pcolormesh.
It looks like it's on a regular grid from your plot, though. In that case, gridding it is quite simple. If it's already ordered, it may be as simple as calling reshape. Otherwise, a simple loop or exploiting numpy.histogram2d with your z values as weights will do what you need.
Well, it's fairly unpolished overall, but here's a version that rounds out the sections.
from matplotlib.pylab import *
ax = subplot(111, projection='polar')
# starts grid and colors
th = array([pi/6 * n for n in range(13)]) # so n = 0..12, allowing for full wrapping
r = array(range(9)) # r = 0..8
c = array([[random_integers(0, 10)/10 for y in range(th.size)] for x in range(r.size)])
# The smoothing
TH = cbook.simple_linear_interpolation(th, 10)
# Properly padding out C so the colors go with the right sectors (can't remember the proper word for such segments of wedges)
# A much more elegant version could probably be created using stuff from itertools or functools
C = zeros((r.size, TH.size))
oldfill = 0
TH_ = TH.tolist()
for i in range(th.size):
fillto = TH_.index(th[i])
for j, x in enumerate(c[:,i]):
C[j, oldfill:fillto].fill(x)
oldfill = fillto
# The plotting
th, r = meshgrid(TH, r)
ax.pcolormesh(th, r, C)
show()

Categories

Resources