Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non descriptive variable name and Duplicate function fixes #19

Open
wants to merge 36 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
075e95b
Add implementation of standard deviation on data
thomaskileyukaea Jul 27, 2023
57e4883
Initial commit of requirements.txt. Ignoring virtual env folder
Oct 7, 2024
887cad5
Spelling fix
Oct 7, 2024
5c6e952
Format models.py with Ruff
Oct 7, 2024
91803d2
Remove unnecessary import
Oct 8, 2024
0523ebc
Fix formatting in inflammation-analysis.py
Oct 8, 2024
bc2da9d
More formatting fixes
Oct 8, 2024
bb7280b
Add docstrings to functions
Oct 8, 2024
d731645
Further docstring improvements
Oct 8, 2024
a17e382
Add unit tests for min and max functions
Oct 8, 2024
aca1410
Parameterise mean, min, max model tests
Oct 8, 2024
e7b7e40
Add coverage support
Oct 8, 2024
3dd1f7a
Add Github actions configuration
Oct 8, 2024
15728c9
Use python 3.10 in CI tests
Oct 8, 2024
8d7eb9a
Remove actionlib version number from requirements
Oct 8, 2024
5b166d6
Remove unnecessary requirements
Oct 8, 2024
c061329
Add OS matrix for pythons 3.8-3.10 over multiple platforms
Oct 8, 2024
1679103
Update CI file again
Oct 8, 2024
06b4116
Fix CI file again
Oct 8, 2024
959705c
Add patient_normalise function with test
Oct 21, 2024
105c58b
Update gitignore
Oct 28, 2024
4473808
Update s_dev function name to standard_deviation
Oct 28, 2024
7fc0602
Add another test for standard_deviation function
Oct 28, 2024
ca77d9a
Merge branch 'main' into feature-std-dev
mjjq Oct 28, 2024
2cf6f05
Merge pull request #1 from mjjq/feature-std-dev
mjjq Oct 28, 2024
783da51
Change README
Oct 28, 2024
fb6cea0
Merge branch 'feature-std-dev' of github.com:mjjq/python-intermediate…
Oct 28, 2024
cdd9aa4
Update standard deviation to pass test
Oct 28, 2024
9aad484
Add patient class to pass test2
Oct 28, 2024
f51dd1e
Merge branch 'feature-std-dev' into main
Oct 28, 2024
961c16a
Merge branch 'feature-std-dev' into develop
Oct 28, 2024
5525083
Fix test models
Oct 28, 2024
2a3e1f8
Merge branch 'develop' into main
Oct 28, 2024
9b05e0a
Add poetry files
Oct 29, 2024
c65ce06
Merge branch 'develop' into main
Oct 29, 2024
364be8f
Closes #5 and #9
JBello1610 Oct 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: CI

# We can specify which Github events will trigger a CI build
on: push

# now define a single job 'build' (but could define more)
jobs:

build:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.8", "3.9", "3.10"]

runs-on: ${{ matrix.os }}

# a job is a seq of steps
steps:

# Next we need to checkout out repository, and set up Python
# A 'name' is just an optional label shown in the log - helpful to clarify progress - and can be anything
- name: Checkout repository
uses: actions/checkout@v2

- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

- name: Install Python dependencies
run: |
python3 -m pip install --upgrade pip
pip3 install -r requirements.txt

- name: Test with PyTest
run: |
python -m pytest --cov=inflammation.models tests/test_models.py
...
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@
*.pyc
*.egg-info
.pytest_cache


