Skip to content

Commit

Permalink
Merge pull request #17 from PfizerRD/development
Browse files Browse the repository at this point in the history
Add logging
  • Loading branch information
LukasAdamowicz authored Nov 10, 2020
2 parents ed90430 + 3515256 commit 88a22c5
Show file tree
Hide file tree
Showing 16 changed files with 173 additions and 138 deletions.
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ def parse_setuppy_commands():
'pandas>=0.23.4',
'lightgbm>=2.3.0',
'pywavelets',
'scikit-learn' # needed for lightgbm
'scikit-learn', # needed for lightgbm
'h5py' # currently for gait classifier dataset loading
]

if sys.version_info < (3, 7):
Expand Down
18 changes: 10 additions & 8 deletions src/skimu/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
Pfizer DMTI 2020
"""
from datetime import date as dt_date
import logging

from pandas import DataFrame


Expand All @@ -30,7 +32,7 @@ def __repr__(self):

return ret

def __init__(self, return_result=True, **kwargs):
def __init__(self, **kwargs):
"""
Intended to be subclassed
Expand All @@ -42,17 +44,17 @@ def __init__(self, return_result=True, **kwargs):
Key-word arguments which are passed to the sub-class
"""
self._name = self.__class__.__name__
self._return_result = return_result
self._in_pipeline = False # initialize to false. Will be set by the pipeline

self._kw = kwargs

def predict(self, *args, **kwargs):
result = self._predict(*args, **kwargs)
self.logger = logging.getLogger(__name__)

if self._return_result:
return result[1]
else:
return result[0]
def predict(self, *args, **kwargs):
"""
Intended to be overwritten in the subclass. Should still be called with super though
"""
self.logger.info(f"Entering {self._name} processing with call {self!r}")

def save_results(self, results, file_name):
"""
Expand Down
69 changes: 28 additions & 41 deletions src/skimu/gait/gait.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
Lukas Adamowicz
Pfizer DMTI 2020
"""
from warnings import warn
from collections.abc import Iterable
from warnings import warn

from numpy import mean, diff, abs, argmax, sign, round, array

Expand Down Expand Up @@ -132,7 +132,6 @@ def __init__(self, use_cwt_scale_relation=True, min_bout_time=8.0,
max_bout_separation_time=0.5, max_stride_time=2.25, loading_factor=0.2,
height_factor=0.53, prov_leg_length=False, filter_order=4, filter_cutoff=20.0):
super().__init__(
True,
# key-word arguments for storage
use_cwt_scale_relation=use_cwt_scale_relation,
min_bout_time=min_bout_time,
Expand Down Expand Up @@ -197,7 +196,7 @@ def add_metrics(self, metrics):
else:
raise ValueError(f'Metric {metrics!r} is not a EventMetric or BoutMetric')

def predict(self, *args, **kwargs):
def predict(self, time=None, accel=None, *, gyro=None, height=None, gait_pred=None, **kwargs):
"""
predict(time, accel, *, gyro=None, height=None, gait_pred=None)
Expand Down Expand Up @@ -234,38 +233,10 @@ def predict(self, *args, **kwargs):
LowFrequencyError
If the sampling frequency is less than 20Hz
"""
return super().predict(*args, **kwargs)

def _predict(self, time=None, accel=None, *, gyro=None, height=None, gait_pred=None,
**kwargs):
"""
predict(time=None, accel=None, *, gyro=None, height=None, gait_pred=None)
Get the gait events and metrics from a time series signal
Parameters
----------
time : numpy.ndarray
(N, ) array of unix timestamps, in seconds
accel : numpy.ndarray
(N, 3) array of accelerations measured by centrally mounted lumbar device, in
units of 'g'
gyro : numpy.ndarray, optional
(N, 3) array of angular velocities measured by the same centrally mounted lumbar
device, in units of 'deg/s'. Only optionally used if provided. Main functionality
is to allow distinguishing step sides.
height : float, optional
Either height (False) or leg length (True) of the subject who wore the inertial
measurement device, in meters, depending on `leg_length`. If not provided,
spatial metrics will not be computed
gait_pred : numpy.ndarray, optional
(N, ) array of boolean predictions of gait. If not provided, gait classification
will be performed on the acceleration data
super().predict(
time=time, accel=accel, gyro=gyro, height=height, gait_pred=gait_pred, **kwargs
)

