Issue with polar plot - python

I am trying to plot some values on a polar plot. The code is as follows:
def plot_values(data):
bridge = CvBridge()
fig = figure(figsize=(4,4))
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True)
w,h = fig.canvas.get_width_height()
plot([0,0],[0,1], color=(0.5,0.5,0.5), linewidth = 3)
plot([1*pi/2,1*pi/2],[0,1], color=(0.5,0.5,0.5), linewidth = 3)
plot([2*pi/2,2*pi/2],[0,1], color=(0.5,0.5,0.5), linewidth = 3)
plot([3*pi/2,3*pi/2],[0,1], color=(0.5,0.5,0.5), linewidth = 3)
HAS= len(HAS_names)*4
radii=np.repeat(data,HAS)
width = 2*np.pi/HAS
for i in range(0, len(HAS_names)):
radii[i] = AUC_value[HAS_names[i]]/HAS_frames[i] #assignment done through lists
theta = np.arange(0.0, 2*np.pi, 2*np.pi/HAS)
bars = ax.bar(theta, radii, width=width, bottom=0.0)
xT=PLT.xticks()[0]
xL=['','HAS','','HAD','','TAS','','TAD']
PLT.xticks(xT, xL)
for r,bar in zip(radii, bars):
bar.set_facecolor( cm.jet(r/10.))
bar.set_alpha(0.5)
fig.canvas.draw()
canvas = PLT.get_current_fig_manager().canvas
canvas.draw()
I get the output as follows:
I am trying to plot the values occurring in the quadrant labeled HAS and rest on the region need to blank. I tried removing radii=np.repeat(data,HAS) and declare it as global variable within the function itself, but I needed to assign some value to radii initially before the for loop.
Any help is appreciated.

Related

How to create a horizontal histogram the other way around?

The gallery of matplotlib has a 2D scatter plot with two adjacent histograms for the marginal distribution of x and y values at the top and right, respectively. I want to modify that to show the histogram of y values on the left (instead of the right) but also oriented towards the scatter plot.
All I managed so far was to merely move it from the right to the left (see below), but not re-orientate it towards the scatter plot. How can I achieve that?
Here is my code:
import numpy as np
import matplotlib.pyplot as plt
# Fixing random state for reproducibility
np.random.seed(19680801)
# some random data
x = np.random.randn(1000)
y = np.random.randn(1000)
def scatter_hist(x, y, ax, ax_histx, ax_histy):
# no labels
ax_histx.tick_params(axis="x", labelbottom=False)
ax_histy.tick_params(axis="y", labelleft=True,labelright=False)
ax.tick_params(axis="y", left=False,labelleft=False,right=True,labelright=True)
# the scatter plot:
ax.scatter(x, y)
# now determine nice limits by hand:
binwidth = 0.25
xymax = max(np.max(np.abs(x)), np.max(np.abs(y)))
lim = (int(xymax/binwidth) + 1) * binwidth
bins = np.arange(-lim, lim + binwidth, binwidth)
ax_histx.hist(x, bins=bins)
ax_histy.hist(y, bins=bins, orientation='horizontal')
# definitions for the axes
left, width = 0.3, 0.65
bottom, height = 0.1, 0.65
spacing = 0.005
rect_scatter = [left, bottom, width, height]
rect_histx = [left, bottom + height + spacing, width, 0.2]
rect_histy = [left-spacing-0.2, bottom, 0.2, height]
# start with a square Figure
fig = plt.figure(figsize=(8, 8))
ax = fig.add_axes(rect_scatter)
ax_histx = fig.add_axes(rect_histx, sharex=ax)
ax_histy = fig.add_axes(rect_histy, sharey=ax)
# use the previously defined function
scatter_hist(x, y, ax, ax_histx, ax_histy)
plt.show()
and here the result:
This can be achieved by setting the y-axis limit in the opposite direction.
ax_histy.hist(y, bins=bins, orientation='horizontal')
ax_histy.set_xlim(100,0) # add

