Legend combined with Text, how to find the legend width and height - python

I would like to set legend and text boxes locations and styles exactly same, the latter especially to make text aligned.
import matplotlib.pyplot as plt
x = np.arange(10)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
for i in range(3):
ax.plot(x, i * x ** 2, label = '$y = %i x^2$'%i)
ax.set_title('example plot')
# Shrink the axis by 20% to put legend and text at the bottom
#+ of the figure
vspace = .2
box = ax.get_position()
ax.set_position([box.x0, box.y0 + box.height * vspace,
box.width, box.height * (1 - vspace)])
# Put a legend to the bottom left of the current axis
x, y = 0, 0
# First solution
leg = ax.legend(loc = 'lower left', bbox_to_anchor = (x, y), \
bbox_transform = plt.gcf().transFigure)
# Second solution
#leg = ax.legend(loc = (x, y)) , bbox_transform = plt.gcf().transFigure)
# getting the legend location and size properties using a code line I found
#+ somewhere in SoF
bb = leg.legendPatch.get_bbox().inverse_transformed(ax.transAxes)
ax.text(x + bb.width, y, 'some text', transform = plt.gcf().transFigure, \
bbox = dict(boxstyle = 'square', ec = (0, 0, 0), fc = (1, 1, 1)))
plt.show()
This should place the text at the right of the legend box but that's not what it does. And the two boxes are not vertically aligned.
The second solution does not actually anchoring the legend to the figure, but to the axes instead.

You can use the frame data to get the right width in order to position the Text() object correctly.
In the example below I had to apply a 1.1 factor for the width (this value I haven't found how to get, and if you don't apply the factor the text clashes with the legend).
Note also that you must plt.draw() before getting the right width value.
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(10)
fig = plt.figure(figsize=(3, 2))
ax = fig.add_subplot(1, 1, 1)
for i in range(3):
ax.plot(x, i*x**2, label=r'$y = %i \cdot x^2$'%i)
ax.set_title('example plot')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
x, y = 0.2, 0.5
leg = ax.legend(loc='lower left', bbox_to_anchor=(x, y),
bbox_transform=fig.transFigure, fontsize=8)
plt.draw()
f = leg.get_frame()
w0, h0 = f.get_width(), f.get_height()
inv = fig.transFigure.inverted()
w, h = inv.transform((w0, h0))
ax.text(x+w*1.1, y+h/2., 'some text', transform=fig.transFigure,
bbox=dict(boxstyle='square', ec=(0, 0, 0), fc=(1, 1, 1)),
fontsize=7)
fig.savefig('test.jpg', bbox_inches='tight')
for x, y = 0.2, 0.5:
for x, y = -0.3, -0.3:

Related

Want to plot a histogram showing zoom part of main graph