# Virtual environments
venv/
.venv/
65 changes: 55 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,62 @@
# Introduction
# Inflam
![Continuous Integration build in GitHub Actions](https://github.com/mjjq/python-intermediate-inflammation/workflows/CI/badge.svg?branch=main)

This is a template software project repository used by the [Intermediate Research Software Development Skills In Python](https://github.com/carpentries-incubator/python-intermediate-development).
Inflam is a data management system written in Python that manages trial data used in clinical inflammation studies.

## Purpose
## Main features
Here are some key features of Inflam:

This repository is intended to be used as a code template which is copied by learners at [Intermediate Research Software Development Skills In Python](https://github.com/carpentries-incubator/python-intermediate-development) course.
This can be done using the `Use this template` button towards the top right of this repo's GitHub page.
- Provide basic statistical analyses over clinical trial data
- Ability to work on trial data in Comma-Separated Value (CSV) format
- Generate plots of trial data
- Analytical functions and views can be easily extended based on its Model-View-Controller architecture

This software project is not finished, is currently failing to run and contains some code style issues. It is used as a starting point for the course - issues will be fixed and code will be added in a number of places during the course by learners in their own copies of the repository, as course topics are introduced.
## Prerequisites
Inflam requires the following Python packages:

## Tests
- [NumPy](https://www.numpy.org/) - makes use of NumPy's statistical functions
- [Matplotlib](https://matplotlib.org/stable/index.html) - uses Matplotlib to generate statistical plots

Several tests have been implemented already, some of which are currently failing.
These failing tests set out the requirements for the additional code to be implemented during the workshop.
The following optional packages are required to run Inflam's unit tests:

The tests should be run using `pytest`, which will be introduced during the workshop.
- [pytest](https://docs.pytest.org/en/stable/) - Inflam's unit tests are written using pytest
- [pytest-cov](https://pypi.org/project/pytest-cov/) - Adds test coverage stats to unit testing

## Installation
Clone the repository

Install requirements (preferably in a virtual environment)

```
python -m pip3 install requirements.txt
```

## Usage
Open a terminal to the root of the repository on your local machine. Run the code with

```
python3 inflammation-analysis.py <path-to-your-data>
```

## License
MIT License

Copyright (c) 2024 Marcus Quantrill

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
24 changes: 16 additions & 8 deletions inflammation-analysis.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#!/usr/bin/env python3
"""Software for managing and analysing patients' inflammation data in our imaginary hospital."""
"""
Software for managing and analysing patients'
inflammation data in our imaginary hospital.
"""

import argparse
import os
Expand All @@ -19,28 +22,33 @@ def main(args):
if not isinstance(InFiles, list):
InFiles = [args.infiles]


if args.full_data_analysis:
analyse_data(os.path.dirname(InFiles[0]))
return

for filename in InFiles:
inflammation_data = models.load_csv(filename)

view_data = {'average': models.daily_mean(inflammation_data), 'max': models.daily_max(inflammation_data), 'min': models.daily_min(inflammation_data)}
view_data = {'average': models.daily_mean(inflammation_data), 'max': models.daily_max(inflammation_data), 'min': models.daily_min(inflammation_data), **(models.standard_deviation(inflammation_data))}


views.visualize(view_data)


if __name__ == "__main__":
parser = argparse.ArgumentParser(
description='A basic patient inflammation data management system')
description="A basic patient inflammation data management system"
)

parser.add_argument(
'infiles',
nargs='+',
help='Input CSV(s) containing inflammation series for each patient')
"infiles",
nargs="+",
help="Input CSV(s) containing inflammation series for each patient",
)

parser.add_argument('--full-data-analysis', action='store_true', dest='full_data_analysis')
parser.add_argument(
"--full-data-analysis", action="store_true", dest="full_data_analysis"
)

args = parser.parse_args()

Expand Down
86 changes: 77 additions & 9 deletions inflammation/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,27 @@

The Model layer is responsible for the 'business logic' part of the software.

Patients' data is held in an inflammation table (2D array) where each row contains
inflammation data for a single patient taken over a number of days
Patients' data is held in an inflammation table (2D array) where each row contains
inflammation data for a single patient taken over a number of days
and each column represents a single day across all patients.
"""

import json
import numpy as np


class Patient:
def __init__(self, name):
self.name = name


def load_csv(filename):
"""Load a Numpy array from a CSV

:param filename: Filename of CSV to load
"""
return np.loadtxt(fname=filename, delimiter=',')
return np.loadtxt(fname=filename, delimiter=",")


def load_json(filename):
"""Load a numpy array from a JSON document.
Expand All @@ -34,23 +40,85 @@ def load_json(filename):
:param filename: Filename of CSV to load

"""
with open(filename, 'r', encoding='utf-8') as file:
with open(filename, "r", encoding="utf-8") as file:
data_as_json = json.load(file)
return [np.array(entry['observations']) for entry in data_as_json]

return [np.array(entry["observations"]) for entry in data_as_json]


def daily_mean(data):
"""Calculate the daily mean of a 2d inflammation data array."""
"""Calculate the daily mean of a 2D inflammation data array.

:param data: 2D array of values to perform mean
:returns: 1D array of values contains means along first axis of data
"""
return np.mean(data, axis=0)


def daily_max(data):
"""Calculate the daily max of a 2d inflammation data array."""
"""Calculate the daily max of a 2D inflammation data array.

:param data: Array of values to perform max
:returns: 1D array of values contains maxes along first axis of data
"""
return np.max(data, axis=0)


def daily_min(data):
"""Calculate the daily min of a 2d inflammation data array."""
"""Calculate the daily min of a 2D inflammation data array.

:param data: Array of values to perform min
:returns: 1D array of values contains mins along first axis of data
"""
return np.min(data, axis=0)


def patient_normalise(data):
"""
Normalise patient data from a 2D inflammation data array.

NaN values are ignored, and normalised to 0.

Negative values are rounded to 0.

:param data: 2D array to be normalised
:returns: Normalised 2D array
"""
maxima = np.nanmax(data, axis=1)
with np.errstate(invalid="ignore", divide="ignore"):
normalised = data / maxima[:, np.newaxis]
normalised[np.isnan(normalised)] = 0
normalised[normalised < 0] = 0
return normalised


def standard_deviation(data):
"""Computes and returns standard deviation for data."""
if len(data) == 0:
return {"standard deviation": 0.0}

mmm = np.mean(data, axis=0)
devs = []
for entry in data:
devs.append((entry - mmm) * (entry - mmm))

standard_dev = sum(devs) / len(data)
return {"standard deviation": np.sqrt(standard_dev)}


def patient_normalise(data):
"""
Normalise patient data from a 2D inflammation data array.

NaN values are ignored, and normalised to 0.

Negative values are rounded to 0.

:param data: 2D array to be normalised
:returns: Normalised 2D array
"""
maxima = np.nanmax(data, axis=1)
with np.errstate(invalid="ignore", divide="ignore"):
normalised = data / maxima[:, np.newaxis]
normalised[np.isnan(normalised)] = 0
normalised[normalised < 0] = 0
return normalised
1 change: 0 additions & 1 deletion inflammation/views.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""Module containing code for plotting inflammation data."""

from matplotlib import pyplot as plt
import numpy as np


def visualize(data_dict):
Expand Down
Loading