Slider in python - python

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()

Related

How to Plot 2 Lines on Log X-axis in Python?

I'm trying to plot blackbody wavelength vs flux for 288 Kelvin temperature (the Earth) and 6000 Kelvin temperature (the sun). I want both of these to be on the same plot and know I will need a log x-axis but I keep having issues having both lines appear. This is the code I have so far:
# Import libraries
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
# Constants
c = 3.0e8 # m/s
h = 6.626e-34 # Js
k = 1.38e-23 # J/K
c1 = 2*np.pi*h*c**2
c2 = (h*c)/k
T1 = 6000
T2 = 288
lam = np.logspace(-8,-3,2000) # Generate x-axis values
F1 = c1/(lam**5*(np.exp(c2/(lam*T1))-1)) # Calculate y-values
F1 = F1/1e9
F2 = c1/(lam**5*(np.exp(c2/(lam*T2))-1)) # Calculate y-values
F2 = F2/1e9
# Create plot
ax = plt.gca()
plt.xlabel(r'$\lambda$ (nm)')
plt.ylabel(r'$F_{BB\lambda}(W\/m^{-2}nm^{-1})$')
plt.text(0.05,.8, 'T = {0:d}K'.format(T1), transform = ax.transAxes, size = 'small')
plt.text(0.05,.3, 'T = {0:d}K'.format(T2), transform = ax.transAxes, size = 'small')
plt.xticks(), plt.yticks()
plt.semilogx(lam*1e9, F1, lam*1e9, F2, color= 'black') # Create figure and axis objects
plt.xlim(10,1e6)
plt.ylim(0,)
plt.show() # Display plot to screen
This plots the attached picture which is correct for 6000K but for some reason it's not plotting the 288K curve and I'm not sure how to fix it.

Understanding plt.norm and plt.cbar using Practical example

I am learning to make color bars, and thus learning to make good use of plt.Normalize , I succeeded to make it work with scipy.stats.norm, but when tryin to use plt.norm, I found out that I have to do two things to make it work well :
defining vmin and vmax to -1.96 and 1.96 respectively,I guess that it's because they are the z value for 95% confidence interval, but I still don't precisely know why they have we have to set vmin and vmax to those values
dividing the standard deviation by sqrt( number of elements )
I don't understand why are those two points important for using the Norm. Any help is welcome ! thank you in advance
# Use the following data for this assignment:
%matplotlib notebook
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as st
df = pd.DataFrame([np.random.normal(33500,150000,3650),
np.random.normal(41000,90000,3650),
np.random.normal(41000,120000,3650),
np.random.normal(48000,55000,3650)],
index=[1992,1993,1994,1995])
new_df = pd.DataFrame()
new_df['mean'] = df.mean(axis =1)
new_df['std'] = df.std(axis =1)
new_df['se'] = df.sem(axis= 1)
new_df['C_low'] = new_df['mean'] - 1.96 * new_df['se']
new_df['C_high'] = new_df['mean'] + 1.96 * new_df['se']
from scipy.stats import norm
import numpy as np
# First, Define a figure
fig = plt.figure()
# next define its the axis and create a plot
ax = fig.add_subplot(1,1,1)
# change the ticks
xticks = np.array(new_df.index,dtype= 'str')
# remove the top and right borders
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# draw the bars in the axis
bars = ax.bar(xticks,new_df['mean'].values,
yerr = (1.96*new_df['se'],1.96*new_df['se']),
capsize= 10)
# define labels
plt.xlabel('YEARS',size = 14)
plt.ylabel('FREQUENCY',size = 14)
# Define color map
cmap = plt.cm.get_cmap('coolwarm')
# define scalar mappable
sm = plt.cm.ScalarMappable(cmap = cmap)
# draw the color bar
cbar = plt.colorbar(cmap = cmap, mappable =sm)
# define norm (will be used later to turn y to a value from 0 to 1 )
# define the events
class Cursor(object):
def __init__(self,ax):
self.ax = ax
self.lx = ax.axhline(color = 'c')
self.txt = ax.text(1,50000,'')
def mouse_movemnt(self,event):
#behaviour outside of the plot
if not event.inaxes:
return
#behavior inside the plot
y = event.ydata
self.lx.set_ydata(y)
for idx,bar in zip(new_df.index, bars):
norm = plt.Normalize(vmin =-1.96,vmax = 1.96)
mean = new_df.loc[idx,'mean']
err = new_df.loc[idx, 'se']
std = new_df.loc[idx,'std']/ np.sqrt(df.shape[1]) # not sure why we re dividing by np.sqrt(df.shape[1])
self.txt.set_text(f'Y = {round(y,2)} \n')
color_prob = norm( (mean - y)/std)
#color_prob = norm.cdf(y,loc = mean, scale = err) # you can also use this
bar.set_color( cmap(color_prob))
# connect the events to the plot
cursor = Cursor(ax)
plt.connect('motion_notify_event', cursor.mouse_movemnt)
None
After few hours of thinking, an explanation barged into my head and I was able to answer all of my inquiries,
first before answering the first point, I will answer the second one, the standard deviation was divided by the sqrt(nbr of element) because the resulting value is the standard error.
I will now move on to answering the first part:
(I can't embed images for now and I can't use latex either so I have to put links of the image instead). But here is the conclusion in advance, for all values within that confidence interval, the function (y-mean)/se will spit out a value within the range [−1.96,1.96]
answer of first part
Please, if I left something out or you have a better answer, share it with me.

