I was wondering how to have a 3d plot with multiple lines showing the projection on the xy plane by means of something like fill_between but in 3D. I have here a sample code.
fig, ax = plt.subplots(figsize=(12,8),subplot_kw={'projection': '3d'})
for i in np.arange(0.0,1,0.1):
x1=np.arange(0,1-i+0.01,0.01)
y1=1.0-i-x1
def z_func(x,y,z):
return x+y**2+0.5*z #can be any fn
coordinates3= [[i,j,1-i-j] for j in np.arange(0,1-i+0.01,0.01)]
z1=np.array([z_func(*k) for k in coordinates3])
ax.plot(x1,y1,z1)
ax.view_init(azim=10,elev=20)
plt.show()
I'd like to have each line 'projected' on the xy plane, with a shaded filling in between the curve and its projection. Anybody knows a quick way?
After the suggestion in the comments of #ImportanceOfBeingErnest, I was able to write a solution. I came up with this:
fig, ax = plt.subplots(figsize=(12,8),subplot_kw={'projection': '3d'})
prop_cycle = plt.rcParams['axes.prop_cycle']
colors = prop_cycle.by_key()['color']
for color,i in enumerate(np.arange(0.0,1,0.1)):
x1=np.arange(0,1-i+0.01,0.01)
y1=1.0-i-x1
def z_func(x,y,z):
return x+y**2+0.5*z #can be any fn
coordinates3= [[i,j,1-i-j] for j in np.arange(0,1-i+0.01,0.01)]
z1=np.array([z_func(*k) for k in coordinates3])
verts=[[(k[1],k[2],z_func(*k)) for k in coordinates3]]
verts[0].insert(0,(coordinates3[0][1],coordinates3[0][2],0))
verts[0].insert(0,(coordinates3[-1][1],coordinates3[-1][2],0))
poly = Poly3DCollection(verts,color=colors[color])
poly.set_alpha(0.2)
ax.add_collection(poly)
ax.plot(x1,y1,z1,linewidth=10)
ax.view_init(azim=10,elev=20)
plt.show()
One thing that puzzles me is that the shade doesn't get the color of the line and that I had to supply it myself. If you remove the color=colors[color] in the Poly3DCollection you always get blue shades, whereas the lines automatically get the different colors, as one can see in the question. Anybody knows a reason for this?
Related
Hey so I'm an undergraduate working in an imaging lab and I have a 3D numpy array that has values from 0-9 to indicate concentration in a 3D space. I'm trying to plot these values in a scatterplot with a colormap to indicate the value between 0-9. The array is 256 x 256 x 48, so I feel like the size of it is making it difficult for me to plot the array in a meaningful way.
I've attached a picture of what it looks like right now. As you can see the concentration looks very "faded" even for very high values and I'm not entirely sure why. Here is the code I'm using to generate the plot:
current heatmap
fig = plt.figure()
x, y, z = np.meshgrid(range(256), range(256), range(48))
col = sum_array.flatten()
ax = fig.add_subplot(111, projection = '3d')
sc = ax.scatter(x, y, z, c = col, cmap='Reds',
linewidths=.01, s=.03, vmin=0, vmax=9,
marker='.', alpha=1)
plt.colorbar(sc)
plt.show()
If anyone can help me display the colors in a more bright/concentrated manner so the heat map is visually useful, I'd really appreciate it. Thank you!
I would like to create a figure where subplots are added dynamically within a for-loop. It should be possible to define the width and height of each subplot in centimeters, that is, the more subplots are added, the bigger the figure needs to be to make room for 'incoming' subplots.
In my case, subplots should be added row-wise so that the figure has to get bigger in the y-dimension. I came across this stackoverflow post, which might lead in the right direction? Maybe also the gridspec module could solve this problem?
I tried out the code as described in the first post, but this couldn't solve my problem (it sets the final figure size, but the more subplots are added to the figure the smaller each subplot gets, as shown in this example):
import matplotlib.pyplot as plt
# set number of plots
n_subplots = 2
def set_size(w,h,ax=None):
""" w, h: width, height in inches """
if not ax: ax=plt.gca()
l = ax.figure.subplotpars.left
r = ax.figure.subplotpars.right
t = ax.figure.subplotpars.top
b = ax.figure.subplotpars.bottom
figw = float(w)/(r-l)
figh = float(h)/(t-b)
ax.figure.set_size_inches(figw, figh)
fig = plt.figure()
for idx in range(0,n_subplots):
ax = fig.add_subplot(n_subplots,1,idx+1)
ax.plot([1,3,2])
set_size(5,5,ax=ax)
plt.show()
You're setting the same figure size (5,5) regardless of the number of subplots. If I understood your question correctly, I think you want to set the height to be proportional to the number of subplots.
However, you'd be better off to create the figure with the right size from the get-go. The code that you are providing gives the correct layout only because you know before hand how many subplots your going to create (in fig.add_subplot(n_subplots,...)). If you are trying to add subplots without knowing the total number of subplot rows you need, the problem is more complicated.
n_subplots = 4
ax_w = 5
ax_h = 5
dpi = 100
fig = plt.figure(figsize=(ax_w, ax_h), dpi=dpi)
for idx in range(0,n_subplots):
ax = fig.add_subplot(n_subplots,1,idx+1)
ax.plot([1,3,2])
fig.set_size_inches(ax_w,ax_h*n_subplots)
fig.tight_layout()
I am reading Data from a Simulation out of an Excel File. Out of this Data I generated two DataFrames containing 200 values. Now i want to plot all the Values from DataFrame one in blue and all Values from DataFrame two in purple. Therefore I have following code:
df = pd.read_excel("###CENSORED####.xlsx", sheetname="Data")
unpatched = df["Unpatched"][:-800]
patched = df["Patched"][:-800]
x = range(0,len(unpatched))
fig = plt.figure(figsize=(10, 5))
plt.scatter(x, unpatched, zorder=10, )
plt.scatter(x, patched, c="purple",zorder=19,)
This results in following Graph:
But now i want to draw in some lines that visualize the difference between the blue and purple dots. I thought about an orange line going from blue dot at simulation-run x to the purple dot at simulation-run x. I've tried to "cheat" with following code, since I'm pretty new to matplotlib.
scale_factor = 300
for a in x:
plt.axvline(a, patched[a]/scale_factor, unpatched[a]/scale_factor, c="orange")
But this resulted in a inaccuracy as seen seen below:
So is there a smarter way to do this? I've realized that the axvline documentation only says that ymin, ymax can only be scalars. Can I somehow turn my given values into fitting scalars?
I am new in pyplot.
I have a Cartesian surface plot:
# offset and omega are arrays
Z = my_function(omega,offset) # my_function give and arrays of omega.size*offset.size
fig, ax = plt.subplots(1)
p = ax.pcolor(offset,omega,Z.T,cmap=cm.jet,vmin=abs(Z).min(),vmax=abs(Z).max())
cb = fig.colorbar(p,ax=ax)
Maybe there is a more simple way to plot a surface but that the way I've found on the internet.
Well, now I want to plot my_function as a surface using polar coordinate, I've tried this:
ax2 = plt.subplot(111, polar=True)
p2 = ax2.pcolor(offset,omega,Z.T,cmap=cm.jet,vmin=abs(Z).min(),vmax=abs(Z).max())
It kind of work, I have a surface plot but it does not take into account the limits of Y.
For example if Y is defined between -15 and 15° I only want my function to be plotted and shown between those angles and not 0 to 360° as my example is doing.
How can I do that ?
I thank you in advance for any answer.
I'm drawing the bloxplot shown below using python and matplotlib. Is there any way I can reduce the distance between the two boxplots on the X axis?
This is the code that I'm using to get the figure above:
import matplotlib.pyplot as plt
from matplotlib import rcParams
rcParams['ytick.direction'] = 'out'
rcParams['xtick.direction'] = 'out'
fig = plt.figure()
xlabels = ["CG", "EG"]
ax = fig.add_subplot(111)
ax.boxplot([values_cg, values_eg])
ax.set_xticks(np.arange(len(xlabels))+1)
ax.set_xticklabels(xlabels, rotation=45, ha='right')
fig.subplots_adjust(bottom=0.3)
ylabels = yticks = np.linspace(0, 20, 5)
ax.set_yticks(yticks)
ax.set_yticklabels(ylabels)
ax.tick_params(axis='x', pad=10)
ax.tick_params(axis='y', pad=10)
plt.savefig(os.path.join(output_dir, "output.pdf"))
And this is an example closer to what I'd like to get visually (although I wouldn't mind if the boxplots were even a bit closer to each other):
You can either change the aspect ratio of plot or use the widths kwarg (doc) as such:
ax.boxplot([values_cg, values_eg], widths=1)
to make the boxes wider.
Try changing the aspect ratio using
ax.set_aspect(1.5) # or some other float
The larger then number, the narrower (and taller) the plot should be:
a circle will be stretched such that the height is num times the width. aspect=1 is the same as aspect=’equal’.
http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.set_aspect
When your code writes:
ax.set_xticks(np.arange(len(xlabels))+1)
You're putting the first box plot on 0 and the second one on 1 (event though you change the tick labels afterwards), just like in the second, "wanted" example you gave they are set on 1,2,3.
So i think an alternative solution would be to play with the xticks position and the xlim of the plot.
for example using
ax.set_xlim(-1.5,2.5)
would place them closer.
positions : array-like, optional
Sets the positions of the boxes. The ticks and limits are automatically set to match the positions. Defaults to range(1, N+1) where N is the number of boxes to be drawn.
https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.boxplot.html
This should do the job!
As #Stevie mentioned, you can use the positions kwarg (doc) to manually set the x-coordinates of the boxes:
ax.boxplot([values_cg, values_eg], positions=[1, 1.3])