xarray-behave 0.35.3__tar.gz → 0.35.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/.github/workflows/publish.yaml +4 -4
  2. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/PKG-INFO +1 -1
  3. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/conda/xarray-behave/meta.yaml +8 -10
  4. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/__init__.py +1 -1
  5. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/annot.py +14 -6
  6. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/gui/app.py +39 -5
  7. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/gui/forms/das_predict.yaml +22 -1
  8. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/gui/utils.py +1 -2
  9. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/gui/views.py +9 -3
  10. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/io/poses.py +6 -2
  11. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/.gitignore +0 -0
  12. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/LICENSE +0 -0
  13. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/README.md +0 -0
  14. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/build_env.yml +0 -0
  15. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/conda/xarray-behave/bld.bat +0 -0
  16. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/conda/xarray-behave/build.sh +0 -0
  17. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/conda/xarray-behave/conda_build_config.yaml +0 -0
  18. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/condarc.yml +0 -0
  19. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/doc/demo.ipynb +0 -0
  20. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/doc/demo_behavioral_features.ipynb +0 -0
  21. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/doc/demo_behavioral_features_large_group.ipynb +0 -0
  22. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/doc/ncb.mplstyle +0 -0
  23. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/pyproject.toml +0 -0
  24. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/setup.py +0 -0
  25. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/event_utils.py +0 -0
  26. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/gui/__init__.py +0 -0
  27. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/gui/audio_player.py +0 -0
  28. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/gui/das.py +0 -0
  29. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/gui/formbuilder.py +0 -0
  30. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/gui/forms/das_make.yaml +0 -0
  31. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/gui/forms/das_train.yaml +0 -0
  32. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/gui/forms/envelope_computation.yaml +0 -0
  33. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/gui/forms/export_for_das.yaml +0 -0
  34. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/gui/forms/from_dir.yaml +0 -0
  35. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/gui/forms/from_file.yaml +0 -0
  36. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/gui/forms/from_zarr.yaml +0 -0
  37. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/gui/forms/spec_freq.yaml +0 -0
  38. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/gui/icon.png +0 -0
  39. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/gui/table.py +0 -0
  40. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/gui/view_dialog.py +0 -0
  41. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/gui/widgets.py +0 -0
  42. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/io/__init__.py +0 -0
  43. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/io/annotations.py +0 -0
  44. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/io/annotations_manual.py +0 -0
  45. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/io/audio.py +0 -0
  46. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/io/balltracks.py +0 -0
  47. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/io/movieparams.py +0 -0
  48. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/io/timestamps.py +0 -0
  49. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/io/tracks.py +0 -0
  50. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/loaders.py +0 -0
  51. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/metrics.py +0 -0
  52. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/src/xarray_behave/xarray_behave.py +0 -0
  53. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/tests/test_annot.py +0 -0
  54. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/tests/test_assemble.py +0 -0
  55. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/tests/test_assemble_metrics.py +0 -0
  56. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/tests/test_imports.py +0 -0
  57. {xarray-behave-0.35.3 → xarray-behave-0.35.5}/tests/test_io.py +0 -0
@@ -19,11 +19,11 @@ jobs:
19
19
  strategy:
20
20
  fail-fast: False
21
21
  matrix:
22
- python-version: [3.9 , '3.10', '3.11']
22
+ python-version: ['3.10']
23
23
  os: [ubuntu-latest, windows-latest, macOS-13, macOS-14]
24
- # os: [macOS-13, macOS-14]
25
- # python-version: ['3.11']
26
- # os: [ubuntu-latest]
24
+ include:
25
+ - python-version: 3.9
26
+ os: windows-latest
27
27
  defaults: # https://github.com/marketplace/actions/setup-miniconda#use-a-default-shell
28
28
  run:
29
29
  shell: bash -l {0}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: xarray-behave
3
- Version: 0.35.3
3
+ Version: 0.35.5
4
4
  Summary: xarray tools for behavioral data.
5
5
  Author-email: Jan Clemens <clemensjan@googlemail.com>
6
6
  Requires-Python: >3.6
@@ -16,6 +16,7 @@ requirements:
16
16
  host:
17
17
  - python {{ python }}
18
18
  - pip
19
+ - numpy=1.23
19
20
  run:
20
21
  - python {{ python }}
21
22
  - defopt=6.3
@@ -24,30 +25,27 @@ requirements:
24
25
  - h5py
25
26
  - librosa>0.8
26
27
  - matplotlib
27
- - matplotlib-scalebar
28
28
  - pandas
29
29
  - scipy>=1.9
30
30
  - peakutils
31
31
  - pyyaml
32
32
  - scikit-learn
33
33
  - zarr
