scikit-diveMove 1.0.0__tar.gz → 1.0.1__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 (39) hide show
  1. {scikit_divemove-1.0.0 → scikit_divemove-1.0.1}/PKG-INFO +4 -4
  2. {scikit_divemove-1.0.0 → scikit_divemove-1.0.1}/pyproject.toml +28 -5
  3. {scikit_divemove-1.0.0 → scikit_divemove-1.0.1}/skdiveMove/__init__.py +1 -1
  4. {scikit_divemove-1.0.0 → scikit_divemove-1.0.1}/skdiveMove/bouts/__init__.py +1 -1
  5. {scikit_divemove-1.0.0 → scikit_divemove-1.0.1}/skdiveMove/bouts/boutsnls.py +9 -6
  6. {scikit_divemove-1.0.0 → scikit_divemove-1.0.1}/skdiveMove/calibspeed.py +1 -1
  7. {scikit_divemove-1.0.0 → scikit_divemove-1.0.1}/skdiveMove/tdr.py +53 -27
  8. {scikit_divemove-1.0.0 → scikit_divemove-1.0.1}/skdiveMove/tdrphases.py +7 -7
  9. {scikit_divemove-1.0.0 → scikit_divemove-1.0.1}/skdiveMove/tdrsource.py +2 -2
  10. scikit_divemove-1.0.1/skdiveMove/tests/conftest.py +18 -0
  11. scikit_divemove-1.0.0/skdiveMove/tests/__init__.py → scikit_divemove-1.0.1/skdiveMove/tests/get_sample_data.py +44 -2
  12. scikit_divemove-1.0.1/skdiveMove/tests/scenarios.py +135 -0
  13. scikit_divemove-1.0.1/skdiveMove/tests/test_BoutsMLE.py +296 -0
  14. scikit_divemove-1.0.1/skdiveMove/tests/test_BoutsNLS.py +307 -0
  15. scikit_divemove-1.0.1/skdiveMove/tests/test_TDR.py +214 -0
  16. scikit_divemove-1.0.1/skdiveMove/tests/test_TDRPhases.py +162 -0
  17. scikit_divemove-1.0.1/skdiveMove/tests/test_TDRSource.py +46 -0
  18. scikit_divemove-1.0.1/skdiveMove/tests/test_ZOC.py +94 -0
  19. scikit_divemove-1.0.1/skdiveMove/tests/test_calibrate.py +42 -0
  20. {scikit_divemove-1.0.0 → scikit_divemove-1.0.1}/skdiveMove/zoc.py +8 -4
  21. scikit_divemove-1.0.0/skdiveMove/tests/get_sample_data.py +0 -45
  22. scikit_divemove-1.0.0/skdiveMove/tests/test_BoutsMLE.py +0 -294
  23. scikit_divemove-1.0.0/skdiveMove/tests/test_BoutsNLS.py +0 -303
  24. scikit_divemove-1.0.0/skdiveMove/tests/test_TDR.py +0 -246
  25. scikit_divemove-1.0.0/skdiveMove/tests/test_TDRPhases.py +0 -205
  26. scikit_divemove-1.0.0/skdiveMove/tests/test_TDRSource.py +0 -48
  27. scikit_divemove-1.0.0/skdiveMove/tests/test_ZOC.py +0 -94
  28. scikit_divemove-1.0.0/skdiveMove/tests/test_calibrate.py +0 -48
  29. {scikit_divemove-1.0.0 → scikit_divemove-1.0.1}/LICENSE +0 -0
  30. {scikit_divemove-1.0.0 → scikit_divemove-1.0.1}/skdiveMove/__main__.py +0 -0
  31. {scikit_divemove-1.0.0 → scikit_divemove-1.0.1}/skdiveMove/bouts/bouts.py +0 -0
  32. {scikit_divemove-1.0.0 → scikit_divemove-1.0.1}/skdiveMove/bouts/boutsmle.py +0 -0
  33. {scikit_divemove-1.0.0 → scikit_divemove-1.0.1}/skdiveMove/calibconfig.py +0 -0
  34. {scikit_divemove-1.0.0 → scikit_divemove-1.0.1}/skdiveMove/config_examples/ag_mk7_2002_022_config.json +0 -0
  35. {scikit_divemove-1.0.0 → scikit_divemove-1.0.1}/skdiveMove/core.py +0 -0
  36. {scikit_divemove-1.0.0 → scikit_divemove-1.0.1}/skdiveMove/helpers.py +0 -0
  37. {scikit_divemove-1.0.0 → scikit_divemove-1.0.1}/skdiveMove/metadata.py +0 -0
  38. {scikit_divemove-1.0.0 → scikit_divemove-1.0.1}/skdiveMove/plotting.py +0 -0
  39. {scikit_divemove-1.0.0 → scikit_divemove-1.0.1}/skdiveMove/tests/data/ag_mk7_2002_022.nc +0 -0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scikit-diveMove
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: Python interface to R package diveMove
5
5
  License: AGPL-3.0-or-later
6
6
  License-File: LICENSE
7
7
  Keywords: animal behaviour,biology,behavioural ecology,diving,diving behaviour
8
8
  Author: Sebastian Luque
9
- Requires-Python: >=3.11
9
+ Requires-Python: >=3.13
10
10
  Classifier: Development Status :: 4 - Beta
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Intended Audience :: Developers
@@ -15,14 +15,14 @@ Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or l
15
15
  Classifier: Topic :: Scientific/Engineering
16
16
  Classifier: Topic :: Scientific/Engineering :: Instrument Drivers
17
17
  Requires-Dist: bottleneck (>=1.6.0,<2.0.0)
18
- Requires-Dist: docutils (>=0.22.4,<0.23.0)
18
+ Requires-Dist: docutils (>=0.23,<0.24)
19
19
  Requires-Dist: matplotlib (>=3.10.8,<4.0.0)
20
20
  Requires-Dist: netcdf4 (>=1.7.4,<2.0.0)
21
21
  Requires-Dist: numpy (>=2.4.3,<3.0.0)
22
22
  Requires-Dist: pandas (>=3.0.1,<4.0.0)
23
23
  Requires-Dist: rpy2 (>=3.6.5,<4.0.0)
24
24
  Requires-Dist: scipy (>=1.17.1,<2.0.0)
25
- Requires-Dist: statsmodels (>=0.14.6,<0.15.0)
25
+ Requires-Dist: statsmodels (>=0.15.0,<0.16.0)
26
26
  Requires-Dist: xarray (>=2026.2.0,<2027.0.0)
27
27
  Project-URL: Documentation, https://spluque.github.io/scikit-diveMove
28
28
  Project-URL: Repository, https://github.com/spluque/scikit-diveMove
@@ -6,17 +6,17 @@ authors = [
6
6
  {name = "Sebastian Luque"}
7
7
  ]
8
8
  license = {text = "AGPL-3.0-or-later"}
9
- requires-python = ">=3.11"
9
+ requires-python = ">=3.13"
10
10
  dependencies = [
11
11
  "bottleneck (>=1.6.0,<2.0.0)",
12
- "docutils (>=0.22.4,<0.23.0)",
12
+ "docutils (>=0.23,<0.24)",
13
13
  "matplotlib (>=3.10.8,<4.0.0)",
14
14
  "netcdf4 (>=1.7.4,<2.0.0)",
15
15
  "numpy (>=2.4.3,<3.0.0)",
16
16
  "pandas (>=3.0.1,<4.0.0)",
17
17
  "rpy2 (>=3.6.5,<4.0.0)",
18
18
  "scipy (>=1.17.1,<2.0.0)",
19
- "statsmodels (>=0.14.6,<0.15.0)",
19
+ "statsmodels (>=0.15.0,<0.16.0)",
20
20
  "xarray (>=2026.2.0,<2027.0.0)"
21
21
  ]
