-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6 from sladkovm/feature/vmpy
Feature/vmpy
- Loading branch information
Showing
19 changed files
with
96,480 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -94,3 +94,6 @@ ENV/ | |
|
||
# MacOS | ||
.DS_Store | ||
|
||
#PyCharm | ||
.idea |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
Changelog | ||
========= | ||
|
||
0.X.X (unreleased) | ||
------------------ | ||
|
||
Breaking changes: | ||
|
||
- *add item here* | ||
|
||
New features: | ||
|
||
- Stress in zone | ||
|
||
Bug fixes: | ||
|
||
- *add item here* | ||
|
||
|
||
0.1.1 (in progress) | ||
------------------ | ||
|
||
Breaking changes: | ||
|
||
- *add item here* | ||
|
||
New features: | ||
|
||
- Merged vmpy functionality as is: | ||
- io/strava.py | ||
- algorithms/streams.py | ||
- algorithms/metrics.py | ||
- utils.py | ||
|
||
Bug fixes: | ||
|
||
- *add item here* | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,4 @@ isort==4.2.15 | |
lmfit==0.9.7 | ||
pandas==0.20.3 | ||
pytest==3.2.5 | ||
requests==2.18.4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
"""Calculation of performance metrics that change the shape of stream""" | ||
|
||
import numpy as np | ||
import pandas as pd | ||
from sweat.algorithms.streams import rolling_mean, mask_fill, compute_zones | ||
from sweat.utils import cast_array_to_original_type | ||
|
||
import logging | ||
logger = logging.getLogger(__name__) | ||
|
||
|
||
def power_duration_curve(arg, mask=None, value=0.0, **kwargs): | ||
"""Power-Duration Curve | ||
Compute power duration curve from the power stream. Mask-filter options can be | ||
added using the keyword arguments. | ||
Parameters | ||
---------- | ||
arg : array-like | ||
Power stream | ||
mask: array-like, optional | ||
Replacement mask (the default is None, which implies no masking) | ||
value: number, optional | ||
Value to use as a replacement (the default is 0.0) | ||
Returns | ||
------- | ||
rv : type of input argument | ||
Power-Duration Curve | ||
""" | ||
|
||
y = mask_fill(arg, mask=mask, value=value) | ||
y = pd.Series(y) | ||
|
||
# Compute the accumulated energy from the power data | ||
energy = y.cumsum() | ||
|
||
# Compute the maximum sustainable power using the difference in energy | ||
# This method is x4 faster than using rolling mean | ||
y = np.array([]) | ||
for t in np.arange(1, len(energy)): | ||
y = np.append(y, energy.diff(t).max()/(t)) | ||
y = cast_array_to_original_type(y, type(arg)) | ||
|
||
return y | ||
|
||
|
||
def best_interval(arg, window, mask=None, value=0.0, **kwargs): | ||
"""Compute best interval of the stream | ||
Masking with replacement is controlled by keyword arguments | ||
Parameters | ||
---------- | ||
arg: array-like | ||
window : int | ||
Duration of the interval in seconds | ||
mask : array-like of bool, optional | ||
default=None, which means no masking | ||
value : number, optional | ||
Value to use for replacement, default=0.0 | ||
Returns | ||
------- | ||
float | ||
""" | ||
|
||
y = rolling_mean(arg, window=window, mask=mask, value=value, **kwargs) | ||
|
||
rv = np.max(y) | ||
|
||
return rv | ||
|
||
|
||
def time_in_zones(arg, **kwargs): | ||
"""Time in zones | ||
Calculate time [sec] spent in each zone | ||
Parameters | ||
---------- | ||
arg : array-like, power or heartrate | ||
kwargs : see zones | ||
Returns | ||
------- | ||
array-like, the same type as arg | ||
""" | ||
type_arg = type(arg) | ||
z = pd.Series(compute_zones(arg, **kwargs)) | ||
tiz = z.groupby(z).count() | ||
rv = cast_array_to_original_type(tiz, type_arg) | ||
|
||
return rv | ||
|
||
|
||
def normalized_power(arg, mask=None, value=0.0, **kwargs): | ||
"""Normalized power | ||
Parameters | ||
---------- | ||
arg : array-like | ||
Power stream | ||
mask: array-like of bool, optional | ||
default=None, which means no masking | ||
value : number, optional | ||
Value to use for replacement, default=0.0 | ||
type : {"xPower", "NP} | ||
Determines calculation method to use, default='xPower' | ||
Returns | ||
------- | ||
number | ||
""" | ||
|
||
if kwargs.get('type', 'NP') == 'xPower': | ||
_rolling_mean = rolling_mean(arg, window=25, mask=mask, value=value, type='emwa') | ||
else: | ||
_rolling_mean = rolling_mean(arg, window=30, mask=mask, value=value) | ||
|
||
if type(_rolling_mean) == list: | ||
_rolling_mean = np.asarray(_rolling_mean, dtype=np.float) | ||
|
||
rv = np.mean(np.power(_rolling_mean, 4)) ** (1/4) | ||
|
||
return rv | ||
|
||
|
||
def relative_intensity(norm_power, threshold_power): | ||
"""Relative intensity | ||
Parameters | ||
---------- | ||
norm_power : number | ||
NP or xPower | ||
threshold_power : number | ||
FTP or CP | ||
Returns | ||
------- | ||
float | ||
IF or RI | ||
""" | ||
|
||
rv = norm_power/threshold_power | ||
|
||
return rv | ||
|
||
|
||
def stress_score(norm_power, threshold_power, duration): | ||
"""Stress Score | ||
Parameters | ||
---------- | ||
norm_power : number | ||
NP or xPower | ||
threshold_power : number | ||
FTP or CP | ||
duration : int | ||
Duration in seconds | ||
Returns | ||
------- | ||
ss: | ||
TSS or BikeScore | ||
""" | ||
|
||
ss = (duration/3600) * (norm_power/threshold_power)**2 * 100 | ||
|
||
return ss |
Oops, something went wrong.