I want to plot a histogram showing the zoom part of the main graph(scatter plot)
Graph from below code Graph from below code
Ideal graph Ideal graph
I want to plot histogram showing it is some point of scatter plot but it creates a different plot
Here actual and Pred is dataframe
actual = [380.40191971 145.59225239 190.70737789 112.4604138
244.80801269
65.35493987 17.44314577 192.42304652 266.70685679 321.51874529
290.25216447 352.55512476 182.61866021 208.35782926 219.94210006
99.79159246 355.93408355 309.99909547 191.54331448 186.93046933
306.24333458 122.12239012 215.55874595 49.22826277 235.22153724 ]
pred = [354.82370437 160.74472764 193.68395055 143.28111271
239.29022664
98.48337116 74.63047227 203.70767313 244.81584894 297.74615263
276.67065251 312.58961576 194.0053793 217.6234544 217.89758764
153.87776083 341.69926994 280.82340828 205.21125304 212.45958412
292.26134849 136.84410903 206.75852888 93.80596594 252.11452717 ]
min_range=pred.min()-10
max_range=pred.max()+10
min_domain=actual.min()-10
max_domain=actual.max()+10
#scaling and creating scatter plot
plt.axes([0, 0, 4, 2])
plt.scatter(x=actual,y=pred, marker="o") #(y = predicted)
plt.gca().set_aspect('equal', adjustable='box')
plt.grid()
plt.xlabel('Actual Values', fontsize = 20)
plt.ylabel('Predicted Values', fontsize = 20)
#adding regression line
plt.plot([min_domain, max_domain], [min_range, max_range], color='g', linestyle='-', linewidth=1,label='regression')
#adding line passing minimum and maximum actual points
plt.plot([min_domain, max_domain],[min_domain, max_domain],color='r',linestyle='-',linewidth=1,label='actual point line')
#adding legend
plt.legend(loc='lower right')
#calculating x and y range
axes = plt.gca()
y_min, y_max = axes.get_ylim()
x_min, x_max = axes.get_xlim()
#Coordinates of interested area
#percentile = 10
#nth_percentile = np.percentile(actual,percentile)
bottom, left, width, height = 0, 0, 100,100
x_hist = x_min +(x_max - x_min)/9 #may have to change value 9
#calculating lines for selected area
x1, y1 = [left, x_hist], [bottom+height, (y_max + y_min)/2]
x2, y2 = [left + width, x_hist], [bottom + height, (y_max + y_min)/2]
L_act = []
L_pred = []
for x, y in zip(actual, pred):
if left <= x <= width+left:
if bottom<= y <= height + bottom:
L_act.append(x)
L_pred.append(y)
#adding rectangle for selected area
rect=mpatches.Rectangle((left, bottom),width, height, fill = False, color = "black",linewidth = 2)
plt.gca().add_patch(rect)
#adding lines to indicated the selected area
plt.plot(x1, y1, x2, y2, color = 'black', linewidth = 2)
#adding histogram
plt.axes([0.2, 1, .6, .6], facecolor='w')
plt.hist(L_act, 30)
plt.xticks([])
plt.yticks([])
plt.show()

How to plot just one label per ax.plot, when the argument of ax.plot is a list?

Having the following line in my plot code
ax.plot(x, pdf_individual, '--k', label = "single Gaussians")
, with pdf_individual being a list of lists, results in this picture:
Is there a way to just have "single Gaussians" once in the labels, instead of 6 times, which is the amount of single Gaussians for the Gaussian Mixture Model?
This is the whole post with the suggested solution
import matplotlib as mpl
import matplotlib.ticker as mtick
from matplotlib.lines import Line2D
mpl.rcParams['figure.dpi'] = 600
test_input = input_list # THIS IS A 1D LIST with a few hundred items
X = np.asarray(test_input).reshape(-1,1)
N = np.arange(1, 11)
models = [None for i in range(len(N))]
for i in range(len(N)):
models[i] = GaussianMixture(N[i]).fit(X)
# compute the AIC and the BIC
AIC = [m.aic(X) for m in models]
BIC = [m.bic(X) for m in models]
fig = plt.figure(figsize=(12, 4))
fig.subplots_adjust(left=0.1, right=0.9,
bottom=0.21, top=0.9, wspace=0.3)
ax = fig.add_subplot(131)
M_best = models[np.argmin(AIC)]
comp_count = str(M_best)
x = np.linspace(0, 0.1, 100)
logprob = M_best.score_samples(x.reshape(-1, 1))
responsibilities = M_best.predict_proba(x.reshape(-1, 1))
pdf = np.exp(logprob)
pdf_individual = responsibilities * pdf[:, np.newaxis]
left, width = .245, .5
bottom, height = .4, .5
right = left + width
top = bottom + height
plt.setp( ax.xaxis.get_majorticklabels(), rotation=-45, ha="left" )
ax.yaxis.set_major_formatter(mtick.PercentFormatter())
ax.hist(X, 30, density=True, histtype='stepfilled', alpha=0.4, label="Data")
ax.plot(x, pdf, '-k', color = "red", label='GMM')
for i, pdf_individual in enumerate(pdf_individual):
ax.plot(x, pdf_individual, '--k', label = "single Gaussians" if i == 0 else "")
#for pdf in pdf_individual[1:]: ax.plot(x, pdf, '--k')
ax.text(right, top, "Anzahl Komponenten: " + comp_count[-2],
horizontalalignment='center',
verticalalignment='bottom',
transform=ax.transAxes)
ax.set_xlabel('$x$')
ax.set_ylabel('$p(x)$')
plt.legend()
plt.show()
It results in this error:
ValueError: x and y must have same first dimension, but have shapes (100,) and (6,)
EDIT:
Putting
pdf_individual = np.transpose(pdf_individual)
makes the code above work

