Sequential plotting on one pair of axes using python - python

The following is code to plot cosine and sine on one pair of axes, from DeCaria's wonderful book Python Programming and Visualization for Scientists. How to I produce the same plot but by plotting cos first, pausing, then plotting sin? Ultimately, I want to plot many functions sequentially and be able to watch for patterns in the family of functions as they emerge.
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(0,100.0)
y1 = np.cos(2*np.pi*x/50.0)
y2 = np.sin(2*np.pi*x/50.0)
plt.plot(x, y1, 'b-', label = 'Cosine')
plt.plot(x, y2, 'r--', label = 'Sine')
plt.legend(('Cosine', 'Sine'))
plt.show()
Thank you for any help that might be available.

A simple way to do what you want is to set block = False in your plt.show() command and then update the figure with Figure.canvas.draw() every time you draw a new curve. Here is an example that illustrates how this would work:
import matplotlib.pyplot as plt
import numpy as np
fig,ax = plt.subplots()
x = np.arange(0,100.0)
functions = [
('Cosine', np.cos(2*np.pi*x/50.0), 'b-'),
('Sine', np.sin(2*np.pi*x/50.0), 'r--'),
]
plt.show(block = False)
for label, y, style in functions:
ax.plot(x, y, style, label = label)
ax.legend()
fig.canvas.draw()
input('press <ENTER>')
This code will loop through all the functions that you define in functions (here I used tuples that hold the function name, the y-values, and the plot style) and pause after every update, waiting for keyboard input in the terminal. If you somehow want to only interact with the figure (no terminal input), you will have to start fiddling with matplotlib events. Hope this helps.

Related

Dynamically update plot Matplotlib Python (for unsteady heat diffusion)

I am new to python and trying to do what have been doing in MATLAB for so long. My current challenge is to dynamically update a plot without drawing a new figure in a for or while loop. I am aware there are similar questions and answers but most of them are too complicated and I believe it should be easier.
I got the example from here
https://pythonspot.com/matplotlib-update-plot/
But I can't see the figure, no error, no nothing. I added two lines just to see if I can see the static plot and I can.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10*np.pi, 100)
y = np.sin(x)
# This is just a test just to see if I can see the plot window
plt.plot(x, y)
plt.show()
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
line1, = ax.plot(x, y, 'b-')
for phase in np.linspace(0, 10*np.pi, 100):
line1.set_ydata(np.sin(0.5 * x + phase))
fig.canvas.draw()
Any idea why I can't see the dynamic plot?
Thank you
Erdem
try to add plt.pause(0.0001) inside the loop after plt.show(block=False), and a final plt.show() outside the loop. This should work fine with plt.ion(); ref to some older answers Plot one figure at a time without closing old figure (matplotlib)

Python: Why do plots of functions with two variables look spurious?

I am using the following code to plot a function of two variables
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
from pylab import meshgrid
import matplotlib.pyplot as plt
x = np.arange(0,1.0,0.01)
y = np.arange(0,1.0,0.01)
X,Y = meshgrid(x, y)
Z = np.sin(2*np.abs(X-0.3)+2*np.sin(5*Y))
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(X, Y, Z)
plt.show()
The result looks like this:
What are those lines that bump out of the surface coming from?
They are not in my data. Changing the resolution to 0.001 fixes them, but this makes the plotting really slow.
By default, ax.plot_surface, ignores some of the data. The problem is that it does not ignore this data to draw the black lines. Therefore, the black lines are based on different data than the connecting blue patches.
This can be turned by passing optional arguments:
ax.plot_surface(X, Y, Z,cstride=1,rstride=1)
It is not clear to me what the idea behind the default settings is. I would be happy to be illuminated.

Python: get corresponding information of data points interactively with mouse