Python basemap: adjust legend height to map area

When plotting with basemap I can define the region where to plot the map (e.g. by ax1 = fig.add_axes([0.05, 0.05, 0.75, 0.9])) and where to plot the legend (e.g. by ax2 = fig.add_axes([0.85, 0.1, 0.05, 0.8])). Depending on the projection and the geographical region to plot, the map will not cover the area predefined by ax1 (in my case vertical extent is much smaller then 0.9) but my legend's vertical extent is still 0.8.
When I try to get minimum and maximum y figure coordinates by transFigure.inverted to scale the legends vertical extent so that it will have the same vertical extent as the map, I will not get the proper coordinates. They are still 0.05 and 0.95, though they should be larger/smaller due to the plot.
How to get the proper extent of the map in figure coordinates ?
Here is the code:
import matplotlib as mpl
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(figsize=(6, 6))
ax1 = fig.add_axes([0.05, 0.05, 0.75, 0.9])
# mp
lon0 = 11.35
lat0 = 50.0
lon1 = 15.5
lat1 = 50.0
lon2 = 15.5
lat2 = 52.0
lon3 = 11.35
lat3 = 52.0
lat_c = (lat0 + lat2) / 2.0
lon_c = (lon0 + lon2) / 2.0
map = Basemap(projection='merc', lat_0 = lat_c, lon_0 = lon_c,
resolution = 'h', area_thresh = 0.1,
llcrnrlon=lon0, llcrnrlat=lat0,
urcrnrlon=lon2, urcrnrlat=lat2)
map.drawcountries(zorder=10)
cmap = mpl.cm.get_cmap('jet')
# get min and max value for legend test
min_overall = 0.0
max_overall = 10.0
# plot blue dots for predefined map edges
x0_data, y0_data = map(lon0,lat0) # lower left
map.plot(x0_data, y0_data, 'bo', markersize=24)
x1_data, y1_data = map(lon1,lat1) # lower right
map.plot(x1_data, y1_data, 'bo', markersize=24)
x2_data, y2_data = map(lon2,lat2) # upper right
map.plot(x2_data, y2_data, 'bo', markersize=24)
x3_data, y3_data = map(lon3,lat3) # upper left
map.plot(x3_data, y3_data, 'bo', markersize=24)
# convert data to display coordinates
x0_y0_display = ax1.transData.transform((x0_data,y0_data))
inv_ax1_transData = ax1.transData.inverted()
x0_y0_data_test = inv_ax1_transData.transform(x0_y0_display)
x3_y3_display = ax1.transData.transform((x3_data,y3_data))
# convert display to figure coordinates
inv_fig_transFigure = fig.transFigure.inverted()
x0_y0_figure = inv_fig_transFigure.transform(x0_y0_display)
x3_y3_figure = inv_fig_transFigure.transform(x3_y3_display)
print(x0_y0_figure)
print(x3_y3_figure)
# convert data to display coordinates
x0_y0_display = ax1.transData.transform((x0_data,y0_data))
inv_ax1_transData = ax1.transData.inverted()
x0_y0_data_test = inv_ax1_transData.transform(x0_y0_display)
x3_y3_display = ax1.transData.transform((x3_data,y3_data))
print(x0_y0_figure)
print(x3_y3_figure)
# set colorbar
cmap = mpl.cm.get_cmap('jet')
ax2 = fig.add_axes([0.85, 0.1, 0.05, 0.8])
norm = mpl.colors.Normalize(vmin=min_overall, vmax=max_overall)
cb1 = mpl.colorbar.ColorbarBase(ax1, cmap=cmap,
norm=norm,
orientation='vertical')
cb1.set_label('Test')
plt.show()
OK, seems the solution seems to be quite simple:
Everything that is needed is stored in the Bbox for the first axis (ax1):
So adding to the code:
bb = ax1.get_position() # get Bbox from ax1
will give the Bbox instance for ax1, containing the edges for what is really needed to plot the map in figure coordinates
values_bb = bb.get_points()
will provide the coordinates for the Bbox edges
ax2 = fig.add_axes([0.85, values_bb[0,1], 0.05, values_bb[1,1]-values_bb[0,1]])
will scale the axis for the legend to the proper vertical extent

