diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e29aede --- /dev/null +++ b/.gitignore @@ -0,0 +1,157 @@ +# Created by https://www.toptal.com/developers/gitignore/api/python,jupyternotebooks +# Edit at https://www.toptal.com/developers/gitignore?templates=python,jupyternotebooks + +### JupyterNotebooks ### +# gitignore template for Jupyter Notebooks +# website: http://jupyter.org/ + +.ipynb_checkpoints +*/.ipynb_checkpoints/* + +# IPython +profile_default/ +ipython_config.py + +# Remove previous ipynb_checkpoints +# git rm -r .ipynb_checkpoints/ + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +pytestdebug.log + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +doc/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook + +# IPython + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +pythonenv* + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# profiling data +.prof + +# Pias add this +.DS_STORE +# End of https://www.toptal.com/developers/gitignore/api/python,jupyternotebooks \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..dad83ef --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +language: python +python: + - "3.7" + - "3.8" +# command to install dependencies +install: + # install dependencies for testing + - pip install pytest pytest-cov + # install the package + - pip install . +# command to run tests +script: + - pytest --cov=./ \ No newline at end of file diff --git a/CITATION.md b/CITATION.md new file mode 100644 index 0000000..860e621 --- /dev/null +++ b/CITATION.md @@ -0,0 +1,16 @@ +To acknowledge this repository please cite our paper + + ```bibtex + @article{Charlene2019, + title = {Reproducible {{Brain}}}, + volume = {12}, + number = {5}, + journal = {Brain Technologies \& Signals}, + doi = {10/v5dar2}, + author = {{Charlene Bultoc {and} Fulanito Shrug}}, + month = sep, + year = {2019}, + pages = {8-13}, + note = {00000} + } + ``` \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..a6824dc --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,29 @@ +BSD 3-Clause License + + Copyright (c) 2019, Charlene Bultoc and others + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a1997db --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +# sagital average + + This is a library that calculate the averages through a sagital plain. + + ## Installation + + Browse to the directory where this file lives, and run: + ```bash + pip install . + ``` + That command will download any dependencies we have + + + ## Usage + + Right now we have only one function, you can use it as shown below: + + + ```python + from sagital_averages import sagital_brain + + sagital_brain.run_averages("input_file.csv", "ouput_file.csv") + ``` + + Or alternativaly you can run it from the terminal as: + + ```bash + $ sagverage input_file.csv -o output_file.csv + ``` + + ## Contributing + + We accept contributions via GitHub!! + + To install the development version, clone this repository and install it on + a virtual environment + + ```bash + git clone git@github.com:UCL-RITS/rsd-sagital_average.git + python -m venv brain + soruce brain/bin/activate + cd rsd-sagital_average + pip install -e . + ... code code code ... + deactivate + ``` + + or using a conda environment as + + ```bash + git clone git@github.com:UCL-RITS/rsd-sagital_average.git + conda create -n brain python=3.7 + conda activate brain + cd rsd-sagital_average + pip install -e . + ... code code code ... + conda deactivate + ``` \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/brain_average.csv b/brain_average.csv deleted file mode 100644 index d30573e..0000000 --- a/brain_average.csv +++ /dev/null @@ -1 +0,0 @@ -10.5,11.5,12.5,13.5,14.5,15.5,16.5,17.5,18.5,19.5,20.5,21.5,22.5,23.5,24.5,25.5,26.5,27.5,28.5,29.5 diff --git a/brain_sample.csv b/brain_sample.csv deleted file mode 100644 index 7b809c6..0000000 --- a/brain_sample.csv +++ /dev/null @@ -1,20 +0,0 @@ -1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 -2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21 -3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22 -4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23 -5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24 -6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 -7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26 -8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27 -9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28 -10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29 -11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30 -12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31 -13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32 -14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33 -15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34 -16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35 -17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36 -18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37 -19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38 -20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39 diff --git a/command.py b/command.py new file mode 100644 index 0000000..be6308f --- /dev/null +++ b/command.py @@ -0,0 +1,25 @@ +from argparse import ArgumentParser +from argparse import ArgumentDefaultsHelpFormatter +from ..sagital_brain import run_averages + + +def process(): + parser = ArgumentParser( + description="Calculates the average for each sagital-horizontal plane.", + formatter_class=ArgumentDefaultsHelpFormatter, + ) + parser.add_argument( + "file_input", + nargs="?", + default="brain_sample.csv", + help="Input CSV file with the results from scikit-brain binning algorithm.", + ) + parser.add_argument( + "--file_output", + "-o", + default="brain_average.csv", + help="Name of the output CSV file.", + ) + arguments = parser.parse_args() + + run_averages(arguments.file_input, arguments.file_output) diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..e39301e --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,59 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + + +# -- Project information ----------------------------------------------------- + +project = "Sagital Average" +copyright = "2020, Charlene Bultoc" +author = "Charlene Bultoc" + +# The full version, including alpha/beta/rc tags +release = "0.0.1" + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + "sphinx.ext.autodoc", # Support automatic documentation + "sphinx.ext.coverage", # Automatically check if functions are documented + "sphinx.ext.mathjax", # Allow support for algebra + "sphinx.ext.viewcode", # Include the source code in documentation +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "alabaster" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..5278473 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,20 @@ +.. Sagital Average documentation master file, created by + sphinx-quickstart on Wed Dec 23 16:05:28 2020. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Sagital Average's documentation! +=========================================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..2119f51 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/gitbisect.out b/gitbisect.out new file mode 100644 index 0000000..7865a46 --- /dev/null +++ b/gitbisect.out @@ -0,0 +1,12 @@ +running python sagital_brain.py brain_sample.csv +0251e1b6819f3bcdcd4f478d7208e9b723635fb0 is the first bad commit +commit 0251e1b6819f3bcdcd4f478d7208e9b723635fb0 +Author: NuttamasT +Date: Thu Dec 17 21:21:19 2020 +0000 + + Create bugs + +:000000 100644 0000000000000000000000000000000000000000 09717d2895db7c2e2587a2a9d18ab3dbf1233d76 A CreateFalseFile.py +:100644 100644 d30573edd18bddab5ef33fd4a627a17a1eb7bbc1 c1f7fbbb7e77f9a4c53558688b7bbe20cd0d15b5 M brain_average.csv +:100644 100644 7b809c65eef69bc79abcb716210e94e84667f531 d0b903532502bc32a79c0c0770db8503bae1d65b M brain_sample.csv +bisect run success diff --git a/sagital_brain.py b/sagital_brain.py index 9c5ed19..b01ea4d 100644 --- a/sagital_brain.py +++ b/sagital_brain.py @@ -1,9 +1,8 @@ from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter - import numpy as np -def run_averages(file_input='brain_sample.csv', file_output='brain_average.csv'): +def run_averages(file_input="brain_sample.csv", file_output="brain_average.csv"): """ Calculates the average through the coronal planes The input file should has as many columns as coronal planes @@ -12,23 +11,34 @@ def run_averages(file_input='brain_sample.csv', file_output='brain_average.csv') The result is the average for each sagital/horizontal plane (rows) """ # Open the file to analyse - planes = np.loadtxt(file_input, dtype=int, delimiter=',') + planes = np.loadtxt(file_input, dtype=int, delimiter=",") # Calculates the averages through the sagital/horizontal planes # and makes it as a row vector - averages = planes.mean(axis=0)[np.newaxis, :] + averages = planes.mean(axis=1)[np.newaxis, :] # write it out on my file - np.savetxt(file_output, averages, fmt='%.1f', delimiter=',') - - -if __name__ == "__main__": - parser = ArgumentParser(description="Calculates the average for each sagital-horizontal plane.", - formatter_class=ArgumentDefaultsHelpFormatter) - parser.add_argument('file_input', nargs='?', default="brain_sample.csv", - help="Input CSV file with the results from scikit-brain binning algorithm.") - parser.add_argument('--file_output', '-o', default="brain_average.csv", - help="Name of the output CSV file.") - arguments = parser.parse_args() - - run_averages(arguments.file_input, arguments.file_output) + np.savetxt(file_output, averages, fmt="%.1f", delimiter=",") + + +# Comment this part out because we already make a command.py file with a process() function +# if __name__ == "__main__": +# parser = ArgumentParser( +# description="Calculates the average for each sagital-horizontal plane.", +# formatter_class=ArgumentDefaultsHelpFormatter, +# ) +# parser.add_argument( +# "file_input", +# nargs="?", +# default="brain_sample.csv", +# help="Input CSV file with the results from scikit-brain binning algorithm.", +# ) +# parser.add_argument( +# "--file_output", +# "-o", +# default="brain_average.csv", +# help="Name of the output CSV file.", +# ) +# arguments = parser.parse_args() +# +# run_averages(arguments.file_input, arguments.file_output) diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..9a6be61 --- /dev/null +++ b/setup.py @@ -0,0 +1,10 @@ +from setuptools import setup +from setuptools import find_packages + +setup( + name="sagital_average", + version="0.0.1", + packages=find_packages(exclude=["*test"]), + install_requires=["numpy"], # The program needs numpy package + entry_points={"console_scripts": ["sagverage = sagital_average.command:process"]}, +) diff --git a/test_sagital_brain.py b/test_sagital_brain.py new file mode 100644 index 0000000..1d903ed --- /dev/null +++ b/test_sagital_brain.py @@ -0,0 +1,44 @@ +import numpy as np +import subprocess + +# Make new output file +data_input = np.zeros((20, 20)) +data_input[-1, :] = 1 +np.savetxt("brain_sample.csv", data_input, fmt="%d", delimiter=",") + +# Run sagital brain file to generate output +subprocess.run( + ["python", "sagital_brain.py"], check=True +) # Add 'check' to be abe to run a file and see the result + +# Load output +loaded = np.loadtxt("brain_average.csv", delimiter=",") + +# Expected output +expected = np.array( + [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + ] +) + +# Check the values are identical +np.testing.assert_array_equal(loaded, expected) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/brain_average.csv b/tests/brain_average.csv new file mode 100644 index 0000000..af02e18 --- /dev/null +++ b/tests/brain_average.csv @@ -0,0 +1 @@ +0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0 diff --git a/tests/brain_sample.csv b/tests/brain_sample.csv new file mode 100644 index 0000000..d0b9035 --- /dev/null +++ b/tests/brain_sample.csv @@ -0,0 +1,20 @@ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 diff --git a/tests/test_sagital_brain.py b/tests/test_sagital_brain.py new file mode 100644 index 0000000..87096ab --- /dev/null +++ b/tests/test_sagital_brain.py @@ -0,0 +1,23 @@ +import numpy as np +from pathlib import Path +from .sagital_brain import run_averages + +Test_Directory = Path(__file__).parent # Tell the program where the test directory is + +def test_average(): + # Creates input file + data_input = np.zeros((20, 20)) + data_input[-1, :] = 1 + + # The expeted result is all zeros, except the last one, it should be 1 + expected = np.zeros(20) + expected[-1] = 1 + + np.savetxt(Test_Directory/"brain_sample.csv", data_input, fmt='%d', delimiter=',') + + # run python program + run_averages(file_input=Test_Directory/ "brain_sample.csv", file_output=Test_Directory/ "brain_average.csv") + + # Check result + result = np.loadtxt(Test_Directory/"brain_average.csv", delimiter=',') + np.testing.assert_array_equal(result, expected) \ No newline at end of file