Skip to content

Commit

Permalink
Merge b30b116 into 9c024ba
Browse files Browse the repository at this point in the history
  • Loading branch information
romainmartinez committed Aug 31, 2020
2 parents 9c024ba + b30b116 commit c3322cb
Show file tree
Hide file tree
Showing 10 changed files with 40 additions and 19 deletions.
8 changes: 4 additions & 4 deletions docs/paper/paper.md
Expand Up @@ -31,9 +31,9 @@ The processing, analysis and visualization of these data could therefore be unif
Most biomechanical data characterizing human and animal movement appear as temporal waveforms representing specific measures such as muscle activity or joint angles.
These data are typically multidimensional arrays structured around labels with arbitrary metadata (\autoref{fig:biomech-data}).
Existing software solutions share some limitations.
Some of them are not free of charge [@Damsgaard2006-gq] or based on closed-source programming language [@Dixon2017-co; @Muller2019-cx].
Some of them are proprietary [@Damsgaard2006-gq] or based on closed-source programming language [@Dixon2017-co; @Muller2019-cx].
Others do not leverage labels and metadata [@Walt2011-em; @Hachaj2019-tk; @Virtanen2020-zv].
`pyomeca` is a python package designed to address these limitations.
`pyomeca` is a Python package designed to address these limitations.
It provides basic operations useful in the daily workflow of a biomechanical researcher such as reading, writing, filtering and plotting, but also more advanced biomechanical routines geared towards rigid body mechanics and signal processing.
By offering a single, efficient and flexible implementation, `pyomeca` standardizes these procedures and avoids wasting valuable research time writing functions that have, sometimes, nothing to do with the research interests.

Expand All @@ -43,9 +43,9 @@ Metadata are also needed to inform about important features of the experiment.\l

# Summary