Embed a small radarchart into matplotlib plot

Right now I can create a radarchart as follows. Note that I made it a function just so that I can simply insert the function into my larger scatterplot more cleanly.
Radar Chart
def radarChart(PlayerLastName):
playerdf = dg.loc[dg['Player Name'] == name].index.tolist()[0]
#print(playerdf)
labels=np.array(['SOG', 'SH', 'G', 'A'])
stats=dg.loc[playerdf,labels].values
#print(stats)
# Set the angle of polar axis.
# And here we need to use the np.concatenate to draw a closed plot in radar chart.
angles=np.linspace(0, 2*np.pi, len(labels), endpoint=False)
# close the plot
stats=np.concatenate((stats,[stats[0]]))
angles=np.concatenate((angles,[angles[0]]))
fig = plt.figure()
ax = fig.add_subplot(111, polar=True)
ax.plot(angles, stats, 'o-', linewidth=1)
ax.fill(angles, stats, alpha=0.3)
ax.set_thetagrids(angles * 180/np.pi, labels)
#plt.title(PlayerLastName + ' vs. ' + namegame)
ax.grid(True)
return
I then want to put this figure in the bottom right of my scatter plot I have elsewhere. This other article does not provide me with any way to do this since my plot is circular. Any help would be great!
When I call radarChart('someones name') I get
I would really like to not have to save it as an image first and then put it in the plot.
I am not sure, what your desired output is. You should always provide a Minimal, Complete, and Verifiable example. Apart from this, I don't know, why a polar plot would be different from any other plot to create an inset:
import matplotlib.pyplot as plt
import numpy as np
#function for the polar plot
def radarChart(Player = "SOG", left = .3, bottom = .6, width = .2, height = .2):
#labels and positions
labels = np.array(['SOG', 'SH', 'G', 'A'])
angles = np.linspace(0, 360, len(labels), endpoint = False)
#inset position
ax = plt.axes([left, bottom, width, height], facecolor = "lightblue", polar = True)
#label polar chart
ax.set_thetagrids(angles, labels)
#polar chart title
plt.title(Player, loc = "left")
return ax
#main figure
x = np.linspace (-3, 1, 1000)
y = 2 * np.exp(3 - x) - 1
plt.plot(x, y)
plt.xlabel("x values")
plt.ylabel("y values")
plt.title("figure with polar insets")
#inset 1
ax = radarChart(Player = "A")
plt.scatter(x[::50], y[::50])
#inset 2
ax = radarChart(left = .6, bottom = .4, width = .2, height = .2)
plt.plot(x, y)
plt.show()
Sample output:

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'))

Laying out several plots in matplotlib + numpy