34
- - numba # >=0.56
34
+ - numba
35
35
  - xarray
36
36
  - dask
37
- #- py
38
- - conda-forge::pyside6 # [linux]
39
- - conda-forge::pyside2 # [osx or arm64]
40
- - pyside6 # [win and py==310]
41
- - pyside2 # [win and py>310]
42
- - pyside2 # [win and py==39]
43
- - pyqtgraph>0.12.2
37
+ - conda-forge::pyside2
38
+ # - conda-forge::pyside2 # [linux or osx or arm64]
39
+ # - conda-forge::pyside6 # [win and py>39]
40
+ # - conda-forge::pyside2 # [osx or arm64]
41
+ # - pyside2 # [win and py==39]
42
+ - pyqtgraph>0.12
44
43
  - qtpy
45
44
  - superqt
46
45
  - rich
47
46
  - colorcet
48
47
  - python-sounddevice
49
48
  - scikit-image
50
- # - opencv
51
49
  - ffmpeg
52
50
  - pyvideoreader
53
51
  - samplestamps>=0.6
@@ -1,6 +1,6 @@
1
1
  """xarray tools for behavioral data."""
2
2
 
3
- __version__ = "0.35.3"
3
+ __version__ = "0.35.5"
4
4
 
5
5
  from .xarray_behave import assemble, assemble_metrics, load, save
6
6
  import os
@@ -347,9 +347,9 @@ class Events(UserDict):
347
347
  event_at_time = matching_start < time
348
348
  elif self.categories[name] == "event":
349
349
  if nearest_is_start:
350
- event_at_time = np.abs(time - nearest_start) < tol
350
+ event_at_time = np.abs(time - nearest_start) <= tol
351
351
  else:
352
- event_at_time = np.abs(time - nearest_stop) < tol
352
+ event_at_time = np.abs(time - nearest_stop) <= tol
353
353
  else:
354
354
  event_at_time = False
355
355
 
@@ -359,7 +359,13 @@ class Events(UserDict):
359
359
  return index
360
360
 
361
361
  def change_name(
362
- self, time: float, new_name: str, tol: float = 0, min_time: Optional[float] = None, max_time: Optional[float] = None
362
+ self,
363
+ time: float,
364
+ new_name: str,
365
+ tol: float = 0,
366
+ min_time: Optional[float] = None,
367
+ max_time: Optional[float] = None,
368
+ old_name: Optional[str] = None,
363
369
  ) -> Tuple[Optional[List[int]], Optional[str], Optional[str]]:
364
370
  """Change the name of the annotation.
365
371
 
@@ -369,19 +375,21 @@ class Events(UserDict):
369
375
  tol (float, optional): Tolerance for matching events. Defaults to 0.
370
376
  min_time (Optional[float], optional): _description_. Defaults to None.
371
377
  max_time (Optional[float], optional): _description_. Defaults to None.
372
-
378
+ old_name (Optional[str]): name of event to move. Defaults to None.
373
379
  Returns:
374
380
  Tuple[List[int], str, str]: ([start_seconds, stop_seconds], old_name, new_name
375
381
  Tuple[None, None, None] if no event near time, or new_name is old_name
376
382
  """
377
- name = self._get_name_of_nearest(time, min_time, max_time)
383
+ if old_name is None:
384
+ name = self._get_name_of_nearest(time, min_time, max_time)
385
+ else:
386
+ name = old_name
378
387
 
379
388
  # nothing to do
380
389
  if name is None or name == new_name or self.categories[name] != self.categories[new_name]:
381
390
  return None, None, None
382
391
 
383
392
  index = self._get_index_of_nearest(time, name, tol, min_time, max_time)
384
- print(index)
385
393
  if index is not None:
386
394
  changed_time = self[name][index, :]
387
395
  old_name = name
@@ -674,6 +674,10 @@ class MainWindow(QtWidgets.QMainWindow):
674
674
  if form_data["segment_minlen"] is None:
675
675
  form_data["segment_minlen"] = 0
676
676
 
677
+ if form_data["filter_song"] == "no":
678
+ form_data["bandpass_low_freq"] = None
679
+ form_data["bandpass_up_freq"] = None
680
+
677
681
  if form_data["file"] != "Current file":
678
682
  if form_data["folder"] != " ":
679
683
  file_path = form_data["folder"]
@@ -696,6 +700,9 @@ class MainWindow(QtWidgets.QMainWindow):
696
700
  segment_thres=form_data["event_thres"],
697
701
  segment_fillgap=form_data["segment_fillgap"],
698
702
  segment_minlen=form_data["segment_minlen"],
703
+ bandpass_low_freq=form_data["bandpass_low_freq"],
704
+ bandpass_up_freq=form_data["bandpass_up_freq"],
705
+ resample=form_data["resample"],
699
706
  )