As a python library, `pyomeca` enables extraction, processing and visualization of biomechanical data for use in research and education.
As a Python library, `pyomeca` enables extraction, processing and visualization of biomechanical data for use in research and education.
It is motivated by the need for simpler tools and more reproducible workflows allowing practitioners to focus on their specific interests and leaving `pyomeca` to handle the computational details for them.
`pyomeca` builds on the core scientific python packages, in particular `numpy` [@Walt2011-em], `scipy` [@Virtanen2020-zv], `matplotlib` [@Hunter2007-fv] and `xarray` [@Hoyer2017-sf].
`pyomeca` builds on the core scientific Python packages, in particular `numpy` [@Walt2011-em], `scipy` [@Virtanen2020-zv], `matplotlib` [@Hunter2007-fv] and `xarray` [@Hoyer2017-sf].
By providing labeled querying and computation, efficient algorithms and persistent metadata, the integration of `xarray` facilitates usability, which is a step towards the adoption of programming in biomechanics.
`xarray` is designed as a general-purpose library and tries to avoid including domain specific functionalities --- but inevitably, the need for more domain specific logic arises.
`pyomeca` provides a biomechanics layer that supports specialized file formats (`c3d`, `mat`, `trc`, `sto`, `mot`, `csv` and `xlsx`) and implements signal processing and matrix manipulation routines commonly used in biomechanics.
Expand Down
2 changes: 1 addition & 1 deletion pyomeca/__init__.py
@@ -1,6 +1,6 @@
from ._version import __version__
from .analogs import Analogs
from .angles import Angles
from .dataarray_accessor import DataArrayAccessor
from .markers import Markers
from .rototrans import Rototrans
from ._version import __version__
2 changes: 1 addition & 1 deletion pyomeca/dataarray_accessor.py
Expand Up @@ -509,7 +509,7 @@ def band_stop(
```
![band_stop](/images/api/band_stop.svg)
!!! note
You can also perform a notch filter with this method.
A notch filter is a band-stop filter with a narrow bandwidth.
Expand Down
13 changes: 11 additions & 2 deletions pyomeca/io/read.py
Expand Up @@ -141,15 +141,24 @@ def read_sto_or_mot(
end_header = find_end_header_in_opensim_file(filename)

data = caller.from_csv(
filename, header=end_header + 1, first_column=0, time_column=0, **kwargs,
filename,
header=end_header + 1,
first_column=0,
time_column=0,
**kwargs,
)
data.attrs["rate"] = (1 / (data.time[1] - data.time[0])).round().item()
return data


def read_trc(caller: Callable, filename: Union[str, Path], **kwargs):
data = caller.from_csv(
filename, header=3, first_row=6, first_column=1, time_column=1, **kwargs,
filename,
header=3,
first_row=6,
first_column=1,
time_column=1,
**kwargs,
)
data.attrs["rate"] = (1 / (data.time[1] - data.time[0])).round().item()
return data
4 changes: 3 additions & 1 deletion pyomeca/processing/interp.py
Expand Up @@ -14,7 +14,9 @@ def time_normalize(
if norm_time:
first_last_time = (0, 99)
array["time"] = np.linspace(
first_last_time[0], first_last_time[1], array["time"].shape[0],
first_last_time[0],
first_last_time[1],
array["time"].shape[0],
)
else:
first_last_time = (array.time[0], array.time[-1])
Expand Down
3 changes: 2 additions & 1 deletion tests/test_docstrings.py
Expand Up @@ -39,7 +39,8 @@ def test_docstring_example(method):
else ""
)
code_block = extract_code_blocks_from_md(method.__doc__).replace(
"plt.show()", plt_show_replacer,
"plt.show()",
plt_show_replacer,
)
exec(code_block, {}, {})

Expand Down
3 changes: 2 additions & 1 deletion tests/test_fileio_read_csv_c3d.py
Expand Up @@ -79,7 +79,8 @@ def test_read_analogs(
@pytest.mark.parametrize("usecols", [[20.0]])
@pytest.mark.parametrize("extension", _extensions)
def test_read_catch_error(
usecols, extension,
usecols,
extension,
):
with pytest.raises(IndexError):
Markers.from_csv(MARKERS_CSV)
Expand Down
18 changes: 12 additions & 6 deletions tests/test_processing_filter.py
Expand Up @@ -14,10 +14,12 @@ def test_proc_filters():
**EXPECTED_VALUES[32],
)
is_expected_array(
ANALOGS_DATA.meca.low_pass(order=order, cutoff=5), **EXPECTED_VALUES[32],
ANALOGS_DATA.meca.low_pass(order=order, cutoff=5),
**EXPECTED_VALUES[32],
)
is_expected_array(
ANALOGS_DATA.meca.high_pass(order=order, cutoff=100), **EXPECTED_VALUES[33],
ANALOGS_DATA.meca.high_pass(order=order, cutoff=100),
**EXPECTED_VALUES[33],
)
is_expected_array(
ANALOGS_DATA.meca.band_pass(order=order, cutoff=[10, 200]),
Expand All @@ -34,16 +36,20 @@ def test_proc_filters():
**EXPECTED_VALUES[36],
)
is_expected_array(
MARKERS_DATA.meca.low_pass(order=order, cutoff=5), **EXPECTED_VALUES[36],
MARKERS_DATA.meca.low_pass(order=order, cutoff=5),
**EXPECTED_VALUES[36],
)
is_expected_array(
MARKERS_DATA.meca.high_pass(order=order, cutoff=10), **EXPECTED_VALUES[37],
MARKERS_DATA.meca.high_pass(order=order, cutoff=10),
**EXPECTED_VALUES[37],
)
is_expected_array(
MARKERS_DATA.meca.band_pass(order=order, cutoff=[1, 10]), **EXPECTED_VALUES[38],
MARKERS_DATA.meca.band_pass(order=order, cutoff=[1, 10]),
**EXPECTED_VALUES[38],
)
is_expected_array(
MARKERS_DATA.meca.band_stop(order=order, cutoff=[5, 6]), **EXPECTED_VALUES[39],
MARKERS_DATA.meca.band_stop(order=order, cutoff=[5, 6]),
**EXPECTED_VALUES[39],
)

with pytest.raises(ValueError):
Expand Down
3 changes: 2 additions & 1 deletion tests/test_processing_marker.py
Expand Up @@ -18,7 +18,8 @@ def test_rotate():
for marker in range(n_markers):
for frame in range(n_frames):
expected_rotated_marker[:, marker, frame] = np.dot(
rt.isel(time=frame), markers.isel(channel=marker, time=frame),
rt.isel(time=frame),
markers.isel(channel=marker, time=frame),
)

np.testing.assert_array_almost_equal(
Expand Down
3 changes: 2 additions & 1 deletion tests/test_processing_rt.py
Expand Up @@ -61,7 +61,8 @@ def test_construct_rt():
np.arange(0, rt_random_angles.time.size / 0.5, 1 / 0.5)

rt_with_time = Rototrans(
rt_random_angles, time=np.arange(0, rt_random_angles.time.size / 100, 1 / 100),
rt_random_angles,
time=np.arange(0, rt_random_angles.time.size / 100, 1 / 100),
)
assert rt_with_time.time[-1] == 0.09

Expand Down

0 comments on commit c3322cb

Please sign in to comment.