Tutorial about tracking LocData objects#
Tracking refers to link localizations that are close in space over multiple frames and collect those localizations in individual tracks. We here make use of the trackpy package through wrapper functions to deal with LocData objects.
%matplotlib inline
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import locan as lc
/tmp/ipykernel_2021/3049569888.py:4: DeprecationWarning:
Pyarrow will become a required dependency of pandas in the next major release of pandas (pandas 3.0),
(to allow more performant data types, such as the Arrow string type, and better interoperability with other libraries)
but was not found to be installed on your system.
If this would cause problems for you,
please provide us feedback at https://github.com/pandas-dev/pandas/issues/54466
import pandas as pd
lc.show_versions(system=False, dependencies=False, verbose=False)
Locan:
version: 0.20.0.dev41+g755b969
Python:
version: 3.11.6
Synthetic data#
A random dataset is created.
dat = lc.simulate_tracks(n_walks=5, n_steps=100, ranges=((0,1000),(0,1000)),
diffusion_constant=1, seed=1)
dat.print_meta()
Jupyter environment detected. Enabling Open3D WebVisualizer.
[Open3D INFO] WebRTC GUI backend enabled.
[Open3D INFO] WebRTCWindowSystem: HTTP handshake server disabled.
identifier: "1"
source: SIMULATION
state: RAW
history {
name: "simulate_tracks"
parameter: "{\'n_walks\': 5, \'n_steps\': 100, \'ranges\': ((0, 1000), (0, 1000)), \'diffusion_constant\': 1, \'time_step\': 10, \'seed\': 1}"
}
element_count: 500
frame_count: 100
creation_time {
2024-03-14T11:10:03.274708Z
}
dat.data.head()
position_x | position_y | frame | |
---|---|---|---|
0 | 518.146180 | 429.651004 | 0 |
1 | 524.470735 | 435.975560 | 1 |
2 | 530.795291 | 429.651004 | 2 |
3 | 524.470735 | 435.975560 | 3 |
4 | 518.146180 | 429.651004 | 4 |
fig, ax = plt.subplots(nrows=1, ncols=1)
dat.data.plot.scatter(x='position_x', y='position_y', ax=ax, color='Blue', label='locdata')
plt.show()
Track locdata#
The track function collects tracks in a new locdata object.
tracks, track_numbers = lc.track(dat, search_range=500)
Frame 99: 5 trajectories present.
tracks.print_summary()
identifier: "7"
comment: ""
source: DESIGN
state: RAW
element_count: 5
frame_count: 1
creation_time {
2024-03-14T11:10:03.783495Z
}
tracks.data
localization_count | position_x | uncertainty_x | position_y | uncertainty_y | frame | region_measure_bb | localization_density_bb | subregion_measure_bb | |
---|---|---|---|---|---|---|---|---|---|
0 | 100 | 882.368107 | 4.321878 | 504.562854 | 2.382224 | 0 | 14720.0 | 0.006793 | 493.315315 |
1 | 100 | 169.710816 | 1.293090 | 381.371093 | 3.679042 | 0 | 6840.0 | 0.014620 | 354.175098 |
2 | 100 | 505.497069 | 2.098581 | 456.720101 | 3.047211 | 0 | 8640.0 | 0.011574 | 379.473319 |
3 | 100 | 307.151281 | 1.787996 | 19.463682 | 1.378273 | 0 | 4800.0 | 0.020833 | 278.280434 |
4 | 100 | 972.599640 | 3.900272 | 805.187177 | 1.217117 | 0 | 7560.0 | 0.013228 | 379.473319 |
tracks.references[0].data.head()
position_x | position_y | frame | |
---|---|---|---|
300 | 954.974002 | 543.269132 | 0 |
301 | 961.298558 | 549.593688 | 1 |
302 | 967.623113 | 555.918243 | 2 |
303 | 961.298558 | 549.593688 | 3 |
304 | 967.623113 | 543.269132 | 4 |
fig, ax = plt.subplots(nrows=1, ncols=1)
dat.data.plot.scatter(x='position_x', y='position_y', ax=ax, color='Blue', label='locdata')
tracks.references[0].data.plot.scatter(x='position_x', y='position_y', ax=ax, color='Red', label='track 0')
plt.show()
fig, ax = plt.subplots(nrows=1, ncols=1)
dat.data.plot.scatter(x='position_x', y='position_y', ax=ax, color='Blue', label='locdata')
tracks.data.plot.scatter(x='position_x', y='position_y', ax=ax, color='Red', label='tracks')
plt.show()
To show individual tracks make use of the reference attribute.
fig, ax = plt.subplots(nrows=1, ncols=1)
jet= plt.get_cmap('jet')
colors = iter(jet(np.linspace(0, 1, len(tracks))))
for ref in tracks.references:
c=next(colors)
ref.data.plot.scatter(x='position_x', y='position_y', ax=ax, color=(c,), label=ref.meta.identifier)
plt.show()
Alternatively to a new locdata object, a pandas DataFrame can be generated using the link_locdata method.
links = lc.link_locdata(dat, search_range=10)
Frame 99: 5 trajectories present.
links.head()
300 0
200 1
0 2
400 3
100 4
Name: track, dtype: int64
Use the following to add particle column to original locdata dataset.
dat.data.loc[links.index,'track']=links
dat.data.head()
position_x | position_y | frame | track | |
---|---|---|---|---|
0 | 518.146180 | 429.651004 | 0 | 2.0 |
1 | 524.470735 | 435.975560 | 1 | 2.0 |
2 | 530.795291 | 429.651004 | 2 | 2.0 |
3 | 524.470735 | 435.975560 | 3 | 2.0 |
4 | 518.146180 | 429.651004 | 4 | 2.0 |
Track using trackpy with locdata.data as input#
For a detailed tracking analysis you might want to use trackpy functions with the pandas DataFrame carrying localization data as input. The following examples will igve a short illustration of using trackpy functions.
import trackpy as tp
t = tp.link_df(dat.data, search_range=100, memory=1, pos_columns=['position_x', 'position_y'], t_column='frame')
t.head(10)
Frame 99: 5 trajectories present.
position_x | position_y | frame | track | particle | |
---|---|---|---|---|---|
300 | 954.974002 | 543.269132 | 0 | 0.0 | 0 |
200 | 150.484168 | 402.874581 | 0 | 1.0 | 1 |
0 | 518.146180 | 429.651004 | 0 | 2.0 | 2 |
400 | 318.156007 | 33.883669 | 0 | 3.0 | 3 |
100 | 944.139141 | 821.378039 | 0 | 4.0 | 4 |
1 | 524.470735 | 435.975560 | 1 | 2.0 | 2 |
201 | 156.808723 | 409.199136 | 1 | 1.0 | 1 |
301 | 961.298558 | 549.593688 | 1 | 0.0 | 0 |
401 | 324.480563 | 27.559113 | 1 | 3.0 | 3 |
101 | 937.814586 | 815.053483 | 1 | 4.0 | 4 |
plt.figure()
tp.plot_traj(t, pos_columns=['position_x', 'position_y'], t_column='frame');
filter#
t.head()
position_x | position_y | frame | track | particle | |
---|---|---|---|---|---|
300 | 954.974002 | 543.269132 | 0 | 0.0 | 0 |
200 | 150.484168 | 402.874581 | 0 | 1.0 | 1 |
0 | 518.146180 | 429.651004 | 0 | 2.0 | 2 |
400 | 318.156007 | 33.883669 | 0 | 3.0 | 3 |
100 | 944.139141 | 821.378039 | 0 | 4.0 | 4 |
t1 = tp.filter_stubs(t, 10).reset_index(drop=True)
len(t1)
500
plt.figure()
tp.plot_traj(t1, pos_columns=['position_x', 'position_y']);
drift#
d = tp.compute_drift(t1, pos_columns=['position_x', 'position_y'])
d.plot()
plt.show()
Mean square displacement (msd)#
em = tp.emsd(t1,0.1, 100, pos_columns=['position_x', 'position_y']) # microns per pixel = 100/285., frames per second = 24
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[25], line 1
----> 1 em = tp.emsd(t1,0.1, 100, pos_columns=['position_x', 'position_y']) # microns per pixel = 100/285., frames per second = 24
File ~/checkouts/readthedocs.org/user_builds/locan/envs/latest/lib/python3.11/site-packages/trackpy/motion.py:235, in emsd(traj, mpp, fps, max_lagtime, detail, pos_columns)
233 ids.append(pid)
234 msds = pandas_concat(msds, keys=ids, names=['particle', 'frame'])
--> 235 results = msds.mul(msds['N'], axis=0).mean(level=1) # weighted average
236 results = results.div(msds['N'].mean(level=1), axis=0) # weights normalized
237 # Above, lagt is lumped in with the rest for simplicity and speed.
238 # Here, rebuild it from the frame index.
File ~/checkouts/readthedocs.org/user_builds/locan/envs/latest/lib/python3.11/site-packages/pandas/core/frame.py:11666, in DataFrame.mean(self, axis, skipna, numeric_only, **kwargs)
11658 @doc(make_doc("mean", ndim=2))
11659 def mean(
11660 self,
(...)
11664 **kwargs,
11665 ):
> 11666 result = super().mean(axis, skipna, numeric_only, **kwargs)
11667 if isinstance(result, Series):
11668 result = result.__finalize__(self, method="mean")
File ~/checkouts/readthedocs.org/user_builds/locan/envs/latest/lib/python3.11/site-packages/pandas/core/generic.py:12413, in NDFrame.mean(self, axis, skipna, numeric_only, **kwargs)
12406 def mean(
12407 self,
12408 axis: Axis | None = 0,
(...)
12411 **kwargs,
12412 ) -> Series | float:
> 12413 return self._stat_function(
12414 "mean", nanops.nanmean, axis, skipna, numeric_only, **kwargs
12415 )
File ~/checkouts/readthedocs.org/user_builds/locan/envs/latest/lib/python3.11/site-packages/pandas/core/generic.py:12366, in NDFrame._stat_function(self, name, func, axis, skipna, numeric_only, **kwargs)
12355 @final
12356 def _stat_function(
12357 self,
(...)
12363 **kwargs,
12364 ):
12365 assert name in ["median", "mean", "min", "max", "kurt", "skew"], name
> 12366 nv.validate_func(name, (), kwargs)
12368 validate_bool_kwarg(skipna, "skipna", none_allowed=False)
12370 return self._reduce(
12371 func, name=name, axis=axis, skipna=skipna, numeric_only=numeric_only
12372 )
File ~/checkouts/readthedocs.org/user_builds/locan/envs/latest/lib/python3.11/site-packages/pandas/compat/numpy/function.py:416, in validate_func(fname, args, kwargs)
413 return validate_stat_func(args, kwargs, fname=fname)
415 validation_func = _validation_funcs[fname]
--> 416 return validation_func(args, kwargs)
File ~/checkouts/readthedocs.org/user_builds/locan/envs/latest/lib/python3.11/site-packages/pandas/compat/numpy/function.py:88, in CompatValidator.__call__(self, args, kwargs, fname, max_fname_arg_count, method)
86 validate_kwargs(fname, kwargs, self.defaults)
87 elif method == "both":
---> 88 validate_args_and_kwargs(
89 fname, args, kwargs, max_fname_arg_count, self.defaults
90 )
91 else:
92 raise ValueError(f"invalid validation method '{method}'")
File ~/checkouts/readthedocs.org/user_builds/locan/envs/latest/lib/python3.11/site-packages/pandas/util/_validators.py:223, in validate_args_and_kwargs(fname, args, kwargs, max_fname_arg_count, compat_args)
218 raise TypeError(
219 f"{fname}() got multiple values for keyword argument '{key}'"
220 )
222 kwargs.update(args_dict)
--> 223 validate_kwargs(fname, kwargs, compat_args)
File ~/checkouts/readthedocs.org/user_builds/locan/envs/latest/lib/python3.11/site-packages/pandas/util/_validators.py:164, in validate_kwargs(fname, kwargs, compat_args)
142 """
143 Checks whether parameters passed to the **kwargs argument in a
144 function `fname` are valid parameters as specified in `*compat_args`
(...)
161 map to the default values specified in `compat_args`
162 """
163 kwds = kwargs.copy()
--> 164 _check_for_invalid_keys(fname, kwargs, compat_args)
165 _check_for_default_values(fname, kwds, compat_args)
File ~/checkouts/readthedocs.org/user_builds/locan/envs/latest/lib/python3.11/site-packages/pandas/util/_validators.py:138, in _check_for_invalid_keys(fname, kwargs, compat_args)
136 if diff:
137 bad_arg = next(iter(diff))
--> 138 raise TypeError(f"{fname}() got an unexpected keyword argument '{bad_arg}'")
TypeError: mean() got an unexpected keyword argument 'level'
fig, ax = plt.subplots()
ax.plot(em.index, em, 'o')
#ax.set_xscale('log')
#ax.set_yscale('log')
ax.set(ylabel=r'$\langle \Delta r^2 \rangle$ [$\mu$m$^2$]',
xlabel='lag time $t$')
#ax.set(ylim=(1e-2, 10));