The implementation is entirely copied from this SO answer : http://stackoverflow.com/questions/34222272/computing-mean-square-displacement-using-python-and-fft#34222273
Thanks to thomasfermi for the implementation. The original algorithm comes from :
nMoldyn - Interfacing spectroscopic experiments, molecular dynamics simulations and models for time correlation functions
V. Calandrini, E. Pellegrini, P. Calligari, K. Hinsen and G.R. Kneller
Publié en ligne: 9 juin 2011
DOI: 10.1051/sfn/201112010
This notebook compares the speed of calculation of MSD with FFT and without with trackpy.
In [2]:
    
%load_ext snakeviz
%load_ext autoreload
%autoreload 2
%matplotlib inline
import timeit
import matplotlib.pyplot as plt
import matplotlib
matplotlib.style.use('ggplot')
import numpy as np
import pandas as pd
import trackpy as tp
    
In [45]:
    
def autocorr_fft(x):
    N = len(x)
    F = np.fft.fft(x, n=2 * N)  # 2*N because of zero-padding
    PSD = F * F.conjugate()
    res = np.fft.ifft(PSD)
    res= (res[:N]).real  # now we have the autocorrelation in convention B
    n = N * np.ones(N) - np.arange(0, N) #divide res(m) by (N-m)
    return res / n  # this is the autocorrelation in convention A
def msd_fft(traj, mpp, fps, max_lagtime=100, detail=False, pos_columns=['x', 'y']):
    """Implementation comes from here : 
    http://stackoverflow.com/questions/34222272/computing-mean-square-displacement-using-python-and-fft#34222273
    """
    
    r = traj[pos_columns].values
    r *= mpp
    t = traj['frame']
    max_lagtime = min(max_lagtime, len(t))  # checking to be safe
    lagtimes = 1 + np.arange(max_lagtime - 1)    
    N = len(r)
    D = np.square(r).sum(axis=1) 
    D = np.append(D, 0)
    S2 = sum([autocorr_fft(r[:, i]) for i in range(len(pos_columns))])
    Q = 2 * D.sum()
    S1 = np.zeros(max_lagtime)
    for m in range(max_lagtime):
        Q = Q - D[m - 1] - D[N - m]
        S1[m] = Q / (N - m)
    msd = S1 - 2 * S2[:max_lagtime]
    msd = msd[1:]
    lagt = lagtimes / fps
    results = pd.DataFrame(np.array([msd, lagt]).T, columns=['msd', 'lagt'])
    results.index = 1 + np.arange(max_lagtime - 1)
    results.index.name = 'lagt'
    
    return results
def get(N, pos_columns):
    traj = np.cumsum(np.random.choice([-1., 0., 1.], size=(N, len(pos_columns))), axis=0)
    traj = pd.DataFrame(traj, columns=pos_columns)
    traj['frame'] = np.arange(N)
    return traj
    
In [46]:
    
# Generate a random trajectory
pos_columns = ['x', 'y', 'z']
mpp = 0.3
fps = 1
max_lagtime = 1000
traj = get(N=10000, pos_columns=pos_columns)
    
In [47]:
    
# Get MSD with trackpy
msd1 = tp.msd(traj, mpp=mpp, fps=fps, max_lagtime=max_lagtime, pos_columns=pos_columns)
msd1[['msd', 'lagt']].head()
    
    Out[47]:
In [48]:
    
# Get MSD with msd_fft
msd2 = msd_fft(traj, mpp=mpp, fps=fps, max_lagtime=max_lagtime, pos_columns=pos_columns)
msd2[['msd', 'lagt']].head()
    
    Out[48]:
In [49]:
    
# Compate MSD with plots
fig, ax = plt.subplots(figsize=(14, 10))
msd1.plot(x='lagt', y='msd', ax=ax)
msd2.plot(x='lagt', y='msd', ax=ax)
    
    Out[49]:
    
In [50]:
    
t1 = %timeit -o tp.msd(traj, mpp=mpp, fps=fps, max_lagtime=max_lagtime, pos_columns=pos_columns)
t1.best
    
    
    Out[50]:
In [51]:
    
t2 = %timeit -o msd_fft(traj, mpp=mpp, fps=fps, max_lagtime=max_lagtime, pos_columns=pos_columns)
t2.best
    
    
    Out[51]: