worklab 2.0.0__tar.gz → 2.1.0__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.
@@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least
632
632
  the "copyright" line and a pointer to where the full notice is found.
633
633
 
634
634
  Analysis
635
- Copyright (C) 2018 Rick de Klerk
635
+ Copyright (C) 2018 Sophie de Klerk
636
636
 
637
637
  This program is free software: you can redistribute it and/or modify
638
638
  it under the terms of the GNU General Public License as published by
@@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
652
652
  If the program does terminal interaction, make it output a short
653
653
  notice like this when it starts in an interactive mode:
654
654
 
655
- Analysis Copyright (C) 2018 Rick de Klerk
655
+ Analysis Copyright (C) 2018 Sophie de Klerk
656
656
  This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657
657
  This is free software, and you are welcome to redistribute it
658
658
  under certain conditions; type `show c' for details.
@@ -1,8 +1,8 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: worklab
3
- Version: 2.0.0
3
+ Version: 2.1.0
4
4
  Summary: Basic scripts for worklab devices
5
- Author-email: Rick de Klerk <r.de.klerk@pl.hanze.nl>, Thomas Rietveld <t.rietveld@lboro.ac.uk>, Rowie Janssen <r.j.f.janssen@umcg.nl>, Jelmer Braaksma <j.braaksma01@umcg.nl>
5
+ Author-email: Sophie de Klerk <r.de.klerk@pl.hanze.nl>, Thomas Rietveld <t.rietveld@lboro.ac.uk>, Rowie Janssen <r.j.f.janssen@umcg.nl>, Jelmer Braaksma <j.braaksma01@umcg.nl>
6
6
  License: GNU GENERAL PUBLIC LICENSE
7
7
  Version 3, 29 June 2007
8
8
 
@@ -637,7 +637,7 @@ License: GNU GENERAL PUBLIC LICENSE
637
637
  the "copyright" line and a pointer to where the full notice is found.
638
638
 
639
639
  Analysis
640
- Copyright (C) 2018 Rick de Klerk
640
+ Copyright (C) 2018 Sophie de Klerk
641
641
 
642
642
  This program is free software: you can redistribute it and/or modify
643
643
  it under the terms of the GNU General Public License as published by
@@ -657,7 +657,7 @@ License: GNU GENERAL PUBLIC LICENSE
657
657
  If the program does terminal interaction, make it output a short
658
658
  notice like this when it starts in an interactive mode:
659
659
 
660
- Analysis Copyright (C) 2018 Rick de Klerk
660
+ Analysis Copyright (C) 2018 Sophie de Klerk
661
661
  This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
662
662
  This is free software, and you are welcome to redistribute it
663
663
  under certain conditions; type `show c' for details.
@@ -678,8 +678,8 @@ License: GNU GENERAL PUBLIC LICENSE
678
678
  Public License instead of this License. But first, please read
679
679
  <http://www.gnu.org/philosophy/why-not-lgpl.html>.
680
680
 
681
- Project-URL: Homepage, https://github.com/rickdkk/worklab
682
- Project-URL: Bug Reports, https://github.com/rickdkk/worklab/issues
681
+ Project-URL: Homepage, https://github.com/sophiedkk/worklab
682
+ Project-URL: Bug Reports, https://github.com/sophiedkk/worklab/issues
683
683
  Keywords: biomechanics,ergometry,physiology
684
684
  Classifier: Intended Audience :: Science/Research
685
685
  Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
@@ -693,16 +693,20 @@ Requires-Dist: numpy
693
693
  Requires-Dist: pandas
694
694
  Requires-Dist: matplotlib
695
695
  Requires-Dist: xlrd
696
+ Requires-Dist: scikit-learn
697
+ Requires-Dist: seaborn
696
698
  Provides-Extra: dev
697
699
  Requires-Dist: pytest; extra == "dev"
698
700
  Requires-Dist: Flake8-pyproject; extra == "dev"
699
701
  Requires-Dist: black; extra == "dev"
700
702
  Requires-Dist: jupyter-book; extra == "dev"
703
+ Requires-Dist: build; extra == "dev"
704
+ Dynamic: license-file
701
705
 
702
706
  # Worklab: a wheelchair biomechanics mini-package
703
707
 
704
708
  [![image](https://zenodo.org/badge/DOI/10.5281/zenodo.8362962.svg)](https://doi.org/10.5281/zenodo.8362962)
705
- [![image](https://badge.fury.io/py/worklab.svg)](https://badge.fury.io/py/worklab) [![image](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gitlab.com/Rickdkk/worklab/tree/master/LICENCE)
709
+ [![image](https://badge.fury.io/py/worklab.svg)](https://badge.fury.io/py/worklab) [![image](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://github.com/sophiedkk/worklab/blob/main/LICENSE)
706
710
 
707
711
  Essential data analysis and (pre-)processing scripts used in projects
708
712
  researching the Lode
@@ -722,7 +726,7 @@ the worklab, which means:
722
726
  # Documentation
723
727
 
724
728
  For more detailed documentation you can look at the
725
- [docs](https://rickdkk.github.io/worklab/).
729
+ [docs](https://sophiedkk.github.io/worklab/).
726
730
 
727
731
  # Prerequisites
728
732
 
@@ -739,7 +743,7 @@ You can install this package with pip:
739
743
  # Examples
740
744
 
741
745
  You can find some Jupyter Notebook examples
742
- [here](https://rickdkk.github.io/worklab/chapters/examples.html).
746
+ [here](https://sophiedkk.github.io/worklab/chapters/examples.html).
743
747
 
744
748
  # Reporting errors
745
749
 
@@ -751,4 +755,4 @@ me or submit an issue through this page.
751
755
  If you want to refer to this package please use this DOI:
752
756
  10.5281/zenodo.8362962, or cite:
753
757
 
754
- Rick de Klerk, Thomas Rietveld, Rowie Janssen, & Jelmer Braaksma. (2023). Worklab: a wheelchair biomechanics mini-package. Zenodo. https://doi.org/10.5281/zenodo.8362963
758
+ Sophie de Klerk, Thomas Rietveld, Rowie Janssen, & Jelmer Braaksma. (2023). Worklab: a wheelchair biomechanics mini-package. Zenodo. https://doi.org/10.5281/zenodo.8362963
@@ -1,7 +1,7 @@
1
1
  # Worklab: a wheelchair biomechanics mini-package
2
2
 
3
3
  [![image](https://zenodo.org/badge/DOI/10.5281/zenodo.8362962.svg)](https://doi.org/10.5281/zenodo.8362962)
4
- [![image](https://badge.fury.io/py/worklab.svg)](https://badge.fury.io/py/worklab) [![image](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gitlab.com/Rickdkk/worklab/tree/master/LICENCE)
4
+ [![image](https://badge.fury.io/py/worklab.svg)](https://badge.fury.io/py/worklab) [![image](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://github.com/sophiedkk/worklab/blob/main/LICENSE)
5
5
 
6
6
  Essential data analysis and (pre-)processing scripts used in projects
7
7
  researching the Lode
@@ -21,7 +21,7 @@ the worklab, which means:
21
21
  # Documentation
22
22
 
23
23
  For more detailed documentation you can look at the
24
- [docs](https://rickdkk.github.io/worklab/).
24
+ [docs](https://sophiedkk.github.io/worklab/).
25
25
 
26
26
  # Prerequisites
27
27
 
@@ -38,7 +38,7 @@ You can install this package with pip:
38
38
  # Examples
39
39
 
40
40
  You can find some Jupyter Notebook examples
41
- [here](https://rickdkk.github.io/worklab/chapters/examples.html).
41
+ [here](https://sophiedkk.github.io/worklab/chapters/examples.html).
42
42
 
43
43
  # Reporting errors
44
44
 
@@ -50,4 +50,4 @@ me or submit an issue through this page.
50
50
  If you want to refer to this package please use this DOI:
51
51
  10.5281/zenodo.8362962, or cite:
52
52
 
53
- Rick de Klerk, Thomas Rietveld, Rowie Janssen, & Jelmer Braaksma. (2023). Worklab: a wheelchair biomechanics mini-package. Zenodo. https://doi.org/10.5281/zenodo.8362963
53
+ Sophie de Klerk, Thomas Rietveld, Rowie Janssen, & Jelmer Braaksma. (2023). Worklab: a wheelchair biomechanics mini-package. Zenodo. https://doi.org/10.5281/zenodo.8362963
@@ -1,13 +1,13 @@
1
1
  [project]
2
2
  name = "worklab"
3
- version = "2.0.0"
3
+ version = "2.1.0"
4
4
  description = "Basic scripts for worklab devices"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.7"
7
7
  license = {file = "LICENSE"}
8
8
  keywords = ["biomechanics", "ergometry", "physiology"]
9
9
  authors = [
10
- {name = "Rick de Klerk", email = "r.de.klerk@pl.hanze.nl"},
10
+ {name = "Sophie de Klerk", email = "r.de.klerk@pl.hanze.nl"},
11
11
  {name = "Thomas Rietveld", email = "t.rietveld@lboro.ac.uk"},
12
12
  {name = "Rowie Janssen", email = "r.j.f.janssen@umcg.nl"},
13
13
  {name = "Jelmer Braaksma", email = "j.braaksma01@umcg.nl"}
@@ -23,7 +23,9 @@ dependencies = [
23
23
  "numpy",
24
24
  "pandas",
25
25
  "matplotlib",
26
- "xlrd"
26
+ "xlrd",
27
+ "scikit-learn",
28
+ "seaborn"
27
29
  ]
28
30
 
29
31
  [project.optional-dependencies]
@@ -31,12 +33,13 @@ dev = [
31
33
  "pytest",
32
34
  "Flake8-pyproject",
33
35
  "black",
34
- "jupyter-book"
36
+ "jupyter-book",
37
+ "build"
35
38
  ]
36
39
 
37
40
  [project.urls]
38
- "Homepage" = "https://github.com/rickdkk/worklab"
39
- "Bug Reports" = "https://github.com/rickdkk/worklab/issues"
41
+ "Homepage" = "https://github.com/sophiedkk/worklab"
42
+ "Bug Reports" = "https://github.com/sophiedkk/worklab/issues"
40
43
 
41
44
  [build-system]
42
45
  requires = ["setuptools>=43.0.0", "wheel"]
@@ -4,6 +4,9 @@ import copy
4
4
  import math
5
5
  import pandas as pd
6
6
  import matplotlib.pyplot as plt
7
+ import numpy as np
8
+ import sklearn
9
+ import seaborn as sns
7
10
 
8
11
  from .plots import plot_power_speed_dist
9
12
  from .physio import calc_weighted_average
@@ -58,7 +61,7 @@ def cut_data(data, start, end, distance=True):
58
61
  data[side] = data[side][(data[side]["time"] > start) & (data[side]["time"] < end)]
59
62
  data[side]["time"] = data[side]["time"] - start
60
63
  if distance:
61
- data[side]["dist"] = data[side]["dist"] - data[side]["dist"].iloc[0]
64
+ data[side]["dist"] -= data[side]["dist"].iloc[0]
62
65
  data[side].reset_index(drop=True, inplace=True)
63
66
 
64
67
  return data
@@ -592,3 +595,93 @@ def ana_submax(data_ergo, data_pbp, data_spiro):
592
595
  outcomes = pd.concat([mean_ergo, mean_spiro], axis=1)
593
596
  outcomes["me"] = (outcomes["meanpower"] / outcomes["EE"]) * 100
594
597
  return outcomes
598
+
599
+
600
+ def force_velocity_curve(data_pbp, upper_lim=800, var='max'):
601
+ """
602
+ Creates force-velocity curves for wheelchair sports
603
+
604
+ Parameters
605
+ ----------
606
+ data_pbp : pd.DataFrame
607
+ processed push-by-push ergometer dataframe with output for all 6 sprints
608
+ upper_lim : int
609
+ upper limit recommendations for LP (800) and HP (1400)
610
+ var : str
611
+ 'max' force and velocity or 'mean' force and velocity
612
+
613
+ Returns
614
+ -------
615
+ fig : figure
616
+ force-velocity plot
617
+ variables : pd.DataFrame
618
+ r2, optimal velocity/power, x/y coordinates and coefficient
619
+
620
+ """
621
+ if var == 'max':
622
+ speed = 'maxspeed'
623
+ force = 'maxuforce'
624
+ else:
625
+ speed = 'meanspeed'
626
+ force = 'meanuforce'
627
+
628
+ data_pbp = data_pbp[data_pbp.index > 0]
629
+ x = np.array(data_pbp[speed]).reshape((-1, 1))
630
+ y = np.array(data_pbp[force])
631
+ data_pbp['x'] = np.array(data_pbp[speed]).reshape((-1, 1))
632
+ data_pbp['y'] = np.array(data_pbp[force])
633
+ model = sklearn.LinearRegression()
634
+ model.fit(x, y)
635
+ model = sklearn.LinearRegression().fit(x, y)
636
+ r_sq = model.score(x, y)
637
+ x1 = np.linspace(0, float(abs(model.intercept_ / model.coef_)), 100)
638
+ xx = np.linspace(x.min(), x.max(), 100)
639
+
640
+ pred_y = model.intercept_ + model.coef_ * x
641
+ pred_y1 = model.intercept_ + model.coef_ * x1
642
+ pred_y2 = model.intercept_ + model.coef_ * xx
643
+ power = xx * pred_y2
644
+ power1 = x1 * pred_y1
645
+ parabola = pd.DataFrame({'POmax': power1, 'vmax': x1})
646
+ pomax_pos = parabola['POmax'].idxmax()
647
+ pomax_opt = parabola['POmax'].max()
648
+ vmax_opt = parabola['vmax'][pomax_pos]
649
+
650
+ variables = pd.DataFrame([])
651
+ variables['R2'] = [r_sq]
652
+ variables['opt_vel'] = vmax_opt
653
+ variables['opt_pow'] = pomax_opt
654
+ variables['y_cor'] = model.intercept_
655
+ variables['x_cor'] = x1[-1]
656
+ variables['coef'] = model.coef_
657
+ variables = round(variables, 2)
658
+
659
+ sns.set_style('darkgrid')
660
+ col_pal = sns.color_palette("dark:#5A9_r")
661
+ sns.set_palette(col_pal)
662
+ fig, ax = plt.subplots(1, 1, figsize=(14, 8))
663
+ ax.set_ylabel('Force [N]', fontsize=14)
664
+ ax.set_ylim(0, upper_lim)
665
+ ax.set_xlabel('Velocity [ms]', fontsize=14)
666
+ ax.set_xlim(0, 6)
667
+ ax.tick_params(axis='both', labelsize=12)
668
+ ax = sns.scatterplot(data=data_pbp, x='x', y='y', hue='Resistance')
669
+ ax.plot(x1, pred_y1, color='k', linestyle='--')
670
+ ax.plot(x, pred_y, color='k')
671
+ ax.annotate('R2 = ' + str(round(r_sq, 2)), xy=(0.75, 0.90), xycoords='axes fraction')
672
+ ax.annotate('y = ' + str(round(model.intercept_, 1)) + ' ' + str(round(model.coef_[0], 1)) + ' * x',
673
+ xy=(0.75, 0.85), xycoords='axes fraction')
674
+
675
+ ax1 = ax.twinx()
676
+ ax1.grid(False)
677
+ ax1.plot(x1, power1, color='grey', linestyle='--')
678
+ ax1.plot(xx, power, color='grey')
679
+ ax1.set_ylim(0, upper_lim)
680
+ ax1.set_ylabel('Power [W]', color='grey', fontsize=14)
681
+ ax1.yaxis.label.set_color('grey')
682
+ ax1.spines['right'].set_color('grey')
683
+ ax1.tick_params(axis='y', colors='grey')
684
+ ax1.tick_params(axis='both', labelsize=12)
685
+ ax1.annotate('Optimal velocity (' + str(round(vmax_opt, 1)) + ' ms)', xy=(0.75, 0.95), xycoords='axes fraction')
686
+
687
+ return fig, variables
@@ -87,7 +87,7 @@ def load_spiro(filename):
87
87
 
88
88
  Loads spirometer data to a pandas DataFrame, converts time to seconds (not datetime), computes energy expenditure,
89
89
  computes weights from the time difference between samples, if no heart rate data is available it fills
90
- the column with np.NaNs. Returns a DataFrame with:
90
+ the column with nans. Returns a DataFrame with:
91
91
 
92
92
  +------------+----------------------------+-----------+
93
93
  | Column | Data | Unit |
@@ -139,7 +139,7 @@ def load_spiro(filename):
139
139
  data["VO2"] = data["VO2"] / 1000 # to l/min
140
140
  data["VCO2"] = data["VCO2"] / 1000 # to l/min
141
141
  data["RER"] = data["VCO2"] / data["VO2"]
142
- data["HR"] = np.NaN if "HR" not in data else data["HR"] # missing when sensor is not detected
142
+ data["HR"] = np.nan if "HR" not in data else data["HR"] # missing when sensor is not detected
143
143
  data["O2pulse"] = data["VO2"] / data["HR"]
144
144
  data["VE/VO2"] = data["VE"] / data["VO2"]
145
145
  data["VE/VCO2"] = data["VE"] / data["VCO2"]
@@ -165,7 +165,7 @@ def load_spiro(filename):
165
165
  ]
166
166
 
167
167
 
168
- def load_spiro_metamax(filename):
168
+ def load_spiro_metamax(filename, sheet_name=0):
169
169
  """
170
170
  Loads metamax 3B spirometer data from Excel file.
171
171
 
@@ -209,6 +209,8 @@ def load_spiro_metamax(filename):
209
209
  ----------
210
210
  filename : str
211
211
  full file path or file in existing path from metamax 3B spirometer
212
+ sheet_name : str or int
213
+ sheet name, default = 0
212
214
 
213
215
  Returns
214
216
  -------
@@ -217,11 +219,19 @@ def load_spiro_metamax(filename):
217
219
 
218
220
 
219
221
  """
220
- data = pd.read_excel(filename, skiprows=[*range(0, 124, 1)])
222
+ excel_data = pd.ExcelFile(filename)
223
+
224
+ # Loop through rows to find the one containing 't' in the first row
225
+ for index, row in enumerate(excel_data.parse(sheet_name=sheet_name, header=None).itertuples(index=False)):
226
+ if 't' in row:
227
+ skip_until = index
228
+ break
229
+
230
+ data = pd.read_excel(filename, skiprows=[*range(0, skip_until, 1)], sheet_name=sheet_name)
221
231
  # units = data.iloc[0, :]
222
232
  data.drop(0, inplace=True)
223
233
 
224
- data.replace("-", np.NaN, inplace=True)
234
+ data.replace("-", np.nan, inplace=True)
225
235
 
226
236
  data.rename(columns={"t": "time", "V'O2": "VO2", "V'E": "VE"}, inplace=True)
227
237
  data["VCO2"] = data["V'E/V'CO2"] * (data["VE"])
@@ -233,7 +243,7 @@ def load_spiro_metamax(filename):
233
243
  data["VO2"] = data["VO2"].astype(float)
234
244
  data["EE"] = ((4.94 * data["RER"] + 16.04) * (1000 * data["VO2"])) / 60
235
245
  data["weights"] = np.insert(np.diff(data["time"]), 0, 0) # used for calculating weighted average
236
- data["HR"] = (np.NaN if "HR" not in data else data["HR"]).astype(int) # missing when sensor is not detected
246
+ data["HR"] = (np.nan if "HR" not in data else data["HR"]).astype(int) # missing when sensor is not detected
237
247
  data["O2pulse"] = data["VO2"] / data["HR"]
238
248
  data["VCO2"].replace(0, 0.01, inplace=True)
239
249
  data["VO2"].replace(0, 0.01, inplace=True)
@@ -946,26 +956,39 @@ def load_ximu3(root_dir, filenames=None, inplace=False):
946
956
 
947
957
  if 'right' in sessiondata.keys():
948
958
  sessiondata["right"] = sessiondata["right"]["Inertial"]
949
- sessiondata["right"]["timestamp"] -= sessiondata["right"]["timestamp"][0]
950
959
  else:
951
960
  print('No right sensor imported')
952
961
  if 'frame' in sessiondata.keys():
953
962
  sessiondata["frame"] = sessiondata["frame"]["Inertial"]
954
- sessiondata["frame"]["timestamp"] -= sessiondata["frame"]["timestamp"][0]
955
963
  else:
956
964
  print('No frame sensor imported')
957
965
  if 'left' in sessiondata.keys():
958
966
  sessiondata["left"] = sessiondata["left"]["Inertial"]
959
- sessiondata["left"]["timestamp"] -= sessiondata["left"]["timestamp"][0]
960
967
  else:
961
968
  print('No left sensor imported')
962
969
  if 'trunk' in sessiondata.keys():
963
970
  sessiondata["trunk"] = sessiondata["trunk"]["Inertial"]
964
- sessiondata["trunk"]["timestamp"] -= sessiondata["trunk"]["timestamp"][0]
965
971
  else:
966
972
  print('No trunk sensor imported')
967
973
 
974
+ start_time = np.max([sessiondata['frame']['timestamp'].min(), sessiondata['left']['timestamp'].min(),
975
+ sessiondata['right']['timestamp'].min()])
976
+ stop_time = np.min([sessiondata['frame']['timestamp'].max(), sessiondata['left']['timestamp'].max(),
977
+ sessiondata['right']['timestamp'].max()])
978
+
979
+ for sensor in sessiondata:
980
+ sessiondata[sensor] = sessiondata[sensor][sessiondata[sensor]['timestamp'] >= start_time]
981
+ sessiondata[sensor] = sessiondata[sensor][sessiondata[sensor]['timestamp'] <= stop_time]
982
+
983
+ start_time = np.max([sessiondata['frame']['timestamp'].min(), sessiondata['left']['timestamp'].min(),
984
+ sessiondata['right']['timestamp'].min()])
985
+ stop_time = np.min([sessiondata['frame']['timestamp'].max(), sessiondata['left']['timestamp'].max(),
986
+ sessiondata['right']['timestamp'].max()])
987
+
968
988
  for sensor in sessiondata:
989
+ sessiondata[sensor] = sessiondata[sensor][sessiondata[sensor]['timestamp'] >= start_time]
990
+ sessiondata[sensor] = sessiondata[sensor][sessiondata[sensor]['timestamp'] <= stop_time]
991
+ sessiondata[sensor] = sessiondata[sensor].reset_index(drop=True)
969
992
  sessiondata[sensor]['time'] = pd.to_datetime(sessiondata[sensor]['timestamp'], unit='us')
970
993
  sessiondata[sensor]['time'] -= sessiondata[sensor]['time'][0]
971
994
  sessiondata[sensor]['time'] = sessiondata[sensor]['time'].dt.total_seconds()
@@ -2,8 +2,8 @@ import copy
2
2
  from warnings import warn
3
3
 
4
4
  import numpy as np
5
- from scipy.integrate import cumtrapz
6
- from scipy.signal import periodogram, find_peaks
5
+ from scipy.integrate import cumulative_trapezoid
6
+ from scipy.signal import periodogram, find_peaks, savgol_filter
7
7
 
8
8
  from .utils import lowpass_butter, pd_interp
9
9
 
@@ -32,10 +32,10 @@ def resample_imu(sessiondata, sfreq=400.0):
32
32
  https://github.com/xioTechnologies/NGIMU-MATLAB-Import-Logged-Data-Example
33
33
 
34
34
  """
35
- end_time = 0
35
+ end_time = np.inf
36
36
  for device in sessiondata:
37
37
  max_time = sessiondata[device]["time"].max()
38
- end_time = max_time if max_time > end_time else end_time
38
+ end_time = max_time if max_time < end_time else end_time
39
39
 
40
40
  new_time = np.arange(0, end_time, 1 / sfreq)
41
41
 
@@ -85,6 +85,7 @@ def process_imu(sessiondata, camber=18, wsize=0.32, wbase=0.80, n_sensors=3, sen
85
85
 
86
86
  sfreq = 1 / frame["time"].diff().mean()
87
87
  frame["rot_vel"] = lowpass_butter(frame["gyroscope_z"], sfreq=sfreq, cutoff=6)
88
+ frame['rot_vel'] = savgol_filter(frame['rot_vel'], window_length=100, polyorder=3)
88
89
  right['gyroscope_y'] = lowpass_butter(right['gyroscope_y'], sfreq=sfreq, cutoff=10)
89
90
 
90
91
  # Wheelchair camber correction
@@ -101,16 +102,16 @@ def process_imu(sessiondata, camber=18, wsize=0.32, wbase=0.80, n_sensors=3, sen
101
102
  frame["gyro_cor"] = right["gyro_cor"]
102
103
 
103
104
  # Calculation of rotations, rotational velocity and rotational acceleration
104
- frame["rot"] = cumtrapz(abs(frame["rot_vel"]) / sfreq, initial=0.0)
105
+ frame["rot"] = cumulative_trapezoid(abs(frame["rot_vel"]) / sfreq, initial=0.0)
105
106
  frame["rot_acc"] = np.gradient(frame["rot_vel"]) * sfreq
106
107
 
107
108
  # Calculation of velocity, acceleration and distance
108
109
  right["vel"] = right["gyro_cor"] * wsize * deg2rad # angular velocity to linear velocity
109
- right["dist"] = cumtrapz(right["vel"] / sfreq, initial=0.0) # integral of velocity gives distance
110
+ right["dist"] = cumulative_trapezoid(right["vel"] / sfreq, initial=0.0) # integral of velocity gives distance
110
111
 
111
112
  if n_sensors == 3:
112
113
  left["vel"] = left["gyro_cor"] * wsize * deg2rad
113
- left["dist"] = cumtrapz(left["vel"] / sfreq, initial=0.0)
114
+ left["dist"] = cumulative_trapezoid(left["vel"] / sfreq, initial=0.0)
114
115
  frame["vel_wheel"] = (right["vel"] + left["vel"]) / 2 # mean velocity both sides
115
116
  frame["dist_wheel"] = (right["dist"] + left["dist"]) / 2 # mean distance
116
117
  else:
@@ -148,17 +149,17 @@ def process_imu(sessiondata, camber=18, wsize=0.32, wbase=0.80, n_sensors=3, sen
148
149
  comb_ratio = np.clip(comb_ratio, 0, 1) # clamp Combine ratio values, not in df
149
150
  frame["skid_vel"] = (frame["vel_right"] * comb_ratio) + (frame["vel_left"] * (1 - comb_ratio))
150
151
  frame["vel"] = (frame["vel_right"] + frame["vel_left"]) / 2
151
- frame['dist'] = cumtrapz(frame["skid_vel"], initial=0.0) / sfreq
152
+ frame['dist'] = cumulative_trapezoid(frame["skid_vel"], initial=0.0) / sfreq
152
153
  else:
153
154
  frame["vel"] = frame["vel_right"]
154
- frame["dist"] = cumtrapz(frame["vel"], initial=0.0) / sfreq # Combined distance
155
+ frame["dist"] = cumulative_trapezoid(frame["vel"], initial=0.0) / sfreq # Combined distance
155
156
 
156
157
  # distance in the x and y direction
157
- frame["dist_y"] = cumtrapz(
158
- np.gradient(frame["dist"]) * np.sin(np.deg2rad(cumtrapz(frame["rot_vel"] / sfreq, initial=0.0))),
158
+ frame["dist_y"] = cumulative_trapezoid(
159
+ frame['vel'] / sfreq * np.sin(np.deg2rad(cumulative_trapezoid(frame["rot_vel"] / sfreq, initial=0.0))),
159
160
  initial=0.0)
160
- frame["dist_x"] = cumtrapz(
161
- np.gradient(frame["dist"]) * np.cos(np.deg2rad(cumtrapz(frame["rot_vel"] / sfreq, initial=0.0))),
161
+ frame["dist_x"] = cumulative_trapezoid(
162
+ frame['vel'] / sfreq * np.cos(np.deg2rad(cumulative_trapezoid(frame["rot_vel"] / sfreq, initial=0.0))),
162
163
  initial=0.0)
163
164
 
164
165
  return sessiondata
@@ -200,7 +201,8 @@ def process_imu_left(sessiondata, camber=18, wsize=0.32, wbase=0.80,
200
201
  # Calculation of rotations, rotational velocity and acceleration
201
202
  frame["rot_vel"] = lowpass_butter(frame["gyroscope_z"],
202
203
  sfreq=sfreq, cutoff=10)
203
- frame["rot"] = cumtrapz(abs(frame["rot_vel"]) / sfreq, initial=0.0)
204
+ frame['rot_vel'] = savgol_filter(frame['rot_vel'], window_length=100, polyorder=3)
205
+ frame["rot"] = cumulative_trapezoid(abs(frame["rot_vel"]) / sfreq, initial=0.0)
204
206
  frame["rot_acc"] = np.gradient(frame["rot_vel"]) * sfreq
205
207
 
206
208
  # Wheelchair camber correction
@@ -211,10 +213,10 @@ def process_imu_left(sessiondata, camber=18, wsize=0.32, wbase=0.80,
211
213
  frame["gyro_cor"] = left["gyro_cor"]
212
214
 
213
215
  left["vel"] = left["gyro_cor"] * wsize * deg2rad
214
- left["dist"] = cumtrapz(left["vel"] / sfreq, initial=0.0)
216
+ left["dist"] = cumulative_trapezoid(left["vel"] / sfreq, initial=0.0)
215
217
  frame["vel_wheel"] = left["vel"]
216
218
  frame["vel_wheel"] = lowpass_butter(frame["vel_wheel"], sfreq=sfreq, cutoff=10)
217
- frame["dist_wheel"] = cumtrapz(frame["vel_wheel"] / sfreq, initial=0.0)
219
+ frame["dist_wheel"] = cumulative_trapezoid(frame["vel_wheel"] / sfreq, initial=0.0)
218
220
 
219
221
  frame["acc_wheel"] = np.gradient(frame["vel_wheel"]) * sfreq
220
222
  frame['acc_wheel'] = lowpass_butter(frame['acc_wheel'],
@@ -228,14 +230,14 @@ def process_imu_left(sessiondata, camber=18, wsize=0.32, wbase=0.80,
228
230
  frame["vel_left"] = left["vel"]
229
231
  frame["vel_left"] += np.tan(np.deg2rad(frame["rot_vel"] / sfreq)) * wbase / 2 * sfreq
230
232
  frame["vel"] = frame["vel_left"]
231
- frame["dist"] = cumtrapz(frame["vel"], initial=0.0) / sfreq
233
+ frame["dist"] = cumulative_trapezoid(frame["vel"], initial=0.0) / sfreq
232
234
 
233
235
  # distance in the x and y direction
234
- frame["dist_y"] = cumtrapz(
235
- np.gradient(frame["dist"]) * np.sin(np.deg2rad(cumtrapz(frame["rot_vel"] / sfreq, initial=0.0))),
236
+ frame["dist_y"] = cumulative_trapezoid(
237
+ frame['vel'] / sfreq * np.sin(np.deg2rad(cumulative_trapezoid(frame["rot_vel"] / sfreq, initial=0.0))),
236
238
  initial=0.0)
237
- frame["dist_x"] = cumtrapz(
238
- np.gradient(frame["dist"]) * np.cos(np.deg2rad(cumtrapz(frame["rot_vel"] / sfreq, initial=0.0))),
239
+ frame["dist_x"] = cumulative_trapezoid(
240
+ frame['vel'] / sfreq * np.cos(np.deg2rad(cumulative_trapezoid(frame["rot_vel"] / sfreq, initial=0.0))),
239
241
  initial=0.0)
240
242
 
241
243
  return sessiondata
@@ -311,7 +313,7 @@ def push_imu(acceleration, sfreq=400.0):
311
313
  return push_idx, acc_filt, n_pushes, cycle_time, push_freq
312
314
 
313
315
 
314
- def movesense_offset(sessiondata, n_sensors=2, right_wheel=True):
316
+ def movesense_offset(sessiondata, n_sensors=2, right_wheel=True, gyro_offset=False):
315
317
  """
316
318
  Remove offset MoveSense sensors
317
319
 
@@ -324,6 +326,8 @@ def movesense_offset(sessiondata, n_sensors=2, right_wheel=True):
324
326
  n_sensors: float
325
327
  number of sensors used, 2: right wheel and frame,
326
328
  3: right, left wheel and frame
329
+ gyro_offset: boolean
330
+ if set to True, an additional gyroscope offset will be used
327
331
 
328
332
  Returns
329
333
  -------
@@ -363,5 +367,10 @@ def movesense_offset(sessiondata, n_sensors=2, right_wheel=True):
363
367
  sessiondata['left']['gyroscope_x'] -= offset_left_x
364
368
  else:
365
369
  print('No offset corrected')
370
+ if gyro_offset is True:
371
+ sessiondata['frame']['gyroscope_z'] = np.sign(
372
+ sessiondata['frame']['gyroscope_z']) * np.sqrt(sessiondata['frame']['gyroscope_x']**2
373
+ + sessiondata['frame']['gyroscope_y']**2
374
+ + sessiondata['frame']['gyroscope_z']**2)
366
375
 
367
376
  return sessiondata
@@ -1,6 +1,6 @@
1
1
  import numpy as np
2
2
  import pandas as pd
3
- from scipy.integrate import cumtrapz
3
+ from scipy.integrate import cumulative_trapezoid
4
4
  from scipy.signal import savgol_filter
5
5
  from .utils import lowpass_butter, find_peaks
6
6
  from .move import rotate_matrix
@@ -220,7 +220,7 @@ def process_mw(data, wheelsize=0.31, rimsize=0.275, sfreq=200):
220
220
  """
221
221
  data["aspeed"] = np.gradient(data["angle"]) * sfreq
222
222
  data["speed"] = data["aspeed"] * wheelsize
223
- data["dist"] = cumtrapz(data["speed"], initial=0.0) / sfreq
223
+ data["dist"] = cumulative_trapezoid(data["speed"], initial=0.0) / sfreq
224
224
  data["acc"] = np.gradient(data["speed"]) * sfreq
225
225
  data["ftot"] = (data["fx"] ** 2 + data["fy"] ** 2 + data["fz"] ** 2) ** 0.5
226
226
  data["uforce"] = data["torque"] / rimsize
@@ -231,31 +231,37 @@ def process_mw(data, wheelsize=0.31, rimsize=0.275, sfreq=200):
231
231
  return data
232
232
 
233
233
 
234
- def process_ergo(data, wheelsize=0.31, rimsize=0.275):
234
+ def process_ergo(data, wheelsize=0.31, rimsize=0.275, unit="ms"):
235
235
  """
236
236
  Basic processing for ergometer data.
237
237
 
238
238
  Basic processing for ergometer data (e.g. speed to distance). Should be performed after filtering.
239
- Added columns:
239
+ Returned columns:
240
240
 
241
241
  +------------+----------------------+-----------+
242
242
  | Column | Data | Unit |
243
243
  +============+======================+===========+
244
- | angle | angle | rad |
244
+ | time | time | s |
245
245
  +------------+----------------------+-----------+
246
- | aspeed | angular velocity | rad/s |
246
+ | force | force (on wheel) | N |
247
+ +------------+----------------------+-----------+
248
+ | speed | speed | m/s |
247
249
  +------------+----------------------+-----------+
248
250
  | acc | acceleration | m/s^2 |
249
251
  +------------+----------------------+-----------+
252
+ | aspeed | angular velocity | rad/s |
253
+ +------------+----------------------+-----------+
254
+ | angle | angle | rad |
255
+ +------------+----------------------+-----------+
250
256
  | dist | cumulative distance | m |
251
257
  +------------+----------------------+-----------+
252
258
  | power | power | W |
253
259
  +------------+----------------------+-----------+
254
- | work | instantaneous work | J |
260
+ | torque | torque around wheel | Nm |
255
261
  +------------+----------------------+-----------+
256
262
  | uforce | effective force | N |
257
263
  +------------+----------------------+-----------+
258
- | torque | torque around wheel | Nm |
264
+ | work | instantaneous work | J |
259
265
  +------------+----------------------+-----------+
260
266
 
261
267
  .. note:: the force column contains force on the wheels, uforce (user force) is force on the handrim
@@ -268,6 +274,8 @@ def process_ergo(data, wheelsize=0.31, rimsize=0.275):
268
274
  wheel radius [m]
269
275
  rimsize : float
270
276
  handrim radius [m]
277
+ unit : str
278
+ unit of measured 'speed' column; ms, kmh or mph
271
279
 
272
280
  Returns
273
281
  -------
@@ -280,14 +288,23 @@ def process_ergo(data, wheelsize=0.31, rimsize=0.275):
280
288
  """
281
289
  sfreq = 100 # ergometer is always 100Hz
282
290
  for side in data:
283
- data[side]["aspeed"] = data[side]["speed"] / wheelsize
284
- data[side]["angle"] = cumtrapz(data[side]["aspeed"], initial=0.0) / sfreq
285
- data[side]["torque"] = data[side]["force"] * wheelsize
291
+ if unit == "kmh":
292
+ data[side]["speed"] /= 3.6
293
+ elif unit == "mph":
294
+ data[side]["speed"] /= 2.23694
295
+ elif unit == "ms":
296
+ data[side]["speed"] = data[side]["speed"]
297
+ else:
298
+ raise Exception("Please specify either 'ms', 'kmh' or 'mph', data not processed!")
299
+
286
300
  data[side]["acc"] = np.gradient(data[side]["speed"]) * sfreq
301
+ data[side]["aspeed"] = data[side]["speed"] / wheelsize
302
+ data[side]["angle"] = cumulative_trapezoid(data[side]["aspeed"], initial=0.0) / sfreq
303
+ data[side]["dist"] = cumulative_trapezoid(data[side]["speed"], initial=0.0) / sfreq
287
304
  data[side]["power"] = data[side]["speed"] * data[side]["force"]
288
- data[side]["dist"] = cumtrapz(data[side]["speed"], initial=0.0) / sfreq
289
- data[side]["work"] = data[side]["power"] / sfreq
305
+ data[side]["torque"] = data[side]["force"] * wheelsize
290
306
  data[side]["uforce"] = data[side]["force"] * (wheelsize / rimsize)
307
+ data[side]["work"] = data[side]["power"] / sfreq
291
308
  return data
292
309
 
293
310
 
@@ -389,7 +406,7 @@ def push_by_push_mw(data, variable="torque", cutoff=0.0, minpeak=5.0, mindist=5,
389
406
  "cwork",
390
407
  "negwork",
391
408
  ]
392
- pbp = pd.DataFrame(data=np.full((len(peaks["start"]), len(keys)), np.NaN), columns=keys) # preallocate dataframe
409
+ pbp = pd.DataFrame(data=np.full((len(peaks["start"]), len(keys)), np.nan), columns=keys) # preallocate dataframe
393
410
 
394
411
  pbp["start"] = peaks["start"]
395
412
  pbp["peak"] = peaks["peak"]
@@ -421,7 +438,7 @@ def push_by_push_mw(data, variable="torque", cutoff=0.0, minpeak=5.0, mindist=5,
421
438
  negative_work = data["work"].copy()
422
439
  negative_work[negative_work >= 0] = 0
423
440
  pbp["negwork"] = negative_work.groupby(cycle_bins).sum()[1:].reset_index(drop=True)
424
- pbp.loc[len(pbp) - 1, ["cwork", "negwork"]] = np.NaN
441
+ pbp.loc[len(pbp) - 1, ["cwork", "negwork"]] = np.nan
425
442
 
426
443
  if verbose:
427
444
  print("\n" + "=" * 80 + f"\nFound {len(pbp)} pushes!\n" + "=" * 80 + "\n")
@@ -434,43 +451,51 @@ def push_by_push_ergo(data, variable="power", cutoff=0.0, minpeak=50.0, mindist=
434
451
 
435
452
  Push detection and push-by-push analysis for ergometer data. Returns a pandas DataFrame with:
436
453
 
437
- +--------------------+----------------------+-----------+
438
- | Column | Data | Unit |
439
- +====================+======================+===========+
440
- | start/stop/peak | respective indices | |
441
- +--------------------+----------------------+-----------+
442
- | tstart/tstop/tpeak | respective samples | s |
443
- +--------------------+----------------------+-----------+
444
- | cangle | contact angle | rad |
445
- +--------------------+----------------------+-----------+
446
- | cangle_deg | contact angle | degrees |
447
- +--------------------+----------------------+-----------+
448
- | mean/maxpower | power per push | W |
449
- +--------------------+----------------------+-----------+
450
- | mean/maxtorque | torque per push | Nm |
451
- +--------------------+----------------------+-----------+
452
- | mean/maxforce | force per push | N |
453
- +--------------------+----------------------+-----------+
454
- | mean/maxuforce | (rim) force per push | N |
455
- +--------------------+----------------------+-----------+
456
- | mean/maxspeed | velocity per push | ms |
457
- +--------------------+----------------------+-----------+
458
- | work | work per push | J |
459
- +--------------------+----------------------+-----------+
460
- | cwork | work per cycle | J |
461
- +--------------------+----------------------+-----------+
462
- | negwork | negative work/cycle | J |
463
- +--------------------+----------------------+-----------+
464
- | slope | slope onset to peak | Nm/s |
465
- +--------------------+----------------------+-----------+
466
- | smoothness | mean/peak force | |
467
- +--------------------+----------------------+-----------+
468
- | ptime | push time | s |
469
- +--------------------+----------------------+-----------+
470
- | ctime | cycle time | s |
471
- +--------------------+----------------------+-----------+
472
- | reltime | relative push/cycle | % |
473
- +--------------------+----------------------+-----------+
454
+ +--------------------+-----------------------+-----------+
455
+ | Column | Data | Unit |
456
+ +====================+=======================+===========+
457
+ | start/stop/peak | respective indices | |
458
+ +--------------------+-----------------------+-----------+
459
+ | tstart/tstop/tpeak | respective samples | s |
460
+ +--------------------+-----------------------+-----------+
461
+ | cangle | contact angle | rad |
462
+ +--------------------+-----------------------+-----------+
463
+ | cangle_deg | contact angle | degrees |
464
+ +--------------------+-----------------------+-----------+
465
+ | mean/maxpower | power per push | W |
466
+ +--------------------+-----------------------+-----------+
467
+ | mean/maxtorque | torque per push | Nm |
468
+ +--------------------+-----------------------+-----------+
469
+ | mean/maxforce | force per push | N |
470
+ +--------------------+-----------------------+-----------+
471
+ | mean/maxuforce | (rim) force per push | N |
472
+ +--------------------+-----------------------+-----------+
473
+ | mean/maxspeed | velocity per push | ms |
474
+ +--------------------+-----------------------+-----------+
475
+ | work | work per push | J |
476
+ +--------------------+-----------------------+-----------+
477
+ | cwork | work per cycle | J |
478
+ +--------------------+-----------------------+-----------+
479
+ | negwork | negative work/cycle | J |
480
+ +--------------------+-----------------------+-----------+
481
+ | slope | slope onset to peak | Nm/s |
482
+ +--------------------+-----------------------+-----------+
483
+ | smoothness | mean/peak force | N |
484
+ +--------------------+-----------------------+-----------+
485
+ | ptime | push time | s |
486
+ +--------------------+-----------------------+-----------+
487
+ | ctime | cycle time | s |
488
+ +--------------------+-----------------------+-----------+
489
+ | reltime | relative push/cycle | % |
490
+ +--------------------+-----------------------+-----------+
491
+ | pnegpos | neg power start push | index |
492
+ +--------------------+-----------------------+-----------+
493
+ | negpos | neg power start push | W |
494
+ +--------------------+-----------------------+-----------+
495
+ | pnegpoe | neg power end push | index |
496
+ +--------------------+-----------------------+-----------+
497
+ | negpoe | neg power end push | W |
498
+ +--------------------+-----------------------+-----------+
474
499
 
475
500
  Parameters
476
501
  ----------
@@ -520,6 +545,10 @@ def push_by_push_ergo(data, variable="power", cutoff=0.0, minpeak=50.0, mindist=
520
545
  "reltime",
521
546
  "cwork",
522
547
  "negwork",
548
+ "pnegpos",
549
+ "negpos",
550
+ "pnegpoe",
551
+ "negpoe",
523
552
  ]
524
553
 
525
554
  for side in data:
@@ -527,7 +556,7 @@ def push_by_push_ergo(data, variable="power", cutoff=0.0, minpeak=50.0, mindist=
527
556
  peaks = find_peaks(data[side][variable], cutoff, minpeak, mindist)
528
557
  else:
529
558
  peaks = find_peaks(data[side][variable], cutoff, (minpeak * 2), mindist)
530
- pbp = pd.DataFrame(data=np.full((len(peaks["start"]), len(keys)), np.NaN), columns=keys) # preallocate
559
+ pbp = pd.DataFrame(data=np.full((len(peaks["start"]), len(keys)), np.nan), columns=keys) # preallocate
531
560
 
532
561
  pbp["start"] = peaks["start"]
533
562
  pbp["peak"] = peaks["peak"]
@@ -559,7 +588,24 @@ def push_by_push_ergo(data, variable="power", cutoff=0.0, minpeak=50.0, mindist=
559
588
  negative_work = data[side]["work"].copy()
560
589
  negative_work[negative_work >= 0] = 0
561
590
  pbp["negwork"] = negative_work.groupby(cycle_bins).sum()[1:].reset_index(drop=True)
562
- pbp.loc[len(pbp) - 1, ["cwork", "negwork"]] = np.NaN
591
+ pbp.loc[len(pbp) - 1, ["cwork", "negwork"]] = np.nan
592
+
593
+ neg_before_all = []
594
+ neg_after_all = []
595
+
596
+ for sample, sample2 in zip(pbp['start'], pbp['stop']):
597
+ neg_before = data['mean'].loc[sample - 10:sample]
598
+ neg_before_min = neg_before[neg_before['power'] == min(neg_before['power'])]
599
+ neg_before_all.append(neg_before_min)
600
+ neg_after = data['mean'].loc[sample2:sample2 + 10]
601
+ neg_after_min = neg_after[neg_after['power'] == min(neg_after['power'])]
602
+ neg_after_all.append(neg_after_min)
603
+
604
+ for push in range(0, len(pbp)):
605
+ pbp.loc[push, "pnegpos"] = neg_before_all[push].index.item()
606
+ pbp.loc[push, "negpos"] = neg_before_all[push]['power'].item()
607
+ pbp.loc[push, "pnegpoe"] = neg_after_all[push].index.item()
608
+ pbp.loc[push, "negpoe"] = neg_after_all[push]['power'].item()
563
609
 
564
610
  pbp_sides[side] = pd.DataFrame(pbp)
565
611
 
@@ -33,7 +33,7 @@ def plot_pushes(data, pushes, var="torque", start=True, stop=True, peak=True, ax
33
33
  ax : axis object
34
34
 
35
35
  """
36
- with plt.style.context("seaborn-white"):
36
+ with plt.style.context("seaborn-v0_8-white"):
37
37
  if not ax:
38
38
  _, ax = plt.subplots(1, 1)
39
39
  ax.plot(data["time"], data[var])
@@ -120,7 +120,7 @@ def bland_altman_plot(data1, data2, ax=None, condition=None):
120
120
  md = np.mean(diff) # Mean of the difference
121
121
  sd = np.std(diff, axis=0) # Standard deviation of the difference
122
122
 
123
- with plt.style.context("seaborn-white"):
123
+ with plt.style.context("seaborn-v0_8-white"):
124
124
  ax.scatter(mean, diff)
125
125
  ax.axhline(0, color="dimgray", linestyle="-")
126
126
  ax.axhline(md, color="darkgray", linestyle="--")
@@ -152,7 +152,7 @@ def vel_plot(time, vel, name=""):
152
152
  ax: axis object
153
153
 
154
154
  """
155
- plt.style.use("seaborn-darkgrid")
155
+ plt.style.use("seaborn-v0_8-darkgrid")
156
156
  fig, ax = plt.subplots(1, 1, figsize=[10, 6])
157
157
  ax.plot(time, vel, "r")
158
158
  ax.set_xlabel("Time [s]", fontsize=12)
@@ -190,7 +190,7 @@ def vel_peak_plot(time, vel, name=""):
190
190
  y_max_vel_value = np.max(vel)
191
191
 
192
192
  # Create time vs. velocity figure with vel_peak
193
- plt.style.use("seaborn-darkgrid")
193
+ plt.style.use("seaborn-v0_8-darkgrid")
194
194
  fig, ax = plt.subplots(1, 1, figsize=[10, 6])
195
195
  ax.plot(time, vel, "r")
196
196
  ax.plot(time[y_max_vel], vel[y_max_vel], "ko", label="Vel$_{peak}$: " + str(round(y_max_vel_value, 2)) + " m/s")
@@ -232,7 +232,7 @@ def vel_peak_dist_plot(time, vel, dist, name=""):
232
232
  y_max_vel_value = np.max(vel)
233
233
 
234
234
  # Create time vs. velocity figure with vel_peak
235
- plt.style.use("seaborn-darkgrid")
235
+ plt.style.use("seaborn-v0_8-darkgrid")
236
236
  fig, ax1 = plt.subplots(1, 1, figsize=[10, 6])
237
237
  ax1.set_ylim(0, y_max_vel_value + 0.5)
238
238
  ax1.plot(time, vel, "r")
@@ -278,7 +278,7 @@ def acc_plot(time, acc, name=""):
278
278
 
279
279
  """
280
280
  # Create time vs. acceleration figure
281
- plt.style.use("seaborn-darkgrid")
281
+ plt.style.use("seaborn-v0_8-darkgrid")
282
282
  fig, ax = plt.subplots(1, 1, figsize=[10, 6])
283
283
  ax.plot(time, acc, "g")
284
284
  ax.set_xlabel("Time [s]", fontsize=12)
@@ -316,7 +316,7 @@ def acc_peak_plot(time, acc, name=""):
316
316
  y_max_acc = acc.idxmax()
317
317
 
318
318
  # Create time vs. acceleration figure with acc_peak
319
- plt.style.use("seaborn-darkgrid")
319
+ plt.style.use("seaborn-v0_8-darkgrid")
320
320
  fig, ax = plt.subplots(1, 1, figsize=[10, 6])
321
321
  ax.plot(time, acc, "g")
322
322
  ax.plot(time[y_max_acc], acc[y_max_acc], "k.", label="Acc$_{peak}$: " + str(round(y_max_acc_value, 2)) + " m/$s^2$")
@@ -358,7 +358,7 @@ def acc_peak_dist_plot(time, acc, dist, name=""):
358
358
  y_max_acc = acc.idxmax()
359
359
 
360
360
  # Create time vs. acceleration figure with acc_peak
361
- plt.style.use("seaborn-darkgrid")
361
+ plt.style.use("seaborn-v0_8-darkgrid")
362
362
  fig, ax1 = plt.subplots(1, 1, figsize=[10, 6])
363
363
  ax1.set_ylim(np.min(acc) - 1, y_max_acc_value + 1)
364
364
  ax1.plot(time, acc, "g")
@@ -406,7 +406,7 @@ def rot_vel_plot(time, rot_vel, name=""):
406
406
 
407
407
  """
408
408
  # Create time vs. rotational velocity figure
409
- plt.style.use("seaborn-darkgrid")
409
+ plt.style.use("seaborn-v0_8-darkgrid")
410
410
  fig, ax = plt.subplots(1, 1, figsize=[10, 6])
411
411
  ax.plot(time, rot_vel, "b")
412
412
  ax.set_xlabel("Time [s]", fontsize=12)
@@ -477,7 +477,7 @@ def imu_push_plot(sessiondata, acc_frame=True, name='', dec=False):
477
477
  push_idx, acc_filt, n_pushes, cycle_time, push_freq = push_imu(acc, sfreq)
478
478
 
479
479
  # Create time vs. velocity with push detection figure
480
- plt.style.use("seaborn-darkgrid")
480
+ plt.style.use("seaborn-v0_8-darkgrid")
481
481
  fig, ax1 = plt.subplots(1, 1, figsize=[10, 6])
482
482
  ax1.set_ylim(-2, np.max(sessiondata['vel']) + 0.5)
483
483
  ax1.plot(sessiondata['time'], sessiondata['vel'], 'r')
@@ -532,7 +532,7 @@ def plot_power_speed_dist(data, title="", ylim_power=None, ylim_speed=None, ylim
532
532
  the three axes objects
533
533
 
534
534
  """
535
- plt.style.use("seaborn-ticks")
535
+ plt.style.use("seaborn-v0_8-ticks")
536
536
  fig = plt.figure()
537
537
 
538
538
  # Generate three axes
@@ -558,7 +558,7 @@ def binned_stats(array, bins=10, pad=True, func=np.mean, nan_func=np.nanmean):
558
558
  """
559
559
  array = np.array(array, dtype=float) # make sure we have an array
560
560
  if pad:
561
- array = np.pad(array, (0, bins - array.size % bins), mode="constant", constant_values=np.NaN)
561
+ array = np.pad(array, (0, bins - array.size % bins), mode="constant", constant_values=np.nan)
562
562
  means = nan_func(array.reshape(-1, bins), axis=1)
563
563
  else:
564
564
  means = func(array[: (len(array) // bins) * bins].reshape(-1, bins), axis=1)
@@ -1,8 +1,8 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: worklab
3
- Version: 2.0.0
3
+ Version: 2.1.0
4
4
  Summary: Basic scripts for worklab devices
5
- Author-email: Rick de Klerk <r.de.klerk@pl.hanze.nl>, Thomas Rietveld <t.rietveld@lboro.ac.uk>, Rowie Janssen <r.j.f.janssen@umcg.nl>, Jelmer Braaksma <j.braaksma01@umcg.nl>
5
+ Author-email: Sophie de Klerk <r.de.klerk@pl.hanze.nl>, Thomas Rietveld <t.rietveld@lboro.ac.uk>, Rowie Janssen <r.j.f.janssen@umcg.nl>, Jelmer Braaksma <j.braaksma01@umcg.nl>
6
6
  License: GNU GENERAL PUBLIC LICENSE
7
7
  Version 3, 29 June 2007
8
8
 
@@ -637,7 +637,7 @@ License: GNU GENERAL PUBLIC LICENSE
637
637
  the "copyright" line and a pointer to where the full notice is found.
638
638
 
639
639
  Analysis
640
- Copyright (C) 2018 Rick de Klerk
640
+ Copyright (C) 2018 Sophie de Klerk
641
641
 
642
642
  This program is free software: you can redistribute it and/or modify
643
643
  it under the terms of the GNU General Public License as published by
@@ -657,7 +657,7 @@ License: GNU GENERAL PUBLIC LICENSE
657
657
  If the program does terminal interaction, make it output a short
658
658
  notice like this when it starts in an interactive mode:
659
659
 
660
- Analysis Copyright (C) 2018 Rick de Klerk
660
+ Analysis Copyright (C) 2018 Sophie de Klerk
661
661
  This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
662
662
  This is free software, and you are welcome to redistribute it
663
663
  under certain conditions; type `show c' for details.
@@ -678,8 +678,8 @@ License: GNU GENERAL PUBLIC LICENSE
678
678
  Public License instead of this License. But first, please read
679
679
  <http://www.gnu.org/philosophy/why-not-lgpl.html>.
680
680
 
681
- Project-URL: Homepage, https://github.com/rickdkk/worklab
682
- Project-URL: Bug Reports, https://github.com/rickdkk/worklab/issues
681
+ Project-URL: Homepage, https://github.com/sophiedkk/worklab
682
+ Project-URL: Bug Reports, https://github.com/sophiedkk/worklab/issues
683
683
  Keywords: biomechanics,ergometry,physiology
684
684
  Classifier: Intended Audience :: Science/Research
685
685
  Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
@@ -693,16 +693,20 @@ Requires-Dist: numpy
693
693
  Requires-Dist: pandas
694
694
  Requires-Dist: matplotlib
695
695
  Requires-Dist: xlrd
696
+ Requires-Dist: scikit-learn
697
+ Requires-Dist: seaborn
696
698
  Provides-Extra: dev
697
699
  Requires-Dist: pytest; extra == "dev"
698
700
  Requires-Dist: Flake8-pyproject; extra == "dev"
699
701
  Requires-Dist: black; extra == "dev"
700
702
  Requires-Dist: jupyter-book; extra == "dev"
703
+ Requires-Dist: build; extra == "dev"
704
+ Dynamic: license-file
701
705
 
702
706
  # Worklab: a wheelchair biomechanics mini-package
703
707
 
704
708
  [![image](https://zenodo.org/badge/DOI/10.5281/zenodo.8362962.svg)](https://doi.org/10.5281/zenodo.8362962)
705
- [![image](https://badge.fury.io/py/worklab.svg)](https://badge.fury.io/py/worklab) [![image](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gitlab.com/Rickdkk/worklab/tree/master/LICENCE)
709
+ [![image](https://badge.fury.io/py/worklab.svg)](https://badge.fury.io/py/worklab) [![image](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://github.com/sophiedkk/worklab/blob/main/LICENSE)
706
710
 
707
711
  Essential data analysis and (pre-)processing scripts used in projects
708
712
  researching the Lode
@@ -722,7 +726,7 @@ the worklab, which means:
722
726
  # Documentation
723
727
 
724
728
  For more detailed documentation you can look at the
725
- [docs](https://rickdkk.github.io/worklab/).
729
+ [docs](https://sophiedkk.github.io/worklab/).
726
730
 
727
731
  # Prerequisites
728
732
 
@@ -739,7 +743,7 @@ You can install this package with pip:
739
743
  # Examples
740
744
 
741
745
  You can find some Jupyter Notebook examples
742
- [here](https://rickdkk.github.io/worklab/chapters/examples.html).
746
+ [here](https://sophiedkk.github.io/worklab/chapters/examples.html).
743
747
 
744
748
  # Reporting errors
745
749
 
@@ -751,4 +755,4 @@ me or submit an issue through this page.
751
755
  If you want to refer to this package please use this DOI:
752
756
  10.5281/zenodo.8362962, or cite:
753
757
 
754
- Rick de Klerk, Thomas Rietveld, Rowie Janssen, & Jelmer Braaksma. (2023). Worklab: a wheelchair biomechanics mini-package. Zenodo. https://doi.org/10.5281/zenodo.8362963
758
+ Sophie de Klerk, Thomas Rietveld, Rowie Janssen, & Jelmer Braaksma. (2023). Worklab: a wheelchair biomechanics mini-package. Zenodo. https://doi.org/10.5281/zenodo.8362963
@@ -3,9 +3,12 @@ numpy
3
3
  pandas
4
4
  matplotlib
5
5
  xlrd
6
+ scikit-learn
7
+ seaborn
6
8
 
7
9
  [dev]
8
10
  pytest
9
11
  Flake8-pyproject
10
12
  black
11
13
  jupyter-book
14
+ build
File without changes
File without changes
File without changes
File without changes
File without changes