700
707
  return
701
708
  elif hasattr(self, "ds") and form_data["file"] == "Current file":
@@ -721,10 +728,10 @@ class MainWindow(QtWidgets.QMainWindow):
721
728
  # select batch size so that at least 10 batches are run
722
729
  # minimizes loss of annotations from batch size "quantization" errors
723
730
  batch_size = 32
724
- nb_batches = lambda batch_size: int(
731
+ nb_batches = int(
725
732
  np.floor((audio.shape[0] - ((batch_size - 1) + params["nb_hist"])) / (params["stride"] * (batch_size)))
726
733
  )
727
- while nb_batches(batch_size) < 10 and batch_size > 1:
734
+ while nb_batches < 10 and batch_size > 1:
728
735
  batch_size -= 1
729
736
 
730
737
  logger.info(" Running inference on audio.")
@@ -733,10 +740,13 @@ class MainWindow(QtWidgets.QMainWindow):
733
740
  fs_model = params["samplerate_x_Hz"]
734
741
  fs_audio = self.ds.song_raw.attrs["sampling_rate_Hz"]
735
742
 
736
- if form_data["resample_audio"] and fs_audio and fs_audio != fs_model:
743
+ if form_data["resample"] and fs_audio and fs_audio != fs_model:
737
744
  logger.info(f" Resampling. Audio rate is {fs_audio}Hz but model was trained on data with {fs_model}Hz.")
738
745
  audio = das.utils.resample(audio, fs_audio, fs_model)
739
746
 
747
+ import rich
748
+
749
+ rich.print(form_data)
740
750
  events, segments, _, _ = das.predict.predict(
741
751
  audio,
742
752
  model_path,
@@ -749,6 +759,10 @@ class MainWindow(QtWidgets.QMainWindow):
749
759
  segment_thres=form_data["event_thres"],
750
760
  segment_fillgap=form_data["segment_fillgap"],
751
761
  segment_minlen=form_data["segment_minlen"],
762
+ bandpass_low_freq=form_data["bandpass_low_freq"],
763
+ bandpass_up_freq=form_data["bandpass_up_freq"],
764
+ resample=form_data["resample"],
765
+ fs_audio=fs_audio,
752
766
  )
753
767
 
754
768
  # Process detected song
@@ -1357,7 +1371,7 @@ class PSV(MainWindow):
1357
1371
  self.spec_win = 200
1358
1372
  self.show_songevents = True
1359
1373
  self.movable_events = True
1360
- self.edit_only_current_events = True
1374
+ self.edit_only_current_events = False
1361
1375
  self.show_all_channels = True
1362
1376
  self.select_loudest_channel = False
1363
1377
  self.threshold_mode = False
@@ -2303,6 +2317,16 @@ class PSV(MainWindow):
2303
2317
  f" Moved {event_name_to_move} from t=[{region.bounds[0]:1.4f}:{region.bounds[1]:1.4f}] to [{new_region[0]:1.4f}:{new_region[1]:1.4f}] seconds."
2304
2318
  )
2305
2319
 
2320
+ # FIXME for moving annotations in ethogram - fails in pyside6
2321
+ if self.annot_view.mousePoint is not None:
2322
+ mp = self.annot_view.mousePoint.y()
2323
+ if mp > 0 and mp < 1:
2324
+ new_event_idx = int(mp * self.nb_eventtypes)
2325
+ new_event_name = self.event_times.names[new_event_idx]
2326
+ _, old_name, new_name = self.event_times.change_name(new_region[0], new_event_name)
2327
+ if old_name is not None:
2328
+ logger.info(f" Changed from {old_name} to {new_name}.")
2329
+
2306
2330
  self.update_xy()
2307
2331
 
2308
2332
  def on_position_change_finished(self, position):
@@ -2312,11 +2336,21 @@ class PSV(MainWindow):
2312
2336
  event_name_to_move = self.current_event_name
2313
2337
  if self.current_event_index != position.event_index:
2314
2338
  event_name_to_move = self.event_times.names[position.event_index]
2315
-
2339
+ print(position.pos(), position.position)
2316
2340
  new_position = position.pos()[0]
2317
2341
  self.event_times.move_time(event_name_to_move, position.position, new_position)
2318
2342
  logger.info(f" Moved {event_name_to_move} from t={position.position:1.4f} to {new_position:1.4f} seconds.")
2319
2343
 
