trial_envelope.py 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940
  1. from collections import namedtuple
  2. import numpy as np
  3. import sliding1d as sliding
  4. def interpolate(vec, min_size=10):
  5. valid = ~np.isnan(vec)
  6. if np.count_nonzero(valid) < min_size:
  7. return np.empty(vec.size) * np.nan
  8. t = np.arange(vec.size)
  9. return np.interp(t, t[valid], vec[valid])
  10. def whisker(trial, side="left", radius_sample=10, smooth=True):
  11. return Envelope.whisker(trial, side=side, radius_sample=radius_sample, smooth=smooth)
  12. class Envelope(namedtuple("_Envelope", ("time", "raw", "bottom", "top"))):
  13. @classmethod
  14. def whisker(cls, trial, side="left", radius_sample=10, smooth=True):
  15. vec = interpolate(trial.tracking[f"{side}_whisker_angle_deg"])
  16. vec = (vec - vec.min()) / (vec.max() - vec.min())
  17. time = np.array(trial.tracking["time"])
  18. return cls.compute(time, vec, radius_sample=radius_sample, smooth=smooth)
  19. @classmethod
  20. def compute(cls, time, vec, radius_sample=10, smooth=True):
  21. bottom = sliding.nanmin(vec, radius_sample)
  22. top = sliding.nanmax(vec, radius_sample)
  23. if smooth == True:
  24. bottom = sliding.nanmean(bottom, radius_sample)
  25. top = sliding.nanmean(top, radius_sample)
  26. return cls(time, vec, bottom, top)
  27. @property
  28. def amplitude(self):
  29. return self.top - self.bottom
  30. def with_range(self, rng):
  31. return self.__class__(self.time[rng],
  32. self.raw[rng],
  33. self.bottom[rng],
  34. self.top[rng])