soft-matter / trackpy

Python particle tracking toolkit
http://soft-matter.github.io/trackpy
Other
441 stars 131 forks source link

'Point' object has no attribute 'counter' #698

Open ajasja opened 2 years ago

ajasja commented 2 years ago

I'm getting an error when I try to link a trajectory image

sub=locs.query('frame>=100 and frame<=150').copy()
sub['mass']=sub.photons
display(sub.head())
tray = tp.link(sub, 20)
Stack trace --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) d:\data\2022-04-26_WALKER_DATA\2022-04-26__analysis\10-analyse-picked.ipynb Cell 5' in () [3](vscode-notebook-cell:/d%3A/data/2022-04-26_WALKER_DATA/2022-04-26__analysis/10-analyse-picked.ipynb#ch0000008?line=2) sub['mass']=sub.photons [4](vscode-notebook-cell:/d%3A/data/2022-04-26_WALKER_DATA/2022-04-26__analysis/10-analyse-picked.ipynb#ch0000008?line=3) display(sub.head()) ----> [5](vscode-notebook-cell:/d%3A/data/2022-04-26_WALKER_DATA/2022-04-26__analysis/10-analyse-picked.ipynb#ch0000008?line=4) tray = tp.link(sub, 20) File C:\bin\python\anaconda64\envs\picasso\lib\site-packages\trackpy\linking\linking.py:188, in link(f, search_range, pos_columns, t_column, **kwargs) [186](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/linking.py?line=185) coords_iter = coords_from_df(f, pos_columns, t_column) [187](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/linking.py?line=186) ids = [] --> [188](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/linking.py?line=187) for i, _ids in link_iter(coords_iter, search_range, **kwargs): [189](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/linking.py?line=188) ids.extend(_ids) [191](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/linking.py?line=190) f['particle'] = ids File C:\bin\python\anaconda64\envs\picasso\lib\site-packages\trackpy\linking\linking.py:96, in link_iter(coords_iter, search_range, **kwargs) [94](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/linking.py?line=93) # initialize the linker and yield the particle ids of the first frame [95](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/linking.py?line=94) linker = Linker(search_range, **kwargs) ---> [96](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/linking.py?line=95) linker.init_level(coords, t) [97](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/linking.py?line=96) yield t, linker.particle_ids [99](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/linking.py?line=98) for t, coords in coords_iter: File C:\bin\python\anaconda64\envs\picasso\lib\site-packages\trackpy\linking\linking.py:470, in Linker.init_level(self, coords, t, extra_data) [467](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/linking.py?line=466) for j in range(self.memory): [468](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/linking.py?line=467) self.mem_history.append(set()) --> [470](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/linking.py?line=469) self.update_hash(coords, t, extra_data) [471](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/linking.py?line=470) # Assume everything in first level starts a Track. [472](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/linking.py?line=471) # Iterate over prev_level, not prev_set, because order -> track ID. [473](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/linking.py?line=472) for p in self.hash.points: File C:\bin\python\anaconda64\envs\picasso\lib\site-packages\trackpy\linking\linking.py:455, in Linker.update_hash(self, coords, t, extra_data) [452](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/linking.py?line=451) if prev_hash is not None and self.predictor is not None: [453](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/linking.py?line=452) prev_hash.set_predictor(self.predictor, t) # Rewrite positions --> [455](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/linking.py?line=454) self.hash = self.hash_cls(points_from_arr(coords, t, extra_data), [456](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/linking.py?line=455) ndim=self.ndim, to_eucl=self.to_eucl, [457](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/linking.py?line=456) dist_func=self.dist_func) [458](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/linking.py?line=457) return prev_hash File C:\bin\python\anaconda64\envs\picasso\lib\site-packages\trackpy\linking\utils.py:26, in points_from_arr(coords, frame_no, extra_data) [24](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/utils.py?line=23) """ Convert an ndarray of coordinates to a list of PointFindLink """ [25](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/utils.py?line=24) if extra_data is None: ---> [26](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/utils.py?line=25) return [Point(frame_no, pos) for pos in coords] [27](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/utils.py?line=26) else: [28](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/utils.py?line=27) return [Point(frame_no, pos, extra_data={key: extra_data[key][i] [29](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/utils.py?line=28) for key in extra_data}) [30](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/utils.py?line=29) for i, pos in enumerate(coords)] File C:\bin\python\anaconda64\envs\picasso\lib\site-packages\trackpy\linking\utils.py:26, in (.0) [24](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/utils.py?line=23) """ Convert an ndarray of coordinates to a list of PointFindLink """ [25](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/utils.py?line=24) if extra_data is None: ---> [26](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/utils.py?line=25) return [Point(frame_no, pos) for pos in coords] [27](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/utils.py?line=26) else: [28](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/utils.py?line=27) return [Point(frame_no, pos, extra_data={key: extra_data[key][i] [29](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/utils.py?line=28) for key in extra_data}) [30](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/utils.py?line=29) for i, pos in enumerate(coords)] File C:\bin\python\anaconda64\envs\picasso\lib\site-packages\trackpy\linking\utils.py:112, in Point.__init__(self, t, pos, id, extra_data) [110](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/utils.py?line=109) def __init__(self, t, pos, id=None, extra_data=None): [111](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/utils.py?line=110) self._track = None --> [112](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/utils.py?line=111) self.uuid = next(self.counter) # unique id for __hash__ [113](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/utils.py?line=112) self.t = t [114](file:///c%3A/bin/python/anaconda64/envs/picasso/lib/site-packages/trackpy/linking/utils.py?line=113) self.pos = np.asarray(pos) AttributeError: 'Point' object has no attribute 'counter'

