Plot straight line along y axes using 3D plot - python

I am trying to plot a straight line on the 3D surface (x,y,z) along the base of this plot from (1,3,0) to (1,6,0). The straight line along the x plane is not plotting and I can't seem to figure out what my error is. I have found a few similar questions to this but couldn't find where my mistake is.
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# points
z = np.repeat(0.1, 100)
x = np.repeat(1.0, 100)
y = np.linspace(start=3.0, stop=6.0, num=100)
# set axes limits
ax.set_xlim(6,3)
ax.set_ylim(0,1.1)
ax.set_zlim(0,1.75)
# plot
ax.plot(x, y, z, c='red',label=r'straight line at $x=1.0$')
plt.show()
I am holding x and z fixed while changing the y coordinates.

You have (likely accidentally) switched the x-axis and y-axis limits. Try this
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# points
z = np.repeat(0.1, 100)
x = np.repeat(1.0, 100)
y = np.linspace(start=3.0, stop=6.0, num=100)
# set axes limits
ax.set_xlim(0,1.1)
ax.set_ylim(6,3)
ax.set_zlim(0,1.75)
# plot
ax.plot(x, y, z, c='red',label=r'straight line at $x=1.0$')
plt.show()

Related

Increasing size of 3d surface plot with matplotlib

Picture of Plot
This should really not be this difficult. I am plotting a 3d surface plot from an array. The code looks like this:
z = arr
y = np.arange(len(z))
x = np.arange(len(z[0]))
(x ,y) = np.meshgrid(x,y)
plt.figure(figsize=(100,100))
ax.plot_surface(x,y,z, cmap=cm.coolwarm)
ax.set_xlabel("Bonus to AC")
ax.set_ylabel("Current AC")
ax.set_zlabel("Reduction in Damage")
plt.show()
It does not matter if I set the fig size to 10,10 or 1000,1000, the image still shows up the same size.
What kind of works is adding subplots,
ax = fig.add_subplot(211, projection='3d')
but this splits it up into one okay plot and one empty plot. Not sure how to use the subplots function.
you are referencing ax from a different figure than the one produced by plt.figure
you should instead use ax= fig.add_subplot after you assign fig= plt.figure as follows.
z = np.ones((100,100))
y = np.arange(len(z))
x = np.arange(len(z[0]))
(x ,y) = np.meshgrid(x,y)
fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(x,y,z)
ax.set_xlabel("Bonus to AC")
ax.set_ylabel("Current AC")
ax.set_zlabel("Reduction in Damage")
plt.show()
note i just swapped the z=np.ones((100,100)) in the first line so viewers can get this working.

Center specified tick labels for matplotlib's pcolomesh at the boxes

I do not understand, how to properly plot my heatmap (pcolormesh) with matplotlib. I want the tick's labels be centered below/beside the corresponding boxes - and only my given data, not some artificially extended ranges.
In the docs I found an example, which works slightly modified to floats just fine for me.
Z = []
for i in range(1, 7):
Z.append([j*i for j in range(1, 11)])
Z = np.asarray(Z)
x = np.linspace(0.1, 1.0, num=10)
y = np.linspace(0.1, 0.6, num=6)
fig, ax = plt.subplots()
ax.pcolormesh(x, y, Z, vmin=np.min(Z), edgecolors='w', linewidths=0.5, vmax=np.max(Z), shading='auto')
plt.show()
The result prints the ticks centered at the boxes, which is exactly what I want.
But as soon as I use my own data it ends up with some weird behaviour
data = pd.DataFrame(index=[0, 0.25, 0.5], data={0: [31.40455938, 101.43291831, 101.67128077], 0.25: [31.40455938, 89.81448724, 99.65066293], 0.5: [31.40455938, 57.01406046, 101.47536496]})
x = data.columns.astype(np.float64).to_numpy()
y = data.index.astype(np.float64).to_numpy()
z = data.to_numpy()
cmap = LinearSegmentedColormap.from_list('G2R', ["green", "red"])
fig, ax = plt.subplots()
ax.pcolormesh(x, y, z, shading='auto', cmap=cmap, edgecolors='w', linewidths=0.5, vmin=0, vmax=100) # shading='gouraud'
ax.set_title('not what i want')
ax.set_xlabel('X')
ax.set_ylabel('Y')
plt.show()
How do I get my heatmap to simply plot the given floats as centered tick labels without those unwanted floats? I literally want to specify my tick labels (floats or strings) to be shown centered to the boxes. I would assume there must be a way, to specify a list or array as tick labels. How do I accomplish that?
After plotting the pcolormesh you can set x and y tick with matplotlib.axes.Axes.set_xticks and matplotlib.axes.Axes.set_yticks respectively:
ax.pcolormesh(x, y, z, shading='auto', cmap=cmap, edgecolors='w', linewidths=0.5, vmin=0, vmax=100) # shading='gouraud'
ax.set_xticks(data.columns)
ax.set_yticks(data.index)

plotting countour profiles in matplotlib

I have a data composed by three 1d-arrays (X,Y,Z) and I want to create a graph like the origin contour profile (https://www.originlab.com/doc/Origin-Help/Contour-Profile), where I have a 3d data plotted in a 2d contour, where the z value is represented by the graphic colors, and two other graphs representing profiles (or slices) of my surface for specific x and y-values. My problem is how to create the profiles.
I started by creating new x and y arrays and a grid surface for z by interpolating the data I had with scipy.interpolate.griddata
and now I can create the contour plot but I don't know how to create the profiles in xz and yz planes. I found out how to this in a 3d plot by using zdir=x and zdir=y (https://matplotlib.org/3.3.1/gallery/mplot3d/contour3d_3.html) but this works only for 3d graphs. I also know that I can trace the profile in the 'xy' plane for a specific z-value with ax.contour(x, y, z, [zvalue]). I want to do something similar to the 'xz' and 'yz' planes.
The code I have is this:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from scipy.interpolate import griddata
# reading the data
path = 'data/2019_12_11_15h17m29s_TZO_1Nd_3Hz_ref_900nm_900mW.txt_output.dat'
df = pd.read_csv(path)
X = df.position
Y = df.time
Z = df.signal
# interpolating data to create a surface
xi = np.linspace(X.min(), X.max(), 100)
yi = np.linspace(Y.min(), Y.max(), 100)
zi = griddata((X, Y), Z, (xi[None,:], yi[:,None]), method='linear')
zi = np.nan_to_num(zi)
# if I want to plot a 2D contour plot
fig = plt.figure()
ax = plt.axes()
ax.contourf(xi, yi, zi, levels=300, cmap="RdBu_r")
plt.show()
# if I want to plot a 3d surface with profiles
xi2, yi2 = np.meshgrid(xi, yi)
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(xi2, yi2, zi, rstride=3, cstride=3, alpha=0.5)
cset = ax.contour(xi2, yi2, zi, zdir='x', offset=X.min()-30, cmap=cm.coolwarm, levels=10)
cset = ax.contour(xi2, yi2, zi, zdir='y', offset=Y.max()+30, cmap=cm.coolwarm, levels=10)
ax.set_xlim(X.min()-30, X.max()+30)
ax.set_ylim(Y.min()-30, Y.max()+30)
ax.set_zlim(Z.min(), Z.max())
plt.show()
# if I want to make a profile in xy plane:
fig = plt.figure()
ax = plt.axes()
ax.contour(xi, yi, zi, [1])
plt.show()
but I don't know how to create the profiles in 'xz' and 'yz' planes
I found myself a way of doing it that is not exactly what I was looking for but works perfectly. The idea is to fix a column/row of zi and plot against xi/yi to do a profile of the xz/yz plane. Varying the column/row in a for loop I got a result similar to levels of contour plot:
for i in range(0,len(zi),step):
z_xz = zi[i,:]
plt.plot(xi,z_xz)
and
for i in range(0,len(zi[0]),step):
z_xz = zi[:,i]
plt.plot(yi,z_xz)
please, let me know if you find a better solution

Having a single y axis values while plotting two variables on secondary axis

I am trying to plot three variables, in a graph using primary and secondary axis with one variable on primary axis and two on secondary axis. My code
vav = floor_data[floor_data['vavId'] == i]
vav = vav.reset_index()
x = vav.index
y1 = vav['nvo_temperature_sensor_pps']
y2 = vav['nvo_airflow']
y3 = vav['nvo_air_damper_position']
fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
ax1.plot(x, y1, 'g-')
ax2.plot(x, y2, 'b-')
ax2.plot(x, y3, 'r-')
ax2 = ax1.twinx()
ax1.set_xlabel('VAV '+str(i))
ax1.set_ylabel('temperature ', color='g')
ax2.set_ylabel('air flow, temperature', color='b')
plt.show()
I have added all the three variables but I am facing problem in y-ticks of secondary axis. My plot looks like
Is it possible to have a single y tick values on secondary axis for better readability?
You need to create new twix axis on host and shrink subplot to create space for additional axis on right side. Then move new axis at right position. Some descriptions in code.
import matplotlib.pyplot as plt
import numpy as np
fig, host = plt.subplots()
# shrink subplot
fig.subplots_adjust(right=0.75)
# create new axis on host
par1 = host.twinx()
par2 = host.twinx()
# place second axis at far right position
par2.spines["right"].set_position(("axes", 1.2))
# define plot functions
def function_sin(x):
return np.sin(x)
def function_parabola(x):
return x**2
def function_line(x):
return x+1
# plot data
x = np.linspace(0, 10, 100)
y_sin = function_sin(x)
y_parabola = function_parabola(x)
y_line = function_line(x)
host.plot(x, y_sin, "b-")
par1.plot(x, y_parabola, "r-")
par2.plot(x, y_line, "g-")
# set labels for each axis
host.set_xlabel("VAV 16")
host.set_ylabel("Temperature")
par1.set_ylabel("Temperature")
par2.set_ylabel("Air Flow")
plt.show()
Output:

How to plot x versus y lines using matplotlib?

How to plot a x versus y line? By x versus y, I mean how to plot x vs y line if the x and y axes have already fixed, as if the axes are reversed for this line.
Update:
Some one asked me why not just reverse the arguments and axes labels. Here is my reason: this x vs y line is only a part of a 2D plot (the main plot) and the main axes are for the 2D plot. What's more, there are also y vs x lines in the same 2D plot. I do this because I want to show certain line clearly.
Update:
Here is a example what I want:
I want to plot the black line in the figure which I draw manually (actually I want to draw Gaussian curve). It is time vs voltage. I still want to keep the existed blue line and I should not reverse the time/voltage labels.
You can easily plot multiple curves in the same subplot in matplotlib. As an example see this annotated code:
import matplotlib.pyplot as plt
import numpy as np
# Data for plotting
t = np.arange(0.0, 2.0, 0.01)
s = 1 + np.sin(2 * np.pi * t)
# Note that using plt.subplots below is equivalent to using
# fig = plt.figure() and then ax = fig.add_subplot(111)
fig, ax = plt.subplots()
#plot sine wave
ax.plot(t, s, label = "sine wave")
#now create y values for the second plot
y = np.linspace(0, 2, 1000)
#calculate the values for the Gaussian curve
x = 2 * np.exp(-0.5 * np.square(-4 * (y - 1)))
#plot the Gaussian curve
ax.plot(x, y, label = "Gaussian curve")
ax.set(xlabel='time (s)', ylabel='voltage (mV)',
title='About as simple as it gets, folks')
ax.grid()
#show the legend
plt.legend()
plt.show()
Output:

Categories