-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
EHN add gradient elevation, speed and heart-rate (#23)
- Loading branch information
Showing
9 changed files
with
243 additions
and
18 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 |
---|---|---|
@@ -0,0 +1,18 @@ | ||
"""Module containing the exceptions used in scikit-cycling.""" | ||
|
||
# Authors: Guillaume Lemaitre <g.lemaitre58@gmail.com> | ||
# Cedric Lemaitre | ||
# License: BSD 3 clause | ||
|
||
__all__ = ['MissingDataError'] | ||
|
||
|
||
class MissingDataError(ValueError): | ||
"""Error raised when there is not the required data to make some | ||
computation. | ||
For instance, :func:`skcycling.extraction.gradient_elevation` required | ||
elevation and distance data which might not be provided. In this case, this | ||
type of error is raised. | ||
""" |
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
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,121 @@ | ||
"""Function to extract gradient information about different features.""" | ||
|
||
# Authors: Guillaume Lemaitre <g.lemaitre58@gmail.com> | ||
# Cedric Lemaitre | ||
# License: BSD 3 clause | ||
|
||
from __future__ import division | ||
|
||
from ..exceptions import MissingDataError | ||
|
||
|
||
def acceleration(activity, periods=5, append=True): | ||
"""Compute the acceleration (i.e. speed gradient). | ||
Parameters | ||
---------- | ||
activity : DataFrame | ||
The activity containing speed information. | ||
periods : int, default=5 | ||
Periods to shift to compute the acceleration. | ||
append : bool, optional | ||
Whether to append the acceleration to the original activity (default) | ||
or to only return the acceleration as a Series. | ||
Returns | ||
------- | ||
data : DataFrame or Series | ||
The original activity with an additional column containing the | ||
acceleration or a single Series containing the acceleration. | ||
""" | ||
if 'speed' not in activity.columns: | ||
raise MissingDataError('To compute the acceleration, speed data are ' | ||
'required. Got {} fields.' | ||
.format(activity.columns)) | ||
|
||
acceleration = activity['speed'].diff(periods=periods) | ||
|
||
if append: | ||
activity['acceleration'] = acceleration | ||
return activity | ||
else: | ||
return acceleration | ||
|
||
|
||
def gradient_elevation(activity, periods=5, append=True): | ||
"""Compute the elevation gradient. | ||
Parameters | ||
---------- | ||
activity : DataFrame | ||
The activity containing elevation and distance information. | ||
periods : int, default=5 | ||
Periods to shift to compute the elevation gradient. | ||
append : bool, optional | ||
Whether to append the elevation gradient to the original activity | ||
(default) or to only return the elevation gradient as a Series. | ||
Returns | ||
------- | ||
data : DataFrame or Series | ||
The original activity with an additional column containing the | ||
elevation gradient or a single Series containing the elevation | ||
gradient. | ||
""" | ||
if not {'elevation', 'distance'}.issubset(activity.columns): | ||
raise MissingDataError('To compute the elevation gradient, elevation ' | ||
'and distance data are required. Got {} fields.' | ||
.format(activity.columns)) | ||
|
||
diff_elevation = activity['elevation'].diff(periods=periods) | ||
diff_distance = activity['distance'].diff(periods=periods) | ||
gradient_elevation = diff_elevation / diff_distance | ||
|
||
if append: | ||
activity['gradient-elevation'] = gradient_elevation | ||
return activity | ||
else: | ||
return gradient_elevation | ||
|
||
|
||
def gradient_heart_rate(activity, periods=5, append=True): | ||
"""Compute the heart-rate gradient. | ||
Parameters | ||
---------- | ||
activity : DataFrame | ||
The activity containing heart-rate information. | ||
periods : int, default=5 | ||
Periods to shift to compute the heart-rate gradient. | ||
append : bool, optional | ||
Whether to append the heart-rate gradient to the original activity | ||
(default) or to only return the heart-rate gradient as a Series. | ||
Returns | ||
------- | ||
data : DataFrame or Series | ||
The original activity with an additional column containing the | ||
heart-rate gradient or a single Series containing the heart-rate | ||
gradient. | ||
""" | ||
if 'heart-rate' not in activity.columns: | ||
raise MissingDataError('To compute the heart-rate gradient, heart-rate' | ||
' data are required. Got {} fields.' | ||
.format(activity.columns)) | ||
|
||
gradient_heart_rate = activity['heart-rate'].diff(periods=periods) | ||
|
||
if append: | ||
activity['gradient-heart-rate'] = gradient_heart_rate | ||
return activity | ||
else: | ||
return gradient_heart_rate |
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
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,74 @@ | ||
"""Test the gradient module.""" | ||
|
||
# Authors: Guillaume Lemaitre <g.lemaitre58@gmail.com> | ||
# Cedric Lemaitre | ||
# License: BSD 3 clause | ||
|
||
import numpy as np | ||
import pandas as pd | ||
|
||
import pytest | ||
|
||
from skcycling.extraction import acceleration | ||
from skcycling.extraction import gradient_elevation | ||
from skcycling.extraction import gradient_heart_rate | ||
from skcycling.exceptions import MissingDataError | ||
|
||
|
||
def test_acceleration_error(): | ||
activity = pd.DataFrame({'A': np.random.random(1000)}) | ||
msg = "speed data are required" | ||
with pytest.raises(MissingDataError, message=msg): | ||
acceleration(activity) | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"activity, append, type_output, shape", | ||
[(pd.DataFrame({'speed': np.random.random(100)}), | ||
False, pd.Series, (100,)), | ||
(pd.DataFrame({'speed': np.random.random(100)}), | ||
True, pd.DataFrame, (100, 2))]) | ||
def test_acceleration(activity, append, type_output, shape): | ||
output = acceleration(activity, append=append) | ||
assert isinstance(output, type_output) | ||
assert output.shape == shape | ||
|
||
|
||
def test_gradient_elevation_error(): | ||
activity = pd.DataFrame({'A': np.random.random(1000)}) | ||
msg = "elevation and distance data are required" | ||
with pytest.raises(MissingDataError, message=msg): | ||
gradient_elevation(activity) | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"activity, append, type_output, shape", | ||
[(pd.DataFrame({'elevation': np.random.random(100), | ||
'distance': np.random.random(100)}), | ||
False, pd.Series, (100,)), | ||
(pd.DataFrame({'elevation': np.random.random(100), | ||
'distance': np.random.random(100)}), | ||
True, pd.DataFrame, (100, 3))]) | ||
def test_gradient_elevation(activity, append, type_output, shape): | ||
output = gradient_elevation(activity, append=append) | ||
assert isinstance(output, type_output) | ||
assert output.shape == shape | ||
|
||
|
||
def test_gradient_heart_rate_error(): | ||
activity = pd.DataFrame({'A': np.random.random(1000)}) | ||
msg = "heart-rate data are required" | ||
with pytest.raises(MissingDataError, message=msg): | ||
gradient_heart_rate(activity) | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"activity, append, type_output, shape", | ||
[(pd.DataFrame({'heart-rate': np.random.random(100)}), | ||
False, pd.Series, (100,)), | ||
(pd.DataFrame({'heart-rate': np.random.random(100)}), | ||
True, pd.DataFrame, (100, 2))]) | ||
def test_gradient_heart_rate(activity, append, type_output, shape): | ||
output = gradient_heart_rate(activity, append=append) | ||
assert isinstance(output, type_output) | ||
assert output.shape == shape |
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
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
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
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