Expand 3D-Plot to canvas size - python

I would like to know how I can expand the 3D Plot to fit the canvas and if one can zoom in and out of the whole cube.
My goal is to make the Plot catch the mouse inside the whole frame, so you can turn the view. Only makeing the background white isn't enough for me. I want the Plot to file the canvas. Right now the axis tick labels can escape the plot frame. By expanding the plot this would no longer be the case, so I would also like to zoom out of the cube a bit. The standard zoom only changes the axis scale.
Look at the sample below to find out what I mean.
#! coding=utf-8
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def func(X,Y):
return 1/np.pi/2*np.exp(-(X**2+Y**2)/2)
x = np.linspace(-5,5,200)
X,Y = np.meshgrid(x,x)
plt.figure()
sub = plt.subplot(111, projection='3d')
sub.plot_surface(X,Y,func(X,Y))
plt.show()

Related

How to efficiently replace element in plot with ipywidgets?

How can I efficiently display similar plots with ipywidgets using Jupyter Notebook?
I wish to plot interactively a heavy plot (heavy in the sense that it has lots of data points and takes some time to plot it) and modify a single element of it using interact from ipywidgets without replotting all the complicated plot. Is there a builtin functionality to do this?
basically what I'm trying to do is
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact
import matplotlib.patches as patches
%matplotlib inline #ideally nbagg
def complicated plot(t):
plt.plot(HEAVY_DATA_SET)
ax = plt.gca()
p = patches.Rectangle(something_that_depends_on_t)
ax.add_patch(p)
interact(complicatedplot, t=(1, 100));
Right now it takes up to 2 seconds for each replot. I expect there are ways to keep the figure there and just replace that rectangle.
A hack would be to create a figure of the constant part, make it background to the plot and just plot the rectangle part. but the sounds too dirty
Thank you
This is an rough example of an interactive way to change a rectangle width (I'm assuming you are in an IPython or Jupyter notebook):
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import ipywidgets
from IPython.display import display
%matplotlib nbagg
f = plt.figure()
ax = plt.gca()
ax.add_patch(
patches.Rectangle(
(0.1, 0.1), # (x,y)
0.5, # width
0.5, # height
)
)
# There must be an easier way to reference the rectangle
rect = ax.get_children()[0]
# Create a slider widget
my_widget = ipywidgets.FloatSlider(value=0.5, min=0.1, max=1, step=0.1, description=('Slider'))
# This function will be called when the slider changes
# It takes the current value of the slider
def change_rectangle_width():
rect.set_width(my_widget.value)
plt.draw()
# Now define what is called when the slider changes
my_widget.on_trait_change(change_rectangle_width)
# Show the slider
display(my_widget)
Then if you move the slider, the width of the rectangle will change. I'll try to tidy up the code, but you may have the idea. To change the coordinates, you have to do rect.xy = (x0, y0), where x0 and y0 are new coordinates.

Plot a (polar) color wheel based on a colormap using Python/Matplotlib

I am trying to create a color wheel in Python, preferably using Matplotlib. The following works OK:
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
xval = np.arange(0, 2*pi, 0.01)
yval = np.ones_like(xval)
colormap = plt.get_cmap('hsv')
norm = mpl.colors.Normalize(0.0, 2*np.pi)
ax = plt.subplot(1, 1, 1, polar=True)
ax.scatter(xval, yval, c=xval, s=300, cmap=colormap, norm=norm, linewidths=0)
ax.set_yticks([])
However, this attempt has two serious drawbacks.
First, when saving the resulting figure as a vector (figure_1.svg), the color wheel consists (as expected) of 621 different shapes, corresponding to the different (x,y) values being plotted. Although the result looks like a circle, it isn't really. I would greatly prefer to use an actual circle, defined by a few path points and Bezier curves between them, as in e.g. matplotlib.patches.Circle. This seems to me the 'proper' way of doing it, and the result would look nicer (no banding, better gradient, better anti-aliasing).
Second (relatedly), the final plotted markers (the last few before 2*pi) overlap the first few. It's very hard to see in the pixel rendering, but if you zoom in on the vector-based rendering you can clearly see the last disc overlap the first few.
I tried using different markers (. or |), but none of them go around the second issue.
Bottom line: can I draw a circle in Python/Matplotlib which is defined in the proper vector/Bezier curve way, and which has an edge color defined according to a colormap (or, failing that, an arbitrary color gradient)?
One way I have found is to produce a colormap and then project it onto a polar axis. Here is a working example - it includes a nasty hack, though (clearly commented). I'm sure there's a way to either adjust limits or (harder) write your own Transform to get around it, but I haven't quite managed that yet. I thought the bounds on the call to Normalize would do that, but apparently not.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
import matplotlib as mpl
fig = plt.figure()
display_axes = fig.add_axes([0.1,0.1,0.8,0.8], projection='polar')
display_axes._direction = 2*np.pi ## This is a nasty hack - using the hidden field to
## multiply the values such that 1 become 2*pi
## this field is supposed to take values 1 or -1 only!!
norm = mpl.colors.Normalize(0.0, 2*np.pi)
# Plot the colorbar onto the polar axis
# note - use orientation horizontal so that the gradient goes around
# the wheel rather than centre out
quant_steps = 2056
cb = mpl.colorbar.ColorbarBase(display_axes, cmap=cm.get_cmap('hsv',quant_steps),
norm=norm,
orientation='horizontal')
# aesthetics - get rid of border and axis labels
cb.outline.set_visible(False)
display_axes.set_axis_off()
plt.show() # Replace with plt.savefig if you want to save a file
This produces
If you want a ring rather than a wheel, use this before plt.show() or plt.savefig
display_axes.set_rlim([-1,1])
This gives
As per #EelkeSpaak in comments - if you save the graphic as an SVG as per the OP, here is a tip for working with the resulting graphic: The little elements of the resulting SVG image are touching and non-overlapping. This leads to faint grey lines in some renderers (Inkscape, Adobe Reader, probably not in print). A simple solution to this is to apply a small (e.g. 120%) scaling to each of the individual gradient elements, using e.g. Inkscape or Illustrator. Note you'll have to apply the transform to each element separately (the mentioned software provides functionality to do this automatically), rather than to the whole drawing, otherwise it has no effect.
I just needed to make a color wheel and decided to update rsnape's solution to be compatible with matplotlib 2.1. Rather than place a colorbar object on an axis, you can instead plot a polar colored mesh on a polar plot.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
import matplotlib as mpl
# If displaying in a Jupyter notebook:
# %matplotlib inline
# Generate a figure with a polar projection
fg = plt.figure(figsize=(8,8))
ax = fg.add_axes([0.1,0.1,0.8,0.8], projection='polar')
# Define colormap normalization for 0 to 2*pi
norm = mpl.colors.Normalize(0, 2*np.pi)
# Plot a color mesh on the polar plot
# with the color set by the angle
n = 200 #the number of secants for the mesh
t = np.linspace(0,2*np.pi,n) #theta values
r = np.linspace(.6,1,2) #radius values change 0.6 to 0 for full circle
rg, tg = np.meshgrid(r,t) #create a r,theta meshgrid
c = tg #define color values as theta value
im = ax.pcolormesh(t, r, c.T,norm=norm) #plot the colormesh on axis with colormap
ax.set_yticklabels([]) #turn of radial tick labels (yticks)
ax.tick_params(pad=15,labelsize=24) #cosmetic changes to tick labels
ax.spines['polar'].set_visible(False) #turn off the axis spine.
It gives this:

matplotlib imshow how to plot points instead of image?

Here is the code:
plots=imshow(Z,extent=extent,origin,cmap=cmap,aspect='auto',vmin=vmin,vmax=vmax)
plots.plot(Response,component,vrange)
It plots an image based on data list Z, how can I let it print data points instead of an image?
Looks like needs to change to scatter(x, y,...) to plot data points, how difficult it is to change array Z to x, y?
As #jdj081 said, you want to produce a scatter plot.
import os.path
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
# get an image from the sample data directory
fname = os.path.join(matplotlib.get_data_path(), 'sample_data', 'lena.png')
im = plt.imread(fname)
# Reduce the data by a factor of 4 (so that we can see the points)
im = im[::4, ::4]
# generate coordinates for the image. Note that the image is "top down", so the y coordinate goes from high to low.
ys, xs = np.mgrid[im.shape[0]:0:-1, 0:im.shape[1]]
# Scatter plots take 1d arrays of xs and ys, and the colour takes a 2d array,
# with the second dimension being RGB
plt.scatter(xs.flatten(), ys.flatten(), s=4,
c=im.flatten().reshape(-1, 3), edgecolor='face')
plt.show()
You didn't provide much information to go on, but it sounds like you really want to create a scatter plot.
There are many options here depending on what you are plotting and what you want to see, but I have found the following helpful:
Fixing color in scatter plots in matplotlib
import pylab
pylab.figure(1)
pylab.plot([1,2,3,4],[1,7,3,5]) # draw on figure one
pylab.show() # show figure on screen

drawing lines and circles on screen using matplotlib

I want to draw some lines and circles on the screen using of matplotlib. I do not need the X axis and Y axis. Is this possible? How can I do it?
You can hide the axes with axes.get_xaxis().set_visible(False) or by using axis('off').
Example:
from pylab import *
gca().get_xaxis().set_visible(False) # Removes x-axis from current figure
gca().get_yaxis().set_visible(False) # Removes y-axis from current figure
a = arange(10)
b = sin(a)
plot(a, b)
show() # Plot has no x and y axes
If you don't want axes, and are happy to work in the range 0-1:
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
fig = plt.figure()
fig.patches.append(mpatches.Circle([0.5, 0.5], 0.25, transform=fig.transFigure))
fig.show()
There are a couple of benefits to using #Dhara's solution. The primary being you can use a data coordinate system which automatically scales to your data, but if you just want to draw a couple of shapes, my solution works pretty well.
Some useful documentation if you go down the route I have explained:
http://matplotlib.sourceforge.net/api/artist_api.html#matplotlib.patches.Circle
http://matplotlib.sourceforge.net/api/artist_api.html#matplotlib.lines.Line2D
http://matplotlib.sourceforge.net/api/artist_api.html#matplotlib.patches.Rectangle

plot circle on unequal axes with pyplot

I would like to plot a circle on an auto-scaled pyplot-generated graphic. When I run
ax.get_aspect()
hoping for a value with which I could manipulate the axes of a ellipse, pyplot returns:
auto
which is less than useful. What methods would you suggest for plotting a circle on a pyplot plot with unequal axes?
This question is more than one year old, but I too just had this question. I needed to add circles to a matplotlib plot and I wanted to be able to specify the circle's location in the plot using data coordinates, and I didn't want the circle radius to change with panning/zooming (or worse the circle turning into an ellipse).
The best and most simple solution that I've found is simply plot a curve with a single point and include a circle marker:
ax.plot(center_x,center_y,'bo',fillstyle='none',markersize=5)
which gives a nice, fixed-size blue circle with no fill!
It really does depend what you want it for.
The problem with defining a circle in data coordinates when aspect ratio is auto, is that you will be able to resize the figure (or its window), and the data scales will stretch nicely. Unfortunately, this would also mean that your circle is no longer a circle, but an ellipse.
There are several ways of addressing this. Firstly, and most simply, you could fix your aspect ratio and then put a circle on the plot in data coordinates:
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = plt.axes()
ax.set_aspect(1)
theta = np.linspace(-np.pi, np.pi, 200)
plt.plot(np.sin(theta), np.cos(theta))
plt.show()
With this, you will be able to zoom and pan around as per usual, but the shape will always be a circle.
If you just want to put a circle on a figure, independent of the data coordinates, such that panning and zooming of an axes did not effect the position and zoom on the circle, then you could do something like:
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = plt.axes()
patch = mpatches.Circle((325, 245), 180, alpha=0.5, transform=None)
fig.artists.append(patch)
plt.show()
This is fairly advanced mpl, but even so, I think it is fairly readable.
HTH,
Building on #user3208430, if you want the circle to always appear at the same place in the axes (regardless of data ranges), you can position it using axes coordinates via transform:
ax.plot(.94, .94, 'ro', fillstyle='full', markersize=5, transform=ax.transAxes)
Where x and y are between [0 and 1]. This example places the marker in the upper right-hand corner of the axes.

Categories

Resources