Matplotlib Title location

i have this several plots and want to correct the title name location. I want to make the Vertical Acceleration (y) on the middle left vertically and the Flare Time (x) on the middle bot horizontally also the Test Title on middle top. Basically I want to be able to move the label location.
Below is the code
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import NullFormatter
x = ip.RESULTS
y = Vert
xy = np.vstack([x,y])
z = gaussian_kde(xy)(xy)
idx = z.argsort()
x, y, z = x[idx], y[idx], z[idx]
nullfmt = NullFormatter() # no labels
# definitions for the axes
left, width = 0.1, 0.65
bottom, height = 0.1, 0.65
bottom_h = left_h = left + width + 0.02
rect_scatter = [left, bottom, width, height]
rect_histx = [left, bottom_h, width, 0.2]
rect_histy = [left_h, bottom, 0.2, height]
# start with a rectangular Figure
plt.figure(1, figsize=(8, 8))
axScatter = plt.axes(rect_scatter)
#plt.plot(np.unique(x), np.poly1d(np.polyfit(x, y, 1))(np.unique(x)))
#plt.plot(np.unique(x), np.poly1d(np.polyfit(x, y, 1))(np.unique(x)))
axHistx = plt.axes(rect_histx)
axHisty = plt.axes(rect_histy)
# no labels
axHistx.xaxis.set_major_formatter(nullfmt)
axHisty.yaxis.set_major_formatter(nullfmt)
# the scatter plot:
axScatter.scatter(x, y, c=z, s=50, edgecolor='')
# now determine nice limits by hand:
binwidth = 1
xymax = np.max([np.max(np.fabs(x)), np.max(np.fabs(y))])
lim = (int(xymax/binwidth) + 1) * binwidth
bins = np.arange(-lim, lim + binwidth, binwidth)
axHistx.hist(x)
axHisty.hist(y, orientation='horizontal')
plt.title('test title', fontsize=20)
axHisty.set_xlabel("Vertical Acceleration")
axHistx.set_xlabel("Flare Time")
and the results look like this. Any help would be appreciated
You have three Axes objects (plot rectangles to say it sloppy) in your graph: axScatter is your main chart in the bottom left. axHisty is the histogram on the right and axHistx is the histogram on the top. Your axis titles belong on the y- and x-axis of axScatter. So just do:
axScatter.set_ylabel('Vertical Acceleration')
axScatter.set_xlabel('Flare Time')
Based on your vague question I have no idea where you want the "test title", but just figure out which Axes object is best and give it an xlabel, ylabel or title.

Axis limits for scatter plot - Matplotlib

