Related
I am trying to make a slider for the second graph. I succeeded in making the slider, but I am having trouble with the function that is supposed to update the values of the y data. Can someone please help me see my mistake?
Thank you
Code is:
import numpy as np
import math
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
from scipy.constants import *
%matplotlib tk
#Defining constants and variables of interests
hbar_sq = hbar**2
omega_0 = 5.63*10**14 #Transition of 532 nm in the visible, expressed in hertz (delta E/hbar)
omega = 5.65*10**14 #Incoming laser of 530 nm in the visible, expressed in hertz
diff = omega_0 - omega #The difference in frequency between the incoming field and the state-to-state frequency
diff_p=abs(diff)
V = np.sqrt((diff**2 * hbar_sq)) #Matrix element value
V_sq=V**2
t=np.linspace(0,(8*pi/diff_p),100)
P=(V_sq/(hbar_sq*diff**2))*np.sin(diff*t/2)*np.sin(diff*t/2)
#Plot parameters
fig=plt.figure()
ax=fig.subplots()
f=ax.plot(t,P,'b')
plt.ylabel('P (t)')
plt.xlabel('time')
#Second part, plotting P(omega)
omega1=np.linspace(4.99*10**14,6.66*10**14,100)
diff1=omega_0-omega1
P1=(V_sq/(hbar_sq*diff1**2))*np.sin(diff1*t/2)*np.sin(diff1*t/2)
#Plot parameters
fig1=plt.figure()
plt.subplots_adjust(bottom=0.25) #Generating some space under the graph to add the slider button
ax1=fig1.subplots()
f1=ax1.plot(omega1,P1)
#Adding slider functionality to plot
# xposition, yposition, width and height
ax1.slide = plt.axes([0.15,0.1,0.65,0.05])
#Properties of the slider
df = Slider(ax1.slide,'driving frequency',valmin=4.99*10**14, valmax=6.66*10**14, valinit=6.66*10**14, valstep=.5*10**14)
#Making a function to update the plot
def update(val):
current_v = df.val
omega1 = np.linspace(4.99*10**14,current_v,100)
P1=(V_sq/(hbar_sq*diff1**2))*np.sin(diff1*t/2)*np.sin(diff1*t/2)
f1.set_ydata(P1)
fig1.canvas.draw()
df.on_changed(update)
plt.show()```
I edited your update() function like this:
def update(val):
current_v = df.val
omega1 = np.linspace(4.99*10**14,current_v,100)
P1=(V_sq/(hbar_sq*diff1**2))*np.sin(diff1*t/2)*np.sin(diff1*t/2)
ax1.cla()
ax1.plot(omega1, P1)
ax1.set_xlim(4.5e14, 6.5e14)
First of all, I clear the previous plot with ax1.cla(), then I plot the new curve with ax1.plot(omega1, P1).
Optionally, you can fix the x-axis limits with ax1.set_xlim(4.5e14, 6.5e14), in order to keep fixed the axis and see the curve changing. Moreover, I suggest to call the function update(df.val) before showing the figure, in order to fix the axis as soon as the figure is shown, even before the user changes the slider value.
Complete Code
import numpy as np
import math
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
from scipy.constants import *
%matplotlib tk
#Defining constants and variables of interests
hbar_sq = hbar**2
omega_0 = 5.63*10**14 #Transition of 532 nm in the visible, expressed in hertz (delta E/hbar)
omega = 5.65*10**14 #Incoming laser of 530 nm in the visible, expressed in hertz
diff = omega_0 - omega #The difference in frequency between the incoming field and the state-to-state frequency
diff_p=abs(diff)
V = np.sqrt((diff**2 * hbar_sq)) #Matrix element value
V_sq=V**2
t=np.linspace(0,(8*pi/diff_p),100)
P=(V_sq/(hbar_sq*diff**2))*np.sin(diff*t/2)*np.sin(diff*t/2)
#Plot parameters
fig=plt.figure()
ax=fig.subplots()
f=ax.plot(t,P,'b')
plt.ylabel('P (t)')
plt.xlabel('time')
#Second part, plotting P(omega)
omega1=np.linspace(4.99*10**14,6.66*10**14,100)
diff1=omega_0-omega1
P1=(V_sq/(hbar_sq*diff1**2))*np.sin(diff1*t/2)*np.sin(diff1*t/2)
#Plot parameters
fig1=plt.figure()
plt.subplots_adjust(bottom=0.25) #Generating some space under the graph to add the slider button
ax1=fig1.subplots()
f1=ax1.plot(omega1,P1)
#Adding slider functionality to plot
# xposition, yposition, width and height
ax1.slide = plt.axes([0.15,0.1,0.65,0.05])
#Properties of the slider
df = Slider(ax1.slide,'driving frequency',valmin=4.99*10**14, valmax=6.66*10**14, valinit=6.66*10**14, valstep=.5*10**13)
#Making a function to update the plot
def update(val):
current_v = df.val
omega1 = np.linspace(4.99*10**14,current_v,100)
P1=(V_sq/(hbar_sq*diff1**2))*np.sin(diff1*t/2)*np.sin(diff1*t/2)
ax1.cla()
ax1.plot(omega1, P1)
ax1.set_xlim(4.5e14, 6.5e14)
df.on_changed(update)
update(df.val)
plt.show()
I would like to plot an animated vibrating string using python but to be able to play it and to control the parameters used during the vibration (much like this Desmos calculation).
So far, this is my code:
from __future__ import print_function
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets
import matplotlib as mpl
%matplotlib inline
def f(n=1, v=0.2, L=2, t=0):
x = np.linspace(0, L, 2001)
func = np.sin((n*np.pi*x)/L)*np.cos((n*np.pi*v*t)/L)
plt.figure(figsize=(6,6))
ax1 = plt.plot(x, func)
plt.show()
interactive_plot = interactive(f, n=(0, 10, 1), v=(0.2, 5, 0.1), L=(0.2, 2, 0.1), t=(0, 10, 1))
output = interactive_plot.children[-1]
interactive_plot
I can control the wavefunction and all parameters, but I am not sure about what is the easiest way to animate it.
So far, I know that matplotlib can do it, but I am wondering if we have a more straightforward way to do animated interactive plots (using another package, maybe?).
Thanks in advance for any help.
Here my version of your script useing opencv and numpy.
You can set all params in realtime using keyboard.
More info in the code
import numpy as np
import cv2
def f(im,n=1, v=0.2, L=2, t=0):
x = np.linspace(0, L, 2001)
func = np.sin( (n*np.pi*x) / L ) * np.cos( (n*np.pi*v*t) / L )
ww2 = int(win_w/2)
wh2 = int(win_h/2)
scale = 100
for i in range(len(x)):
ix=x[i]
iy=func[i]
p = (int(ww2 + ix*scale) , int(wh2 + iy*scale))
cv2.circle( im, p ,1, (255,255,255) )
win_w=640
win_h=480
#params={
# "n":(0, 10, 1),
# "v":(0.2, 5, 0.1),
# "L":(0.2, 2, 0.1),
# "t":(0, 10, 1)
#}
params={
"n":[10],
"v":[5],
"L":[2],
"t":[0]
}
while True:
im = np.zeros( (win_h,win_w,3), dtype="uint8")
for i in range(len(params["n"])):
n=params["n"][i]
v=params["v"][i]
L=params["L"][i]
t=params["t"][i]
f(im,n,v,L,t)
cv2.imshow("f",im)
k = cv2.waitKey(33) & 0xFF
if k==ord('q'):break
# Parameter setting with the keyboard
# comment / uncomment this for animation:
params["t"][0]+=.001
# n param setting +,- use n,b
if k==ord('n'): params["n"][0]+=.001
if k==ord('b'): params["n"][0]-=.001
# v param setting +,- use v,c
if k==ord('v'): params["v"][0]+=.001
if k==ord('c'): params["v"][0]-=.001
# L param setting +,- use l,k
if k==ord('l'): params["L"][0]+=.001
if k==ord('k'): params["L"][0]-=.001
# t param setting +,- use t,r
if k==ord('t'): params["t"][0]+=.001
if k==ord('r'): params["t"][0]-=.001
print(params)
cv2.destroyAllWindows()
Take a look at the gif python package. I have only used it for simple gif saves. But it can be integrated with plotly or Altair, which may give better interactions.
Here is a simple plot I just made based on this YouTube video.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button
x = np.arange(0,np.pi,0.01)
y_base = 0.5*np.sin(x)
y = y_base+1
fig, ax = plt.subplots()
plt.subplots_adjust(left=0.1, bottom=0.35)
p, = plt.plot(x,y, linewidth=2, color='blue')
plt.axis([0,np.pi,0,2])
axSlider = plt.axes([0.1,0.2,0.8,0.05])
slider1 = Slider(axSlider, "Slider", valmin=-100, valmax=100)
def val_update(val):
yval = slider1.val/50
p.set_ydata(yval*y_base+1)
plt.draw()
slider1.on_changed(val_update)
plt.show()
I am making a Matplotlib GUI where I want to interactively rescale the x and y axis. For this I want to use four textboxes where I can set the minimum and maximum of the x and y axis limits respectively. The code I have up until now is:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.widgets import TextBox
import matplotlib.gridspec as gridspec
fig, ax = plt.subplots()
ax.plot([1,3,2])
fig.subplots_adjust(bottom=0.4)
gs = gridspec.GridSpec(2,2)
gs.update(left=0.4, right=0.7, bottom=0.15, top=0.25, hspace=0.1)
axes = [fig.add_subplot(gs[i,j]) for i,j in [[0,0],[0,1],[1,0],[1,1]]]
# create the textboxes
tb_xmin = TextBox(axes[0],'x', hovercolor='0.975', label_pad=0.1)
tb_xmax = TextBox(axes[1],'', hovercolor='0.975')
tb_ymin = TextBox(axes[2],'y', hovercolor='0.975', label_pad=0.1)
tb_ymax = TextBox(axes[3],'', hovercolor='0.975')
def submit(val):
data = eval(val)
# how to know which limit to set here?
ax.set_xlim(data)
plt.draw()
for tb in [tb_xmin,tb_xmax,tb_ymin,tb_ymax]:
tb.on_submit(submit)
plt.show()
However I am stuck with the submit function. I want to have a function which recognises which textbox submits and input and sets the appropriate minimum or maximum value under the condition that the input is valid and within the datarange.
Can anyone help me out?
In order to update the limits via the TextBox inputs, you may pass some arguments together with the values to the function. A way to do this would be to create a lambda function, which has those arguments preset, depending on which limit to update.
def submit(val,func,pos):
data = float(val)
func([data,None][::pos])
tb_xmin.on_submit(lambda val: submit(val, ax.set_xlim, 1))
tb_xmax.on_submit(lambda val: submit(val, ax.set_xlim, -1))
tb_ymin.on_submit(lambda val: submit(val, ax.set_ylim, 1))
tb_ymax.on_submit(lambda val: submit(val, ax.set_ylim, -1))
Another option would be to always call the same function on submit and let the function gather the values from all 4 TextBoxes and set the limits simulataneously.
import matplotlib.pyplot as plt
from matplotlib.widgets import TextBox
import matplotlib.gridspec as gridspec
fig, ax = plt.subplots()
ax.plot([1,3,2])
fig.subplots_adjust(bottom=0.4)
gs = gridspec.GridSpec(2,2)
gs.update(left=0.4, right=0.7, bottom=0.15, top=0.25, hspace=0.1)
axes = [fig.add_subplot(gs[i,j]) for i,j in [[0,0],[0,1],[1,0],[1,1]]]
# create the textboxes
xlim = ax.get_xlim()
ylim = ax.get_ylim()
tb_xmin = TextBox(axes[0],'x', initial = str(xlim[0]), hovercolor='0.975', label_pad=0.1)
tb_xmax = TextBox(axes[1],'', initial = str(xlim[1]), hovercolor='0.975')
tb_ymin = TextBox(axes[2],'y', initial = str(ylim[0]), hovercolor='0.975', label_pad=0.1)
tb_ymax = TextBox(axes[3],'', initial = str(ylim[1]), hovercolor='0.975')
def submit(val):
lim = [float(tb.text) for tb in [tb_xmin,tb_xmax,tb_ymin,tb_ymax]]
ax.axis(lim)
fig.canvas.draw_idle()
for tb in [tb_xmin,tb_xmax,tb_ymin,tb_ymax]:
tb.on_submit(submit)
plt.show()
I am creating two Python scripts to produce some plots for a technical report. In the first script I am defining functions that produce plots from raw data on my hard-disk. Each function produces one specific kind of plot that I need. The second script is more like a batch file which is supposed to loop around those functions and store the produced plots on my hard-disk.
What I need is a way to return a plot in Python. So basically I want to do this:
fig = some_function_that_returns_a_plot(args)
fig.savefig('plot_name')
But what I do not know is how to make a plot a variable that I can return. Is this possible? Is so, how?
You can define your plotting functions like
import numpy as np
import matplotlib.pyplot as plt
# an example graph type
def fig_barh(ylabels, xvalues, title=''):
# create a new figure
fig = plt.figure()
# plot to it
yvalues = 0.1 + np.arange(len(ylabels))
plt.barh(yvalues, xvalues, figure=fig)
yvalues += 0.4
plt.yticks(yvalues, ylabels, figure=fig)
if title:
plt.title(title, figure=fig)
# return it
return fig
then use them like
from matplotlib.backends.backend_pdf import PdfPages
def write_pdf(fname, figures):
doc = PdfPages(fname)
for fig in figures:
fig.savefig(doc, format='pdf')
doc.close()
def main():
a = fig_barh(['a','b','c'], [1, 2, 3], 'Test #1')
b = fig_barh(['x','y','z'], [5, 3, 1], 'Test #2')
write_pdf('test.pdf', [a, b])
if __name__=="__main__":
main()
If you don't want the picture to be displayed and only get a variable in return, then you can try the following (with some additional stuff to remove axis):
def myplot(t,x):
fig = Figure(figsize=(2,1), dpi=80)
canvas = FigureCanvasAgg(fig)
ax = fig.add_subplot()
ax.fill_between(t,x)
ax.autoscale(tight=True)
ax.axis('off')
canvas.draw()
buf = canvas.buffer_rgba()
X = np.asarray(buf)
return X
The returned variable X can be used with OpenCV for example and do a
cv2.imshow('',X)
These import must be included:
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg
The currently accepted answer didn't work for me as such, as I was using scipy.stats.probplot() to plot. I used matplotlib.pyplot.gca() to access an Axes instance directly instead:
"""
For my plotting ideas, see:
https://pythonfordatascience.org/independent-t-test-python/
For the dataset, see:
https://github.com/Opensourcefordatascience/Data-sets
"""
# Import modules.
from scipy import stats
import matplotlib.pyplot as plt
import pandas as pd
from tempfile import gettempdir
from os import path
from slugify import slugify
# Define plot func.
def get_plots(df):
# plt.figure(): Create a new P-P plot. If we're inside a loop, and want
# a new plot for every iteration, this is important!
plt.figure()
stats.probplot(diff, plot=plt)
plt.title('Sepal Width P-P Plot')
pp_p = plt.gca() # Assign an Axes instance of the plot.
# Plot histogram. This uses pandas.DataFrame.plot(), which returns
# an instance of the Axes directly.
hist_p = df.plot(kind = 'hist', title = 'Sepal Width Histogram Plot',
figure=plt.figure()) # Create a new plot again.
return pp_p, hist_p
# Import raw data.
df = pd.read_csv('https://raw.githubusercontent.com/'
'Opensourcefordatascience/Data-sets/master//Iris_Data.csv')
# Subset the dataset.
setosa = df[(df['species'] == 'Iris-setosa')]
setosa.reset_index(inplace= True)
versicolor = df[(df['species'] == 'Iris-versicolor')]
versicolor.reset_index(inplace= True)
# Calculate a variable for analysis.
diff = setosa['sepal_width'] - versicolor['sepal_width']
# Create plots, save each of them to a temp file, and show them afterwards.
# As they're just Axes instances, we need to call get_figure() at first.
for plot in get_plots(diff):
outfn = path.join(gettempdir(), slugify(plot.title.get_text()) + '.png')
print('Saving a plot to "' + outfn + '".')
plot.get_figure().savefig(outfn)
plot.get_figure().show()
How does one set the color of a line in matplotlib with scalar values provided at run time using a colormap (say jet)? I tried a couple of different approaches here and I think I'm stumped. values[] is a storted array of scalars. curves are a set of 1-d arrays, and labels are an array of text strings. Each of the arrays have the same length.
fig = plt.figure()
ax = fig.add_subplot(111)
jet = colors.Colormap('jet')
cNorm = colors.Normalize(vmin=0, vmax=values[-1])
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=jet)
lines = []
for idx in range(len(curves)):
line = curves[idx]
colorVal = scalarMap.to_rgba(values[idx])
retLine, = ax.plot(line, color=colorVal)
#retLine.set_color()
lines.append(retLine)
ax.legend(lines, labels, loc='upper right')
ax.grid()
plt.show()
The error you are receiving is due to how you define jet. You are creating the base class Colormap with the name 'jet', but this is very different from getting the default definition of the 'jet' colormap. This base class should never be created directly, and only the subclasses should be instantiated.
What you've found with your example is a buggy behavior in Matplotlib. There should be a clearer error message generated when this code is run.
This is an updated version of your example:
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import matplotlib.cm as cmx
import numpy as np
# define some random data that emulates your indeded code:
NCURVES = 10
np.random.seed(101)
curves = [np.random.random(20) for i in range(NCURVES)]
values = range(NCURVES)
fig = plt.figure()
ax = fig.add_subplot(111)
# replace the next line
#jet = colors.Colormap('jet')
# with
jet = cm = plt.get_cmap('jet')
cNorm = colors.Normalize(vmin=0, vmax=values[-1])
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=jet)
print scalarMap.get_clim()
lines = []
for idx in range(len(curves)):
line = curves[idx]
colorVal = scalarMap.to_rgba(values[idx])
colorText = (
'color: (%4.2f,%4.2f,%4.2f)'%(colorVal[0],colorVal[1],colorVal[2])
)
retLine, = ax.plot(line,
color=colorVal,
label=colorText)
lines.append(retLine)
#added this to get the legend to work
handles,labels = ax.get_legend_handles_labels()
ax.legend(handles, labels, loc='upper right')
ax.grid()
plt.show()
Resulting in:
Using a ScalarMappable is an improvement over the approach presented in my related answer:
creating over 20 unique legend colors using matplotlib
I thought it would be beneficial to include what I consider to be a more simple method using numpy's linspace coupled with matplotlib's cm-type object. It's possible that the above solution is for an older version. I am using the python 3.4.3, matplotlib 1.4.3, and numpy 1.9.3., and my solution is as follows.
import matplotlib.pyplot as plt
from matplotlib import cm
from numpy import linspace
start = 0.0
stop = 1.0
number_of_lines= 1000
cm_subsection = linspace(start, stop, number_of_lines)
colors = [ cm.jet(x) for x in cm_subsection ]
for i, color in enumerate(colors):
plt.axhline(i, color=color)
plt.ylabel('Line Number')
plt.show()
This results in 1000 uniquely-colored lines that span the entire cm.jet colormap as pictured below. If you run this script you'll find that you can zoom in on the individual lines.
Now say I want my 1000 line colors to just span the greenish portion between lines 400 to 600. I simply change my start and stop values to 0.4 and 0.6 and this results in using only 20% of the cm.jet color map between 0.4 and 0.6.
So in a one line summary you can create a list of rgba colors from a matplotlib.cm colormap accordingly:
colors = [ cm.jet(x) for x in linspace(start, stop, number_of_lines) ]
In this case I use the commonly invoked map named jet but you can find the complete list of colormaps available in your matplotlib version by invoking:
>>> from matplotlib import cm
>>> dir(cm)
A combination of line styles, markers, and qualitative colors from matplotlib:
import itertools
import matplotlib as mpl
import matplotlib.pyplot as plt
N = 8*4+10
l_styles = ['-','--','-.',':']
m_styles = ['','.','o','^','*']
colormap = mpl.cm.Dark2.colors # Qualitative colormap
for i,(marker,linestyle,color) in zip(range(N),itertools.product(m_styles,l_styles, colormap)):
plt.plot([0,1,2],[0,2*i,2*i], color=color, linestyle=linestyle,marker=marker,label=i)
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.,ncol=4);
UPDATE: Supporting not only ListedColormap, but also LinearSegmentedColormap
import itertools
import matplotlib.pyplot as plt
Ncolors = 8
#colormap = plt.cm.Dark2# ListedColormap
colormap = plt.cm.viridis# LinearSegmentedColormap
Ncolors = min(colormap.N,Ncolors)
mapcolors = [colormap(int(x*colormap.N/Ncolors)) for x in range(Ncolors)]
N = Ncolors*4+10
l_styles = ['-','--','-.',':']
m_styles = ['','.','o','^','*']
fig,ax = plt.subplots(gridspec_kw=dict(right=0.6))
for i,(marker,linestyle,color) in zip(range(N),itertools.product(m_styles,l_styles, mapcolors)):
ax.plot([0,1,2],[0,2*i,2*i], color=color, linestyle=linestyle,marker=marker,label=i)
ax.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.,ncol=3,prop={'size': 8})
U may do as I have written from my deleted account (ban for new posts :( there was). Its rather simple and nice looking.
Im using 3-rd one of these 3 ones usually, also I wasny checking 1 and 2 version.
from matplotlib.pyplot import cm
import numpy as np
#variable n should be number of curves to plot (I skipped this earlier thinking that it is obvious when looking at picture - sorry my bad mistake xD): n=len(array_of_curves_to_plot)
#version 1:
color=cm.rainbow(np.linspace(0,1,n))
for i,c in zip(range(n),color):
ax1.plot(x, y,c=c)
#or version 2: - faster and better:
color=iter(cm.rainbow(np.linspace(0,1,n)))
c=next(color)
plt.plot(x,y,c=c)
#or version 3:
color=iter(cm.rainbow(np.linspace(0,1,n)))
for i in range(n):
c=next(color)
ax1.plot(x, y,c=c)
example of 3:
Ship RAO of Roll vs Ikeda damping in function of Roll amplitude A44