mplot3D fill_between extends over axis limits - python

I have questions related to creating a simple lineplot in Python with mplot3D where the area under the plot is filled. I am using Python 2.7.5 on RedHatEnterprise 7.2, matplotlib 1.2.0 and numpy 1.7.2.
Using the code below, I am able to generate a line plot. This is displayed as expected with the beginning / end of the plot set by the limits of the imported data set.
I am then trying to fill the area between the line plot and -0.1 using the answer given by Bart from Plotting a series of 2D plots projected in 3D in a perspectival way. This works, however, the filled area is continued beyond the limits of the data set. This is also the case when running the example from the link.
This screen shot shows the plot generated with filled area extending beyond the set axis limits.
How do I achieve that the filled area is only the range of the data set or the axis limits whichever is smaller?
How do I add a legend for those plots onto the figure?
Code as follows:
from numpy import *
import matplotlib.pylab as plt
from mpl_toolkits.mplot3d import Axes3D
x,y = genfromtxt("data.dat",unpack=True)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.add_collection3d(plt.fill_between(x,y,-0.1, color='orange', alpha=0.3,label="filled plot"),1, zdir='y')
ax.plot(x,y,1,zdir="y",label="line plot")
ax.legend()
ax.set_xlim3d(852.353,852.359)
ax.set_zlim3d(-0.1,5)
ax.set_ylim3d(0,2)
ax.get_xaxis().get_major_formatter().set_useOffset(False)
plt.show()

I don't know how to put fill_between working the way you want it to, but I can provide an alternative using a 3D polygon:
from numpy import *
import matplotlib.pylab as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection # New import
#x,y = genfromtxt("data.dat",unpack=True)
# Generated some random data
w = 3
x,y = np.arange(100), np.random.randint(0,100+w,100)
y = np.array([y[i-w:i+w].mean() for i in range(3,100+w)])
z = np.zeros(x.shape)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
#ax.add_collection3d(plt.fill_between(x,y,-0.1, color='orange', alpha=0.3,label="filled plot"),1, zdir='y')
verts = [(x[i],z[i],y[i]) for i in range(len(x))] + [(x.max(),0,0),(x.min(),0,0)]
ax.add_collection3d(Poly3DCollection([verts],color='orange')) # Add a polygon instead of fill_between
ax.plot(x,z,y,label="line plot")
ax.legend()
ax.set_ylim(-1,1)
plt.show()
The code above generates some random data. Builds vertices from it and plots a polygon with those vertices. This will give you the plot you wish (but does not use fill_between). The result is:

Related

How to scale and set up RGB ortogonal axes in a 3D plot?

I'm trying to plot a wave function over one dimension but it has real and imaginary parts, so I did a 3D plot animation of it. This is a screenshot:
The main thing I would like to do is to spread it along the x-axis (which now is vertical) so it doesn't look squeezed. Also, it would be nice to set it up in a set of 3 RGB axes that intersect at the point (0,0,0). In the documentation I couldn't find any straight forward way to do this. I'm attaching the part of the code I'm using to animate it:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import mpl_toolkits.mplot3d.axes3d as p3
fig = plt.figure()
ax = fig.gca(projection='3d')
line, = ax.plot(REAL[0,:],IMAG[0,:],x,"r",linewidth=0.5)
def animacio(i):
ax.collections.clear()
line.set_data(REAL[i,:],IMAG[i,:])
line.set_3d_properties(x, 'z')
return line,
ani = animation.FuncAnimation(fig,animacio,interval=50, frames=Nt,repeat=True)
nom = 'EvoluciĆ³_'
ani.save(str(nom)+'['+str(V0)+','+str(L)+','+str(l)+','+str(xi)+','+str(sigmax)+','+str(T)+']'+'.mp4', writer="ffmpeg", dpi=300)
plt.show()
print('Animation saved as: '+str(nom)+'['+str(V0)+','+str(L)+','+str(l)+','+str(xi)+','+str(sigmax)+','+str(T)+']'+'.mp4')
You can add colored lines to the plot, just by giving start and end points and assigning a color. The limits for the 'up'-axis can be set by ax.set_zlim. I created a demo curve that roughly resembles yours.
import numpy as np
from mpl_toolkits import mplot3d
import matplotlib.pyplot as plt
x = np.linspace(-10, 10, 1000)
y = np.sin(10*x)/(x*x+1)
z = np.cos(10*x)/(x*x+1)
ax = plt.axes(projection='3d')
ax.plot3D([0,0], [0,0], [-10,10], color='crimson')
ax.plot3D([0,0], [-1,1], [0,0], color='limegreen')
ax.plot3D([-1,1], [0,0], [0,0], color='dodgerblue')
line, = ax.plot3D(y, z, x, color='blueviolet')
ax.set_zlim(-1, 1)
plt.show()
At the left the plot without limiting, at the right with limits:
To get a more elongated view, you could use something like:
plt.gcf().set_size_inches(4, 12)