I'm having the same problem presented here, however, the proposed solution didn't work for me.
I'm plotting a set of data which the main plot have this pattern:
Which is a plot which axis limits varies from (-1, 1) in both x and y, with a margin set with this piece of code:
plt.figure()
plt.show(data)
## Add some margin
l, r, b, t = plt.axis()
dx, dy = r-l, t-b
plt.axis([l-0.1*dx, r+0.1*dx, b-0.1*dy, t+0.1*dy])
The problem is 'cause I have more "complex" plot in which some changes had to me made. This is the code that produces it:
def plot_quiver_singularities(min_points, max_points, vector_field_x, vector_field_y, file_path):
"""
Plot the singularities of vector field
:param file_path : the path to save the data
:param vector_field_x : the vector field x component to be plot
:param vector_field_y : the vector field y component to be plot
:param min_points : a set (x, y) of min points field
:param max_points : a set (x, y) of max points field
"""
fig = plt.figure(figsize=(8, 8))
ax = fig.add_axes([.13, .3, .6, .6])
## Plot quiver
x, y = numpy.mgrid[-1:1:100*1j, -1:1:100*1j]
m = numpy.sqrt(numpy.power(vector_field_x, 2) + numpy.power(vector_field_y, 2))
quiver = ax.quiver(x, y, vector_field_x, vector_field_y, m, zorder=1)
## Plot critical points
x = numpy.linspace(-1, 1, x_steps)
y = numpy.linspace(-1, 1, y_steps)
# Draw the min points
x_indices = numpy.nonzero(min_points)[0]
y_indices = numpy.nonzero(min_points)[1]
ax.scatter(x[x_indices], y[y_indices], marker='$\\circlearrowright$', s=100, zorder=2)
# Draw the max points
x_indices = numpy.nonzero(max_points)[0]
y_indices = numpy.nonzero(max_points)[1]
ax.scatter(x[x_indices], y[y_indices], marker='$\\circlearrowleft$', s=100, zorder=2)
## Put legends
marker_min = plt.Line2D((0, 0), (0, 0), markeredgecolor=(1.0, 0.4, 0.0), linestyle='',
marker='$\\circlearrowright$', markeredgewidth=1, markersize=10)
marker_max = plt.Line2D((0, 0), (0, 0), markeredgecolor=(0.2, 0.2, 1.0), linestyle='',
marker='$\\circlearrowleft$', markeredgewidth=1, markersize=10)
plt.legend([marker_min, marker_max], ['CW rot. center', 'CCW rot. center'], numpoints=1,
loc='center left', bbox_to_anchor=(1, 0.5))
quiver_cax = fig.add_axes([.13, .2, .6, .03])
fig.colorbar(quiver, orientation='horizontal', cax=quiver_cax)
## Set axis limits
plt.xlim(-1, 1)
plt.ylim(-1, 1)
## Add some margin
# l, r, b, t = plt.axis()
# dx, dy = r-l, t-b
# plt.axis([l-0.1*dx, r+0.1*dx, b-0.1*dy, t+0.1*dy])
plt.savefig(file_path + '.png', dpi=dpi)
plt.close()
This produces the following image:
As can be seen, the axis limits do not hold and I didn't found why yet.
Any help would be appreciated.
Thank you in advance.
I was able to solve the problem putting this piece of code
plt.xlim(-1, 1)
plt.ylim(-1, 1)
Right after calling scatter().
You can also set those to the ax object:
ax.set_xlim((-1,1))
ax.set_ylim((-1,1))

How to fit result of matplotlib.pyplot.contourf into circle?