I am pretty new to python and want to plot a dataset using a histogram and a heatmap below. However, I am a bit confused about
How to put a title above both plots and
How to insert some text into bots plots
How to reference the upper and the lower plot
For my first task I used the title instruction, which inserted a caption in between both plots instead of putting it above both plots
For my second task I used the figtext instruction. However, I could not see the text anywhere in the plot. I played a bit with the x, y and fontsize parameters without any success.
Here is my code:
def drawHeatmap(xDim, yDim, plot, threshold, verbose):
global heatmapList
stableCells = 0
print("\n[I] - Plotting Heatmaps ...")
for currentHeatmap in heatmapList:
if -1 in heatmapList[currentHeatmap]:
continue
print("[I] - Plotting heatmap for PUF instance", currentHeatmap,"(",len(heatmapList[currentHeatmap])," values)")
# Convert data to ndarray
#floatMap = list(map(float, currentHeatmap[1]))
myArray = np.array(heatmapList[currentHeatmap]).reshape(xDim,yDim)
# Setup two plots per page
fig, ax = plt.subplots(2)
# Histogram
weights = np.ones_like(heatmapList[currentHeatmap]) / len(heatmapList[currentHeatmap])
hist, bins = np.histogram(heatmapList[currentHeatmap], bins=50, weights=weights)
width = 0.7 * (bins[1] - bins[0])
center = (bins[:-1] + bins[1:]) / 2
ax[0].bar(center, hist, align='center', width=width)
stableCells = calcPercentageStable(threshold, verbose)
plt.figtext(100,100,"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", fontsize=40)
heatmap = ax[1].pcolor(myArray, cmap=plt.cm.Blues, alpha=0.8, vmin=0, vmax=1)
cbar = fig.colorbar(heatmap, shrink=0.8, aspect=10, fraction=.1,pad=.01)
#cbar.ax.tick_params(labelsize=40)
for y in range(myArray.shape[0]):
for x in range(myArray.shape[1]):
plt.text(x + 0.5, y + 0.5, '%.2f' % myArray[y, x],
horizontalalignment='center',
verticalalignment='center',
fontsize=(xDim/yDim)*5
)
#fig = plt.figure()
fig = matplotlib.pyplot.gcf()
fig.set_size_inches(60.5,55.5)
plt.savefig(dataDirectory+"/"+currentHeatmap+".pdf", dpi=800, papertype="a3", format="pdf")
#plt.title("Heatmap for PUF instance "+str(currentHeatmap[0][0])+" ("+str(numberOfMeasurements)+" measurements; "+str(sizeOfMeasurements)+" bytes)")
if plot:
plt.show()
print("\t[I] - Done ...")
And here is my current output:
Perhaps this example will make things easier to understand. Things to note are:
Use fig.suptitle to add a title to the top of a figure.
Use ax[i].text(x, y, str) to add text to an Axes object
Each Axes object, ax[i] in your case, holds all the information about a single plot. Use them instead of calling plt, which only really works well with one subplot per figure or to modify all subplots at once. For example, instead of calling plt.figtext, call ax[0].text to add text to the top plot.
Try following the example code below, or at least read through it to get a better idea how to use your ax list.
import numpy as np
import matplotlib.pyplot as plt
histogram_data = np.random.rand(1000)
heatmap_data = np.random.rand(10, 100)
# Set up figure and axes
fig = plt.figure()
fig.suptitle("These are my two plots")
top_ax = fig.add_subplot(211) #2 rows, 1 col, 1st plot
bot_ax = fig.add_subplot(212) #2 rows, 1 col, 2nd plot
# This is the same as doing 'fig, (top_ax, bot_ax) = plt.subplots(2)'
# Histogram
weights = np.ones_like(histogram_data) / histogram_data.shape[0]
hist, bins = np.histogram(histogram_data, bins=50, weights=weights)
width = 0.7 * (bins[1] - bins[0])
center = (bins[:-1] + bins[1:]) / 2
# Use top_ax to modify anything with the histogram plot
top_ax.bar(center, hist, align='center', width=width)
# ax.text(x, y, str). Make sure x,y are within your plot bounds ((0, 1), (0, .5))
top_ax.text(0.5, 0.5, "Here is text on the top plot", color='r')
# Heatmap
heatmap_params = {'cmap':plt.cm.Blues, 'alpha':0.8, 'vmin':0, 'vmax':1}
# Use bot_ax to modify anything with the heatmap plot
heatmap = bot_ax.pcolor(heatmap_data, **heatmap_params)
cbar = fig.colorbar(heatmap, shrink=0.8, aspect=10, fraction=.1,pad=.01)
# See how it looks
plt.show()

Categories

Resources