Displaying Wavenumber and Wavelength on One Plot

I currently work with an instrument that provides data in Wavenumber, but most of my community works in wavelength. Because of this I would like to create plots that display Wavenumber in cm^-1 along the bottom x-axis and wavelength in µm along the top. However the spacing doesn't quite match up between the two units of measurement to display a single spectrum. How do I create a different spacing for wavelength?
Here is an example of how a portion of one spectrum looks when plotted as a function of wavenumber against when it's plotted as a function of wavelength. Below is the code I'm currently implementing.
wn = wn_tot[425:3175] #range of 250 to 3000 cm-1
wl = 10000/wn #wavelength in microns
fig = plt.figure(1)
ax1 = plt.subplot(1,1,1)
ax2 = ax1.twiny()
ax1.plot(wn, spc[45], 'c', label='Wavenumber')
ax2.plot(wl, spc[45], 'm', label='Wavelength')
ax1.set_xlabel('Wavenumber (cm$^{-1}$)')
ax2.set_xlabel('Wavelength ($\mu$m)')
ax1.set_ylabel('Relative Intensity')
ax2.invert_xaxis()
fig.legend(loc=2, bbox_to_anchor=(0,1), bbox_transform=ax1.transAxes)
As said in the comment on the OP, both scales cannot be simultaneously linear, since one cannot be obtained from the other via a linear transformation. You must hence accept that one (or both) have ticks at non-regular intervals.
The correct way to do it
Apply a transformation to the scale, which causes matplotlib to have a non-homogeneous scale.
The doc for Axes.set_yscale leads to that example which demonstrate the syntax ax1.set_xscale('function', functions=(forward, inverse)). Here in that case, the transformation functions are simply
def forward(wn):
# cm^{-1} to μm
return 1.0e4 / wn
def reverse(lam):
# μm to cm^{-1}
return 1.0e4 / lam
However, my matplotlib is stuck on version 2.2.2 which does not have that feature, so I cannot give a working example.
The hacky way that works with older versions
Give tick positions and labels by hand, performing the calculations yourself.
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
def lambda_to_wave(lam):
# μm to cm^{-1}
return 1.0e4 / lam
x_wave = np.linspace(2000.0, 3000.0)
y_arb = np.linspace(0.0, 1.0e6)
ticks_wavelength_values = np.linspace(3.5, 5.5, num=5)
ticks_labels = [str(lam) for lam in ticks_wavelength_values]
ticks_wavenumber_positions = lambda_to_wave(ticks_wavelength_values)
print ticks_wavelength_values
print ticks_wavenumber_positions
fig = plt.figure(1)
ax1 = plt.subplot(1,1,1) # wavenumber
ax2 = ax1.twiny() # wavelength
ax2.get_shared_x_axes().join(ax1, ax2) # https://stackoverflow.com/questions/42973223/how-share-x-axis-of-two-subplots-after-they-are-created
ax1.plot(x_wave, y_arb, 'c', label='Data')
ax1.set_xlabel('Wavenumber (cm$^{-1}$)')
ax1.set_ylabel('Relative Intensity')
ax2.set_xticks(ticks_wavenumber_positions)
ax2.set_xticklabels(ticks_labels)
ax2.set_xlabel('Wavelength ($\mu$m)')
ax1.set_xlim(left=1800.0, right=3000.0)
fig.legend(loc=2, bbox_to_anchor=(0,1), bbox_transform=ax1.transAxes)
plt.show()
You can do without the second call to plot if you prefer: https://matplotlib.org/gallery/subplots_axes_and_figures/secondary_axis.html#sphx-glr-gallery-subplots-axes-and-figures-secondary-axis-py
wn = wn_tot[425:3175] #range of 250 to 3000 cm-1
fig = plt.figure(1)
ax1 = plt.subplot(1,1,1)
ax1.plot(wn, spc[45], 'c', label='Wavenumber')
def forward(x):
return 10000 / x
def inverse(x):
return 10000 / x
secax = ax.secondary_xaxis('top', functions=(forward, inverse))
ax1.set_xlabel('Wavenumber (cm$^{-1}$)')
secax.set_xlabel('Wavelength ($\mu$m)')
ax1.set_ylabel('Relative Intensity')

