Exploring time varying dynamics
Contents
Exploring time varying dynamics¶
Recall that with differential dynamic microscopy, we find the difference between images separated by some lag time \(\Delta t\):
For a given \(\Delta t\) all such image differences are calculated. For typical DDM analysis, we then Fourier transform each \(\Delta I\) and average all of the same :math:`Delta t`.
However, if the dynamics are varying over time, then we might not want to average over all times. We instead might want to have a DDM matrix which is a function of lagtime and time (and wavevector too).
Importing the necessary modules¶
Modules for plotting¶
We use matplotlib for creating figures and plots. Note that we use:
%matplotlib inline
This sets the backend of matplotlib to inline
which means the plots are included in the notebook. If you want the plots to also be interactive (e.g., having the ability to zoom, scroll, and save) then use:
%matplotlib notebook
[1]:
%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt
Modules for numerical work¶
Here, we import numpy and xarray.
Note that xarray
might not have come with your Anaconda Python distribution (or whichever other distribution you installed). If that is the case, you’ll need to install this package.
[2]:
import numpy as np #numerical python used for working with arrays, mathematical operations
import xarray as xr #package for labeling and adding metadata to multi-dimensional arrays
Initializing DDM_Analysis class and computing the DDM matrix¶
The instance of the DDM_Analysis
class we create will need, when initialized, metadata about the images to analyze and the analysis and fitting parameters. This can be done by using a yaml file as shown in the following cell of code (there, the metadata is saved in the file “example_data_silica_beads.yml”.
[4]:
#The yaml file `example_data_silica_beads.yml` contains the metadata and parameters above
#Note that here we *are* applying a 2x2 binning
ddm_calc = ddm.DDM_Analysis("../../Examples/example_data_silica_beads.yml")
Provided metadata: {'pixel_size': 0.242, 'frame_rate': 41.7}
Image shape: 3000-by-128-by-128
Number of frames to use for analysis: 999
Maximum lag time (in frames): 600
Number of lag times to compute DDM matrix: 40
Applying binning...
Dimensions after binning (999, 64, 64), the new pixel size 0.484
Below, with the method calculate_DDM_matrix
, we compute the DDM matrix and some associated data. The data will be stored as an xarray Dataset as an attribute to ddm_calc
called ddm_dataset
.
Note: There are a few optional arguments we can pass to calculate_DDM_matrix
. There is an optional argument quiet
(True or False, False by default). Then we have some optional keyword arguments (all of which could also be specified in the YAML file). These are: overlap_method
which sets the degree of overlap between image pairs when finding all image differences for a each lag time and is either 0, 1, 2, or 3, background_method
which sets how to estimate the
background parameter B and is either 0, 1, 2, or 3, and number_lag_times
. If any of these three keyword arguments are set here, the value specified in the YAML file will be overwritten.
[5]:
#If a file is found that matches what, by default, the program will save the computed DDM matrix as,
#then you will be prompted as to whether or not you want to proceed with this calculation. If you
#do not, then enter 'n' and the program will read from the existing file to load the DDM dataset
#into memory. If you enter 'y', then the DDM matrix will be calculated.
ddm_calc.calculate_DDM_matrix()
The file C:/Users/rmcgorty/Documents/GitHub/PyDDM/Examples/images_nobin_40x_128x128_8bit_ddmmatrix.nc already exists. So perhaps the DDM matrix was calculated already?
Do you still want to calculate the DDM matrix? (y/n): n
Finding variability in the DDM matrix¶
When computing the DDM matrix, we average over all pairs of images separated by the given set of lag times. For example, for a lag time of 2 frames, we will find the differences between frames 1 and 3, 2 and 4, 3 and 5, etc. The power spectrum of those differences are then taken to yield the DDM matrix. But what if the dynamics in the sample are changing? For example, perhaps the difference between frames 1 and 3 will be very different from the difference between frames 1001 and 1003.
[6]:
tlags = np.arange(1,998,dtype=int) #We will compute the DDM matrix for all lag times from 1 frame to 998 frames
#With the function 'variationInDDMMatrix', we compute the DDM matrix for the given lag times
# **without** doing any averaging over time. With the optional paramter 'save_full_ddmmat' set to
# False, we will be radially averaging the 2D DDM matrix. This will save memory.
ddm_variability = ddm_calc.variationInDDMMatrix(tlags, save_full_ddmmat=False)
[68]:
ddm_variability.to_netcdf("ddm_variability_silicabeads.nc") #save generated dataset to disk
[7]:
ddm_variability
[7]:
<xarray.Dataset> Dimensions: (lagtime: 997, time: 998, q: 32) Coordinates: * time (time) float64 0.0 0.02398 0.04796 ... 23.86 23.88 23.91 * lagtime (lagtime) int32 1 2 3 4 5 6 7 ... 992 993 994 995 996 997 * q (q) float64 0.0 0.2028 0.4057 0.6085 ... 5.882 6.085 6.288 Data variables: ddm_matrix (lagtime, time, q) float64 0.0 220.0 184.4 ... nan nan nan alignment_factor (lagtime, time, q) float64 nan 6.123e-17 ... nan nan Attributes: AlignmentFactorAxis: 0
- lagtime: 997
- time: 998
- q: 32
- time(time)float640.0 0.02398 0.04796 ... 23.88 23.91
array([ 0. , 0.023981, 0.047962, ..., 23.860911, 23.884892, 23.908873])
- lagtime(lagtime)int321 2 3 4 5 6 ... 993 994 995 996 997
array([ 1, 2, 3, ..., 995, 996, 997])
- q(q)float640.0 0.2028 0.4057 ... 6.085 6.288
array([0. , 0.20284 , 0.405681, 0.608521, 0.811362, 1.014202, 1.217043, 1.419883, 1.622723, 1.825564, 2.028404, 2.231245, 2.434085, 2.636926, 2.839766, 3.042607, 3.245447, 3.448287, 3.651128, 3.853968, 4.056809, 4.259649, 4.46249 , 4.66533 , 4.86817 , 5.071011, 5.273851, 5.476692, 5.679532, 5.882373, 6.085213, 6.288053])
- ddm_matrix(lagtime, time, q)float640.0 220.0 184.4 ... nan nan nan
array([[[ 0. , 220.04545857, 184.38399034, ..., 94.58914071, 93.7690884 , 81.66854128], [ 0. , 112.3428197 , 60.96815397, ..., 66.38218294, 53.6350686 , 62.51979943], [ 0. , 98.41897164, 101.97738069, ..., 54.49735033, 55.10627058, 52.23193462], ..., [ 0. , 77.70965475, 135.90005735, ..., 58.92433517, 58.4778158 , 55.18350923], [ 0. , 89.67129696, 123.87543033, ..., 60.85931764, 52.93569647, 49.83894966], [ 0. , 147.59986325, 153.57530257, ..., 53.08847067, 57.61446569, 55.32019147]], [[ 0. , 373.29665834, 216.51303491, ..., 81.99647868, 109.95927323, 101.96291398], [ 0. , 116.57465737, 105.10468156, ..., 76.24483146, 63.19264613, 68.05139254], [ 0. , 247.78469075, 147.30543405, ..., 61.80097638, 65.04270436, 49.63940288], ... [ nan, nan, nan, ..., nan, nan, nan], [ nan, nan, nan, ..., nan, nan, nan], [ nan, nan, nan, ..., nan, nan, nan]], [[ 0. , 1512.98915149, 3566.39329903, ..., 119.86221091, 110.30273635, 113.88357923], [ 0. , 1391.98865504, 2736.34523223, ..., 129.87626829, 110.08957292, 100.17179273], [ nan, nan, nan, ..., nan, nan, nan], ..., [ nan, nan, nan, ..., nan, nan, nan], [ nan, nan, nan, ..., nan, nan, nan], [ nan, nan, nan, ..., nan, nan, nan]]])
- alignment_factor(lagtime, time, q)float64nan 6.123e-17 -0.09804 ... nan nan
array([[[ nan, 6.12323400e-17, -9.80384669e-02, ..., 2.85253420e-02, -9.50111080e-02, -1.57947399e-01], [ nan, 6.12323400e-17, -2.49717851e-01, ..., -5.26666998e-03, 8.37042726e-02, -5.91464131e-02], [ nan, 6.12323400e-17, 1.45570391e-01, ..., 2.90914553e-02, 5.51512863e-02, -2.96649245e-02], ..., [ nan, 6.12323400e-17, -1.93642012e-01, ..., -6.88032939e-02, -3.55367564e-02, 1.01651029e-01], [ nan, 6.12323400e-17, 1.28455034e-01, ..., -5.07279273e-02, 1.22883479e-01, -1.21307943e-01], [ nan, 6.12323400e-17, -3.23709260e-01, ..., -8.71467418e-02, -7.72352729e-02, -1.05252909e-01]], [[ nan, 6.12323400e-17, -1.25068532e-01, ..., -8.48296229e-02, -2.46594244e-03, 8.77949563e-03], [ nan, 6.12323400e-17, 2.06295396e-01, ..., -1.55475424e-01, -1.33299485e-01, -9.02193195e-03], [ nan, 6.12323400e-17, 1.97397855e-01, ..., 1.47180620e-01, 1.13735351e-01, -1.06306659e-02], ... [ nan, nan, nan, ..., nan, nan, nan], [ nan, nan, nan, ..., nan, nan, nan], [ nan, nan, nan, ..., nan, nan, nan]], [[ nan, 6.12323400e-17, 2.90721919e-01, ..., 4.76096954e-02, -3.37923445e-03, 1.07607578e-01], [ nan, 6.12323400e-17, 1.65293491e-01, ..., -1.30611866e-01, -8.31035597e-02, 1.87609867e-03], [ nan, nan, nan, ..., nan, nan, nan], ..., [ nan, nan, nan, ..., nan, nan, nan], [ nan, nan, nan, ..., nan, nan, nan], [ nan, nan, nan, ..., nan, nan, nan]]])
- AlignmentFactorAxis :
- 0
[9]:
q_index_of_twotimecorrelation = 10
print("Calculating the two-time correlation for q = %.2f $\mu$m$^{-1}$" % ddm_calc.q[q_index_of_twotimecorrelation])
twotimecorr = ddm_calc.createTwoTimeCorr(ddm_variability,q_index_of_twotimecorrelation)
Calculating the two-time correlation for q = 2.03 $\mu$m$^{-1}$
[12]:
plt.figure(figsize=(10,10))
plt.matshow(twotimecorr, cmap='jet_r', fignum=0)
plt.xlabel("Frame 2")
plt.ylabel("Frame 1")
plt.figure(figsize=(8,5))
plt.matshow(twotimecorr[0:50,0:70], cmap='jet_r', fignum=0)
plt.xlabel("Frame 2")
plt.ylabel("Frame 1")
[12]:
Text(0, 0.5, 'Frame 1')
[15]:
#Averaging over all time...
fig,ax = plt.subplots()
ax.semilogx(ddm_variability.lagtime, ddm_variability.ddm_matrix[:,:,q_index_of_twotimecorrelation].mean(axis=1),'bo')
ax.set_xlabel("Lag time (s)")
ax.set_ylabel("DDM Matrix")
[15]:
Text(0, 0.5, 'DDM Matrix')
[17]:
q_index = 10
variations = np.empty((len(ddm_variability.lagtime), len(ddm_variability.time)))
variations.fill(np.nan)
skewnesses = np.empty(len(ddm_variability.lagtime))
for i in range(len(ddm_variability.lagtime)):
variations[i] = ddm_variability.ddm_matrix[i,:,q_index] - np.nanmean(ddm_variability.ddm_matrix[i,:,q_index])
skewnesses[i] = np.nanmean(variations[i]**3) / (np.nanmean(variations[i]**2)**1.5)
[21]:
fig,ax = plt.subplots(figsize = (7,7/1.618))
ax.plot(ddm_variability.lagtime / 41.7, skewnesses, 'ro')
ax.set_xlabel("Lag time (s)")
ax.set_ylabel("Skewness")
ax.set_title("q = %.2f $\mu$m$^{-1}$" % ddm_variability.q[q_index])
[21]:
Text(0.5, 1.0, 'q = 2.03 $\\mu$m$^{-1}$')
[22]:
fig,ax = plt.subplots()
q_index = 18
lagtimeindex = 10
ax.plot(ddm_variability.time, ddm_variability.ddm_matrix[lagtimeindex,:,q_index], 'r.')
ax.set_xlabel("Time")
ax.set_ylabel("DDM Matrix at q = %.2f $\mu$m$^{-1}$" % ddm_calc.q[q_index])
fig,ax = plt.subplots()
variation = ddm_variability.ddm_matrix[lagtimeindex,:,q_index]**2 - np.mean(ddm_variability.ddm_matrix[lagtimeindex,:,q_index])**2
ax.plot(ddm_variability.time, variation, 'b.')
ax.set_xlabel("Time")
ax.set_ylabel("Deviation of DDM Matrix at q = %.2f $\mu$m$^{-1}$" % ddm_calc.q[q_index])
fig,ax = plt.subplots(figsize = (8,8/1.618))
plt.hist(variation, bins=20)
ax.set_xlabel("DDM Matrix at q = %.2f $\mu$m$^{-1}$" % ddm_calc.q[q_index])
ax.set_ylabel("Counts")
ax.set_xlim(-1.8e7,1.8e7)
plt.savefig("DDMMat_hist_q18_lag10.png")
skewness = np.mean(variation**3) / (np.mean(variation**2)**1.5)
print("Skewness: %.4f" % skewness)
Skewness: 1.0917
[ ]:
[ ]: