Diving behaviour analysis#

Here is a bird’s-eye view of the functionality of scikit-diveMove, loosely following diveMove’s vignette.

Set up the environment. Consider loading the logging module and setting up a logger to monitor progress to this section.

1# Set up
2import importlib.resources as rsrc
3import matplotlib.pyplot as plt
4import skdiveMove as skdive
5
6# Declare figure sizes
7_FIG1X1 = (11, 5)
8_FIG2X1 = (10, 8)
9_FIG3X1 = (11, 11)

Reading data files#

Load diveMove’s example data, using TDR.__init__() method, and print:

18ifile = (rsrc.files("skdiveMove") / "tests" / "data" /
19         "ag_mk7_2002_022.nc")
20tdrX = skdive.TDR.read_netcdf(ifile, depth_name="depth",
21                              time_name="date_time", has_speed=True)
22# Or simply use function ``skdive.tests.diveMove2skd`` to do the
23# same with this particular data set.
24print(tdrX)
Time-Depth Recorder -- Class TDR object
Source File          /opt/hostedtoolcache/Python/3.12.6/x64/lib/python3.12/site-packages/skdiveMove/tests/data/ag_mk7_2002_022.nc
Sampling interval    0 days 00:00:05
Number of Samples    34199
Sampling Begins      2002-01-05 11:32:00
Sampling Ends        2002-01-07 11:01:50
Total duration       1 days 23:29:50
Measured depth range [-4.0,91.0]
Other variables      ['light', 'temperature', 'speed']
Attributes:
                          animal_id: Ag_022
              animal_species_common: Antarctic fur seal
             animal_species_science: Arctocephalus gazella
                 data_creation_date: 2020-07-13 17:00:00
                        data_format: CSV
                        data_nfiles: 1
                        data_source: dives.csv
       dep_device_regional_settings: YYYY-mm-dd HH:MM:SS
                   dep_device_tzone: +5
                             dep_id: ag_mk7_2002_022
              deploy_device_time_on: 2002-01-05 11:32:00
                         deploy_lat: 51.9833294
                    deploy_locality: Iles Crozet, Southern Indian Ocean
                         deploy_lon: -46.416665
                      deploy_method: glued
                        device_make: Wildlife Computers
                       device_model: MK7
                        device_type: archival
                   project_date_beg: 2000-11-01
                   project_date_end: 2003-04-30
                       project_name: Antarctic and Subantarctic fur seal foraging/energetics
               provider_affiliation: Centre d'Etudes Biologiques de Chize, France
                     provider_email: spluque@gmail.com
                   provider_license: AGPLv3
                      provider_name: Sebastian Luque
                       sensors_list: pressure,light,temperature,speed
ZOC method:          None
ZOC parameters:      None
Wet/Dry parameters:  {}
Dives parameters:    {}
Speed calibration coefficients: (a=None, b=None)

Notice that TDR.__init__() reads files in NetCDF4 format, which is a very versatile file format, and encourages using properly documented data sets. skdiveMove relies on xarray.Dataset objects to represent such data sets. It is easy to generate a xarray.Dataset objects from Pandas DataFrames by using method to_xarray(). skdiveMove documents processing steps by appending to the history attribute, in an effort towards building metadata standards.

Access measured data:

25tdrX.get_depth("measured")
<xarray.DataArray 'depth' (date_time: 34199)> Size: 274kB
array([+nan, +nan, +nan, ..., +nan, +nan, +nan])
Coordinates:
  * date_time  (date_time) datetime64[ns] 274kB 2002-01-05T11:32:00 ... 2002-...
Attributes:
    sampling:             regular
    sampling_rate:        5
    sampling_rate_units:  s
    name:                 P
    full_name:            Depth
    description:          dive depth
    units:                m H2O
    units_name:           meters H2O (salt)
    units_label:          meters
    column_name:          depth
    axes:                 D
    files:                dives.csv

Plot measured data:

26tdrX.plot(xlim=["2002-01-05 21:00:00", "2002-01-06 04:10:00"],
27          depth_lim=[-1, 95], figsize=_FIG1X1);
../_images/demo_tdr_4_0.png

Plot concurrent data:

28ccvars = ["light", "speed"]
29tdrX.plot(xlim=["2002-01-05 21:00:00", "2002-01-06 04:10:00"],
30          depth_lim=[-1, 95], concur_vars=ccvars, figsize=_FIG3X1);
../_images/demo_tdr_5_0.png

Calibrate measurements#

Calibration of TDR measurements involves the following steps, which rely on data from pressure sensors (barometers):

Zero offset correction (ZOC) of depth measurements#

Using the offset method here for speed performance reasons:

31# Helper dict to set parameter values
32pars = {"offset_zoc": 3,
33        "dry_thr": 70,
34        "wet_thr": 3610,
35        "dive_thr": 3,
36        "dive_model": "unimodal",
37        "knot_factor": 3,
38        "descent_crit_q": 0,
39        "ascent_crit_q": 0}
40
41tdrX.zoc("offset", offset=pars["offset_zoc"])
42
43# Plot ZOC job
44tdrX.plot_zoc(xlim=["2002-01-05 21:00:00", "2002-01-06 04:10:00"],
45              figsize=(13, 6));
../_images/demo_tdr_6_0.png

Detection of wet vs dry phases#

Periods of missing depth measurements longer than dry_thr are considered dry phases, whereas periods that are briefer than wet_thr are not considered to represent a transition to a wet phase.

46tdrX.detect_wet(dry_thr=pars["dry_thr"], wet_thr=pars["wet_thr"])

Other options, not explored here, include providing a boolean mask Series to indicate which periods to consider wet phases (argument wet_cond), and whether to linearly interpolate depth through wet phases with duration below wet_thr (argument interp_wet).

Detection of dive events#

When depth measurements are greater than dive_thr, a dive event is deemed to have started, ending when measurements cross that threshold again.

47tdrX.detect_dives(dive_thr=pars["dive_thr"])

Detection of dive phases#

Two methods for dive phase detection are available (unimodal and smooth.spline), and this demo uses the default unimodal method:

48tdrX.detect_dive_phases(dive_model=pars["dive_model"],
49                        knot_factor=pars["knot_factor"],
50                        descent_crit_q=pars["descent_crit_q"],
51                        ascent_crit_q=pars["ascent_crit_q"])
52
53print(tdrX)
Time-Depth Recorder -- Class TDR object
Source File          /opt/hostedtoolcache/Python/3.12.6/x64/lib/python3.12/site-packages/skdiveMove/tests/data/ag_mk7_2002_022.nc
Sampling interval    0 days 00:00:05
Number of Samples    34199
Sampling Begins      2002-01-05 11:32:00
Sampling Ends        2002-01-07 11:01:50
Total duration       1 days 23:29:50
Measured depth range [-4.0,91.0]
Other variables      ['light', 'temperature', 'speed']
Attributes:
                          animal_id: Ag_022
              animal_species_common: Antarctic fur seal
             animal_species_science: Arctocephalus gazella
                 data_creation_date: 2020-07-13 17:00:00
                        data_format: CSV
                        data_nfiles: 1
                        data_source: dives.csv
       dep_device_regional_settings: YYYY-mm-dd HH:MM:SS
                   dep_device_tzone: +5
                             dep_id: ag_mk7_2002_022
              deploy_device_time_on: 2002-01-05 11:32:00
                         deploy_lat: 51.9833294
                    deploy_locality: Iles Crozet, Southern Indian Ocean
                         deploy_lon: -46.416665
                      deploy_method: glued
                        device_make: Wildlife Computers
                       device_model: MK7
                        device_type: archival
                   project_date_beg: 2000-11-01
                   project_date_end: 2003-04-30
                       project_name: Antarctic and Subantarctic fur seal foraging/energetics
               provider_affiliation: Centre d'Etudes Biologiques de Chize, France
                     provider_email: spluque@gmail.com
                   provider_license: AGPLv3
                      provider_name: Sebastian Luque
                       sensors_list: pressure,light,temperature,speed
ZOC method:          offset
ZOC parameters:      {'offset': 3}
Wet/Dry parameters:  {'dry_thr': 70, 'wet_thr': 3610}
Dives parameters:    {'dive_thr': 3, 'dive_model': 'unimodal', 'smooth_par': 0.1, 'knot_factor': 3, 'descent_crit_q': 0, 'ascent_crit_q': 0}
Speed calibration coefficients: (a=None, b=None)

Alternatively, all these steps can be performed together via the calibrate() function:

54help(skdive.calibrate)
Help on function calibrate in module skdiveMove.tdr:

calibrate(tdr_file, config_file=None)
    Perform all major TDR calibration operations

    Detect periods of major activities in a `TDR` object, calibrate depth
    readings, and speed if appropriate, in preparation for subsequent
    summaries of diving behaviour.

    This function is a convenience wrapper around :meth:`~TDR.detect_wet`,
    :meth:`~TDR.detect_dives`, :meth:`~TDR.detect_dive_phases`,
    :meth:`~TDR.zoc`, and :meth:`~TDR.calibrate_speed`.  It performs
    wet/dry phase detection, zero-offset correction of depth, detection of
    dives, as well as proper labelling of the latter, and calibrates speed
    data if appropriate.

    Due to the complexity of this procedure, and the number of settings
    required for it, a calibration configuration file (JSON) is used to
    guide the operations.

    Parameters
    ----------
    tdr_file : str, Path or xarray.backends.*DataStore
        As first argument for :func:`xarray.load_dataset`.
    config_file : str
        A valid string path for TDR calibration configuration file.

    Returns
    -------
    out : TDR

    See Also
    --------
    dump_config_template : configuration template

which is demonstrated in Bout analysis.

Plot dive phases#

Once TDR data are properly calibrated and phases detected, results can be visualized:

55tdrX.plot_phases(diveNo=list(range(250, 300)), surface=True, figsize=_FIG1X1);
../_images/demo_tdr_11_0.png
56# Plot dive model for a dive
57tdrX.plot_dive_model(diveNo=20, figsize=(10, 10));
../_images/demo_tdr_12_0.png

Calibrate speed measurements#

In addition to the calibration procedure described above, other variables in the data set may also need to be calibrated. skdiveMove provides support for calibrating speed sensor data, by taking advantage of its relationship with the rate of change in depth in the vertical dimension.

58fig, ax = plt.subplots(figsize=(7, 6))
59# Consider only changes in depth larger than 2 m
60tdrX.calibrate_speed(z=2, ax=ax)
61print(tdrX.speed_calib_fit.summary())
                         QuantReg Regression Results                          
==============================================================================
Dep. Variable:                      y   Pseudo R-squared:              0.07433
Model:                       QuantReg   Bandwidth:                      0.2129
Method:                 Least Squares   Sparsity:                        2.682
Date:                Tue, 15 Oct 2024   No. Observations:                 2108
Time:                        02:58:09   Df Residuals:                     2106
                                        Df Model:                            1
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
Intercept      0.3163      0.049      6.405      0.000       0.219       0.413
x              0.6872      0.049     13.902      0.000       0.590       0.784
==============================================================================
../_images/demo_tdr_13_1.png

Notice processing steps have been appended to the history attribute of the xarray.DataArray:

62print(tdrX.get_depth("zoc"))
<xarray.DataArray 'depth' (date_time: 34199)> Size: 274kB
array([+nan, +nan, +nan, ..., +nan, +nan, +nan])
Coordinates:
  * date_time  (date_time) datetime64[ns] 274kB 2002-01-05T11:32:00 ... 2002-...
Attributes: (12/13)
    sampling:             regular
    sampling_rate:        5
    sampling_rate_units:  s
    name:                 P
    full_name:            Depth
    description:          dive depth
    ...                   ...
    units_name:           meters H2O (salt)
    units_label:          meters
    column_name:          depth
    axes:                 D
    files:                dives.csv
    history:              ZOC
63print(tdrX.get_speed("calibrated"))
<xarray.DataArray 'speed' (date_time: 34199)> Size: 274kB
array([+nan, +nan, +nan, ..., +nan, +nan, +nan])
Coordinates:
  * date_time  (date_time) datetime64[ns] 274kB 2002-01-05T11:32:00 ... 2002-...
Attributes: (12/13)
    sampling:             regular
    sampling_rate:        5
    sampling_rate_units:  s
    name:                 S
    full_name:            Speed
    description:          speed with respect to medium
    ...                   ...
    units_name:           meters per second
    units_label:          meters m/s
    column_name:          speed
    axes:                 F
    files:                dives.csv
    history:              speed_calib_fit

Access attributes of TDR instance#

Following calibration, use the different accessor methods:

64# Time series of the wet/dry phases
65print(tdrX.wet_dry)
                     phase_id phase_label
date_time                                
2002-01-05 11:32:00         1           L
2002-01-05 11:32:05         1           L
2002-01-05 11:32:10         1           L
2002-01-05 11:32:15         1           L
2002-01-05 11:32:20         1           L
...                       ...         ...
2002-01-07 11:01:30         7           L
2002-01-07 11:01:35         7           L
2002-01-07 11:01:40         7           L
2002-01-07 11:01:45         7           L
2002-01-07 11:01:50         7           L

[34199 rows x 2 columns]
66print(tdrX.get_phases_params("wet_dry")["dry_thr"])
70
67print(tdrX.get_phases_params("wet_dry")["wet_thr"])
3610
68print(tdrX.get_dives_details("row_ids"))
                     dive_id  postdive_id dive_phase
date_time                                           
2002-01-05 11:32:00        0            0          X
2002-01-05 11:32:05        0            0          X
2002-01-05 11:32:10        0            0          X
2002-01-05 11:32:15        0            0          X
2002-01-05 11:32:20        0            0          X
...                      ...          ...        ...
2002-01-07 11:01:30        0          426          X
2002-01-07 11:01:35        0          426          X
2002-01-07 11:01:40        0          426          X
2002-01-07 11:01:45        0          426          X
2002-01-07 11:01:50        0          426          X

[34199 rows x 3 columns]
69print(tdrX.get_dives_details("spline_derivs"))
                                   y
1   0 days 00:00:00            1.449
    0 days 00:00:01.363636364  1.386
    0 days 00:00:02.727272727  1.223
    0 days 00:00:04.090909091  0.953
    0 days 00:00:05.454545455  0.594
...                              ...
426 0 days 00:02:38.465346535 -0.935
    0 days 00:02:40.099009901 -0.967
    0 days 00:02:41.732673267 -0.992
    0 days 00:02:43.366336634 -1.011
    0 days 00:02:45              NaN

[10920 rows x 1 columns]
70print(tdrX.get_dives_details("crit_vals"))
         descent_crit  ascent_crit  descent_crit_rate  ascent_crit_rate
dive_id                                                                
1                   1          2.0          1.989e-01        -1.989e-01
2                  11         12.0          5.317e-02        -2.508e-03
3                  15         21.0          7.406e-04        -1.047e-01
4                  16         17.0          4.277e-03        -2.780e-02
5                   3          4.0          1.012e-01        -2.485e-02
...               ...          ...                ...               ...
422                10         17.0          1.857e-03        -5.450e-04
423                 9         16.0          8.120e-03        -1.982e-01
424                 8         13.0          2.544e-01        -1.630e-01
425                11         15.0          3.591e-03        -7.462e-02
426                14         15.0          4.773e-02        -7.283e-02

[426 rows x 4 columns]

Time budgets#

71print(tdrX.time_budget(ignore_z=True, ignore_du=False))
                         beg phase_label                 end
phase_id                                                    
1        2002-01-05 11:32:00           L 2002-01-05 11:39:40
2        2002-01-05 11:39:45           W 2002-01-05 11:42:05
3        2002-01-05 11:42:10           U 2002-01-05 11:42:10
4        2002-01-05 11:42:15           W 2002-01-05 11:46:10
5        2002-01-05 11:46:15           U 2002-01-05 11:46:15
...                      ...         ...                 ...
2928     2002-01-07 09:06:25           U 2002-01-07 09:06:25
2929     2002-01-07 09:06:30           W 2002-01-07 09:06:55
2930     2002-01-07 09:07:00           U 2002-01-07 09:07:00
2931     2002-01-07 09:07:05           W 2002-01-07 11:00:05
2932     2002-01-07 11:00:10           L 2002-01-07 11:01:50

[2932 rows x 3 columns]
72print(tdrX.time_budget(ignore_z=True, ignore_du=True))
                         beg phase_label                 end
phase_id                                                    
1        2002-01-05 11:32:00           L 2002-01-05 11:39:40
2        2002-01-05 11:39:45           W 2002-01-06 06:30:00
3        2002-01-06 06:30:05           L 2002-01-06 17:01:10
4        2002-01-06 17:01:15           W 2002-01-07 05:00:30
5        2002-01-07 05:00:35           L 2002-01-07 07:34:00
6        2002-01-07 07:34:05           W 2002-01-07 11:00:05
7        2002-01-07 11:00:10           L 2002-01-07 11:01:50

Dive statistics#

73print(tdrX.dive_stats())
                begdesc             enddesc              begasc  desctim  \