22
22
  classifiers = [
@@ -34,7 +34,7 @@ keywords = ["animal behaviour",
34
34
  "diving",
35
35
  "diving behaviour"
36
36
  ]
37
- version = "1.0.0"
37
+ version = "1.0.1"
38
38
 
39
39
  [project.urls]
40
40
  repository = "https://github.com/spluque/scikit-diveMove"
@@ -44,7 +44,7 @@ documentation = "https://spluque.github.io/scikit-diveMove"
44
44
  docs = [
45
45
  "ipykernel (>=7.2.0,<8.0.0)",
46
46
  "myst-nb (>=1.4.0,<2.0.0)",
47
- "pydata-sphinx-theme (>=0.16.1,<0.17.0)"
47
+ "pydata-sphinx-theme (>=0.24.0,<0.25.0)"
48
48
  ]
49
49
  test = [
50
50
  "pytest (>=9.0.2,<10.0.0)",
@@ -77,6 +77,29 @@ enable = false
77
77
  source = "skdiveMove/__init__.py"
78
78
  pattern = "^__version__\\s*=\\s*['\"](?P<version>.+?)['\"]"
79
79
 
80
+ [tool.pytest]
81
+ minversion = "9.0"
82
+ testpaths = ["skdiveMove"]
83
+ addopts = [
84
+ "--doctest-modules",
85
+ "--ignore-glob=skdiveMove/config_examples/*.json",
86
+ "-v",
87
+ "--tb=short",
88
+ "--strict-markers"
89
+ ]
90
+ doctest_optionflags = ["NORMALIZE_WHITESPACE", "IGNORE_EXCEPTION_DETAIL"]
91
+ markers = [
92
+ "integration: integration tests (deselect with '-m \"not integration\"')",
93
+ "slow: slow tests (deselect with '-m \"not slow\"')",
94
+ "plotting: tests that generate plots"
95
+ ]
96
+ filterwarnings = [
97
+ "ignore:.*ABCs.+:DeprecationWarning",
98
+ "ignore:.*tostring.+:DeprecationWarning"
99
+ ]
100
+ log_format = "%(asctime)s[%(levelname)s] %(name)s:%(lineno)d: %(message)s"
101
+ log_date_format = "%Y-%m-%dT%H:%M:%S%z"
102
+
80
103
  [build-system]
81
104
  requires = [
82
105
  "poetry-core>=2.0.0,<3.0.0",
@@ -88,5 +88,5 @@ from skdiveMove.calibconfig import dump_config_template
88
88
 
89
89
  __author__ = "Sebastian Luque <spluque@gmail.com>"
90
90
  __license__ = "AGPLv3"
91
- __version__ = "1.0.0"
91
+ __version__ = "1.0.1"
92
92
  __all__ = ["TDR", "calibrate", "dump_config_template"]
@@ -107,7 +107,7 @@ Methods summary
107
107
  from .bouts import Bouts, label_bouts
108
108
  from .boutsnls import BoutsNLS
109
109
  from .boutsmle import BoutsMLE
110
- from skdiveMove.tests import random_mixexp
110
+ from skdiveMove.tests.get_sample_data import random_mixexp
111
111
 
112
112
  __all__ = ["Bouts", "BoutsNLS", "BoutsMLE", "label_bouts",
113
113
  "random_mixexp"]
@@ -26,7 +26,7 @@ class BoutsNLS(bouts.Bouts):
26
26
  :math:`p < 0.7` and the second process occurs with the remaining
27
27
  probability.
28
28
 
29
- >>> from skdiveMove.tests import random_mixexp
29
+ >>> from skdiveMove.tests.get_sample_data import random_mixexp
30
30
  >>> rng = np.random.default_rng(123)
31
31
  >>> x2 = random_mixexp(1000, p=0.7, lda=np.array([0.05, 0.005]),
32
32
  ... rng=rng)
@@ -48,13 +48,16 @@ class BoutsNLS(bouts.Bouts):
48
48
 
49
49
  Plot observed and predicted data:
50
50
 
51
- >>> xbouts2.plot_fit(coefs) # doctest: +ELLIPSIS
52
- <Axes: ...>
51
+ >>> from matplotlib.pyplot import Axes
52
+ >>> ax = xbouts2.plot_fit(coefs)
53
+ >>> isinstance(ax, Axes)
54
+ True
53
55
 
54
56
  Plot ECDF:
55
57
 
56
- >>> xbouts2.plot_ecdf(coefs) # doctest: +ELLIPSIS
57
- <Axes: ...>
58
+ >>> ax = xbouts2.plot_ecdf(coefs)
59
+ >>> isinstance(ax, Axes)
60
+ True
58
61
 
59
62
  """
60
63
  def fit(self, start, **kwargs):
@@ -156,7 +159,7 @@ class BoutsNLS(bouts.Bouts):
156
159
 
157
160
 
158
161
  if __name__ == '__main__':
159
- from skdiveMove.tests import diveMove2skd
162
+ from skdiveMove.tests.get_sample_data import diveMove2skd
160
163
  import pandas as pd
161
164
 
162
165
  tdrX = diveMove2skd()
@@ -107,6 +107,6 @@ def calibrate_speed(x, tau, contour_level, plot=True, ax=None):
107
107
 
108
108
 
109
109
  if __name__ == '__main__':
110
- from skdiveMove.tests import diveMove2skd
110
+ from skdiveMove.tests.get_sample_data import diveMove2skd
111
111
  tdrX = diveMove2skd()
112
112
  print(tdrX)
@@ -48,13 +48,18 @@ class TDR(TDRPhases):
48
48
  --------
49
49
  Construct an instance from diveMove example dataset
50
50
 
51
- >>> from skdiveMove.tests import diveMove2skd
51
+ >>> import matplotlib.pyplot as plt
52
+ >>> from skdiveMove.tests.get_sample_data import diveMove2skd
53
+
52
54
  >>> tdrX = diveMove2skd()
53
55
 
54
56
  Plot the `TDR` object
55
57
 
56
- >>> tdrX.plot() # doctest: +ELLIPSIS
57
- (<Figure ... 1 Axes>, <Axes: ...>)
58
+ >>> fig, ax = tdrX.plot()
59
+ >>> isinstance(fig, plt.Figure)
60
+ True
61
+ >>> ax is not None
62
+ True
58
63
 
59
64
  """
60
65
 
@@ -131,7 +136,7 @@ class TDR(TDRPhases):
131
136
 
132
137
  Examples
133
138
  --------
134
- >>> from skdiveMove.tests import diveMove2skd
139
+ >>> from skdiveMove.tests.get_sample_data import diveMove2skd
135
140
  >>> tdrX = diveMove2skd(has_speed=True)
136
141
  >>> tdrX.zoc("offset", offset=3)
137
142
  >>> tdrX.calibrate_speed(z=2)
@@ -210,7 +215,7 @@ class TDR(TDRPhases):
210
215
  --------
211
216
  ZOC using the "filter" method
212
217
 
213
- >>> from skdiveMove.tests import diveMove2skd
218
+ >>> from skdiveMove.tests.get_sample_data import diveMove2skd
214
219
  >>> tdrX = diveMove2skd(has_speed=True)
215
220
 
216
221
  >>> # Window lengths and probabilities
@@ -285,7 +290,7 @@ class TDR(TDRPhases):
285
290
  ones_list.append(res)
286
291
 
287
292
  ones_df = pd.concat(ones_list, ignore_index=True)
288
- ones_df.set_index(dive_ids[ok].unique(), inplace=True)
293
+ ones_df = ones_df.set_index(dive_ids[ok].unique())
289
294
  ones_df.index.rename("dive_id", inplace=True)
290
295
  ones_df["postdive_dur"] = postdive_dur[idx_name]
291
296
 
@@ -355,11 +360,16 @@ class TDR(TDRPhases):
355
360
 
356
361
  Examples
357
362
  --------
358
- >>> from skdiveMove.tests import diveMove2skd
363
+ >>> import matplotlib.pyplot as plt
364
+ >>> from skdiveMove.tests.get_sample_data import diveMove2skd
359
365
  >>> tdrX = diveMove2skd()
360
- >>> tdrX.plot(xlim=["2002-01-05 21:00:00", "2002-01-06 04:10:00"],
361
- ... depth_lim=[95, -1]) # doctest: +ELLIPSIS
362
- (<Figure ... with 1 Axes>, <Axes: ...'>)
366
+ >>> fig, ax = tdrX.plot(
367
+ ... xlim=["2002-01-05 21:00:00", "2002-01-06 04:10:00"],
368
+ ... depth_lim=[95, -1])
369
+ >>> isinstance(fig, plt.Figure)
370
+ True
371
+ >>> ax is not None
372
+ True
363
373
 
364
374
  """
365
375
  try:
@@ -419,7 +429,9 @@ class TDR(TDRPhases):
419
429
 
420
430
  Examples
421
431
  --------
422
- >>> from skdiveMove.tests import diveMove2skd
432
+ >>> import matplotlib.pyplot as plt
433
+ >>> from skdiveMove.tests.get_sample_data import diveMove2skd
434
+
423
435
  >>> tdrX = diveMove2skd()
424
436
  >>> # Window lengths and probabilities
425
437
  >>> DB = [-2, 5]
@@ -430,9 +442,11 @@ class TDR(TDRPhases):
430
442
  >>> tdrX.detect_dives(3)
431
443
  >>> tdrX.detect_dive_phases("unimodal", descent_crit_q=0.01,
432
444
  ... ascent_crit_q=0, knot_factor=20)
433
- >>> tdrX.plot_zoc() # doctest: +ELLIPSIS
434
- (<Figure ... with 3 Axes>, array([<Axes: ...'>,
435
- <Axes: ...'>, <Axes: ...>], dtype=object))
445
+ >>> fig, axs = tdrX.plot_zoc()
446
+ >>> isinstance(fig, plt.Figure)
447
+ True
448
+ >>> len(axs) == 3
449
+ True
436
450
 
437
451
  """
438
452
  zoc_method = self.zoc_method
@@ -497,16 +511,21 @@ class TDR(TDRPhases):
497
511
 
498
512
  Examples
499
513
  --------
500
- >>> from skdiveMove.tests import diveMove2skd
514
+ >>> import matplotlib.pyplot as plt
515
+ >>> from skdiveMove.tests.get_sample_data import diveMove2skd
516
+
501
517
  >>> tdrX = diveMove2skd()
502
518
  >>> tdrX.zoc("offset", offset=3)
503
519
  >>> tdrX.detect_wet()
504
520
  >>> tdrX.detect_dives(3)
505
521
  >>> tdrX.detect_dive_phases("unimodal", descent_crit_q=0.01,
506
522
  ... ascent_crit_q=0, knot_factor=20)
507
- >>> tdrX.plot_phases(list(range(250, 300)),
508
- ... surface=True) # doctest: +ELLIPSIS
509
- (<Figure ... with 1 Axes>, <Axes: ...>)
523
+ >>> fig, ax = tdrX.plot_phases(list(range(250, 300)),
524
+ ... surface=True)
525
+ >>> isinstance(fig, plt.Figure)
526
+ True
527
+ >>> ax is not None
528
+ True
510
529
 
511
530
  """
512
531
  row_ids = self.get_dives_details("row_ids")
@@ -595,16 +614,21 @@ class TDR(TDRPhases):
595
614
 
596
615
  Examples
597
616
  --------
598
- >>> from skdiveMove.tests import diveMove2skd
617
+ >>> import matplotlib.pyplot as plt
618
+ >>> from skdiveMove.tests.get_sample_data import diveMove2skd
619
+
599
620
  >>> tdrX = diveMove2skd()
600
621
  >>> tdrX.zoc("offset", offset=3)
601
622
  >>> tdrX.detect_wet()
602
623
  >>> tdrX.detect_dives(3)
603
624
  >>> tdrX.detect_dive_phases("unimodal", descent_crit_q=0.01,
604
625
  ... ascent_crit_q=0, knot_factor=20)
605
- >>> tdrX.plot_dive_model(diveNo=20,
606
- ... figsize=(10, 10)) # doctest: +ELLIPSIS
607
- (<Figure ... with 2 Axes>, (<Axes: ...>, <Axes: ...>))
626
+ >>> fig, axs = tdrX.plot_dive_model(diveNo=20,
627
+ ... figsize=(10, 10))
628
+ >>> isinstance(fig, plt.Figure)
629
+ True
630
+ >>> len(axs) == 2
631
+ True
608
632
 
609
633
  """
610
634
  dive_ids = self.get_dives_details("row_ids", "dive_id")
@@ -776,16 +800,18 @@ class TDR(TDRPhases):
776
800
 
777
801
  Examples
778
802
  --------
779
- >>> from skdiveMove.tests import diveMove2skd
803
+ >>> from skdiveMove.tests.get_sample_data import diveMove2skd
804
+ >>> from xarray import Dataset
805
+
780
806
  >>> tdrX = diveMove2skd()
781
807
  >>> tdrX.zoc("offset", offset=3)
782
808
  >>> tdrX.detect_wet()
783
809
  >>> tdrX.detect_dives(3)
784
810
  >>> tdrX.detect_dive_phases("unimodal", descent_crit_q=0.01,
785
811
  ... ascent_crit_q=0, knot_factor=20)
786
- >>> tdrX.extract_dives(diveNo=20) # doctest: +ELLIPSIS
787
- <xarray.Dataset> ...
788
- Dimensions: ...
812
+ >>> dives_ds = tdrX.extract_dives(diveNo=20)
813
+ >>> isinstance(dives_ds, Dataset)
814
+ True
789
815
 
790
816
  """
791
817
  dive_ids = self.get_dives_details("row_ids", "dive_id")
@@ -832,7 +858,7 @@ def calibrate(tdr_file, config_file=None):
832
858
 
833
859
  """
834
860
  if config_file is None:
835
- config = calibconfig._DEFAULT_CONFIG
861
+ config = calibconfig._DEFAULT_CONFIG.copy()
836
862
  else:
837
863
  config = calibconfig.read_config(config_file)
838
864
 
@@ -148,7 +148,7 @@ class TDRPhases(ZOC):
148
148
  --------
149
149
  ZOC using the "offset" method for convenience
150
150
 
151
- >>> from skdiveMove.tests import diveMove2skd
151
+ >>> from skdiveMove.tests.get_sample_data import diveMove2skd
152
152
  >>> tdrX = diveMove2skd("TDRPhases")
153
153
  >>> tdrX.zoc("offset", offset=3)
154
154
 
@@ -180,7 +180,7 @@ class TDRPhases(ZOC):
180
180
  dry_thr=dry_thr,
181
181
  wet_thr=wet_thr,
182
182
  wet_cond=(robjs.vectors
183
- .BoolVector(~depth_py.isna())),
183
+ .BoolVector(wet_cond)),
184
184
  interval=dtime))
185
185
  with (robjs.default_converter + pandas2ri.converter).context():
186
186
  phases = pd.DataFrame({'phase_id': phases_l.rx2("phase.id"),
@@ -234,7 +234,7 @@ class TDRPhases(ZOC):
234
234
  --------
235
235
  ZOC using the ``offset`` method for convenience
236
236
 
237
- >>> from skdiveMove.tests import diveMove2skd
237
+ >>> from skdiveMove.tests.get_sample_data import diveMove2skd
238
238
  >>> tdrX = diveMove2skd("TDRPhases")
239
239
  >>> tdrX.zoc("offset", offset=3)
240
240
 
@@ -256,7 +256,7 @@ class TDRPhases(ZOC):
256
256
  # Replace dots with underscore
257
257
  phases_df.columns = (phases_df.columns.str
258
258
  .replace(".", "_", regex=False))
259
- phases_df.set_index(depth_py.index, inplace=True)
259
+ phases_df = phases_df.set_index(depth_py.index)
260
260
  dive_activity = phases_df.pop("dive_activity")
261
261
  # Dive and post-dive ID should be integer
262
262
  phases_df = phases_df.astype(int)
@@ -383,7 +383,7 @@ class TDRPhases(ZOC):
383
383
  --------
384
384
  ZOC using the "offset" method for convenience
385
385
 
386
- >>> from skdiveMove.tests import diveMove2skd
386
+ >>> from skdiveMove.tests.get_sample_data import diveMove2skd
387
387
  >>> tdrX = diveMove2skd("TDRPhases")
388
388
  >>> tdrX.zoc("offset", offset=3)
389
389
 
@@ -664,7 +664,7 @@ class TDRPhases(ZOC):
664
664
 
665
665
  Examples
666
666
  --------
667
- >>> from skdiveMove.tests import diveMove2skd
667
+ >>> from skdiveMove.tests.get_sample_data import diveMove2skd
668
668
  >>> tdrX = diveMove2skd("TDRPhases")
669
669
  >>> tdrX.zoc("offset", offset=3)
670
670
  >>> tdrX.detect_wet()
@@ -712,7 +712,7 @@ class TDRPhases(ZOC):
712
712
 
713
713
  Examples
714
714
  --------
715
- >>> from skdiveMove.tests import diveMove2skd
715
+ >>> from skdiveMove.tests.get_sample_data import diveMove2skd
716
716
  >>> tdrX = diveMove2skd("TDRPhases")
717
717
  >>> tdrX.zoc("offset", offset=3)
718
718
  >>> tdrX.detect_wet()
@@ -31,7 +31,7 @@ class TDRSource:
31
31
 
32
32
  Examples
33
33
  --------
34
- >>> from skdiveMove.tests import diveMove2skd
34
+ >>> from skdiveMove.tests.get_sample_data import diveMove2skd
35
35
  >>> tdrX = diveMove2skd()
36
36
  >>> print(tdrX) # doctest: +ELLIPSIS
37
37
  Time-Depth Recorder -- Class TDR object ...
@@ -66,7 +66,7 @@ class TDRSource:
66
66
  for vname, da in self.tdr.data_vars.items():
67
67
  da.attrs["sampling_rate"] = (1.0 /
68
68
  pd.to_timedelta(subsample)
69
- .seconds)
69
+ .total_seconds())
70
70
  da.attrs["sampling_rate_units"] = "Hz"
71
71
  _append_xr_attr(da, "history",
72
72
  "Resampled to {}\n".format(subsample))
@@ -0,0 +1,18 @@
1
+ """Shared pytest fixtures for scikit-diveMove tests.
2
+
3
+ Provides reusable fixtures for test data, directory management, matplotlib
4
+ backend configuration, and sample data loading.
5
+
6
+ """
7
+
8
+ import pytest
9
+ import matplotlib as mpl
10
+ mpl.use("Agg")
11
+ import matplotlib.pyplot as plt # noqa: E402
12
+
13
+
14
+ @pytest.fixture(autouse=True)
15
+ def close_matplotlib_figures():
16
+ """Close all matplotlib figures after each test."""
17
+ yield
18
+ plt.close("all")
@@ -1,7 +1,49 @@
1
- """scikit-diveMove tests"""
1
+ """Helper function to load sample data onto a TDR object
2
2
 
3
+ """
4
+
5
+ import importlib.resources as rsrc
3
6
  import numpy as np
4
- from .get_sample_data import diveMove2skd # noqa: F401
7
+ from skdiveMove.tdr import TDR
8
+ from skdiveMove.tdrsource import TDRSource
9
+ from skdiveMove.tdrphases import TDRPhases
10
+ from skdiveMove.zoc import ZOC
11
+
12
+
13
+ def diveMove2skd(oclass="TDR", **kwargs):
14
+ """Create a `TDRSource` instance from `diveMove` example data set
15
+
16
+ The sample data set has been converted to NetCDF4 format, to include
17
+ all necessary metadata.
18
+
19
+ Parameters
20
+ ----------
21
+ oclass : {"TDRSource", "ZOC", "TDRPhases", "TDR"}
22
+ The class to return.
23
+ **kwargs
24
+ Optional keyword arguments passed to class instantiator.
25
+
26
+ Returns
27
+ -------
28
+ `TDR`
29
+
30
+ """
31
+ class_names = ["TDRSource", "ZOC", "TDRPhases", "TDR"]
32
+
33
+ if oclass in class_names:
34
+ ifile = (rsrc.files("skdiveMove") / "tests" /
35
+ "data" / "ag_mk7_2002_022.nc")
36
+
37
+ classes = [TDRSource, ZOC, TDRPhases, TDR]
38
+ class_map = dict(zip(class_names, classes))
39
+ tdrX = class_map[oclass].read_netcdf(ifile, depth_name="depth",
40
+ time_name="date_time",
41
+ **kwargs)
42
+ else:
43
+ raise LookupError(("Requested class ({}) does not exist"
44
+ .format(oclass)))
45
+
46
+ return tdrX
5
47
 
6
48
 
7
49
  def random_mixexp(n, p, lda, rng=None):
@@ -0,0 +1,135 @@
1
+ """Meta classes for flexible testing of TDR and bout analysis classes
2
+
3
+ """
4
+ import numpy as np
5
+ import pandas as pd
6
+
7
+ from abc import ABC, abstractmethod
8
+ from skdiveMove.tests.get_sample_data import (
9
+ diveMove2skd, random_mixexp) # noqa: E402
10
+
11
+
12
+ class TDRScenario:
13
+ """Test `TDR` class.
14
+
15
+ Parameters
16
+ ----------
17
+ oclass : {"TDRSource", "ZOC", "TDRPhases", "TDR"}
18
+ The class to retrieve from sample data.
19
+ **kwargs
20
+ Optional keyword arguments passed to class instantiator.
21
+
22
+ Attributes
23
+ ----------
24
+ tdrX : TDR
25
+ TDR instance.
26
+ default_pars : dict
27
+ Calibrate parameters.
28
+ tdr_calib : TDRPhases
29
+ Calibrated TDR instance.
30
+ diveNo_seq : ndarray of shape (N,)
31
+ Sequence of dive numbers in the time series.
32
+
33
+ """
34
+ def __init__(self, oclass, **kwargs) -> None:
35
+ # An instance to work with
36
+ self.tdrX = diveMove2skd(oclass, **kwargs)
37
+ zoc_offset = 3
38
+ dry_thr = 70
39
+ wet_thr = 3610
40
+ dive_thr = 3
41
+ dive_model = "unimodal"
42
+ smooth_par = 0.1
43
+ knot_factor = 3
44
+ descent_crit_q = 0
45
+ ascent_crit_q = 0
46
+ self.default_pars = {"zoc_offset": zoc_offset,
47
+ "dry_thr": dry_thr,
48
+ "wet_thr": wet_thr,
49
+ "dive_thr": dive_thr,
50
+ "dive_model": dive_model,
51
+ "smooth_par": smooth_par,
52
+ "knot_factor": knot_factor,
53
+ "descent_crit_q": descent_crit_q,
54
+ "ascent_crit_q": ascent_crit_q}
55
+ self.diveNo_seq = None
56
+
57
+ def zoc(self):
58
+ tdrX = self.tdrX
59
+ default_pars = self.default_pars
60
+ tdrX.zoc(method="offset", offset=default_pars["zoc_offset"])
61
+
62
+ def detect_wet(self):
63
+ default_pars = self.default_pars
64
+ self.zoc()
65
+ self.tdrX.detect_wet(dry_thr=default_pars["dry_thr"],
66
+ wet_thr=default_pars["wet_thr"])
67
+
68
+ def detect_dives(self):
69
+ default_pars = self.default_pars
70
+ self.detect_wet()
71
+ self.tdrX.detect_dives(dive_thr=default_pars["dive_thr"])
72
+ diveNo_max = (self.tdrX.get_dives_details("row_ids", "dive_id")
73
+ .max())
74
+ self.diveNo_seq = np.arange(diveNo_max) + 1
75
+
76
+ def detect_dive_phases(self):
77
+ default_pars = self.default_pars
78
+ self.detect_dives()
79
+ self.tdrX.detect_dive_phases(
80
+ dive_model=default_pars["dive_model"],
81
+ smooth_par=default_pars["smooth_par"],
82
+ knot_factor=default_pars["knot_factor"],
83
+ descent_crit_q=default_pars["descent_crit_q"],
84
+ ascent_crit_q=default_pars["ascent_crit_q"])
85
+
86
+
87
+ class BoutsBaseScenario(ABC):
88
+ """Common base scenario for testing
89
+
90
+ Subclasses must implement their own __init__ method.
91
+
92
+ Attributes
93
+ ----------
94
+ nsamp : int
95
+ Number of samples to draw from the mixed distributions.
96
+ x : array_like
97
+ 1D array with samples from mixed distribution.
98
+ pars_true : dict
99
+ Dictionary with true parameters used to generate `x`, with keys:
100
+ `p`, `lambda0`, and `lambda1`, if it is a 2-process scenario, or
101
+ `p0`, `p1`, `lambda0`, `lambda1`, and `lambda2`, if it is a
102
+ 3-process scenario.
103
+ nls_opts, mle_opts1, mle_opts2 : dict
104
+ Dictionary with keys and values passed to optimization routine for
105
+ first and second fits.
106
+
107
+ """
108
+
109
+ @abstractmethod
110
+ def __init__(self, nsamp: int) -> None:
111
+ self.nsamp = nsamp
112
+ self.rng = np.random.default_rng()
113
+ self.pars_true = None
114
+ self.nls_opts = None
115
+ self.mle_opts1 = None
116
+ self.mle_opts2 = None
117
+ self.x = None
118
+
119
+ def generate_proc2(self, pars_true):
120
+ # Generate samples for 2-process case
121
+ x2 = random_mixexp(self.nsamp,
122
+ pars_true.loc["p"],
123
+ (pars_true.loc[["lambda0", "lambda1"]]
124
+ .to_numpy()), rng=self.rng)
125
+
126
+ return pd.Series(x2)
127
+
128
+ def generate_proc3(self, pars_true):
129
+ # Generate samples for 3-process case
130
+ x3 = random_mixexp(self.nsamp,
131
+ pars_true.loc[["p0", "p1"]].to_numpy(),
132
+ (pars_true.loc[
133
+ ["lambda0", "lambda1", "lambda2"]]
134
+ .to_numpy()), rng=self.rng)
135
+ return pd.Series(x3)