I have a set of data in my research project, and each data point contains lots of related information and it is impossible to show all of them in a figure. What I would like to do to visualize these data is to plot the data points using their two key parameters, and when the mouse cursor hovers over one data point, it shows all information related to this data point. I am wondering if there are any ways to implement this?
Thanks!
You have a matplotlib example that provides something like what you are asking here. #root gave an answer here that provides a basic interface with output for the console (by mouse click over the points):
from matplotlib.pyplot import figure, show
import numpy as npy
from numpy.random import rand
if 1: # picking on a scatter plot (matplotlib.collections.RegularPolyCollection)
x, y, c, s = rand(4, 100)
def onpick3(event):
ind = event.ind
print 'onpick3 scatter:', ind, npy.take(x, ind), npy.take(y, ind)
fig = figure()
ax1 = fig.add_subplot(111)
col = ax1.scatter(x, y, 100*s, c, picker=True)
#fig.savefig('pscoll.eps')
fig.canvas.mpl_connect('pick_event', onpick3)
show()
Yet I would suggest mpldatacursor. #moooeeeep gave an example of it's use here:
import matplotlib.pyplot as plt
from mpldatacursor import datacursor
import random
fig, ax = plt.subplots()
ax.set_title('Click on a dot to display its label')
# Plot a number of random dots
for i in range(1, 1000):
ax.scatter([random.random()], [random.random()], label='$ID: {}$'.format(i))
# Use a DataCursor to interactively display the label for a selected line...
datacursor(formatter='{label}'.format)
plt.show()
With this result:

Plotting second figure with matplotlib while first is still open

K here's a more precise example of what I am trying to do. I am using WXBuilder for Python as my user interface with multiple plotting functionality i.e. the user must be able to plot a graph based on their chosen parameters. After a graph is plotted I want the user to be able to plot a second without closing the first figure. This is for comparison purposes. Below is an oversimplified example of what I am looking to do.
import matplotlib as plt
def OnPlotClick1(self, event):
plt.plot(self.DateArray1, self.kVAArray2)
plt.show()
def OnPlotClick2(self, event):
plt.plot(self.DateArray1, self.kVAArray2)
plt.show()
Now I am assuming my problem is arising due plotting and showing() the graph, and therefore the program somehow is blocked from functionality until the first figure or plot window is closed.
I hope this explains my problem better.
You should not block show. Use:
import matplotlib.pylab as plt
plt.plot([1,2,3]) # first plot
plt.show(block=False) # do not block
plt.plot([11,21,31]) # second plot
Each window is in matplotlib parlance, a new figure. You can call plt.subplots twice to create two figures:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 10, 1000)
y1 = np.sin(x)*np.exp(-x/5.0)
y2 = np.sin(x**2)*x
fig1, ax1 = plt.subplots()
ax1.plot(x, y1)
fig2, ax2 = plt.subplots()
ax2.plot(x, y2)
plt.show()
Note that plt.show() starts a GUI event loop and so generally it should only be called once per script.
You can also draw 2 or more than 2 plotters in the same figure
import matplotlib.pyplot as plt
def my_plotter(ax, data1, data2, param_dict):
out = ax.plot(data1, data2, **param_dict)
return out
fig, (ax1, ax2) = plt.subplots(1, 2)
#here you put your data
data1=[0,1,2,3,8]
data2=[0,1,2,3,8]
data3=[0,1,2,3,8]
data4=[0,1,2,3,8]
my_plotter(ax1, data1, data2, {'marker':'x'})
my_plotter(ax2, data3, data4, {'marker':'o'})
plt.show()
You can either follow #(Corrupted MyStack) suggestion or with interactive graphic devide. Run
plt.ion()
once, anytime before you start the plots. To turn it off
plt.ioff()

Plotting mplot3d / axes3D xyz surface plot with log scale?