I fixed the error by adding 'counter' to __slots__ on line 104 of utils.py (https://github.com/soft-matter/trackpy/blob/a316c658ffd03d4b6fe705b9bedd63c1ab8276c0/trackpy/linking/utils.py#L104)

pytrack; 0.50. Python: 3.8.5 OS: Win10

nkeim commented 2 years ago

Thanks for this report and for the workaround! This is weird, since this counter class attribute doesn't generally cause issues, and I doubt that your version of Python has a uniquely different implementation of __slots__. Could the real issue be that we are not calling the reset_counter() classmethod before attempting to access self.counter? Based on a brief look at 'link_iter() that doesn't seem possible…

I'd love to know if anyone else is encountering this. @ajasja were you seeing this error very consistently before you fixed it, or was it only with certain data or scripts?

The suggested change is reasonable, but I just want to make sure that we are not seeing signs of an edge-case bug somewhere else in trackpy…

ajasja commented 2 years ago

Thanks @nkeim ! I was getting this on every invocation (if that is what you mean by consistent:) But I can try to run the same code on google colab if it helps.

nkeim commented 2 years ago

Thanks. I guess I'd like to know if it's an issue with the specific data you're tracking or the parameters you're using—this bug is not triggered in any of our tests, and it hasn't been reported before. Have you tried this with a different portion of your data, or with a different search_radius for link?

Just as a sanity check, can you also please verify that the first line of Linker.init_level, which should be linking.py line 461, is a call to Point.reset_counter()? I ask because your traceback involves init_level, and the error you're getting should be impossible once Point.reset_counter has been called.

nkeim commented 2 years ago

Also, could you please check that the following code snippet runs on the same Python you use for trackpy?

class Cls:
    __slots__ = ['a']

o = Cls()
Cls.b = []
o.b.append('test')
print(o.b)

This should be the minimum example to reproduce the issue you're seeing with counter. It produces the output ['test'] on my Python 3.8.

nkeim commented 2 years ago

Sorry, I missed the point in my original code snippet. Please see the edited version!

ajasja commented 2 years ago

Hi, that snippets does indeed work image

ajasja commented 2 years ago

And I do have a call to reset counter. image

ajasja commented 2 years ago

I've tried a search radius of 1,2,3,4,5 px.

ajasja commented 2 years ago

Here is the data 2B9_SingleMolecule_1nM_A8fibres_25C_75mMNaCl_100-200__locs_lq_box5_grad600_drift0.zip

import trackpy as tp
for max_link_displacement_px in [2]:    
    locs = pd.read_hdf('2B9_SingleMolecule_1nM_A8fibres_25C_75mMNaCl_100-200__locs_lq_box5_grad600_drift0.hdf5', 'locs')
    locs['mass']=locs.photons

    tray = tp.link(locs, max_link_displacement_px)