2344
+ # FIXME for moving annotations in ethogram - fails in pyside6
2345
+ if self.annot_view.mousePoint is not None:
2346
+ mp = self.annot_view.mousePoint.y()
2347
+ if mp > 0 and mp < 1:
2348
+ new_event_idx = int(mp * self.nb_eventtypes)
2349
+ new_event_name = self.event_times.names[new_event_idx]
2350
+ _, old_name, new_name = self.event_times.change_name(new_position, new_event_name, old_name=event_name_to_move)
2351
+ if old_name is not None:
2352
+ logger.info(f" Changed from {old_name} to {new_name}.")
2353
+
2320
2354
  self.update_xy()
2321
2355
 
2322
2356
  def on_position_dragged(self, fly, pos, offset):
@@ -65,11 +65,32 @@ main:
65
65
  default: True
66
66
  help: Appends '_proposals' to song name. Individual proposals can be corrected and approved.
67
67
 
68
- - name: resample_audio
68
+ - name: resample
69
69
  label: Resample audio to rate used for training
70
70
  type: bool
71
71
  default: True
72
72
 
73
+ - name: filter_song
74
+ label: Bandpass filter audio
75
+ type: stacked
76
+ default: "no"
77
+ options: "no,yes"
78
+
79
+ "no":
80
+
81
+ "yes":
82
+ - name: bandpass_low_freq
83
+ label: Lower cutoff
84
+ type: double
85
+ default: 50.0
86
+ range: 0.0, 1000000.0
87
+
88
+ - name: bandpass_up_freq
89
+ label: Upper cutoff
90
+ type: double
91
+ default: 10000.0
92
+ range: 0.0, 1000000.0
93
+
73
94
  - type: text
74
95
  text: '<b>Event detection</b>'
75
96
 
@@ -115,8 +115,7 @@ class FastImageWidget(pg.GraphicsLayoutWidget):
115
115
  image_format ([type], optional): [description]. Defaults to QtGui.QImage.Format_RGB888.
116
116
  auto_scale (bool, optional): [description]. Defaults to False.
117
117
  """
118
- qimg = QtGui.QImage(image, image.shape[1], image.shape[0], image_format)
119
- # qimg = QtGui.QImage(image.ctypes.data, image.shape[1], image.shape[0], image_format)
118
+ qimg = QtGui.QImage(image, image.shape[1], image.shape[0], 3 * image.shape[1], image_format)
120
119
 
121
120
  qpix = QtGui.QPixmap(qimg)
122
121
  self.pixmapItem.setPixmap(qpix)
@@ -572,8 +572,6 @@ class AnnotView(pg.PlotWidget):
572
572
  # additionally make names of trace and event arrays in ds args?
573
573
  super().__init__()
574
574
  self.setMouseEnabled(x=False, y=False)
575
- # this should be just a link/ref so changes in ds made by the controller will propagate
576
- # mabe make Model as thin wrapper around ds that also handles ion and use ref to Modle instance
577
575
  self.disableAutoRange()
578
576
  self.enableAutoRange(False, False)
579
577
  self.setDefaultPadding(0.0)
@@ -655,12 +653,20 @@ class AnnotView(pg.PlotWidget):
655
653
  return np.interp(pos, self.xrange, self.m.trange)
656
654
 
657
655
  def _click(self, event):
658
- event.accept()
656
+ # event.accept()
659
657
  pos = event.pos()
660
658
  mouseT = self.getPlotItem().getViewBox().mapSceneToView(pos).x()
661
659
  self.callback(mouseT, event.button())
662
660
 
661
+ # def mouseMoveEvent(self, ev):
662
+ # pos = ev.pos()
663
+ # # print(pos)
664
+ # if self.sceneBoundingRect().contains(pos):
665
+ # self.mousePoint = self.getPlotItem().vb.mapSceneToView(pos)
666
+ # print(self.mousePoint)
667
+
663
668
  def mouseMoved(self, pos):
669
+ # pass
664
670
  if self.sceneBoundingRect().contains(pos):
665
671
  self.mousePoint = self.getPlotItem().vb.mapSceneToView(pos)
666
672
 
@@ -293,8 +293,12 @@ class Sleap(Poses, io.BaseProvider):
293
293
 
294
294
  # indices and coordinate logic for sleap tracks. This could be cleaned/simplified later
295
295
  nb_flies = tracks.shape[0]
296
- thorax_idx = np.argwhere(pose_parts == b"thorax")[0][0]
297
- head_idx = np.argwhere(pose_parts == b"head")[0][0]
296
+ try:
297
+ thorax_idx = np.argwhere(pose_parts == b"thorax")[0][0]
298
+ head_idx = np.argwhere(pose_parts == b"head")[0][0]
299
+ except IndexError:
300
+ thorax_idx = 0
301
+ head_idx = 1
298
302
  x_idx = 0
299
303
  y_idx = 1
300
304
 
File without changes
File without changes
File without changes