Re-plotting lines after clearing Axes3D - matplotlib

I have a list to which I append Axes3D plots. Like this:
self.myList.append(self.axes.plot(xValues,
yValues,
zValues,
picker=self.line_picker)[0])
When that line of code runs, the instance returned by the plot() function is stored in the list AND the line is automatically plotted, which is what I want. This code runs on program startup and draws a variable number of lines.
Now what I need to do is this:
When the user checks a checkbox, I want to REPLACE the data that is currently plotted with another set of data. Then, when the user unchecks the checkbox, I want the initial data to be plotted again.
I have no problem with clearing the initial data; I simply do:
self.axes.clear()
self.canvas.draw()
self.axes.mouse_init()
and now I have a blank 3D graph.
How can I re-plot the same data once it is cleared? Can I somehow use the plot instances stored in the list and re-plot them?
My ultimate question is, do I have to re-plot the original plot using the raw data like I did the first time, or can I somehow hide/disable the initial axes and then simply restore it?

Probably the easiest solution is to keep a reference of the items and simply toggle the specific artist's visible attribute:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
plt.ion()
ax = plt.axes(projection='3d')
n = 3
xs = np.random.randint(23, 32, n)
ys = np.random.randint(0, 100, n)
zs = np.random.randint(0, 10, n)
scatter = ax.scatter(xs, ys, zs=zs)
plt.draw()
scatter.set_visible(False)
plt.draw()
scatter.set_visible(True)
plt.draw()

Related

plotting graph of 3 parameters (PosX ,PosY) vs Time .It is a timeseries data

I am new to this module. I have time series data for movement of particle against time. The movement has its X and Y component against the the time T. I want to plot these 3 parameters in the graph. The sample data looks like this. The first coloumn represent time, 2nd- Xcordinate , 3rd Y-cordinate.
1.5193 618.3349 487.5595
1.5193 619.3349 487.5595
2.5193 619.8688 489.5869
2.5193 620.8688 489.5869
3.5193 622.9027 493.3156
3.5193 623.9027 493.3156
If you want to add a 3rd info to a 2D curve, one possibility is to use a color mapping instituting a relationship between the value of the 3rd coordinate and a set of colors.
In Matplotlib we have not a direct way of plotting a curve with changing color, but we can fake one using matplotlib.collections.LineCollection.
In the following I've used some arbitrary curve but I have no doubt that you could adjust my code to your particular use case if my code suits your needs.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
# e.g., a Lissajous curve
t = np.linspace(0, 2*np.pi, 6280)
x, y = np.sin(4*t), np.sin(5*t)
# to use LineCollection we need an array of segments
# the canonical answer (to upvote...) is https://stackoverflow.com/a/58880037/2749397
points = np.array([x, y]).T.reshape(-1,1,2)
segments = np.concatenate([points[:-1],points[1:]], axis=1)
# instantiate the line collection with appropriate parameters,
# the associated array controls the color mapping, we set it to time
lc = LineCollection(segments, cmap='nipy_spectral', linewidth=6, alpha=0.85)
lc.set_array(t)
# usual stuff, just note ax.autoscale, not needed here because we
# replot the same data but tipically needed with ax.add_collection
fig, ax = plt.subplots()
plt.xlabel('x/mm') ; plt.ylabel('y/mm')
ax.add_collection(lc)
ax.autoscale()
cb = plt.colorbar(lc)
cb.set_label('t/s')
# we plot a thin line over the colormapped line collection, especially
# useful when our colormap contains white...
plt.plot(x, y, color='black', linewidth=0.5, zorder=3)
plt.show()

Matplotlib streamplot with streamlines that don't break or end

