I want to insert some arrows into a plot of some exponential distributions:
import pylab as pl
import numpy as np
def gauss2d(x,sigma):
return (1/np.sqrt(2*np.pi*sigma ))*np.exp(-1/2*(x/sigma)**2 )
def draw_arrow(zero, sigma, function):
startx = zero
print startx,function(sigma, sigma)
arr = pl.Arrow(startx,function(startx+sigma, sigma), sigma,0,fc="k",ec="k")
ax = pl.gca()
ax.add_patch(arr)
def plot_gauss2d():
x = np.mgrid[115:135:100j]
#x=np.array(zip(range(5)),dtype=float)
sigma = 1
off=1.0
pl.plot(x,gauss2d(x-126.21,3.56), 'b-')
draw_arrow(126.21, 3.56, gauss2d)
pl.plot(x,gauss2d(x-126.71,4.57), 'b-')
pl.plot(x,gauss2d(x-120.64,3.5), 'b-')
pl.ylabel('frequency')
pl.xlabel('ppm of N')
pl.title
pl.show()
def main():
plot_gauss2d()
if __name__ == "__main__":
main()
Somehow I can't seem to get the arrow right. What I essentially would like to have is something like this:
what I simply cannot figure out is how to set the arrow straight to where I want it to be. It should mark the point of the standard deviation in the correct height. The whole thing should of course produce multiple exponential curves.
The problem with arrow is that it uses the figure coordinate as compared to the data coordinates. Hence, as #Paul have suggested, you can use annotate, as
import pylab as pl
import numpy as np
def gauss2d(x,sigma):
return (1/np.sqrt(2*np.pi*sigma ))*np.exp(-1/2*(x/sigma)**2 )
def markParameters(m,s):
p1=gauss2d(s,s)
p2=gauss2d(0,s)
pl.annotate("", xy=(m-s, p1), xycoords='data', xytext=(m+s, p1), textcoords='data', arrowprops=dict(arrowstyle="<->", connectionstyle="arc3"),)
pl.text(m,p1,'sigma',horizontalalignment='center',verticalalignment='top')
pl.annotate("", xy=(m, 0), xycoords='data', xytext=(m, p2), textcoords='data', arrowprops=dict(arrowstyle="<->", connectionstyle="arc3"),)
pl.text(m,p2*0.75,'mean',horizontalalignment='right',verticalalignment='center',rotation=90)
def plot_gauss2d():
x = np.mgrid[115:135:100j]
#x=np.array(zip(range(5)),dtype=float)
m,s=126,3.56
pl.plot(x,gauss2d(x-m,s), 'b-')
markParameters(m,s)
pl.ylabel('frequency')
pl.xlabel('ppm of N')
pl.title
pl.show()
def main():
plot_gauss2d()
if __name__ == "__main__":
main()
check out this demo for the annotate method:
http://matplotlib.sourceforge.net/examples/pylab_examples/annotation_demo.html
That should take care of what you need.
Related
I'm working on fitting of the experimental data. In order to fit it I use the minimization of the function of residual. Everything is quite trivial, but but this time I can't find what's wrong and why the result of fitting is so weird. The example is simplified in comparison with original problem. But anyway it gives wrong parameters even when I set used values of parameters as initial guess.
import matplotlib.pyplot as plt
import numpy as np
import csv
from scipy.optimize import curve_fit, minimize
x=np.arange(0,10,0.5)
a=0.5
b=3
ini_pars=[a, b]
def func(x, a, b):
return a*x+b
plt.plot(x, func(x,a,b))
plt.show()
def fit(pars):
A,B = pars
res = (func(x,a, b)-func(x, *pars))**2
s=sum(res)
return s
bnds=[(0.1,0.5),(1,5)]
x0=[0.1,4]
opt = minimize(fit, x0, bounds=bnds)
new_pars=[opt.x[0], opt.x[0]]
example = fit(ini_pars)
print(example)
example = fit(new_pars)
print(example)
print(new_pars)
plt.plot(x, func(x, *ini_pars))
plt.plot(x, func(x, *new_pars))
plt.show()
```[enter image description here][1]
[1]: https://i.stack.imgur.com/qc1Nu.png
It should be new_pars=[opt.x[0], opt.x[1]] instead of new_pars=[opt.x[0], opt.x[0]]. Note also that you can directly extract the values by new_pars = opt.x.
I have a plot a bit like this:
The differences between the two lines (red and blue) are most important in my actual data (a ROC curve) at say the grid cell 0.2<x<0.4, 0.8<y<1. Now, I could crop for that grid cell, but let's say I'd rather scale both the x and y axes hyperbolically -- where the y-axis hyperbolic curve has its peak at about 0.9 and the x-axis has its peak at about 0.3 -- such that the 2D space gets stretched out for the grid cell of interest and gets compacted elsewhere (and preserving the meaning of the axes tick numbers). How would one accomplish this? The beginnings of my attempt are below. How would my code be modified to implement the axis scaling I described?
from matplotlib import gridspec
from matplotlib import scale as mscale
from matplotlib import transforms as mtransforms
from matplotlib.ticker import FormatStrFormatter
from matplotlib.ticker import NullFormatter, NullLocator, MultipleLocator
import math
import matplotlib
import matplotlib.patches as mpatches
import matplotlib.pylab as plt
import matplotlib.pyplot as plt
import matplotlib.ticker
import numpy as np
import seaborn as sns
sns.set_palette('husl')
sns.set()
plt.rcParams["figure.figsize"] = [5, 5]
x = np.arange(0, 1, step=0.01)
y1 = 1-1/np.exp(10*x)
y2 = 1-1.1/np.exp(10*x)
plt.scatter(x, y1, s=1, facecolor='red')
plt.scatter(x, y2, s=1, facecolor='blue')
plt.show();
class CustomScale(mscale.ScaleBase):
name = 'custom'
def __init__(self, axis, **kwargs):
mscale.ScaleBase.__init__(self)
self.thresh = None #thresh
self.name = 'custom'
def get_transform(self):
return self.CustomTransform(self.thresh)
def set_default_locators_and_formatters(self, axis):
pass
class CustomTransform(mtransforms.Transform):
input_dims = 1
output_dims = 1
is_separable = True
def __init__(self, thresh):
mtransforms.Transform.__init__(self)
self.thresh = thresh
def transform_non_affine(self, a):
#return np.log(1+a)
return np.exp(a)-1
#return 1+(1/2)*a
mscale.register_scale(CustomScale)
plt.scatter(x, y1, s=1, facecolor='red')
plt.scatter(x, y2, s=1, facecolor='blue')
plt.xscale('custom')
plt.show();
You may be able to achieve this using FuncScale (registered as 'function').
f = lambda a: np.exp(a) - 1
g = lambda b: np.log(b + 1)
plt.xscale('function', functions=(f, g))
For hyperbolic scaling, you could use lambda x: 1 / x for both functions.
See the example in the scales documentation: https://matplotlib.org/3.3.4/gallery/scales/scales.html
I would like to get the red function to fully appear. I have tried
plt.tight_layout
plt.gcf()
This is the code I have for it so far
plt.figure(figsize=(6,6))
plt.plot(x, ppf(x,1))
plt.plot(x,ppf(x,2))
plt.xlabel("Coconuts")
plt.ylabel("Fish")
plt.xlim(0,20)
plt.ylim(0,20)
plt.margins(y=.1, x=.1)
plt.tight_layout
plt.gcf()
plt.show()
enter image description here
How should I go about fixing this issue?
This is PPF:
cmax =1000
x = np.linspace(0.1,10, 400)
def ppf(x,Ax):
return np.sqrt(100-(x**2/Ax))
x runs from 0.1 to 10 in both (blue and red) cases. If you want to have one of the curves evaluated at values of x larger than 10, you need to supply an array which has those values in it.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0.1,10, 400)
x2 = np.linspace(0.1,np.sqrt(199.99), 800)
def ppf(x,Ax):
return np.sqrt(100-(x**2/Ax))
plt.figure(figsize=(6,6))
plt.plot(x, ppf(x,1.))
plt.plot(x2, ppf(x2,2.))
plt.xlabel("Coconuts")
plt.ylabel("Fish")
plt.xlim(0,20)
plt.ylim(0,20)
plt.margins(.1)
plt.show()
I'm having issues with redrawing the figure here. I allow the user to specify the units in the time scale (x-axis) and then I recalculate and call this function plots(). I want the plot to simply update, not append another plot to the figure.
def plots():
global vlgaBuffSorted
cntr()
result = collections.defaultdict(list)
for d in vlgaBuffSorted:
result[d['event']].append(d)
result_list = result.values()
f = Figure()
graph1 = f.add_subplot(211)
graph2 = f.add_subplot(212,sharex=graph1)
for item in result_list:
tL = []
vgsL = []
vdsL = []
isubL = []
for dict in item:
tL.append(dict['time'])
vgsL.append(dict['vgs'])
vdsL.append(dict['vds'])
isubL.append(dict['isub'])
graph1.plot(tL,vdsL,'bo',label='a')
graph1.plot(tL,vgsL,'rp',label='b')
graph2.plot(tL,isubL,'b-',label='c')
plotCanvas = FigureCanvasTkAgg(f, pltFrame)
toolbar = NavigationToolbar2TkAgg(plotCanvas, pltFrame)
toolbar.pack(side=BOTTOM)
plotCanvas.get_tk_widget().pack(side=TOP)
You essentially have two options:
Do exactly what you're currently doing, but call graph1.clear() and graph2.clear() before replotting the data. This is the slowest, but most simplest and most robust option.
Instead of replotting, you can just update the data of the plot objects. You'll need to make some changes in your code, but this should be much, much faster than replotting things every time. However, the shape of the data that you're plotting can't change, and if the range of your data is changing, you'll need to manually reset the x and y axis limits.
To give an example of the second option:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 6*np.pi, 100)
y = np.sin(x)
# You probably won't need this if you're embedding things in a tkinter plot...
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
line1, = ax.plot(x, y, 'r-') # Returns a tuple of line objects, thus the comma
for phase in np.linspace(0, 10*np.pi, 500):
line1.set_ydata(np.sin(x + phase))
fig.canvas.draw()
fig.canvas.flush_events()
You can also do like the following:
This will draw a 10x1 random matrix data on the plot for 50 cycles of the for loop.
import matplotlib.pyplot as plt
import numpy as np
plt.ion()
for i in range(50):
y = np.random.random([10,1])
plt.plot(y)
plt.draw()
plt.pause(0.0001)
plt.clf()
This worked for me. Repeatedly calls a function updating the graph every time.
import matplotlib.pyplot as plt
import matplotlib.animation as anim
def plot_cont(fun, xmax):
y = []
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
def update(i):
yi = fun()
y.append(yi)
x = range(len(y))
ax.clear()
ax.plot(x, y)
print i, ': ', yi
a = anim.FuncAnimation(fig, update, frames=xmax, repeat=False)
plt.show()
"fun" is a function that returns an integer.
FuncAnimation will repeatedly call "update", it will do that "xmax" times.
This worked for me:
from matplotlib import pyplot as plt
from IPython.display import clear_output
import numpy as np
for i in range(50):
clear_output(wait=True)
y = np.random.random([10,1])
plt.plot(y)
plt.show()
I have released a package called python-drawnow that provides functionality to let a figure update, typically called within a for loop, similar to Matlab's drawnow.
An example usage:
from pylab import figure, plot, ion, linspace, arange, sin, pi
def draw_fig():
# can be arbitrarily complex; just to draw a figure
#figure() # don't call!
plot(t, x)
#show() # don't call!
N = 1e3
figure() # call here instead!
ion() # enable interactivity
t = linspace(0, 2*pi, num=N)
for i in arange(100):
x = sin(2 * pi * i**2 * t / 100.0)
drawnow(draw_fig)
This package works with any matplotlib figure and provides options to wait after each figure update or drop into the debugger.
In case anyone comes across this article looking for what I was looking for, I found examples at
How to visualize scalar 2D data with Matplotlib?
and
http://mri.brechmos.org/2009/07/automatically-update-a-figure-in-a-loop
(on web.archive.org)
then modified them to use imshow with an input stack of frames, instead of generating and using contours on the fly.
Starting with a 3D array of images of shape (nBins, nBins, nBins), called frames.
def animate_frames(frames):
nBins = frames.shape[0]
frame = frames[0]
tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
for k in range(nBins):
frame = frames[k]
tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
del tempCS1
fig.canvas.draw()
#time.sleep(1e-2) #unnecessary, but useful
fig.clf()
fig = plt.figure()
ax = fig.add_subplot(111)
win = fig.canvas.manager.window
fig.canvas.manager.window.after(100, animate_frames, frames)
I also found a much simpler way to go about this whole process, albeit less robust:
fig = plt.figure()
for k in range(nBins):
plt.clf()
plt.imshow(frames[k],cmap=plt.cm.gray)
fig.canvas.draw()
time.sleep(1e-6) #unnecessary, but useful
Note that both of these only seem to work with ipython --pylab=tk, a.k.a.backend = TkAgg
Thank you for the help with everything.
All of the above might be true, however for me "online-updating" of figures only works with some backends, specifically wx. You just might try to change to this, e.g. by starting ipython/pylab by ipython --pylab=wx! Good luck!
Based on the other answers, I wrapped the figure's update in a python decorator to separate the plot's update mechanism from the actual plot. This way, it is much easier to update any plot.
def plotlive(func):
plt.ion()
#functools.wraps(func)
def new_func(*args, **kwargs):
# Clear all axes in the current figure.
axes = plt.gcf().get_axes()
for axis in axes:
axis.cla()
# Call func to plot something
result = func(*args, **kwargs)
# Draw the plot
plt.draw()
plt.pause(0.01)
return result
return new_func
Usage example
And then you can use it like any other decorator.
#plotlive
def plot_something_live(ax, x, y):
ax.plot(x, y)
ax.set_ylim([0, 100])
The only constraint is that you have to create the figure before the loop:
fig, ax = plt.subplots()
for i in range(100):
x = np.arange(100)
y = np.full([100], fill_value=i)
plot_something_live(ax, x, y)
I am trying to make a simple user interface where the user selects some pixel coordinates in an image. I was thinking to do it using matplotlib, and thus I came across this stack overflow question:
Store mouse click event coordinates with matplotlib
Where a solution is given that stores clicked coordinates in a global list
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(-10,10)
y = x**2
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x,y)
coords = []
def onclick(event):
global ix, iy
ix, iy = event.xdata, event.ydata
print 'x = %d, y = %d'%(
ix, iy)
global coords
coords.append((ix, iy))
if len(coords) == 2:
fig.canvas.mpl_disconnect(cid)
return coords
cid = fig.canvas.mpl_connect('button_press_event', onclick)
The solution works just fine, however I would like to get rid of those global variables, and I am thinking that getting clicked coordinates would be a perfect job for asyncio.
Naively I tried following code, which obviously doesn't work (however it shows the general idea of what I wish to achieve):
import asyncio
import numpy as np
import matplotlib.pyplot as plt
queue = asyncio.Queue()
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(np.random.rand(10))
#asyncio.coroutine
def onclick(event):
yield from queue.put(event.x)
print('button=%d, x=%d, y=%d, xdata=%f, ydata=%f' % (
event.button, event.x, event.y, event.xdata, event.ydata))
cid = fig.canvas.mpl_connect('button_press_event', onclick)
#asyncio.coroutine
def consume():
while True:
value = yield from queue.get()
print("Consumed", value)
loop = asyncio.get_event_loop()
loop.create_task(plt.show())
loop.create_task(consume())
loop.run_forever()
How can I utilize matplotlib and asyncio together to react to or collect events?
I found a solution to using asyncio and matplotlib together.
Basically the main problems seems to be that the gui of matplotlib must be run in the main thread and that running the plot gui will block everything else in the main thread. My solution to this, is to run the asyncio loop in another thread and to use loop.call_soon_thread_safe and queue.put_no_wait.
Not sure if this is a good solution, but at least it seems to work so far.
import asyncio
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import threading
queue = asyncio.Queue()
loop = asyncio.get_event_loop()
fig = plt.figure()
img = mpimg.imread('1970_0101_1015_47_1.jpg')
plt.imshow(img)
def onclick(event):
loop.call_soon_threadsafe(queue.put_nowait, (event.x,event.y))
print('button=%d, x=%d, y=%d, xdata=%f, ydata=%f' % (
event.button, event.x, event.y, event.xdata, event.ydata))
cid = fig.canvas.mpl_connect('button_press_event', onclick)
#asyncio.coroutine
def consume():
while True:
value = yield from queue.get()
print("Consumed", value)
def start_async_stuff():
print('lets async!')
loop.create_task(consume())
loop.run_forever()
threading.Thread(target=start_async_stuff).start()
plt.show()