I know that matplotlib 3D is not reliable for plotting multiple 3D objects (planes, lines, points) in the right order: please see Matplotlib 3D plot zorder issue and How to draw intersecting planes?.
However these questions seem quite old, so the proposed solutions. Thus, I would like to know if there are some new developments, tools, workarounds or hard-coded solutions for the following specific simple scenario:
import mpl_toolkits.mplot3d as a3
import matplotlib.pylab as plt
import numpy as np
fig = plt.figure()
ax = a3.Axes3D(fig)
# create an orizontal plane
corners = [[0,0,0],[0,5,0],[5,5,0],[5,0,0]]
tri = a3.art3d.Poly3DCollection([corners], alpha=1)
tri.set_color('w')
tri.set_edgecolor('k')
ax.add_collection3d(tri)
# plot a vector
ax.plot([2,2],[2,2],[0,4], c = 'r')
# plot some points
ax.scatter([1,3],[1,3],[1,3], c = 'r')
ax.set_xlim([0, 5.0])
ax.set_ylim([0, 5.0])
ax.set_zlim([0, 2.5]);
plt.show()
In this image you can see the visualization issues: the vector it is not starting from the plane, as it should since his intiali point is (2,2,0)
Related
I have a 3-D surface plot that shows x and y coordinates and depths. I also have a 2-D contourf plot with x and y coordinates and the filled contours at the different locations. If I know the depths at the coordinates in the contourf plot, is there a way I can show the contours on the 3-D surface plot?
I have created a 3-D surface plot using plotly with the code below:
import plotly.graph_objects as go
import oceansdb
import numpy as np
import matplotlib.pyplot as plt
Xa = np.linspace(29.005,29.405,200)
Ya = np.linspace(-93.6683,-93.2683,200)
db = oceansdb.ETOPO()
dcont = db['topography'].extract(lat=Xa, lon=Ya)
depth = dcont['height']
fig = go.Figure(data=[go.Surface(z=depth, x=Xa, y=Ya)])
fig.show()
Say my contourf plot can be created with the code below:
X = np.array([29.1,29.15,29.2,29.25])
Y = np.array([-93.5,-93.45,-93.4,-93.35])
r = np.array([0,0,0,2,3,0,0,6,7,8,9,1,9,0,0,0])
plt.figure()
plt.contourf(X,Y,r.reshape(len(X),len(Y)))
plt.show()
Assuming that the depth at each location can be determined using the oceansdb module, can I overlay the contour plot on the surface plot at the correct depth?
Using matplotlib the short answer is "yes", but there are two buts you have face:
Visualizing 3d data is difficult, and overlapping multiple datasets is more often than not confusing beyond the simplest cases
Matplotlib has a 2d renderer, so even though you can plot multiple objects in the same 3d figure, there will often be rendering artifacts (in particular, two objects can typically be either fully in front of or behind one another).
The key methods you need are Axes3D.contour or Axes3D.contourf. Here are these in action with your example data:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D # this enables 3d axes
X = np.array([29.1,29.15,29.2,29.25])
Y = np.array([-93.5,-93.45,-93.4,-93.35])
r = np.array([0,0,0,2,3,0,0,6,7,8,9,1,9,0,0,0]).reshape(X.size, Y.size)
# plot your 2d contourf for reference
fig,ax = plt.subplots()
ax.contourf(X, Y, r)
# plot in 3d using contourf
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.contourf(X, Y, r)
# plot in 3d using contour
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.contour(X, Y, r)
plt.show()
Here's your 2d contourf plot:
Here's the 3d contourf version:
And here's the 3d contour version:
As you can see the difference between the latter two is that contourf also plots horizontal planes for each level (just like terraces), whereas contour only plots the level lines themselves.
Since repeated plots using the same axes will accumulate plots there's nothing stopping you from superimposing either of these 3d contour plots on your original surface. However, in line with my earlier warnings you'll have to watch if the contours are rendered correctly over the surface (under all view angles), and even if so the result might not be all that transparent for conveying information. I personally tend to find contourf much easier to comprehend than contour on a 3d plot, but I suspect that if we put these on top of full surface plots the latter will fare better.
I want to create a smooth cylinder using matplotlib/pyplot. I've adapted a tutorial online and produced the following minimal example:
from numpy import meshgrid,linspace,pi,sin,cos,shape
from matplotlib import pyplot
import matplotlib.tri as mtri
from mpl_toolkits.mplot3d import Axes3D
u,v = meshgrid(linspace(0,10,10),linspace(0,2*pi,20))
u = u.flatten()
v = v.flatten()
x = u
z = sin(v)
y = cos(v)
tri = mtri.Triangulation(u, v)
fig = pyplot.figure()
ax = fig.add_axes([0,0,1,1],projection='3d')
ax.plot_trisurf(x,y,z,triangles=tri.triangles,linewidth=0)
pyplot.show()
which produces a cylinder. I set linewidth=0 to remove the wireframe, however, there is now the "ghost" of the wireframe because the triangulation has (presumably) been spaced assuming the wireframe is there to fill in the gaps. This looks to be specific to plot_trisurf, because there are other 3d plotting examples (e.g., using plot_surface) which set linewidth=0 without these gaps showing up.
Doing an mtri.Triangulation?, it seems like it might not be possible to "perfectly" fill in the gaps, since it states
>Notes
> -----
> For a Triangulation to be valid it must not have duplicate points,
> triangles formed from colinear points, or overlapping triangles.
One partial solution is to just color the wireframe the same shade of blue, but after I've fixed this problem I also want to add a light source/shading on the surface, which would put me back at square one.
Is there a way to make this work? Or can someone suggest a different approach? Thanks for any help.
ax.plot_trisurf(x,y,z,triangles=tri.triangles,linewidth=0, antialiased=False)
I would like to make beautiful scatter plots with histograms above and right of the scatter plot, as it is possible in seaborn with jointplot:
I am looking for suggestions on how to achieve this. In fact I am having some troubles in installing pandas, and also I do not need the entire seaborn module
I encountered the same problem today. Additionally I wanted a CDF for the marginals.
Code:
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import numpy as np
x = np.random.beta(2,5,size=int(1e4))
y = np.random.randn(int(1e4))
fig = plt.figure(figsize=(8,8))
gs = gridspec.GridSpec(3, 3)
ax_main = plt.subplot(gs[1:3, :2])
ax_xDist = plt.subplot(gs[0, :2],sharex=ax_main)
ax_yDist = plt.subplot(gs[1:3, 2],sharey=ax_main)
ax_main.scatter(x,y,marker='.')
ax_main.set(xlabel="x data", ylabel="y data")
ax_xDist.hist(x,bins=100,align='mid')
ax_xDist.set(ylabel='count')
ax_xCumDist = ax_xDist.twinx()
ax_xCumDist.hist(x,bins=100,cumulative=True,histtype='step',density=True,color='r',align='mid')
ax_xCumDist.tick_params('y', colors='r')
ax_xCumDist.set_ylabel('cumulative',color='r')
ax_yDist.hist(y,bins=100,orientation='horizontal',align='mid')
ax_yDist.set(xlabel='count')
ax_yCumDist = ax_yDist.twiny()
ax_yCumDist.hist(y,bins=100,cumulative=True,histtype='step',density=True,color='r',align='mid',orientation='horizontal')
ax_yCumDist.tick_params('x', colors='r')
ax_yCumDist.set_xlabel('cumulative',color='r')
plt.show()
Hope it helps the next person searching for scatter-plot with marginal distribution.
Here's an example of how to do it, using gridspec.GridSpec:
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import numpy as np
x = np.random.rand(50)
y = np.random.rand(50)
fig = plt.figure()
gs = GridSpec(4,4)
ax_joint = fig.add_subplot(gs[1:4,0:3])
ax_marg_x = fig.add_subplot(gs[0,0:3])
ax_marg_y = fig.add_subplot(gs[1:4,3])
ax_joint.scatter(x,y)
ax_marg_x.hist(x)
ax_marg_y.hist(y,orientation="horizontal")
# Turn off tick labels on marginals
plt.setp(ax_marg_x.get_xticklabels(), visible=False)
plt.setp(ax_marg_y.get_yticklabels(), visible=False)
# Set labels on joint
ax_joint.set_xlabel('Joint x label')
ax_joint.set_ylabel('Joint y label')
# Set labels on marginals
ax_marg_y.set_xlabel('Marginal x label')
ax_marg_x.set_ylabel('Marginal y label')
plt.show()
I strongly recommend to flip the right histogram by adding these 3 lines of code to the current best answer before plt.show() :
ax_yDist.invert_xaxis()
ax_yDist.yaxis.tick_right()
ax_yCumDist.invert_xaxis()
The advantage is that any person who is visualizing it can compare easily the two histograms just by moving and rotating clockwise the right histogram on their mind.
On contrast, in the plot of the question and in all other answers, if you want to compare the two histograms, your first reaction is to rotate the right histogram counterclockwise, which leads to wrong conclusions because the y axis gets inverted. Indeed, the right CDF of the current best answer looks decreasing at first sight:
I am plotting a 2D data array with imshow in matplotlib. I have a problem trying to scale the resulting plot. The size of the array is 30x1295 points, but the extent in units are:
extent = [-130,130,0,77]
If I plot the array without the extent, I get the right plot, but if I use extent, I get this plot with the wrong aspect.
It is a pretty beginner question, but there is always a first time: How I can control the aspect and the size of the plot at the same time?
Thanks,
Alex
P.D. The code is, for the right case:
imshow(np.log10(psirhoz+1e-5),origin='lower')
and for the wrong one:
imshow(np.log10(psirhoz+1e-5),origin='lower',
extent =[z_ax.min(),z_ax.max(),rho_ax.min(),rho_ax.max()])
I hope this clarify a bit things.
I'm guessing that you're wanting "square" pixels in the final plot?
For example, if we plot random data similar to yours:
import numpy as np
import matplotlib.pyplot as plt
data = np.random.random((30, 1295))
fig, ax = plt.subplots()
ax.imshow(data, extent=[-130,130,0,77])
plt.show()
We'll get an image with "stretched" pixels:
So, first off, "aspect" in matplotlib refers to the aspect in data coordinates. This means we have to jump through a couple of hoops to get what you want.
import numpy as np
import matplotlib.pyplot as plt
def main():
shape = (30, 1295)
extent = [-130,130,0,77]
data = np.random.random(shape)
fig, ax = plt.subplots()
ax.imshow(data, extent=extent, aspect=calculate_aspect(shape, extent))
plt.show()
def calculate_aspect(shape, extent):
dx = (extent[1] - extent[0]) / float(shape[1])
dy = (extent[3] - extent[2]) / float(shape[0])
return dx / dy
main()
In this case, pyplot.matshow() might also be useful:
from matplotlib import pyplot as plt
import numpy as np
dat = np.array(range(9)).reshape(3,3)
plt.matshow(dat)
plt.show()
result:
Using matplotlib in Python I drew a 3D graph. When I rotate the graph I noticed that the axes labels swap automatically which does not look interesting or helping to me. In fact it disturbs my focusing on the purpose of rotation which is to explore visually the presented data.
Q: How to disable auto-swapping axes labels while rotating in matplotlib?
I grabbed some ideas from SO, examined many and finally developed the following solution. It simply works.
from __future__ import division
import scipy as sp
import mpl_toolkits.mplot3d as a3d
import pylab as pl
nan = sp.nan
def axesoff():
box = [[-1,1,1,-1,-1,1,1,-1,-1,-1,nan,1,1,nan,1,1,nan,-1,-1],
[-1,-1,-1,-1,1,1,1,1,-1,-1,nan,-1,1,nan,1,-1,nan,1,1],
[-1,-1,1,1,1,1,-1,-1,-1,1,nan,-1,-1,nan,1,1,nan,-1,1]]
ax3.plot(*box,color='k')
for axis in (ax3.w_xaxis,ax3.w_yaxis,ax3.w_zaxis):
for obj in axis.get_ticklines(): obj.set_visible(False)
axis.set_ticklabels('')
axis.line.set_visible(False)
axis.pane.set_visible(False)
ax3.grid(False)
ax3.axis('equal')
#------here we go
x,y,z = sp.random.uniform(low=-1,high=1,size=(3,1000))
c = (x+1)+(y+1)+(z+1)
s = c*10
ax3 = a3d.Axes3D(pl.figure())
ax3.scatter(x,y,z,lw=0,s=s,c=c,alpha=0.5)
axesoff()
pl.show()