Want to plot graph side by side - python

I wan to two plots side by side instead of this vertically, right now it's showing one by one
def scatter_plot(surrogate, building, actual, pred,index):
#calculating max and min x axis range
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, 2, 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)
plt.title(f'{building.idf}_{building.epw}_{variable} Scatter Plot of NN vs E+', fontsize= 25)
#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 error metrics
location = building.metadata['building_attributes']['Location']
building_type = building.idf
df = csv.loc[(csv['id'] == surrogate.surrogate_id) &
(csv['Location'] == location) & (csv['Building Type'] == building_type)]
rmse = df[f'{variable} RMSE'].values[0]
r2 = df[f'{variable} R2'].values[0]
#Adding Error metric annotations
textstr = '\n'.join((r'Total Error Metrics', r'$RMSE=%.2f$' % (rmse, ),r'$R2=%.2f$' % (r2, )))
props = dict(boxstyle='round', facecolor='ivory', alpha=0.5)
plt.text(max_range, max_domain, textstr, fontsize=20, verticalalignment='top', bbox=props)
#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, nth_percentile,nth_percentile
try:
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)
#calculating error metrics for selected area
rmse = RMSE(L_act, L_pred)
r2 = R2(L_act, L_pred)
#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([])
textstr = '\n'.join((r'Selected Section Error Metrics', r'$RMSE=%.2f$' % (rmse, ),r'$R2=%.2f$' % (r2, )))
props = dict(boxstyle='round', facecolor='ivory', alpha=0.8)
#adding error metrics annotations for selected area
axes = plt.gca()
y_min, y_max = axes.get_ylim()
x_min, x_max = axes.get_xlim()
plt.text(x_min + x_min/10, y_max - y_max/30, textstr, fontsize=10, verticalalignment='top', bbox=props)
except ValueError:
print("Selected section doesn't contain any data points")
plt.show()
I tried using a subplot but that didn't work
def s_plot(surrogate,building):
figure, axis = plt.subplots(1, 2)
actual, pred = np.array(surrogate.test_samples[variable].values[:]), np.array(surrogate.training_samples[variable].values[:])
actual_train, pred_train = np.array(surrogate.train_actual[variable].values[:]), np.array(surrogate.train_pred[variable].values[:])
data =[[actual,pred],[actual_train, pred_train]
for ax,i in zip(axes.flatten(),data):
scatter_plot(surrogate,building,i[0],i[1],ax)
Here I am using axes instead of plt but there are so many parameters that axes doesn't have such as gca, scaling using axes, etc. and I am not able to plot histogram by subplot use
Is there any way to plot this side by side

here is a simple example of using histogram with subplots:
def func(ax):
# example data
mu = 100 # mean of distribution
sigma = 15 # standard deviation of distribution
x = mu + sigma * np.random.randn(437)
num_bins = 50
# the histogram of the data
n, bins, patches = ax.hist(x, num_bins, density=True)
# add a 'best fit' line
y = ((1 / (np.sqrt(2 * np.pi) * sigma)) *
np.exp(-0.5 * (1 / sigma * (bins - mu))**2))
ax.plot(bins, y, '--')
ax.set_xlabel('Smarts')
ax.set_ylabel('Probability density')
ax.set_title(r'Histogram of IQ: $\mu=100$, $\sigma=15$')
fig, (ax1, ax2) = plt.subplots(1, 2)
func(ax1)
func(ax2)

Related

Saving Pyplot 3d plot to image

I've been trying to convert a 3d plot to a png image, but I keep getting weird artifacts with the plots texts and ticks. I don't know what I'm doing wrong
Here is my 3d plot function :
def plot_3d(frame, fig, title, _max, _min =0):
ax = fig.add_subplot(111, projection='3d')
dz = frame.flatten()
wherepos = np.where(dz>0)
dz = dz[wherepos]
dz -= (_min)
ys = np.concatenate([[i]*4 for i in range(4)])[wherepos]
xs = np.mod(range(16),4)[wherepos]
dx = 1
dy = 1
zs = np.zeros(len(wherepos)) + _min
# creating the plot
ax.bar3d(xs, ys, zs, dx,
dy, dz, color='green')
# setting title and labels
ax.set_title("3D bar plot")
ax.set_xlabel('column')
ax.set_ylabel('row')
ax.set_zlabel('obj 1 distance')
ax.set_xlim([0, 4])
# ax.set_ylim([0, 4])
ax.set_ylim([4,0])
ax.set_zlim([_min,_max])
ax.set_xticks(list(range(5)))
ax.set_yticks(list(range(5)))
# Plot with angle (pixel (4,0) in front)
ax.view_init(elev = 50 ,azim=-135)
# Plot with no angle (row 0 on top)
# ax.view_init(elev = 50 ,azim=-90)
plt.title(title)
This function is called there :
plot_3d(frame, fig, plot_name, _max, _min)
fig.canvas.draw()
img = np.frombuffer(fig.canvas.tostring_rgb(), dtype="uint8")
img = img.reshape(frameSize[1], frameSize[0], 3)
plt.imsave("test.png",img)
The result is as follows:
[Plot with weird artifacts]

Different shading under Seaborn Distplot

I'm trying to create plot with shadings which are based on this MIC(1) line.
Different shading above than beneath.
from scipy import stats
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
def createSkewDist(mean, sd, skew, size):
# calculate the degrees of freedom 1 required to obtain the specific skewness statistic, derived from simulations
loglog_slope=-2.211897875506251
loglog_intercept=1.002555437670879
df2=500
df1 = 10**(loglog_slope*np.log10(abs(skew)) + loglog_intercept)
# sample from F distribution
fsample = np.sort(stats.f(df1, df2).rvs(size=size))
# adjust the variance by scaling the distance from each point to the distribution mean by a constant, derived from simulations
k1_slope = 0.5670830069364579
k1_intercept = -0.09239985798819927
k2_slope = 0.5823114978219056
k2_intercept = -0.11748300123471256
scaling_slope = abs(skew)*k1_slope + k1_intercept
scaling_intercept = abs(skew)*k2_slope + k2_intercept
scale_factor = (sd - scaling_intercept)/scaling_slope
new_dist = (fsample - np.mean(fsample))*scale_factor + fsample
# flip the distribution if specified skew is negative
if skew < 0:
new_dist = np.mean(new_dist) - new_dist
# adjust the distribution mean to the specified value
final_dist = new_dist + (mean - np.mean(new_dist))
return final_dist
desired_mean = 30
desired_skew = 1.5
desired_sd = 20
final_dist = createSkewDist(mean=desired_mean, sd=desired_sd, skew=desired_skew, size=1000000)
# inspect the plots & moments, try random sample
fig, ax = plt.subplots(figsize=(12,7))
sns.distplot(final_dist,
hist=False,
ax=ax,
color='darkred',
kde_kws=dict(linewidth=4))
l1 = ax.lines[0]
# Get the xy data from the lines so that we can shade
x1 = l1.get_xydata()[:,0]
x1[0] = 0
y1 = l1.get_xydata()[:,1]
y1[0] = 0
ax.fill_between(x1,y1, color="lemonchiffon", alpha=0.3)
ax.set_ylim(0.0001,0.03)
ax.axhline(0.002, ls="--")
ax.set_xlim(1.5, 200)
ax.set_yticklabels([])
ax.set_xticklabels([])
trans = transforms.blended_transform_factory(
ax.get_yticklabels()[0].get_transform(), ax.transData)
ax.text(0,0.0025, "{}".format("MIC(1) = 1"), color="blue", transform=trans,
ha="right", va="top", fontsize = 12)
trans_2 = transforms.blended_transform_factory(
ax.get_xticklabels()[0].get_transform(), ax.transData)
ax.text(84,0, "{}".format("\n84"), color="darkred", transform=trans_2,
ha="center", va="top", fontsize = 12)
ax.text(1.5,0, "{}".format("\n0"), color="darkred", transform=trans_2,
ha="center", va="top", fontsize = 12)
ax.axvline(x = 84, ymin = 0, ymax = 0.03, ls = '--', color = 'darkred' )
ax.set_yticks([])
ax.set_xticks([])
ax.spines['top'].set_color(None)
ax.spines['right'].set_color(None)
ax.spines['left'].set_linewidth(2)
ax.spines['bottom'].set_linewidth(2)
ax.set_ylabel("Concentration [mg/L]", labelpad = 80, fontsize = 15)
ax.set_xlabel("Time [h]", labelpad = 80, fontsize = 15)
ax.set_title("AUC/MIC", fontsize = 20, pad = 30)
plt.annotate("AUC/MIC",
xy=(18, 0.02),
xytext=(18, 0.03),
arrowprops=dict(arrowstyle="->"), fontsize = 12);
;
That's what I have:
And that's what I'd like to have (it's done in paint, so forgive me :) ):
I was experimenting with fill_between and fill_betweenx. However, without any satisfying results. Definitely, run out of ideas. I'd really appreciate any help on this. Best wishes!
Your fill_between works as expected. The problem is that color="lemonchiffon" with alpha=0.3 is barely visible. Try to use a brighter color and/or a higher value for alpha.
So, this colors the part of the graph between zero and the kde curve.
Now, to create a different coloring above and below the horizontal line, where= and np.minimum can be used in fill_between:
pos_hline = 0.002
ax.fill_between(x1, pos_hline, y1, color="yellow", alpha=0.3, where=y1 > pos_hline)
ax.fill_between(x1, 0, np.minimum(y1, pos_hline), color="blue", alpha=0.3)
Without where=y1 > pos_hline, fill_between would also color the region above the curve where the curve falls below that horizontal line.
PS: Note that sns.histplot has been deprecated since Seaborn version 0.11. To only plot the kde curve, you can use sns.kdeplot:
sns.kdeplot(final_dist, ax=ax, color='darkred', linewidth=4)

How to change the format xticks labels and keep the colorbar of a heatmap

In this plot
inclination = np.pi/6
def power(inclination,phi):
h1=1.7
h2=0.5
D = np.arange(0.5, 12.0, 0.015)
r = np.sqrt((h1-h2)**2 + D**2)
freq = 865.7
lmb = 300/freq
H = D**2/(D**2+2*h1*h2)
theta = 4*np.pi*h1*h2/(lmb*D)
q_e = H**2*(np.sin(theta))**2 + (1 - H*np.cos(theta))**2
sigma = 1.94
N_1 = np.random.normal(0,sigma,D.shape)
rnd = 10**(-N_1/10)
F = 10
power=0.8
R,PHI = np.meshgrid(r,phi[1:-1])
alpha=inclination + np.arcsin((h1-h2)/R)
gain=3.136*(np.tan(alpha)*np.sin(np.pi/2*np.cos(alpha)*np.sin(PHI)))**2
y=10*np.log10( 1000*(power*gain*1.622*((lmb)**2) *0.5*1) / (((4*np.pi*R)**2) *1.2*1*F)*q_e*rnd )
return (R,PHI,y)
phi=np.linspace(0, np.pi,num=787)
x,y,z = power(np.pi/4,phi)
import cmocean
cmap = cmocean.cm.oxy
I would like to take out the characters x10^0 of the x ticks labels and show 2,3, 4, 6 ... and 10.
I have test a precedent post set ticks with logarithmic scale, but I cannot make it work and keep the colorbar of the heatmap.
EDIT
As suggested by #ImportanceOfBeingErnest, to plot the heatmap, I have changed the next lines
plt.contourf(x, y, z, 20, cmap=cmap)
cb=plt.colorbar();
plt.xlim(None, 12)
plt.ylim(0, np.pi)
plt.xlabel('Distance [m]', fontsize=12)
plt.ylabel('Phi [radians]', fontsize=12)
plt.xscale('log')
that plots this figure,
by this
fig1, ax1 = plt.subplots()
cs1 = ax1.contourf(x, y, z, 20, cmap=cmap)
fig1.colorbar(cs1,ax=ax1);
plt.xscale('log')
ax1.set_xlabel('Distance [m]', fontsize=12)
ax1.set_ylabel('Phi [radians]', fontsize=12)
#--- format y-labels in radians
y_pi = y/np.pi
unit = 0.25
y_tick = np.arange(0, 1 + unit, unit)
y_label = [r"$0$", r"$\frac{\pi}{4}$", r"$\frac{\pi}{2}$", r"$3\frac{\pi}{4}$", r"$\pi$"]
#y_label = [r"$" + format(r, ".2g")+ r"\pi$" for r in y_tick]
ax1.set_yticks(y_tick*np.pi)
ax1.set_yticklabels(y_label, fontsize=12)
#---
#--- x-labels removing the log format (i.e. 2x10^0 to 2)
ax1.set_xticks([2, 3, 4, 6, 10])
#ax1.get_xaxis().set_major_formatter(matplotlib.ticker.ScalarFormatter())
#ax1.get_xaxis().get_major_formatter().labelOnlyBase = False
ax1.set_xticklabels(["2", "3", "4", "6", "10"])
plots this figure,
which tests the solutions of set ticks with logarithmic scale and prints the desired labels but without removing the default log labels format.

How to put the scaling on the ticks in ternary plot instead of x and y axis

I am try to work out with my atomic composition with ternary phase diagram, here is my picture
I wish to put my scale to the ticks on the ternary phase diagram (i.e. those triangular axis) instead of x and y axis. Is there a ways to put the scale on the tick at triangular axis instead of axis x and y? How to remove the x-axis and y-axis while still maintain its labels?
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.tri as tri
def plot_ticks(start, stop, tick, n):
r = np.linspace(0, 1, n+1)
x = start[0] * (1 - r) + stop[0] * r
x = np.vstack((x, x + tick[0]))
y = start[1] * (1 - r) + stop[1] * r
y = np.vstack((y, y + tick[1]))
plt.plot(x, y, 'k', lw=1)
n = 5
tick_size = 0.1
margin = 0.05
# define corners of triangle
left = np.r_[0, 0]
right = np.r_[1, 0]
top = np.r_[0.5, np.sqrt(3)*0.576]
triangle = np.c_[left, right, top, left]
# define corners of triangle
left = np.r_[0, 0]
right = np.r_[1, 0]
top = np.r_[0.5, np.sqrt(3)*0.576]
triangle = np.c_[left, right, top, left]
# define vectors for ticks
bottom_tick = 0.8264*tick_size * (right - top) / n
right_tick = 0.8264*tick_size * (top - left) / n
left_tick = 0.8264*tick_size * (left - right) / n
# first load some data: format x1,x2,x3,value
test_data = np.array([[4,0,0,2.238],
[0,4,0,2.315],
[0,0,4,2.147],
[3,1,0,2.494],
[2,2,0,2.190],
[2,2,0,2.632],
[3,0,1,2.173],
[2,0,2,2.329],
[1,0,3,2.526],
[0,3,1,2.365],
[0,2,2,2.220],
[0,1,3,2.080],
[2,1,1,2.231],
[1,2,1,2.291],
[1,1,2,2.088]])
#Define twin axis
#ax = plt.gca()
fig, ax = plt.subplots()
plot_ticks(left, right, bottom_tick, n)
plot_ticks(right, top, right_tick, n)
plot_ticks(left, top, left_tick, n)
#ax2 = ax.twinx()
# barycentric coords: (a,b,c)
a=test_data[:,0]
b=test_data[:,1]
c=test_data[:,2]
# values is stored in the last column
v = test_data[:,-1]
# translate the data to cartesian corrds
x = 0.5 * ( 2.*b+c ) / ( a+b+c )
y = 0.576*np.sqrt(3) * c / (a+b+c)
# create a triangulation out of these points
T = tri.Triangulation(x,y)
# plot the contour
plt.tricontourf(x,y,T.triangles,v,cmap='jet')
# create the grid
corners = np.array([[0, 0], [1, 0], [0.5, np.sqrt(3)*0.576]])
triangle = tri.Triangulation(corners[:, 0], corners[:, 1])
# creating the grid
refiner = tri.UniformTriRefiner(triangle)
trimesh = refiner.refine_triangulation(subdiv=4)
#plotting the mesh and caliberate the axis
plt.triplot(trimesh,'k--')
#plt.title('Binding energy peratom of Al-Ti-Ni clusters')
ax.set_xlabel('Al-Ti',fontsize=12,color='black')
ax.set_ylabel('Ti-Ni',fontsize=12,color='black')
ax2 = ax.twinx()
ax2.set_ylabel('Al-Ni',fontsize=12,color='black')
plt.gcf().text(0.07, 0.05, 'Ti', fontsize=12,color='black')
plt.gcf().text(0.93, 0.05, 'Al', fontsize=12,color='black')
plt.gcf().text(0.5, 0.9, 'Ni', fontsize=12,color='black')
#set scale for axis
ax.set_xlim(1, 0)
ax.set_ylim(0, 1)
ax2.set_ylim(1, 0)
cax = plt.axes([0.75, 0.55, 0.055, 0.3])
plt.colorbar(cax=cax,format='%.3f')
plt.savefig("AID.png", dpi=1000)
plt.show()
As was mentioned in the comments you can make your own axis just by adding a text to the ticks you generate. Most of the time you need a little tweaking
to get the offsets right...
def plot_ticks(start, stop, tick, n, offset=(.0, .0)):
r = np.linspace(0, 1, n+1)
x = start[0] * (1 - r) + stop[0] * r
x = np.vstack((x, x + tick[0]))
y = start[1] * (1 - r) + stop[1] * r
y = np.vstack((y, y + tick[1]))
plt.plot(x, y, 'k', lw=1)
# add tick labels
for xx, yy, rr in zip(x[1], y[1], r):
plt.text(xx+offset[0], yy+offset[1], "{:.2}".format(rr))
# Note that the ordering from start to stop is important for the tick labels
plot_ticks(right, left, bottom_tick, n, offset=(0, -0.04))
plot_ticks(left, top, left_tick, n, offset=(-0.06, -0.0))
plot_ticks(top, right, right_tick, n)
In addition I switched the axis off via ax.set_axis_off() and I also deleted the twin axis, as you used these only to display the ticks and labels for the connections. These labels can also easily be placed via fig.text() as you did with the corners:
# Corners
fig.text(0.07, 0.05, 'Ti', fontsize=12, color='black')
fig.text(0.93, 0.05, 'Al', fontsize=12, color='black')
fig.text(0.50, 0.90, 'Ni', fontsize=12, color='black')
# Connections
fig.text(0.47, 0.05, 'Ti-Al', fontsize=12, color='black') # Note: not sure about
fig.text(0.72, 0.50, 'Al-Ni', fontsize=12, color='black') # the nomenclature;
fig.text(0.25, 0.50, 'Ti-Ni', fontsize=12, color='black') # might be switched

How do I get this to show the legend on the plot?

I am trying to get this code to show a legend on it, but everything I try is not working. Here is my code. I have tried put.legend() in the past and it has worked for me and I am confused why this is not working.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
#declaring my plot
fig1 = plt.figure()
#declaring xvalues
xes = np.arange(-10, 10, 0.01)
xlen = len(xes)
#zeros for yvalues along the axis
yes = np.zeros(xlen)
#declaring my variables
Efieldx = np.zeros((xlen, 1))
Efieldy = np.zeros((xlen, 1))
#locations of my two particles
p1x = 0;
p1y = 1;
p2x = 0;
p2y = -1
q = 1;
Efieldx1 = q/((xes-p1x)*(xes-p1x) + (yes-p1y)*(yes-p1y))**(1.5)*(xes-p1x)
Efieldy1 = q/((xes-p1x)*(xes-p1x) + (yes-p1y)*(yes-p1y))**(1.5)*(yes-p1y)
Efieldx2 = q/((xes-p2x)*(xes-p2x) + (yes-p2y)*(yes-p2y))**(1.5)*(xes-p2x)
Efieldy2 = q/((xes-p1x)*(xes-p1x) + (yes-p1y)*(yes-p1y))**(1.5)*(yes-p2y)
Efieldx = Efieldx1 + Efieldx2
Efieldy = Efieldy1 + Efieldy2
#Efieldx = -1/(xs * xs + ys * ys)^(0.5)
#let's define a function instead:
def f_Efield(q, x, y, xs, ys):
Ex = q*((xs-x)*(xs-x) + (ys-y)*(ys-y))**(-1.5)*(xs-x)
Ey = q/((xs-x)*(xs-x) + (ys-y)*(ys-y))**(1.5)*(ys-y)
return Ex, Ey
#using my new function
Exhere, Eyhere = f_Efield(2, 0, 0,xes, yes)
#plotting:
l, = plt.plot(xes, Efieldx, 'g-')
l, = plt.plot(xes, Exhere, 'r--')
plt.xlim(-10, 10)
plt.ylim(-2, 2)
plt.xlabel('x')
plt.title('Electric field along x-direction \n Andrew Richardson')
#adding a legend
plt.legend()
#displaying the plot
plt.show()
#saving the plot
fig1.savefig('Efield.pdf')
Exhere, Eyhere = f_Efield(-1, 0, 0, xes, yes)
You need to either specify the label property for your plots or pass handles (optional but recommended) and labels to your call to legend otherwise matplotlib has no way of knowing what text to put in the legend
# Using label kwarg
plt.plot(xes, Efieldx, 'g-', label='Efieldx')
plt.plot(xes, Exhere, 'r--', label='Exhere')
plt.legend()
# Using explicit plot handles and labels
p1 = plt.plot(xes, Efieldx, 'g-')
p2 = plt.plot(xes, Exhere, 'r--')
plt.legend([p1, p2], ['Efieldx', 'Exhere'])
# Using just the labels (not recommended)
plt.plot(xes, Efieldx, 'g-')
plt.plot(xes, Exhere, 'r--')
plt.legend(['Efieldx', 'Exhere'])

Categories