Here is my code to plot some data:
from scipy.interpolate import griddata
from numpy import linspace
import matplotlib.pyplot as plt
meanR = [9.95184937, 9.87947708, 9.87628496, 9.78414422,
9.79365258, 9.96168969, 9.87537519, 9.74536093,
10.16686878, 10.04425475, 10.10444126, 10.2917172 ,
10.16745917, 10.0235203 , 9.89914 , 10.11263505,
9.99756449, 10.17861254, 10.04704248]
koord = [[1,4],[3,4],[1,3],[3,3],[2,3],[1,2],[3,2],[2,2],[1,1],[3,1],[2,1],[1,0],[3,0],[0,3],[4,3],[0,2],[4,2],[0,1],[4,1]]
x,y=[],[]
for i in koord:
x.append(i[0])
y.append(i[1])
z = meanR
xi = linspace(-2,6,300);
yi = linspace(-2,6,300);
zi = griddata((x, y), z, (xi[None,:], yi[:,None]), method='cubic')
CS = plt.contourf(xi,yi,zi,15,cmap=plt.cm.jet)
plt.scatter(x,y,marker='o',c='b',s=15)
plt.xlim(min(x),max(x))
plt.ylim(min(y),max(y))
plt.show()
In result we have:
How can I inscribe it in a circle? something like this
Because you don't seem to need any axes you can also use a normal projection, remove the axes and draw a circle. I had some fun and added some bonus ears, a nose and a color bar. I annotated the code, I hope it is clear.
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
import scipy.interpolate
import numpy
import matplotlib
import matplotlib.pyplot as plt
# close old plots
plt.close("all")
# some parameters
N = 300 # number of points for interpolation
xy_center = [2,2] # center of the plot
radius = 2 # radius
# mostly original code
meanR = [9.95184937, 9.87947708, 9.87628496, 9.78414422,
9.79365258, 9.96168969, 9.87537519, 9.74536093,
10.16686878, 10.04425475, 10.10444126, 10.2917172 ,
10.16745917, 10.0235203 , 9.89914 , 10.11263505,
9.99756449, 10.17861254, 10.04704248]
koord = [[1,4],[3,4],[1,3],[3,3],[2,3],[1,2],[3,2],[2,2],[1,1],[3,1],[2,1],[1,0],[3,0],[0,3],[4,3],[0,2],[4,2],[0,1],[4,1]]
x,y = [],[]
for i in koord:
x.append(i[0])
y.append(i[1])
z = meanR
xi = numpy.linspace(-2, 6, N)
yi = numpy.linspace(-2, 6, N)
zi = scipy.interpolate.griddata((x, y), z, (xi[None,:], yi[:,None]), method='cubic')
# set points > radius to not-a-number. They will not be plotted.
# the dr/2 makes the edges a bit smoother
dr = xi[1] - xi[0]
for i in range(N):
for j in range(N):
r = numpy.sqrt((xi[i] - xy_center[0])**2 + (yi[j] - xy_center[1])**2)
if (r - dr/2) > radius:
zi[j,i] = "nan"
# make figure
fig = plt.figure()
# set aspect = 1 to make it a circle
ax = fig.add_subplot(111, aspect = 1)
# use different number of levels for the fill and the lines
CS = ax.contourf(xi, yi, zi, 60, cmap = plt.cm.jet, zorder = 1)
ax.contour(xi, yi, zi, 15, colors = "grey", zorder = 2)
# make a color bar
cbar = fig.colorbar(CS, ax=ax)
# add the data points
# I guess there are no data points outside the head...
ax.scatter(x, y, marker = 'o', c = 'b', s = 15, zorder = 3)
# draw a circle
# change the linewidth to hide the
circle = matplotlib.patches.Circle(xy = xy_center, radius = radius, edgecolor = "k", facecolor = "none")
ax.add_patch(circle)
# make the axis invisible
for loc, spine in ax.spines.iteritems():
# use ax.spines.items() in Python 3
spine.set_linewidth(0)
# remove the ticks
ax.set_xticks([])
ax.set_yticks([])
# Add some body parts. Hide unwanted parts by setting the zorder low
# add two ears
circle = matplotlib.patches.Ellipse(xy = [0,2], width = 0.5, height = 1.0, angle = 0, edgecolor = "k", facecolor = "w", zorder = 0)
ax.add_patch(circle)
circle = matplotlib.patches.Ellipse(xy = [4,2], width = 0.5, height = 1.0, angle = 0, edgecolor = "k", facecolor = "w", zorder = 0)
ax.add_patch(circle)
# add a nose
xy = [[1.5,3], [2,4.5],[2.5,3]]
polygon = matplotlib.patches.Polygon(xy = xy, facecolor = "w", zorder = 0)
ax.add_patch(polygon)
# set axes limits
ax.set_xlim(-0.5, 4.5)
ax.set_ylim(-0.5, 4.5)
plt.show()
If you replace the part where you do the plotting with:
fig = plt.figure()
ax = fig.add_subplot(111, polar=True)
CS = ax.contourf(xi,yi,zi,15,cmap=plt.cm.jet)
ax.scatter(x,y,marker='o',c='b',s=15)
ax.set_xlim(min(x),max(x))
ax.set_ylim(min(y),max(y))
you get this
To get what you want, you have to rescale the x, y, xi, yi such that the image is centered in zero. You might also need to convert to polar coordinates. Now I don't have time to provide more info, but I hope that this helps you in getting started

Categories

Resources