I have run an MCMC chain for parameter estimation and have obtained accepted parameter values. I have 3 parameters and about 300 000 accepted values for each parameter.
I would now like to do a contour plot (doable) but in a 3-choose-2 triangular matrix type (a very specific requirement) Please see the attached photo contour-plot. The image shows some unrelated contour-plots from a paper but I want to have a similar type of plot for my parameters.
In total, I will have 6 plots: 3 single parameter histograms (like the top plot in each column in the image) and 3-choose-2 = 3 contour plots (as the lower triangle). Again, I need it to look as much as possible like the image.
How to achieve this on Python?
Update:
I have been able to write the below code which gives me a plot as my-plot-here.
However, I need an exact / as best as possible match with the type of figure 1. i.e. I need my xticks, yticks to show inside and not outside, the spaces between the figues to go away, a better way to show the left vertical plot labels (I'm currently using set_ylabel), the outer crooked contour level to go away, and have detailed (long-short) ticks along the x-axis of the pdfs.
def plot_histogram_fig(param, nbins, subplot_index, subplot_title):
counts, bins = np.histogram(param, bins = nbins)
plotcounts = np.insert(counts, -1, counts[-1])
bincentres = (bins[:-1] + bins[1:])/2
ax = fig.add_subplot(3, 3, subplot_index)
#ax.step(bins, plotcounts, where='post', c='y')
ax.plot(bincentres, counts, 'b')
#ax.plot([bins[np.argmax(counts)], bins[np.argmax(counts)]], [0, np.max(counts)], 'y')
ax.set_yticks([])
return [ax, counts, bincentres]
def plot_contour_fig(p1, p2, nbins, subplot_index):
H, xedges, yedges = np.histogram2d(p1, p2, bins = nbins)
Z = H.T
#Z_gauss = scipy.ndimage.gaussian_filter(Z, sigma = 0.8, order = 0) #filtering
X, Y = np.meshgrid(xedges[:-1], yedges[:-1])
ax = fig.add_subplot(3, 3, subplot_index)
im = ax.contour(X, Y, Z, levels = 6)
#plt.colorbar(im, ax = ax)
ax.clabel(im, inline=True, fontsize=4)
return [ax, H, xedges, yedges]
nbins = 50
fig = plt.figure(figsize = (10, 6))
#Histograms
ax1 = plot_histogram_fig(all_alphas, nbins, 1, subplot_title = 'alpha')
ax1[0].set_xticks([])
ax1[0].set_ylabel('alpha')
ax5 = plot_histogram_fig(all_betas, nbins, 5, subplot_title = 'beta')
ax5[0].set_xticks([])
ax9 = plot_histogram_fig(all_gammas, nbins, 9, subplot_title = 'gamma')
ax9[0].set_title('gamma', y = -0.5)
#Contours
ax4 = plot_contour_fig(all_alphas, all_betas, nbins, 4)
ax4[0].set_xticklabels([])
ax4[0].set_ylabel('beta')
ax7 = plot_contour_fig(all_alphas, all_gammas, nbins, 7)
ax7[0].set_title('alpha', y = -0.5)
ax7[0].set_ylabel('gamma')
ax8 = plot_contour_fig(all_betas, all_gammas, nbins, 8)
ax8[0].set_yticklabels([])
ax8[0].set_title('beta', y = -0.5)
plt.show()
all_alphas, all_betas, all_gammas are 1d numpy arrays storing the accepted parameter values.
Related
I have been following a tutorial on plotting F1 data over a circuit, color coded with the fastf1 library.
I wanted to add some extra's to the script to utilize the official team colors.
It works but the end result shows the colormap with the circuit covering the n bins 100.
In the picture above I used the same colormap as in the tutorial 'winter' so there is most certainly something wrong in my code.
However, the original tutorial gets a cleaner end result with only the circuit showing like this:
the tutorial in question uses a default colormap from matplotlib 'winter'. To get the team colors working I had to create a custom colormap from the 2 colors that are fetched from api.
Let's get into the code, I have tried so much and searched everywhere without success...
The custom colormap is build with this sequence of code I got from the matplotlib docs.
# Create custom colormap
teamcolor1 = to_rgb('{}'.format(team1_color))
teamcolor2 = to_rgb('{}'.format(team2_color))
colors = [teamcolor1, teamcolor2]
n_bins = [3, 6, 10, 100]
cmap_name = 'colors'
fig, axs = plt.subplots(2, 2, figsize=(6, 9))
fig.subplots_adjust(left=0.02, bottom=0.06, right=0.95, top=0.94, wspace=0.05)
x = np.arange(0, np.pi, 0.1)
y = np.arange(0, 2 * np.pi, 0.1)
X, Y = np.meshgrid(x, y)
Z = np.cos(X) * np.sin(Y) * 10
for n_bin, ax in zip(n_bins, axs.ravel()):
colormap = LinearSegmentedColormap.from_list(cmap_name, colors, N=n_bin)
im = ax.imshow(Z, interpolation='nearest', origin='lower', cmap=colormap)
ax.set_title("N bins: %s" % n_bin)
fig.colorbar(im, ax=ax)
cm.register_cmap(cmap_name, colormap)
I register the colormap to easily call it later in the script with get_cmap.
The eventual plotting of the circuit is done in this piece of code:
x = np.array(telemetry['X'].values)
y = np.array(telemetry['Y'].values)
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
fastest_driver_array = telemetry['Fastest_driver_int'].to_numpy().astype(float)
cmap = cm.get_cmap('winter', 2)
lc_comp = LineCollection(segments, norm=plt.Normalize(1, cmap.N+1), cmap=cmap)
lc_comp.set_array(fastest_driver_array)
lc_comp.set_linewidth(5)
plt.rcParams['figure.figsize'] = [18, 10]
plt.gca().add_collection(lc_comp)
plt.axis('equal')
plt.tick_params(labelleft=False, left=False, labelbottom=False, bottom=False)
cbar = plt.colorbar(mappable=lc_comp, boundaries=np.arange(1, 4))
cbar.set_ticks(np.arange(1.5, 9.5))
cbar.set_ticklabels(['{}'.format(driver1), '{}'.format(driver2)])
plt.savefig(
'{}_'.format(year) + '{}_'.format(driver1) + '{}_'.format(driver2) + '{}_'.format(circuit) + '{}.png'.format(
session), dpi=300)
plt.show()
This is where I think things go wrong, but I'm unsure of what is going wrong. I guess it has to do with how I use the colormap. But everything I changed broke the whole script.
As I don't have a lot of experience with matplotlib, it's getting very complicated.
As I don't want this question to be overly long the whole code can be read here:
https://gist.github.com/platinaCoder/7b5be22405f2003bd577189692a2b36b
Instead of creating a whole custome cmap, I got rid of this piece of code:
# Create custom colormap
teamcolor1 = to_rgb('{}'.format(team1_color))
teamcolor2 = to_rgb('{}'.format(team2_color))
colors = [teamcolor1, teamcolor2]
n_bins = [3, 6, 10, 100]
cmap_name = 'colors'
fig, axs = plt.subplots(2, 2, figsize=(6, 9))
fig.subplots_adjust(left=0.02, bottom=0.06, right=0.95, top=0.94, wspace=0.05)
x = np.arange(0, np.pi, 0.1)
y = np.arange(0, 2 * np.pi, 0.1)
X, Y = np.meshgrid(x, y)
Z = np.cos(X) * np.sin(Y) * 10
for n_bin, ax in zip(n_bins, axs.ravel()):
colormap = LinearSegmentedColormap.from_list(cmap_name, colors, N=n_bin)
im = ax.imshow(Z, interpolation='nearest', origin='lower', cmap=colormap)
ax.set_title("N bins: %s" % n_bin)
fig.colorbar(im, ax=ax)
cm.register_cmap(cmap_name, colormap)
and replaced cmap = cm.get_cmap('colors', 2) with cmap = cm.colors.ListedColormap(['{}'.format(team1_color), '{}'.format(team2_color)])
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'))
I have an patch collection that I'd like to display a color map for. Because of some manipulations I do on top of the colormap, it's not possible for me to define it using a matplotlib.colorbar instance. At least not as far as I can tell; doing so strips some manipulations I do with my colors that blank out patches lacking data:
cmap = matplotlib.cm.YlOrRd
colors = [cmap(n) if pd.notnull(n) else [1,1,1,1]
for n in plt.Normalize(0, 1)([nullity for _, nullity in squares])]
# Now we draw.
for i, ((min_x, max_x, min_y, max_y), _) in enumerate(squares):
square = shapely.geometry.Polygon([[min_x, min_y], [max_x, min_y],
[max_x, max_y], [min_x, max_y]])
ax0.add_patch(descartes.PolygonPatch(square, fc=colors[i],
ec='white', alpha=1, zorder=4))
So I define a matplotlib.colorbar.ColorbarBase instance instead, which works:
matplotlib.colorbar.ColorbarBase(ax1, cmap=cmap, orientation='vertical',
norm=matplotlib.colors.Normalize(vmin=0, vmax=1))
Which results in e.g.:
The problem I have is that I want to reduce the size of this colorbar (specifically, the shrink it down to a specific vertical size, say, 500 pixels), but I don't see any obvious way of doing this. If I had a colorbar instance, I could adjust this easily using its axis property arguments, but ColorbarBase lacks these.
For further reference:
The example my implementation is based on.
The source code in question (warning: lengthy).
The size and shape is defined with the axis. This is a snippet from code I have where I group 2 plots together and add a colorbar at the top independently. I played with the values in that add_axes instance until I got a size that worked for me:
cax = fig.add_axes([0.125, 0.925, 0.775, 0.0725]) #has to be as a list - starts with x, y coordinates for start and then width and height in % of figure width
norm = mpl.colors.Normalize(vmin = low_val, vmax = high_val)
mpl.colorbar.ColorbarBase(cax, cmap = self.cmap, norm = norm, orientation = 'horizontal')
The question may be a bit old, but I found another solution that can be of help for anyone who is not willing to manually create a colorbar axes for the ColorbarBase class.
The solution below uses the matplotlib.colorbar.make_axes class to create a dependent sub_axes from the given axes. That sub_axes can then be supplied for the ColorbarBase class for the colorbar creation.
The code is derived from the matplotlib code example describe in here
Here is a snippet code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
import matplotlib.colorbar as mcbar
from matplotlib import ticker
import matplotlib.colors as mcolors
# Make some illustrative fake data:
x = np.arange(0, np.pi, 0.1)
y = np.arange(0, 2 * np.pi, 0.1)
X, Y = np.meshgrid(x, y)
Z = np.cos(X) * np.sin(Y) * 10
colors = [(1, 0, 0), (0, 1, 0), (0, 0, 1)] # R -> G -> B
n_bins = [3, 6, 10, 100] # Discretizes the interpolation into bins
cmap_name = 'my_list'
fig, axs = plt.subplots(2, 2, figsize=(9, 7))
fig.subplots_adjust(left=0.02, bottom=0.06, right=0.95, top=0.94, wspace=0.05)
for n_bin, ax in zip(n_bins, axs.ravel()):
# Create the colormap
cm = LinearSegmentedColormap.from_list(cmap_name, colors, N=n_bin)
# Fewer bins will result in "coarser" colomap interpolation
im = ax.imshow(Z, interpolation='nearest', origin='lower', cmap=cm)
ax.set_title("N bins: %s" % n_bin)
cax, cbar_kwds = mcbar.make_axes(ax, location = 'right',
fraction=0.15, shrink=0.5, aspect=20)
cbar = mcbar.ColorbarBase(cax, cmap=cm,
norm=mcolors.Normalize(clip=False),
alpha=None,
values=None,
boundaries=None,
orientation='vertical', ticklocation='auto', extend='both',
ticks=n_bins,
format=ticker.FormatStrFormatter('%.2f'),
drawedges=False,
filled=True,
extendfrac=None,
extendrect=False, label='my label')
if n_bin <= 10:
cbar.locator = ticker.MaxNLocator(n_bin)
cbar.update_ticks()
else:
cbar.locator = ticker.MaxNLocator(5)
cbar.update_ticks()
fig.show()
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()
I am looking for a python plot on the lines of http://www.r-bloggers.com/visually-weighted-watercolor-plots-new-variants-please-vote/
This gives the equivalent of the standard deviation bands:
# generate random variables
x,y = generate_random()
# bin the values and determine the envelopes
df = bin_by(x, y, nbins=25, bins = None)
###
# Plot 1
###
# determine the colors
cols = ['#EE7550', '#F19463', '#F6B176']
with plt.style.context('fivethirtyeight'):
# plot the 3rd stdv
plt.fill_between(df.x, df['5th'], df['95th'], alpha=0.7,color = cols[2])
plt.fill_between(df.x, df['10th'], df['90th'], alpha=0.7,color = cols[1])
plt.fill_between(df.x, df['25th'], df['75th'], alpha=0.7,color = cols[0])
# plt the line
plt.plot(df.x, df['median'], color = '1', alpha = 0.7, linewidth = 1)
# plot the points
plt.scatter(x, y, facecolors='white', edgecolors='0', s = 5, lw = 0.7)
plt.savefig('fig1.png', facecolor='white', edgecolor='none')
plt.show()
def bin_by(x, y, nbins=30, bins = None):
"""
Divide the x axis into sections and return groups of y based on its x value
"""
if bins is None:
bins = np.linspace(x.min(), x.max(), nbins)
bin_space = (bins[-1] - bins[0])/(len(bins)-1)/2
indicies = np.digitize(x, bins + bin_space)
Bit of a discussion and link to my Github from my blog
cut-paste from my larger piece of code. It does not give what I want. I am posting per Evert's suggestion
fig = plt.figure(figsize=(8, 8))
plt.plot(xlist, ylist, 'b,')
plt.plot([0.0,0.8],[0.0,0.8],'y-')
data2d=zip(xlist,ylist)
bins = np.linspace(0.0, 0.2, 21)
medianlist=binpercentile(data2d,bins)
c10list=binpercentile(data2d,bins,0.1)
c90list=binpercentile(data2d,bins,0.9)
centerbins=[(x+y)/2.0 for x,y in zip(bins[:-1],bins[1:])]
centerbins.insert(0,0)
medianlist.insert(0,0)
c10list.insert(0,0)
c90list.insert(0,0)
plt.plot(centerbins,c10list,'r--')
plt.plot(centerbins,c90list,'r--')
plt.plot(centerbins,medianlist,'r-')
imagefilename='%s.%s'%('.'.join(infile.split('.')[0:-1]),'diffmed.pdf')
plt.savefig(imagefilename)