How to do a 3D circle in Matplotlib

I am a beginner in Python. I'm trying to plot a circle using matplotlib that has tangent to Z axis. I know how to draw a sphere in 3D but don't know how to draw a circle/ring in 3D plot. Can someone help me with the code? Thanks in advance!
You need the usual imports, plus the 3D toolkit
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
You need a 3D enabled axes object
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
You need a circle, contained in the plane y-z
theta = np.linspace(0, 2 * np.pi, 201)
y = 10*np.cos(theta)
z = 10*np.sin(theta)
now we can plot the original circle and, as an example, a number of circles rotated about the z-axis and whose centers are also placed at a fixed distance (equal to the c ircles'radius) from the z-axis, so that they are tangent to it
for i in range(18):
phi = i*np.pi/9
ax.plot(y*np.sin(phi)+10*np.sin(phi),
y*np.cos(phi)+10*np.cos(phi), z)
eventually we place a vertical axis and a legend
ax.plot((0,0),(0,0), (-10,10), '-k', label='z-axis')
ax.legend()
It's time to see what we got
plt.show()
mpl_toolkits.mplot3d.art3d
https://matplotlib.org/3.2.1/gallery/mplot3d/pathpatch3d.html was mentioned
in a comment, the example can be minimized to:
#!/usr/bin/env python3
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
import mpl_toolkits.mplot3d.art3d as art3d
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# Draw a circle on the x=0 'wall'
p = Circle((5, 5), 3)
ax.add_patch(p)
art3d.pathpatch_2d_to_3d(p, z=0, zdir="x")
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
ax.set_zlim(0, 10)
plt.show()
which gives:
This is a bit nicer than https://stackoverflow.com/a/56871467/895245 as it uses a higher level Circle object directly, instead of requiring you to explicitly plot the lines.
Unfortunately, 3D support in matplotlib is a bit limited as mentioned in the documentation itself, and you have to do some extra work to plot on planes not parallel to the main coordinate plane: How can matplotlib 2D patches be transformed to 3D with arbitrary normals?
Tested on matplotlib==3.2.2.

Histogram at specific coordinates inside axes

What I want to achieve with Python 3.6 is something like this :
Obviously made in paint and missing some ticks on the xAxis. Is something like this possible? Essentially, can I control exactly where to plot a histogram (and with what orientation)?
I specifically want them to be on the same axes just like the figure above and not on separate axes or subplots.
fig = plt.figure()
ax2Handler = fig.gca()
ax2Handler.scatter(np.array(np.arange(0,len(xData),1)), xData)
ax2Handler.hist(xData,bins=60,orientation='horizontal',normed=True)
This and other approaches (of inverting the axes) gave me no results. xData is loaded from a panda dataframe.
# This also doesn't work as intended
fig = plt.figure()
axHistHandler = fig.gca()
axScatterHandler = fig.gca()
axHistHandler.invert_xaxis()
axHistHandler.hist(xData,orientation='horizontal')
axScatterHandler.scatter(np.array(np.arange(0,len(xData),1)), xData)
A. using two axes
There is simply no reason not to use two different axes. The plot from the question can easily be reproduced with two different axes:
import numpy as np
import matplotlib.pyplot as plt
plt.style.use("ggplot")
xData = np.random.rand(1000)
fig,(ax,ax2)= plt.subplots(ncols=2, sharey=True)
fig.subplots_adjust(wspace=0)
ax2.scatter(np.linspace(0,1,len(xData)), xData, s=9)
ax.hist(xData,bins=60,orientation='horizontal',normed=True)
ax.invert_xaxis()
ax.spines['right'].set_visible(False)
ax2.spines['left'].set_visible(False)
ax2.tick_params(axis="y", left=0)
plt.show()
B. using a single axes
Just for the sake of answering the question: In order to plot both in the same axes, one can shift the bars by their length towards the left, effectively giving a mirrored histogram.
import numpy as np
import matplotlib.pyplot as plt
plt.style.use("ggplot")
xData = np.random.rand(1000)
fig,ax= plt.subplots(ncols=1)
fig.subplots_adjust(wspace=0)
ax.scatter(np.linspace(0,1,len(xData)), xData, s=9)
xlim1 = ax.get_xlim()
_,__,bars = ax.hist(xData,bins=60,orientation='horizontal',normed=True)
for bar in bars:
bar.set_x(-bar.get_width())
xlim2 = ax.get_xlim()
ax.set_xlim(-xlim2[1],xlim1[1])
plt.show()
You might be interested in seaborn jointplots:
# Import and fake data
import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt
data = np.random.randn(2,1000)
# actual plot
jg = sns.jointplot(data[0], data[1], marginal_kws={"bins":100})
jg.ax_marg_x.set_visible(False) # remove the top axis
plt.subplots_adjust(top=1.15) # fill the empty space
produces this:
See more examples of bivariate distribution representations, available in Seaborn.

Invert y axis on matplotlib trisurf 3d graph python

I'm using matplotlib to produce a 3d trisurf graph. I have everything working except that I would like to invert the y-axis, so that the origin is 0,0 not 0,100. I've looked through the matplotlib axes3d API and cannot figure out how to do this. Here is my code:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
# my data, xs=xaxis, ys=yaxis, zs=zaxis
mortar_xs = []
cycles_ys = []
score_zs = []
#... populate my data for the 3 arrays: mortar_xs, cycles_ys, score_zs
# plot
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_trisurf(mortar_xs,cycles_ys,score_zs,cmap=cm.coolwarm)
ax.set_zlim(bottom=0.0,top=1.0)
ax.legend()
ax.set_xlabel("# Mortar")
ax.set_ylabel("# Goals")
ax.set_zlabel("# Score")
plt.show()
My graph produced is the following, but I need the '# Goals' or the y-axis inverted, so that the origin is 0,0 not 0,100. If possible, I would like to do this without changing my data.
tmdavison's comment is what I was looking for:
ax.set_ylim(0,100)
Or
ax.set_ylim(100,0)
The simplest method would be to use ax.invert_yaxis()

Adding Radial Axis Label in matplotlib

I am making a polar scatter plot for a college project with matplotlib and I can't find out how to add a label to the radial axis. Here is my code ( I left out the data because it was read out of a csv)
import matplotlib.pyplot as plt
ax = plt.subplot(111, polar=True)
ax.set_rmax(1)
c = plt.scatter(theta, radii)
ax.set_title("Spread of Abell Cluster Supernova Events as a Function of Fractional Radius", va='bottom')
ax.legend(['Supernova'])
plt.show()
(My plot looks like this. I can't seem to find any straight forward method to do it. Has anyone dealt with this before and have any suggestions?
I don't know of a built in way to do it, but you could use ax.text to make your own. You can get the position of the radial tick labels using ax.get_rlabel_position(), and the mid point of the radial axis using ax.get_rmax()/2.
For example, here's your code (with some random data):
import matplotlib.pyplot as plt
import numpy as np
theta=np.random.rand(40)*np.pi*2.
radii=np.random.rand(40)
ax = plt.subplot(111, polar=True)
ax.set_rmax(1)
c = plt.scatter(theta, radii)
ax.set_title("Spread of Abell Cluster Supernova Events as a Function of Fractional Radius", va='bottom')
ax.legend(['Supernova'])
label_position=ax.get_rlabel_position()
ax.text(np.radians(label_position+10),ax.get_rmax()/2.,'My label',
rotation=label_position,ha='center',va='center')
plt.show()
And here's the output:
I'd be interested to see if there's a more elegant solution, but hopefully this helps you.
from pylab import *
N = 150
r = 2*rand(N)
theta = 2*pi*rand(N)
area = 200*r**2*rand(N)
colors = theta
ax = subplot(111, polar=True)
c = scatter(theta, r, c=colors, s=area, cmap=cm.hsv)
c.set_alpha(0.75)
ax.set_ylabel('Radius', rotation=45, size=11)
show()
A slightly different method from #tom. This uses directly the plt.legend option.
Example:
import matplotlib.pyplot as plt
import numpy as np
theta=np.random.rand(40)*np.pi*2.
radii=np.random.rand(40)
ax = plt.subplot(111, polar=True)
ax.set_rmax(1)
c = plt.scatter(theta, radii,label='Supernova')
ax.set_title("Spread of Abell Cluster Supernova Events as a Function of Fractional Radius", va='bottom')
ax.legend(loc='lower right', scatterpoints=1)
plt.show()
You can change lower right to upper right or even to best to leave the alignment of the legend to matplotlib.

Categories