Returns
-------
gait_results : dict
"""
if height is None:
warn('height not provided, not computing spatial metrics', UserWarning)
leg_length = None
Expand All @@ -278,10 +249,6 @@ def _predict(self, time=None, accel=None, *, gyro=None, height=None, gait_pred=N
if (1 / dt) < 20.0:
raise LowFrequencyError(f"Frequency ({1/dt:.2f}Hz) is too low (<20Hz)")

# figure out vertical axis
acc_mean = mean(accel, axis=0)
v_axis = argmax(abs(acc_mean))

# original scale. compute outside loop.
# 1.25 comes from original paper, corresponds to desired frequency
# 0.4 comes from using the 'gaus1' wavelet
Expand All @@ -298,7 +265,7 @@ def _predict(self, time=None, accel=None, *, gyro=None, height=None, gait_pred=N
}
# auxiliary dictionary for storing values for computing gait metrics
gait_aux = {
'vert axis': v_axis,
'vert axis': [],
'accel': [],
'vert velocity': [],
'vert position': [],
Expand All @@ -322,6 +289,10 @@ def _predict(self, time=None, accel=None, *, gyro=None, height=None, gait_pred=N
for ibout, bout in enumerate(gait_bouts):
bstart = start + bout[0]

# figure out vertical axis on a per-bout basis
acc_mean = mean(accel[bstart:start + bout[1]], axis=0)
v_axis = argmax(abs(acc_mean))

ic, fc, vert_acc = get_gait_events(
accel[bstart:start + bout[1], v_axis], dt, time[bstart:start + bout[1]],
sign(acc_mean[v_axis]), scale_original,
Expand All @@ -338,6 +309,7 @@ def _predict(self, time=None, accel=None, *, gyro=None, height=None, gait_pred=N
gait_aux['accel'].append(accel[bstart:start + bout[1], :])
# add the index for the corresponding accel/velocity/position
gait_aux['inertial data i'].extend([len(gait_aux['accel']) - 1] * sib)
gait_aux['vert axis'].extend([v_axis] * sib)

# save some default per bout metrics
gait['Bout N'].extend([ibout + 1] * sib)
Expand All @@ -354,11 +326,26 @@ def _predict(self, time=None, accel=None, *, gyro=None, height=None, gait_pred=N
for key in gait:
gait[key] = array(gait[key])
# convert inertial data index to an array
gait_aux['vert axis'] = array(gait_aux['vert axis'])
gait_aux['inertial data i'] = array(gait_aux['inertial data i'])

# loop over metrics and compute
for param in self._params:
param().predict(dt, leg_length, gait, gait_aux)
param(self.logger).predict(dt, leg_length, gait, gait_aux)

# remove unnecessary stuff from gait dict
gait.pop('IC', None)
gait.pop('FC', None)
gait.pop('FC opp foot', None)

# remove in-valid gait cycles
for k in [i for i in gait if i != 'b valid cycle']:
gait[k] = gait[k][gait['b valid cycle']]

gait.pop('b valid cycle')

kwargs.update({self._acc: accel, self._time: time, self._gyro: gyro, 'height': height})
return kwargs, gait
if self._in_pipeline:
return kwargs, gait
else:
return gait
2 changes: 1 addition & 1 deletion src/skimu/gait/gait_metrics/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def predict(self, dt, leg_length, gait, gait_aux):
self._predict(dt, leg_length, gait, gait_aux)

def _predict_asymmetry(self, dt, leg_length, gait, gait_aux):
asy_name = f'{self.name} asymmetry'
asy_name = f'{self.k_} asymmetry'
gait[asy_name] = full(gait['IC'].size, nan, dtype=float_)

mask = self._get_mask(gait, 1)
Expand Down
Loading

0 comments on commit 88a22c5

Please sign in to comment.