Place text label in 3D coordinates that are not data-dependent - python

I have a subplot containing 2x2 3-D graphs. In this graph I want to place a text box, to put some additional information.
To this end, I've found the matplotlib.pyplot.text function.
However, this function uses the data-coordinates as input. As my four subplots all have a different range of z-values, placing a text at a single z-coordinate will give them other relative placements.
MWE
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
def randrange(n, vmin, vmax):
return (vmax-vmin)*np.random.rand(n) + vmin
fig = plt.figure()
for plotnr in [1,2,3,4]:
ax = fig.add_subplot(2, 2, plotnr, projection='3d')
n = 10
for c, m, zl, zh in [('r', 'o', -50, -25), ('b', '^', -30, -5)]:
xs = randrange(n, 23, 32)
ys = randrange(n, 0, 100)
zs = np.divide(randrange(n, zl, zh),1000/(10**(plotnr-1)))
ax.scatter(xs, ys, zs, c=c, marker=m)
ax.text(27, 60,3,'matplotlib', ha='center', va='center',transform=ax.transAxes)
plt.show()
As you can see from the image the 'matplotlib' is moving around, or isn't even present at all. I have the idea that this is becauseo of the changing values of z.
Is it possible to place the text box at coordinates relative to the image, rather than at coordinates relative to the data?

I've found the answer here, it's possible to switch to 2D label placement using:
ax.text2D(0.05, 0.95, "2D Text", transform=ax.transAxes)

Related

Exclude grid lines from boundaries of plot in python?

I am looking for a way to remove grid lines from the axes of a plot, but unfortunately, I've not come up to a solution for this issue and neither found it anywhere else.
Is there a way to remove certain grid lines or choose which grid lines to plot without having to rely on the automatic function?
I've coded a quick example outputting a plot for illustration below and would be glad for any help.
import matplotlib.pyplot as plt
import numpy as np
def linear(x, a, b):
return a*x+b
x = np.linspace(0, 1, 20)
y = linear(x, a=1, b=2)
fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(10, 6))
ax.plot(x, y, color='darkred')
ax.set_xlim(0, 1)
ax.set_ylim(2, 3)
ax.grid(which='major', axis='y', linestyle='--', color='grey', linewidth=3)
plt.savefig("Testplot.pdf", format='pdf')
The major gridlines appear at positions of the major ticks. You can set any individual gridline invisible. E.g. to set the fifth gridline off,
ax.yaxis.get_major_ticks()[5].gridline.set_visible(False)
Here is a proposition with ticks and horizontal lines. The idea is to specify the ticks (not really necessary, but why not), and then to draw horizontal dashes lines where you want your grid.
import matplotlib.pyplot as plt
import numpy as np
def linear(x, a, b):
return a*x+b
x = np.linspace(0, 1, 20)
y = linear(x, a=1, b=2)
fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(10, 6))
ax.plot(x, y, color='darkred')
ax.set_xlim(0, 1)
ax.set_ylim(2, 3)
yticks = np.arange(2, 3, 0.2)
grid_lines = np.arange(2.2, 3, 0.2)
ax.set_yticks(yticks)
for grid in grid_lines:
ax.axhline(grid, linestyle='--', color='grey', linewidth=3)
Output:
Why did I include the yticks? Well you could design a function which takes in input the yticks and return the position of the grid lines accordingly. I think it could be handy depending on your needs. Good luck!

How to keep interactivity of 3D plot after converting jupyter notebook(python) to html?