1   2002-01-05 12:20:15 2002-01-05 12:20:15 2002-01-05 12:20:20      2.5   
2   2002-01-05 21:19:45 2002-01-05 21:20:35 2002-01-05 21:20:40     52.5   
3   2002-01-05 21:22:10 2002-01-05 21:23:20 2002-01-05 21:23:50     72.5   
4   2002-01-05 21:26:25 2002-01-05 21:27:40 2002-01-05 21:27:45     77.5   
5   2002-01-05 21:30:40 2002-01-05 21:30:50 2002-01-05 21:30:55     12.5   
..                  ...                 ...                 ...      ...   
422 2002-01-07 03:54:15 2002-01-07 03:55:00 2002-01-07 03:55:35     47.5   
423 2002-01-07 03:57:35 2002-01-07 03:58:15 2002-01-07 03:58:50     42.5   
424 2002-01-07 04:00:55 2002-01-07 04:01:30 2002-01-07 04:01:55     37.5   
425 2002-01-07 04:04:50 2002-01-07 04:05:40 2002-01-07 04:06:00     52.5   
426 2002-01-07 04:13:40 2002-01-07 04:14:45 2002-01-07 04:14:50     67.5   

     botttim  asctim  divetim  descdist  bottdist  ascdist  ...  ascD_mean  \
1        5.0     2.5     10.0       6.0       0.0      6.0  ...     -1.253   
2        5.0    42.5    100.0      29.0       0.0     29.0  ...     -0.630   
3       30.0    72.5    175.0      63.0       8.0     67.0  ...     -0.849   
4        5.0    82.5    165.0      67.0       1.0     66.0  ...     -0.769   
5        5.0     7.5     25.0       7.0       0.0      7.0  ...     -0.716   
..       ...     ...      ...       ...       ...      ...  ...        ...   
422     35.0    57.5    140.0      54.0      10.0     54.0  ...     -0.851   
423     35.0    52.5    130.0      54.0       9.0     59.0  ...     -1.041   
424     25.0    77.5    140.0      57.0      20.0     77.0  ...     -0.917   
425     20.0    67.5    140.0      77.0      12.0     70.0  ...     -0.989   
426      5.0    87.5    160.0      86.0       2.0     84.0  ...     -0.930   

     ascD_std  ascD_min  ascD_25%  ascD_50%  ascD_75%  ascD_max  \
1       0.222    -1.449    -1.402    -1.305    -1.156    -0.953   
2       0.175    -0.810    -0.740    -0.701    -0.572    -0.152   
3       0.153    -1.172    -0.929    -0.807    -0.759    -0.482   
4       0.216    -1.055    -0.949    -0.777    -0.654    -0.134   
5       0.139    -0.810    -0.807    -0.786    -0.680    -0.438   
..        ...       ...       ...       ...       ...       ...   
422     0.225    -1.140    -1.008    -0.887    -0.713    -0.213   
423     0.387    -1.832    -1.255    -0.936    -0.729    -0.539   
424     0.382    -1.822    -0.958    -0.745    -0.713    -0.163   
425     0.359    -1.655    -1.224    -0.883    -0.766    -0.153   
426     0.319    -1.619    -1.063    -0.827    -0.708    -0.315   

       postdive_dur  postdive_tdist  postdive_mean_speed  
1   0 days 08:59:15       50785.609                1.575  
2   0 days 00:00:40          27.279                0.682  
3   0 days 00:01:15          63.263                0.844  
4   0 days 00:01:25         161.248                1.897  
5   0 days 00:00:25          39.568                1.583  
..              ...             ...                  ...  
422 0 days 00:00:55          37.836                0.688  
423 0 days 00:01:05          32.042                0.534  
424 0 days 00:01:30          16.169                0.190  
425 0 days 00:06:25        -107.598               -0.291  
426 0 days 06:45:30       -4925.427               -0.329  

[426 rows x 46 columns]

Dive stamps#

74print(tdrX.stamp_dives())
         phase_id                 beg                 end
dive_id                                                  
1               2 2002-01-05 12:20:15 2002-01-06 03:50:40
2               2 2002-01-05 12:20:15 2002-01-06 03:50:40
3               2 2002-01-05 12:20:15 2002-01-06 03:50:40
4               2 2002-01-05 12:20:15 2002-01-06 03:50:40
5               2 2002-01-05 12:20:15 2002-01-06 03:50:40
...           ...                 ...                 ...
422             4 2002-01-06 17:31:35 2002-01-07 04:16:15
423             4 2002-01-06 17:31:35 2002-01-07 04:16:15
424             4 2002-01-06 17:31:35 2002-01-07 04:16:15
425             4 2002-01-06 17:31:35 2002-01-07 04:16:15
426             4 2002-01-06 17:31:35 2002-01-07 04:16:15

[426 rows x 3 columns]

Feel free to download a copy of this demo (demo_tdr.py).