How to rotate axis labels for minor ticks within a semilogx plot?

Within the following code using matplotlib, I would like to rotate also the minor tick labels of the x axis. Unfortunately neither
plt.setp(axes.xaxis.get_minorticklabels(), rotation=90)
nor
for text in axes.get_xminorticklabels():
print("text rotated")
text.set_rotation(90)
have an effect. How can I control in this setup the orientation of these labels?
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter
fig, axes = plt.subplots()
import numpy as np
ONE_YEAR_IN_DAYS = 365
ONE_DAY_IN_TIMESTAMP_UNITS = 86400000000000
import pandas as pd
start = pd.Timestamp('2016-07-01')
end = pd.Timestamp('2017-07-02')
t = np.linspace(start.value, end.value, 100 * ONE_YEAR_IN_DAYS)
sinus = np.sin(2 * np.pi * 1 * t / ONE_DAY_IN_TIMESTAMP_UNITS)
t = end.value - t
t = 1000 * t / ONE_DAY_IN_TIMESTAMP_UNITS
plt.xticks(rotation=90)
plt.setp(axes.xaxis.get_minorticklabels(), rotation=90)
for text in axes.get_xminorticklabels():
print("text rotated")
text.set_rotation(90)
axes.semilogx(t, sinus)
def method_name():
# return lambda y, _: '{:.16g}'.format(2*y)
return lambda y, _: '{:.16g}'.format(2*y)
for axis in [axes.xaxis, axes.yaxis]:
formatter = FuncFormatter(method_name())
axis.set_major_formatter(formatter)
axis.set_minor_formatter(formatter)
plt.show()
You have to create the plot then update the properties of the minor tick labels.
So move the following code
for text in axes.get_xminorticklabels():
print("text rotated")
text.set_rotation(90)
below the plot creation code
axes.semilogx(t, sinus)
This is the order you must do things in:
create data
create plot
modify plot properties

IPython notebook interactive function: how to set the slider range

I wrote the code below in Ipython notebook to generate a sigmoid function controlled by parameters a which defines the position of the sigmoid center, and b which defines its width:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(x,a,b):
#sigmoid function with parameters a = center; b = width
s= 1/(1+np.exp(-(x-a)/b))
return 100.0*(s-min(s))/(max(s)-min(s)) # normalize sigmoid to 0-100
x = np.linspace(0,10,256)
sigm = sigmoid(x, a=5, b=1)
fig = plt.figure(figsize=(24,6))
ax1 = fig.add_subplot(2, 1, 1)
ax1.set_xticks([])
ax1.set_xticks([])
plt.plot(x,sigm,lw=2,color='black')
plt.xlim(x.min(), x.max())
I wanted to add interactivity for parameters a and b so I re-wrote the function as below:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from IPython.html.widgets import interactive
from IPython.display import display
def sigmoid_demo(a=5,b=1):
x = np.linspace(0,10,256)
s = 1/(1+np.exp(-(x-a)/(b+0.1))) # +0.1 to avoid dividing by 0
sn = 100.0*(s-min(s))/(max(s)-min(s)) # normalize sigmoid to 0-100
fig = plt.figure(figsize=(24,6))
ax1 = fig.add_subplot(2, 1, 1)
ax1.set_xticks([])
ax1.set_yticks([])
plt.plot(x,sn,lw=2,color='black')
plt.xlim(x.min(), x.max())
w=widgets.interactive(sigmoid_demo,a=5,b=1)
display(w)
Is there any way to se the range of the sliders to be symmetrical (for example around zero)? It does not seem to me to be possible by just setting the starting value for the parameters.
You can create widgets manually and bind them to variables in the interactive function. This way you are much more flexible and can tailor those widgets to your needs.
This example creates two different sliders and sets their max, min, stepsize and initial value and uses them in the interactive function.
a_slider = widgets.IntSliderWidget(min=-5, max=5, step=1, value=0)
b_slider = widgets.FloatSliderWidget(min=-5, max=5, step=0.3, value=0)
w=widgets.interactive(sigmoid_demo,a=a_slider,b=b_slider)
display(w)

Categories

Resources