using matplotlib I can make an interactive 3D plot in jupyter notebook .
I use the following example code . It s easy and not the problem. But after converting the notebook in html, the interactive 3D plot became non-interactive. I mean, it can not be rotated like before. It is just a static image. I used menue File->Download as -> HTML to convert the notebook to html file.
But I just want to keep the interactivity even after converting. Does anyone have a good idea?
I have already known that there is a way for 2D interactive plot. But I can not find any idea to do this for a 3D interactive plot.
Thanks for any idea.
'''
==============
3D scatterplot
==============
Demonstration of a basic scatterplot in 3D.
'''
%matplotlib notebook
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import animation
def randrange(n, vmin, vmax):
'''
Helper function to make an array of random numbers having shape (n, )
with each number distributed Uniform(vmin, vmax).
'''
return (vmax - vmin)*np.random.rand(n) + vmin
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
n = 100
# For each set of style and range settings, plot n random points in the box
# defined by x in [23, 32], y in [0, 100], z in [zlow, zhigh].
for c, m, zlow, zhigh in [('r', 'o', -50, -25), ('b', '^', -30, -5)]:
xs = randrange(n, 23, 32)
ys = randrange(n, 0, 100)
zs = randrange(n, zlow, zhigh)
ax.scatter(xs, ys, zs, c=c, marker=m)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.show()

3dplot scatter points via python

I just used numpy.loadtxt('filename', usecols=(0,)) to load a csv with the following format:
x,y,z
1.1,2.2,3.3
5.5,1.45,6.77
(There are ~1M lines). I'd like to make a scatterplot. I searched the web and found numpy.meshgrid and mlab.surf but I'm not sure what to do. Please point me in the right direction.
I think you can use matplotlib, it's very powerful, and widely used, most importantly, it has good document and is easy to use.
Hope helps!
There is a very simple example from here.
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
def randrange(n, vmin, vmax):
return (vmax-vmin)*np.random.rand(n) + vmin
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
n = 100
for c, m, zl, zh in [('r', 'o', -50, -25), ('b', '^', -30, -5)]:
xs = randrange(n, 23, 32)
ys = randrange(n, 0, 100)
zs = randrange(n, zl, zh)
ax.scatter(xs, ys, zs, c=c, marker=m)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.show()
You just need to parse your data instead of using randrange.

matplotlib 3d back to 2d

I have a matplotlib figure that I want to be able to switch between 2D and 3D projections. I can go from 2D to 3D but I can't seem to work out how to go the other way. Example...
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
def randrange(n, vmin, vmax):
return (vmax-vmin)*np.random.rand(n) + vmin
fig = plt.figure()
# Create a 3D scatter plot...
ax = fig.add_subplot(111, projection='3d')
n = 100
for c, m, zl, zh in [('r', 'o', -50, -25), ('b', '^', -30, -5)]:
xs = randrange(n, 23, 32)
ys = randrange(n, 0, 100)
zs = randrange(n, zl, zh)
ax.scatter(xs, ys, zs, c=c, marker=m)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
# Now I want a 2D plot...
ax.cla()
ax = fig.add_subplot(111)
ax.plot(xs, ys)
plt.show()
The plot stays in the 3D projection and projection="2D" isn't a valid kwarg...
I thought perhaps ax.clf() would do what I wanted and let me define a new figure. But it just gives me the following error:
ValueError: Unknown element o
Can anyone give me a hint as to the solution to this? Is the ValueError related to the problem or a hint to something else wrong with my setup? Is there a kwarg to switch the projection from 3D to 2D?
Many thanks in advance for any pointer you can provide.
Dan
Let me start out by saying that if you boosted your acceptance rate (which is currently 0%) by going and demarcating the answers from your previous questions that you actually used, then perhaps more people would be willing to help you.
Now, to answer your question, there is no '2d' projection kwarg. However, if you want to go through and quickly decide which type of projection you would like, depending on a keyword 'q', the following should help you. I also altered the basic setup to avoid confusion between your different kinds of plots, since you had some plotting calls outside of the loop, and generally clean up your organization.
Hope this helps.
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
def randrange(n, vmin, vmax):
return (vmax-vmin)*np.random.rand(n) + vmin
fig = plt.figure()
plt.clf()
q='2d'
n = 100
for c, m, zl, zh in [('r', 'o', -50, -25), ('b', '^', -30, -5)]:
xs = randrange(n, 23, 32)
ys = randrange(n, 0, 100)
zs = randrange(n, zl, zh)
if q=='3d':
# Create a 3D scatter plot...
ax = fig.add_subplot(111, projection='3d')
ax.scatter(xs, ys, zs, c=c, marker=m)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.show()
else:
plt.clf()
ax = fig.add_subplot(111)
ax.plot(xs, ys)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
plt.show()
I believe I have found one possible solution, although it seems to result in a bit of a memory issue. I suspect that it isn't actually deleting the initial plot data, just removing it from the figure so memory usage does climb every time the projection is changed.
# Delete the 3D subplot
self.fig.delaxes(self.axes)
# Create a new subplot that is 2D
self.axes = self.fig.add_subplot(111)
# 2D scatter
self.axes.plot(10*np.random.randn(100), 10*np.random.randn(100), 'o')
# Update the figure
self.canvas.draw()
I was having the same problem and tried the accepted solution (posted by Dan). It worked, but gave me the following warning:
"UserWarning: This figure includes Axes that are not compatible with
tight_layout, so its results might be incorrect."
However if I use:
self.figure.clf()
self.axes = self.figure.add_subplot(111)
then it works without any warnings.