I'd like to make a streamplot with lines that don't stop when they get too close together. I'd rather each streamline be calculated in both directions until it hits the edge of the window. The result is there'd be some areas where they'd all jumble up. But that's what I want.
I there anyway to do this in matplotlib? If not, is there another tool I can use for this that could interface with python/numpy?
import numpy as np
import matplotlib.pyplot as plt
Y,X = np.mgrid[-10:10:.01, -10:10:.01]
U, V = Y**2, X**2
plt.streamplot(X,Y, U,V, density=1)
plt.show(False)
Ok, I've figured out I can get mostly what I want by turning up the density a lot and using custom start points. I'm still interested if there is a better or alternate way to do this.
Here's my solution. Doesn't it look so much better?
import numpy as np
import matplotlib.pyplot as plt
Y,X = np.mgrid[-10:10:.01, -10:10:.01]
y,x = Y[:,0], X[0,:]
U, V = Y**2, X**2
stream_points = np.array(zip(np.arange(-9,9,.5), -np.arange(-9,9,.5)))
plt.streamplot(x,y, U,V, start_points=stream_points, density=35)
plt.show(False)
Edit: By the way, there seems to be some bug in streamplot such that start_points keyword only works if you use 1d arrays for the grid data. See Python Matplotlib Streamplot providing start points
As of Matplotlib version 3.6.0, an optional parameter broken_streamlines has been added for disabling streamline breaks.
Adding it to your snippet produces the following result:
import numpy as np
import matplotlib.pyplot as plt
Y,X = np.mgrid[-10:10:.01, -10:10:.01]
U, V = Y**2, X**2
plt.streamplot(X,Y, U,V, density=1, broken_streamlines=False)
plt.show(False)
Note
This parameter just extends the streamlines which were originally drawn (as in the question). This means that the streamlines in the modified plot above are much more uneven than the result obtained in the other answer, with custom start_points. The density of streamlines on any stream plot does not represent the magnitude of U or V at that point, only their direction. See the documentation for the density parameter of matplotlib.pyplot.streamplot for more details on how streamline start points are chosen by default, when they aren't specified by the optional start_points parameter.
For accurate streamline density, consider using matplotlib.pyplot.contour, but be aware that contour does not show arrows.
Choosing start points automatically
It may not always be easy to choose a set of good starting points automatically. However, if you know the streamfunction corresponding to the flow you wish to plot you can use matplotlib.pyplot.contour to produce a contour plot (which can be hidden from the output), and then extract a suitable starting point from each of the plotted contours.
In the following example, psi_expression is the streamfunction corresponding to the flow. When modifying this example for your own needs, make sure to update both the line defining psi_expression, as well as the one defining U and V. Ensure these both correspond to the same flow.
The density of the streamlines can be altered by changing contour_levels. Here, the contours are uniformly distributed.
import numpy as np
import matplotlib.pyplot as plt
import sympy as sy
x, y = sy.symbols("x y")
psi_expression = x**3 - y**3
psi_function = sy.lambdify((x, y), psi_expression)
Y, X = np.mgrid[-10:10:0.01, -10:10:0.01]
psi_evaluated = psi_function(X, Y)
U, V = Y**2, X**2
contour_levels = np.linspace(np.amin(psi_evaluated), np.amax(psi_evaluated), 30)
# Draw a temporary contour plot.
temp_figure = plt.figure()
contour_plot = plt.contour(X, Y, psi_evaluated, contour_levels)
plt.close(temp_figure)
points_list = []
# Iterate over each contour.
for collection in contour_plot.collections:
# Iterate over each segment in this contour.
for path in collection.get_paths():
middle_point = path.vertices[len(path.vertices) // 2]
points_list.append(middle_point)
# Reshape python list into numpy array of coords.
stream_points = np.reshape(np.array(points_list), (-1, 2))
plt.streamplot(X, Y, U, V, density=1, start_points=stream_points, broken_streamlines=False)
plt.show(False)

how to use cycler in matplotlib? [duplicate]

Is it possible to query the current state of the matplotlib color cycle? In other words is there a function get_cycle_state that will behave in the following way?
>>> plot(x1, y1)
>>> plot(x2, y2)
>>> state = get_cycle_state()
>>> print state
2
Where I expect the state to be the index of the next color that will be used in a plot. Alternatively, if it returned the next color ("r" for the default cycle in the example above), that would be fine too.
Accessing the color cycle iterator
There's no "user-facing" (a.k.a. "public") method to access the underlying iterator, but you can access it through "private" (by convention) methods. However, you'd can't get the state of an iterator without changing it.
Setting the color cycle
Quick aside: You can set the color/property cycle in a variety of ways (e.g. ax.set_color_cycle in versions <1.5 or ax.set_prop_cycler in >=1.5). Have a look at the example here for version 1.5 or greater, or the previous style here.
Accessing the underlying iterator
However, while there's no public-facing method to access the iterable, you can access it for a given axes object (ax) through the _get_lines helper class instance. ax._get_lines is a touch confusingly named, but it's the behind-the-scenes machinery that allows the plot command to process all of the odd and varied ways that plot can be called. Among other things, it's what keeps track of what colors to automatically assign. Similarly, there's ax._get_patches_for_fill to control cycling through default fill colors and patch properties.
At any rate, the color cycle iterable is ax._get_lines.color_cycle for lines and ax._get_patches_for_fill.color_cycle for patches. On matplotlib >=1.5, this has changed to use the cycler library, and the iterable is called prop_cycler instead of color_cycle and yields a dict of properties instead of only a color.
All in all, you'd do something like:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
color_cycle = ax._get_lines.color_cycle
# or ax._get_lines.prop_cycler on version >= 1.5
# Note that prop_cycler cycles over dicts, so you'll want next(cycle)['color']
You can't view the state of an iterator
However, this object is a "bare" iterator. We can easily get the next item (e.g. next_color = next(color_cycle), but that means that the next color after that is what will be plotted. By design, there's no way to get the current state of an iterator without changing it.
In v1.5 or greater, it would be nice to get the cycler object that's used, as we could infer its current state. However, the cycler object itself isn't accessible (publicly or privately) anywhere. Instead, only the itertools.cycle instance created from the cycler object is accessible. Either way, there's no way to get to the underlying state of the color/property cycler.
Match the color of the previously plotted item instead
In your case, it sounds like you're wanting to match the color of something that was just plotted. Instead of trying to determine what the color/property will be, set the color/etc of your new item based on the properties of what's plotted.
For example, in the case you described, I'd do something like this:
import matplotlib.pyplot as plt
import numpy as np
def custom_plot(x, y, **kwargs):
ax = kwargs.pop('ax', plt.gca())
base_line, = ax.plot(x, y, **kwargs)
ax.fill_between(x, 0.9*y, 1.1*y, facecolor=base_line.get_color(), alpha=0.5)
x = np.linspace(0, 1, 10)
custom_plot(x, x)
custom_plot(x, 2*x)
custom_plot(x, -x, color='yellow', lw=3)
plt.show()
It's not the only way, but its cleaner than trying to get the color of the plotted line before-hand, in this case.
Here's a way that works in 1.5 which will hopefully be future-proof as it doesn't rely on methods prepended with underscores:
colors = plt.rcParams["axes.prop_cycle"].by_key()["color"]
This will give you a list of the colors defined in order for the present style.
Note: In the latest versions of matplotlib (>= 1.5) _get_lines has changed. You now need to use next(ax._get_lines.prop_cycler)['color'] in Python 2 or 3 (or ax._get_lines.prop_cycler.next()['color'] in Python 2) to get the next color from the color cycle.
Wherever possible use the more direct approach shown in the lower part of #joe-kington's answer. As _get_lines is not API-facing it might change again in a not backward compatible manner in the future.
Sure, this will do it.
#rainbow
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0,2*np.pi)
ax= plt.subplot(1,1,1)
ax.plot(np.sin(x))
ax.plot(np.cos(x))
rainbow = ax._get_lines.color_cycle
print rainbow
for i, color in enumerate(rainbow):
if i<10:
print color,
Gives:
<itertools.cycle object at 0x034CB288>
r c m y k b g r c m
Here is the itertools function that matplotlib uses itertools.cycle
Edit: Thanks for the comment, it seems that it is not possible to copy an iterator. An idea would be to dump a full cycle and keep track of which value you are using, let me get back on that.
Edit2: Allright, this will give you the next color and make a new iterator that behaves as if next was not called. This does not preserve the order of coloring, just the next color value, I leave that to you.
This gives the following output, notice that steepness in the plot corresponds to index, eg first g is the bottomest graph and so on.
#rainbow
import matplotlib.pyplot as plt
import numpy as np
import collections
import itertools
x = np.linspace(0,2*np.pi)
ax= plt.subplot(1,1,1)
def create_rainbow():
rainbow = [ax._get_lines.color_cycle.next()]
while True:
nextval = ax._get_lines.color_cycle.next()
if nextval not in rainbow:
rainbow.append(nextval)
else:
return rainbow
def next_color(axis_handle=ax):
rainbow = create_rainbow()
double_rainbow = collections.deque(rainbow)
nextval = ax._get_lines.color_cycle.next()
double_rainbow.rotate(-1)
return nextval, itertools.cycle(double_rainbow)
for i in range(1,10):
nextval, ax._get_lines.color_cycle = next_color(ax)
print "Next color is: ", nextval
ax.plot(i*(x))
plt.savefig("SO_rotate_color.png")
plt.show()
Console
Next color is: g
Next color is: c
Next color is: y
Next color is: b
Next color is: r
Next color is: m
Next color is: k
Next color is: g
Next color is: c
I just want to add onto what #Andi said above. Since color_cycle is deprecated in matplotlib 1.5, you have to use prop_cycler, however, Andi's solution (ax._get_lines.prop_cycler.next()['color']) returned this error for me:
AttributeError: 'itertools.cycle' object has no attribute 'next'
The code that worked for me was: next(ax._get_lines.prop_cycler), which actually isn't far off from #joe-kington's original response.
Personally, I ran into this problem when making a twinx() axis, which reset the color cycler. I needed a way to make the colors cycle correctly because I was using style.use('ggplot'). There might be an easier/better way to do this, so feel free to correct me.
Since matplotlib uses itertools.cycle we can actually look through the entire color cycle and then restore the iterator to its previous state:
def list_from_cycle(cycle):
first = next(cycle)
result = [first]
for current in cycle:
if current == first:
break
result.append(current)
# Reset iterator state:
for current in cycle:
if current == result[-1]:
break
return result
This should return the list without changing the state of the iterator.
Use it with matplotlib >= 1.5:
>>> list_from_cycle(ax._get_lines.prop_cycler)
[{'color': 'r'}, {'color': 'g'}, {'color': 'b'}]
or with matplotlib < 1.5:
>>> list_from_cycle(ax._get_lines.color_cycle)
['r', 'g', 'b']
The simplest way possible I could find without doing the whole loop through the cycler is ax1.lines[-1].get_color().
How to access the color (and complete style) cycle?
The current state is stored in ax._get_lines.prop_cycler.
There are no built-in methods to expose the "base list" for a generic itertools.cycle, and in particular for ax._get_lines.prop_cycler (see below).
I have posted here a few functions to get info on a itertools.cycle.
One could then use
style_cycle = ax._get_lines.prop_cycler
curr_style = get_cycle_state(style_cycle) # <-- my (non-builtin) function
curr_color = curr_style['color']
to get the current color without changing the state of the cycle.
TL;DR
Where is the color (and complete style) cycle stored?
The style cycle is stored in two different places, one for the default, and one for the current axes (assuming import matplotlib.pyplot as plt and ax is an axis handler):
default_prop_cycler = plt.rcParams['axes.prop_cycle']
current_prop_cycle = ax._get_lines.prop_cycler
Note these have different classes.
The default is a "base cycle setting" and it does not know about any current state for any axes, while the current knows about the cycle to follow and its current state:
print('type(default_prop_cycler) =', type(default_prop_cycler))
print('type(current_prop_cycle) =', type(current_prop_cycle))
[]: type(default_prop_cycler) = <class 'cycler.Cycler'>
[]: type(current_prop_cycle) = <class 'itertools.cycle'>
The default cycle may have several keys (properties) to cycle, and one can get only the colors:
print('default_prop_cycler.keys =', default_prop_cycler.keys)
default_prop_cycler2 = plt.rcParams['axes.prop_cycle'].by_key()
print(default_prop_cycler2)
print('colors =', default_prop_cycler2['color'])
[]: default_prop_cycler.keys = {'color', 'linestyle'}
[]: {'color': ['r', 'g', 'b', 'y'], 'linestyle': ['-', '--', ':', '-.']}
[]: colors = ['r', 'g', 'b', 'y']
One could even change the cycler to use for a given axes, after defining that custom_prop_cycler, with
ax.set_prop_cycle(custom_prop_cycler)
But there are no built-in methods to expose the "base list" for a generic itertools.cycle, and in particular for ax._get_lines.prop_cycler.
In matplotlib version 2.2.3 there is a get_next_color() method on the _get_lines property:
import from matplotlib import pyplot as plt
fig, ax = plt.subplots()
next_color = ax._get_lines.get_next_color()
get_next_color() returns an html color string, and advances the color cycle iterator.
minimal working example
I struggelt with this quite a few times already.
This is a minimal working example for Andis answer.
code
import numpy as np
import matplotlib.pyplot as plt
xs = np.arange(10)
fig, ax = plt.subplots()
for ii in range(3):
color = next(ax._get_lines.prop_cycler)['color']
lbl = 'line {:d}, color {:}'.format(ii, color)
ys = np.random.rand(len(xs))
ax.plot(xs, ys, color=color, label=lbl)
ax.legend()

Matplotlib: why are plots always closed shapes?

Using 1.5.1 in Python 2.7.
I'm creating a figure, adding an axes object to it, creating a canvas, and putting it into a window. To draw a simple graph, I set the X and Y limits in the axes object, and then call the plot member function with a numpy arange of values and an array of y values of the same length, along with a few formatting options.
What I get is a nice graph of my data, but it is drawn as a closed curve, meaning that there is a diagonal line leading from the end of my graph back to the beginning.
Why would it do this? I can see the occasional utility of an option that does this, when the X values aren't monotonically increasing (say, to draw a polygon), but it hardly seems like a reasonable default. I don't see any axes attribute that would affect this, or any plot parameter. Does anyone know how to make it not wrap around like this?
EDIT: here is some sample code. It assumes PyGTK as the GUI environment:
import numpy
import gtk
import matplotlib
from matplotlib.figure import Figure
from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas
class junk:
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.connect('destroy', self.destroy)
self.window.set_title('junk')
self.window.resize(400, 400)
self.figure = Figure()
self.axes = self.figure.add_axes((0, 0, 1, 1))
self.canvas = FigureCanvas(self.figure)
self.canvas.show()
self.window.add(self.canvas)
self.axes.set_xlim(-10, 12)
self.axes.set_ylim(-1, 122)
x = numpy.arange(-9, 12)
self.axes.plot(x, x * x, linestyle = 'solid')
self.canvas.draw()
self.window.show_all()
def destroy(self, widget, data = None):
gtk.main_quit()
def main(self):
gtk.main()
if __name__ == '__main__':
app = junk()
app.main()
This displays an off-center parabola, and the result looks like this:
Now change the lower Y limit from -1 to 1, so that it clips the bottom a little, and the result looks like this:
This shows that if more than one path is needed to draw the graph, each one has the spurious wraparound.
I'm doing this on Windows, but I had this same problem a couple years ago running on a Gumstix SOM running Linux.
I can not reproduce your issue with the given code
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x = np.arange(-9, 12)
ax.plot(x, x*x)
plt.show()
A line is drawn between the points in the order you pass them in. This is the behavior so you can plot things with are not strict functions of x.
BrenBarn identified the solution in the comment to the original post: use the GTKAgg backend instead of the GTK backend. Thanks.

matplotlib real-time linear line

I am having a major setback on this question on a while now...
import numpy as np
import matplotlib.pyplot as plt
plt.ion()
fig = plt.figure(1)
ax = fig.add_subplot(111)
ax.set_title("linear realtime")
line, = ax.plot([],[])
i = 0
while ( i < 1000 ):
#EDIT:
# this is just sample data, but I would eventually like to set data
# where it can be floating numbers...
line.set_data(i,i)
fig.canvas.draw()
i += 1
I am trying to draw a linear line in real time but I am unable to come up with the result. Thanks in advance. So far, I have a figure coming up but nothing is being drawn on the canvas.
EDIT:
Interesting.... I am now able to plot the dots on the line but now, I am unable to show their connectivity between each point... I also noticed that if you removed ko- when it is being plotted... nothing appears, does anybody know why?
import numpy as n
import pylab as p
import time
x=0
y=0
p.ion()
fig=p.figure(1)
ax=fig.add_subplot(111)
ax.set_xlim(0,10)
ax.set_ylim(0,10)
line,=ax.plot(x,y,'ko-')
for i in range(10):
x = i
y = i
line.set_data(x,y)
p.draw()
add a p.pause(.001) in your loop. You need to allow time for the gui event loops to trigger and update the display.
This is related to issue #1646.
The other issue you have is that when you do set_data it replaces the data that is plotted with the x and y passed in, not append to the data that is already there. (To see this clearly use p.pause(1)) When you remove 'ko-', which defaults to no marker with a line connecting points you are plotting a single point, hence nothing shows up.
I think you intended to write this:
x=0
y=0
fig=plt.figure(1)
ax=fig.add_subplot(111)
ax.set_xlim(0,10)
ax.set_ylim(0,10)
line,=ax.plot(x,y,'ko-')
for i in range(10):
x = np.concatenate((line.get_xdata(),[i]))
y = np.concatenate((line.get_ydata(),[i]))
line.set_data(x,y)
plt.pause(1)