Skip to content

Commit 903bb23

Browse files
authored
Merge pull request #2 from CINPLA/modernize
Modernize and release
2 parents 3109909 + ebb2c57 commit 903bb23

File tree

10 files changed

+194
-71
lines changed

10 files changed

+194
-71
lines changed

Diff for: .github/workflows/full_tests.yml

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Test on Ubuntu
2+
3+
on:
4+
pull_request:
5+
branches: [dev]
6+
types: [synchronize, opened, reopened]
7+
8+
9+
jobs:
10+
build-and-test:
11+
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- uses: actions/checkout@v3
16+
- name: Set up Python
17+
uses: actions/setup-python@v4
18+
with:
19+
python-version: "3.10"
20+
- name: Install package
21+
run: |
22+
python -m pip install --upgrade pip
23+
pip install .[test]
24+
- name: Pytest
25+
run: |
26+
pytest -v

Diff for: .github/workflows/publish-to-pypi.yml

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Release to PyPI
2+
3+
on:
4+
push:
5+
tags:
6+
- '*'
7+
jobs:
8+
release:
9+
runs-on: ubuntu-latest
10+
11+
steps:
12+
- uses: actions/checkout@v2
13+
- name: Set up Python 3.10
14+
uses: actions/setup-python@v4
15+
with:
16+
python-version: "3.10"
17+
- name: Install Tools
18+
run: |
19+
python -m pip install --upgrade pip
20+
pip install setuptools wheel twine build
21+
- name: Package and Upload
22+
env:
23+
STACKMANAGER_VERSION: ${{ github.event.release.tag_name }}
24+
TWINE_USERNAME: __token__
25+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
26+
run: |
27+
python -m build --sdist --wheel
28+
twine upload dist/*

Diff for: .pre-commit-config.yaml

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
repos:
2+
- repo: https://github.com/pre-commit/pre-commit-hooks
3+
rev: v4.5.0
4+
hooks:
5+
- id: fix-encoding-pragma
6+
exclude: tests/test_data
7+
- id: trailing-whitespace
8+
- id: end-of-file-fixer
9+
- id: check-docstring-first
10+
- id: debug-statements
11+
- id: check-toml
12+
- id: check-yaml
13+
- id: requirements-txt-fixer
14+
- id: detect-private-key
15+
- id: check-merge-conflict
16+
17+
- repo: https://github.com/psf/black
18+
rev: 24.4.2
19+
hooks:
20+
- id: black
21+
- id: black-jupyter
22+
23+
- repo: https://github.com/pycqa/isort
24+
rev: 5.13.2
25+
hooks:
26+
- id: isort
27+
args: ["--profile", "black"]
28+
29+
- repo: https://github.com/astral-sh/ruff-pre-commit
30+
rev: v0.4.4
31+
hooks:
32+
- id: ruff

Diff for: head_direction/__init__.py

-1
This file was deleted.

Diff for: pyproject.toml

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
[project]
2+
name = "head_direction"
3+
version = "0.1.0"
4+
authors = [
5+
{ name = "Mikkel Lepperod", email = "[email protected]" },
6+
{ name = "Alessio Buccino", email = "[email protected]" },
7+
]
8+
9+
description = "Analysis of head direction cells."
10+
readme = "README.md"
11+
requires-python = ">=3.10"
12+
classifiers = [
13+
"Programming Language :: Python :: 3",
14+
"License :: OSI Approved :: MIT License",
15+
"Operating System :: OS Independent",
16+
]
17+
18+
dependencies = [
19+
"numpy<2",
20+
"scipy",
21+
"astropy",
22+
"pycircstat",
23+
"pandas",
24+
"elephant",
25+
"matplotlib",
26+
"nose"
27+
]
28+
29+
[project.urls]
30+
homepage = "https://github.com/CINPLA/head-directopm"
31+
repository = "https://github.com/CINPLA/head-direction"
32+
33+
[build-system]
34+
requires = ["setuptools>=62.0"]
35+
build-backend = "setuptools.build_meta"
36+
37+
[tool.setuptools]
38+
include-package-data = true
39+
40+
[tool.setuptools.packages.find]
41+
where = ["src"]
42+
include = ["head_direction*"]
43+
namespaces = false
44+
45+
[project.optional-dependencies]
46+
dev = ["pre-commit", "black[jupyter]", "isort", "ruff"]
47+
test = ["pytest", "pytest-cov", "pytest-dependency", "mountainsort5"]
48+
docs = ["sphinx-gallery", "sphinx_rtd_theme"]
49+
full = [
50+
"head_direction[dev]",
51+
"head_direction[test]",
52+
"head_direction[docs]",
53+
]
54+
55+
[tool.coverage.run]
56+
omit = ["tests/*"]
57+
58+
[tool.black]
59+
line-length = 120

Diff for: setup.py

+3-28
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,6 @@
11
# -*- coding: utf-8 -*-
2-
from setuptools import setup
3-
import os
42

5-
from setuptools import setup, find_packages
3+
import setuptools
64

7-
long_description = open("README.md").read()
8-
9-
install_requires = [
10-
'numpy>=1.9',
11-
'scipy',
12-
'astropy',
13-
'pandas>=0.14.1',
14-
'elephant',
15-
'matplotlib']
16-
extras_require = {
17-
'testing': ['pytest'],
18-
'docs': ['numpydoc>=0.5',
19-
'sphinx>=1.2.2',
20-
'sphinx_rtd_theme']
21-
}
22-
23-
setup(
24-
name="head_direction",
25-
install_requires=install_requires,
26-
tests_require=install_requires,
27-
extras_require=extras_require,
28-
packages=find_packages(),
29-
include_package_data=True,
30-
version='0.1',
31-
)
5+
if __name__ == "__main__":
6+
setuptools.setup()

Diff for: src/head_direction/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# -*- coding: utf-8 -*-
2+
from .head import head_direction as head_direction
3+
from .head import head_direction_rate as head_direction_rate
4+
from .head import head_direction_score as head_direction_score

Diff for: head_direction/head.py renamed to src/head_direction/head.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
# -*- coding: utf-8 -*-
12
import numpy as np
3+
24
from .tools import moving_average
35

46

5-
def head_direction_rate(spike_train, head_angles, t,
6-
n_bins=36, avg_window=4):
7+
def head_direction_rate(spike_train, head_angles, t, n_bins=36, avg_window=4):
78
"""
89
Calculeate firing rate at head direction in binned head angles for time t.
910
Moving average filter is applied on firing rate
@@ -37,7 +38,7 @@ def head_direction_rate(spike_train, head_angles, t,
3738
spikes_in_ang, _ = np.histogram(head_angles, weights=spikes_in_bin, bins=ang_bins)
3839
time_in_ang, _ = np.histogram(head_angles, weights=time_in_bin, bins=ang_bins)
3940

40-
with np.errstate(divide='ignore', invalid='ignore'):
41+
with np.errstate(divide="ignore", invalid="ignore"):
4142
rate_in_ang = np.divide(spikes_in_ang, time_in_ang)
4243
rate_in_ang = moving_average(rate_in_ang, avg_window)
4344
return ang_bins[:-1], rate_in_ang
@@ -59,8 +60,8 @@ def head_direction_score(head_angle_bins, rate):
5960
out : float, float
6061
mean angle, mean vector length
6162
"""
62-
import math
6363
import pycircstat as pc
64+
6465
nanIndices = np.where(np.isnan(rate))
6566
head_angle_bins = np.delete(head_angle_bins, nanIndices)
6667
mean_ang = pc.mean(head_angle_bins, w=rate)
@@ -69,7 +70,7 @@ def head_direction_score(head_angle_bins, rate):
6970
return mean_ang, mean_vec_len
7071

7172

72-
def head_direction(x1, y1, x2, y2, t, filt=2.):
73+
def head_direction(x1, y1, x2, y2, t, filt=2.0):
7374
"""
7475
Calculeate head direction in angles or radians for time t
7576
@@ -98,7 +99,7 @@ def head_direction(x1, y1, x2, y2, t, filt=2.):
9899
r = np.linalg.norm(dr, axis=0)
99100
r_mean = np.mean(r)
100101
r_std = np.std(r)
101-
mask = (r > r_mean - filt*r_std)
102+
mask = r > r_mean - filt * r_std
102103
x1 = x1[mask]
103104
y1 = y1[mask]
104105
x2 = x2[mask]

Diff for: head_direction/tools.py renamed to src/head_direction/tools.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1+
# -*- coding: utf-8 -*-
12
import numpy as np
2-
import scipy.signal as sig
33

44

55
def unit_vector(v):
6-
""" Return unit vector of v
6+
"""Return unit vector of v
77
modified from David Wolever,
88
https://stackoverflow.com/questions/2827393/angles
99
-between-two-n-dimensional-vectors-in-python
@@ -47,11 +47,11 @@ def moving_average(vector, N):
4747
if N * 2 > len(vector):
4848
raise ValueError('Window must be at least half of "len(vector)"')
4949
vector = np.concatenate((vector[-N:], vector, vector[:N]))
50-
return np.convolve(vector, np.ones((N,)) / N, mode='same')[N:-N]
50+
return np.convolve(vector, np.ones((N,)) / N, mode="same")[N:-N]
5151

5252

5353
def angle_between_vectors(v1, v2):
54-
""" Returns the angle in radians between vectors 'v1' and 'v2'
54+
"""Returns the angle in radians between vectors 'v1' and 'v2'
5555
modified from David Wolever,
5656
https://stackoverflow.com/questions/2827393/angles
5757
-between-two-n-dimensional-vectors-in-python

Diff for: head_direction/tests/test_head.py renamed to tests/test_head.py

+31-32
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,74 @@
1-
import pytest
1+
# -*- coding: utf-8 -*-
22
import numpy as np
3-
import math
3+
4+
from head_direction.head import (
5+
head_direction,
6+
head_direction_rate,
7+
head_direction_score,
8+
)
9+
410

511
def test_head_direction_45():
6-
from head_direction.head import head_direction
7-
x1 = np.linspace(.01,1,10)
12+
x1 = np.linspace(0.01, 1, 10)
813
y1 = x1
9-
x2 = x1 + .01 # 1cm between
10-
y2 = x1 - .01
14+
x2 = x1 + 0.01 # 1cm between
15+
y2 = x1 - 0.01
1116
t = x1
1217
a, t = head_direction(x1, y1, x2, y2, t)
1318
assert np.allclose(a, np.pi / 4)
1419

1520

1621
def test_head_direction_135():
17-
from head_direction.head import head_direction
18-
x1 = np.linspace(.01,1,10)[::-1]
22+
x1 = np.linspace(0.01, 1, 10)[::-1]
1923
y1 = x1[::-1]
20-
x2 = x1 - .01 # 1cm between
21-
y2 = y1 - .01
24+
x2 = x1 - 0.01 # 1cm between
25+
y2 = y1 - 0.01
2226
t = x1
2327
a, t = head_direction(x1, y1, x2, y2, t)
2428
assert np.allclose(a, np.pi - np.pi / 4)
2529

2630

2731
def test_head_direction_225():
28-
from head_direction.head import head_direction
29-
x1 = np.linspace(.01,1,10)[::-1]
32+
x1 = np.linspace(0.01, 1, 10)[::-1]
3033
y1 = x1
31-
x2 = x1 - .01 # 1cm between
32-
y2 = y1 + .01
34+
x2 = x1 - 0.01 # 1cm between
35+
y2 = y1 + 0.01
3336
t = x1
3437
a, t = head_direction(x1, y1, x2, y2, t)
3538
assert np.allclose(a, np.pi + np.pi / 4)
3639

3740

3841
def test_head_direction_reverse_315():
39-
from head_direction.head import head_direction
40-
x1 = np.linspace(.01,1,10)
42+
x1 = np.linspace(0.01, 1, 10)
4143
y1 = x1[::-1]
42-
x2 = x1 + .01 # 1cm between
43-
y2 = y1 + .01
44+
x2 = x1 + 0.01 # 1cm between
45+
y2 = y1 + 0.01
4446
t = x1
4547
a, t = head_direction(x1, y1, x2, y2, t)
4648
assert np.allclose(a, 2 * np.pi - np.pi / 4)
4749

4850

4951
def test_head_rate():
50-
from head_direction.head import head_direction, head_direction_rate
51-
x1 = np.linspace(.01,1,10)
52+
x1 = np.linspace(0.01, 1, 10)
5253
y1 = x1
53-
x2 = x1 + .01 # 1cm between
54-
y2 = x1 - .01
55-
t = np.linspace(0,1,10)
54+
x2 = x1 + 0.01 # 1cm between
55+
y2 = x1 - 0.01
56+
t = np.linspace(0, 1, 10)
5657
a, t = head_direction(x1, y1, x2, y2, t)
57-
sptr = np.linspace(0,1,100)
58+
sptr = np.linspace(0, 1, 100)
5859
bins, rate = head_direction_rate(sptr, a, t, n_bins=8, avg_window=1)
5960
assert bins[1] == np.pi / 4
60-
assert abs(rate[1] - 100) < .5
61+
assert abs(rate[1] - 100) < 0.5
6162

6263

6364
def test_head_score():
64-
from head_direction.head import (
65-
head_direction, head_direction_rate, head_direction_score)
66-
x1 = np.linspace(.01,1,10)
65+
x1 = np.linspace(0.01, 1, 10)
6766
y1 = x1
68-
x2 = x1 + .01 # 1cm between
69-
y2 = x1 - .01
70-
t = np.linspace(0,1,10)
67+
x2 = x1 + 0.01 # 1cm between
68+
y2 = x1 - 0.01
69+
t = np.linspace(0, 1, 10)
7170
a, t = head_direction(x1, y1, x2, y2, t)
72-
sptr = np.linspace(0,1,100)
71+
sptr = np.linspace(0, 1, 100)
7372
bins, rate = head_direction_rate(sptr, a, t, n_bins=100, avg_window=2)
7473
ang, score = head_direction_score(bins, rate)
7574
assert abs(score - 1) < 0.001

0 commit comments

Comments
 (0)