I've been looking high and low for a solution to this simple problem but I can't find it anywhere! There are a loads of posts detailing semilog / loglog plotting of data in 2D e.g. plt.setxscale('log') however I'm interested in using log scales on a 3d plot(mplot3d).
I don't have the exact code to hand and so can't post it here, however the simple example below should be enough to explain the situation. I'm currently using Matplotlib 0.99.1 but should shortly be updating to 1.0.0 - I know I'll have to update my code for the mplot3d implementation.
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FixedLocator, FormatStrFormatter
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = Axes3D(fig)
X = np.arange(-5, 5, 0.025)
Y = np.arange(-5, 5, 0.025)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.jet, extend3d=True)
ax.set_zlim3d(-1.01, 1.01)
ax.w_zaxis.set_major_locator(LinearLocator(10))
ax.w_zaxis.set_major_formatter(FormatStrFormatter('%.03f'))
fig.colorbar(surf)
plt.show()
The above code will plot fine in 3D, however the three scales (X, Y, Z) are all linear. My 'Y' data spans several orders of magnitude (like 9!), so it would be very useful to plot it on a log scale. I can work around this by taking the log of the 'Y', recreating the numpy array and plotting the log(Y) on a linear scale, but in true python style I'm looking for smarter solution which will plot the data on a log scale.
Is it possible to produce a 3D surface plot of my XYZ data using log scales, ideally I'd like X & Z on linear scales and Y on a log scale?
Any help would be greatly appreciated. Please forgive any obvious mistakes in the above example, as mentioned I don't have my exact code to have and so have altered a matplotlib gallery example from my memory.
Thanks
Since I encountered the same question and Alejandros answer did not produced the desired Results here is what I found out so far.
The log scaling for Axes in 3D is an ongoing issue in matplotlib. Currently you can only relabel the axes with:
ax.yaxis.set_scale('log')
This will however not cause the axes to be scaled logarithmic but labeled logarithmic.
ax.set_yscale('log') will cause an exception in 3D
See on github issue 209
Therefore you still have to recreate the numpy array
I came up with a nice and easy solution taking inspiration from Issue 209. You define a small formatter function in which you set your own notation.
import matplotlib.ticker as mticker
# My axis should display 10⁻¹ but you can switch to e-notation 1.00e+01
def log_tick_formatter(val, pos=None):
return f"$10^{{{int(val)}}}$" # remove int() if you don't use MaxNLocator
# return f"{10**val:.2e}" # e-Notation
ax.zaxis.set_major_formatter(mticker.FuncFormatter(log_tick_formatter))
ax.zaxis.set_major_locator(mticker.MaxNLocator(integer=True))
set_major_locator sets the exponential to only use integers 10⁻¹, 10⁻² without 10^-1.5 etc. Source
Important! remove the cast int() in the return statement if you don't use set_major_locator and you want to display 10^-1.5 otherwise it will still print 10⁻¹ instead of 10^-1.5.
Example:
Try it yourself!
from mpl_toolkits.mplot3d import axes3d
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
fig = plt.figure(figsize=(11,8))
ax1 = fig.add_subplot(121,projection="3d")
# Grab some test data.
X, Y, Z = axes3d.get_test_data(0.05)
# Now Z has a range from 10⁻³ until 10³, so 6 magnitudes
Z = (np.full((120, 120), 10)) ** (Z / 20)
ax1.plot_wireframe(X, Y, Z, rstride=10, cstride=10)
ax1.set(title="Linear z-axis (small values not visible)")
def log_tick_formatter(val, pos=None):
return f"$10^{{{int(val)}}}$"
ax2 = fig.add_subplot(122,projection="3d")
# You still have to take log10(Z) but thats just one operation
ax2.plot_wireframe(X, Y, np.log10(Z), rstride=10, cstride=10)
ax2.zaxis.set_major_formatter(mticker.FuncFormatter(log_tick_formatter))
ax2.zaxis.set_major_locator(mticker.MaxNLocator(integer=True))
ax2.set(title="Logarithmic z-axis (much better)")
plt.savefig("LinearLog.png", bbox_inches='tight')
plt.show()
in osx: ran ax.zaxis._set_scale('log') (notice the underscore)
There is no solution because of the issue 209. However, you can try doing this:
ax.plot_surface(X, np.log10(Y), Z, cmap='jet', linewidth=0.5)
If in "Y" there is a 0, it is going to appear a warning but still works. Because of this warning color maps don´t work, so try to avoid 0 and negative numbers. For example:
Y[Y != 0] = np.log10(Y[Y != 0])
ax.plot_surface(X, Y, Z, cmap='jet', linewidth=0.5)
I wanted a symlog plot and, since I fill the data array by hand, I just made a custom function to calculate the log to avoid having negative bars in the bar3d if the data is < 1:
import math as math
def manual_log(data):
if data < 10: # Linear scaling up to 1
return data/10
else: # Log scale above 1
return math.log10(data)
Since I have no negative values, I did not implement handling this values in this function, but it should not be hard to change it.

Categories

Resources