Any easy way to plot a 3d scatter in Python that I can rotate around?

Currently I'm using matplotlib to plot a 3d scatter and while it gets the job done, I can't seem to find a way to rotate it to see my data better.
Here's an example:
import pylab as p
import mpl_toolkits.mplot3d.axes3d as p3
#data is an ndarray with the necessary data and colors is an ndarray with
#'b', 'g' and 'r' to paint each point according to its class
...
fig=p.figure()
ax = p3.Axes3D(fig)
ax.scatter(data[:,0], data[:,2], data[:,3], c=colors)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
fig.add_axes(ax)
p.show()
I'd like a solution that lets me do it during execution time but as long as I can rotate it and it's short/quick I'm fine with it.
Here's a comparison of the plots produced after applying a PCA to the iris dataset:
1. mayavi
2. matplotlib
Mayavi makes it easier to visualize the data, but MatPlotLib looks more professional. Matplotlib is also lighter.
Well, first you need to define what you mean by "see my data better"...
You can rotate and zoom in on the plot using the mouse, if you're wanting to work interactively.
If you're just wanting to rotate the axes programatically, then use ax.view_init(elev, azim) where elev and azim are the elevation and azimuth angles (in degrees) that you want to view your plot from.
Alternatively, you can use the ax.elev, ax.azim, and ax.dist properties to get/set the elevation, azimuth, and distance of the current view point.
Borrowing the source from this example:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
def randrange(n, vmin, vmax):
return (vmax-vmin)*np.random.rand(n) + vmin
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
n = 100
for c, m, zl, zh in [('r', 'o', -50, -25), ('b', '^', -30, -5)]:
xs = randrange(n, 23, 32)
ys = randrange(n, 0, 100)
zs = randrange(n, zl, zh)
ax.scatter(xs, ys, zs, c=c, marker=m)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.show()
We get a nice scatterplot:
You can rotate the axes programatically as shown:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
def randrange(n, vmin, vmax):
return (vmax-vmin)*np.random.rand(n) + vmin
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
n = 100
for c, m, zl, zh in [('r', 'o', -50, -25), ('b', '^', -30, -5)]:
xs = randrange(n, 23, 32)
ys = randrange(n, 0, 100)
zs = randrange(n, zl, zh)
ax.scatter(xs, ys, zs, c=c, marker=m)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
ax.azim = 200
ax.elev = -45
plt.show()
Hope that helps a bit!
Using mayavi, you can create such a plot with
import enthought.mayavi.mlab as mylab
import numpy as np
x, y, z, value = np.random.random((4, 40))
mylab.points3d(x, y, z, value)
mylab.show()
The GUI allows rotation via clicking-and-dragging, and zooming in/out via right-clicking-and-dragging.

Categories