Skip to content

Commit

Permalink
Fixing documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
gviejo committed Nov 7, 2023
1 parent 941e918 commit c6dd574
Show file tree
Hide file tree
Showing 8 changed files with 304 additions and 17 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.nwb
*.pickle
*.py.md5
*.npz
Expand Down
7 changes: 6 additions & 1 deletion docs/examples/tutorial_HD_dataset.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# coding: utf-8
"""
Peyrache et al (2015) Dataset Tutorial
Peyrache et al (2015) Tutorial
============
This tutorial demonstrates how we use Pynapple to generate Figure 4a in the [publication](https://elifesciences.org/reviewed-preprints/85786).
Expand All @@ -27,9 +27,14 @@
import pynapple as nap
import scipy.ndimage
import matplotlib.pyplot as plt
import seaborn as sns
import requests, math, os
import tqdm

custom_params = {"axes.spines.right": False, "axes.spines.top": False}
sns.set_theme(style="ticks", palette="colorblind", font_scale=1.5, rc=custom_params)


# %%
# ***
# Downloading the data
Expand Down
139 changes: 139 additions & 0 deletions docs/examples/tutorial_calcium_imaging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# -*- coding: utf-8 -*-
"""
Calcium Imaging
============
Working with calcium data.
For the example dataset, we will be working with a recording of a freely-moving mouse imaged with a Miniscope (1-photon imaging). The area recorded for this experiment is the postsubiculum - a region that is known to contain head-direction cells, or cells that fire when the animal's head is pointing in a specific direction.
The NWB file for the example is hosted on [OSF](https://osf.io/sbnaw). We show below how to stream it.
See the [documentation](https://pynapple-org.github.io/pynapple/) of Pynapple for instructions on installing the package.
This tutorial was made by Sofia Skromne Carrasco and Guillaume Viejo.
"""
# %%
# !!! warning
# This tutorial uses seaborn and matplotlib for displaying the figure
#
# You can install all with `pip install matplotlib seaborn tqdm`
#
# mkdocs_gallery_thumbnail_number = 1
#
# Now, import the necessary libraries:

import numpy as pd
import pynapple as nap
import matplotlib.pyplot as plt
import seaborn as sns
import sys, os
import requests, math
import tqdm

custom_params = {"axes.spines.right": False, "axes.spines.top": False}
sns.set_theme(style="ticks", palette="colorblind", font_scale=1.5, rc=custom_params)


# %%
# ***
# Downloading the data
# ------------------
# First things first: Let's find our file
path = "A0670-221213.nwb"
if path not in os.listdir("."):
r = requests.get(f"https://osf.io/sbnaw/download", stream=True)
block_size = 1024*1024
with open(path, 'wb') as f:
for data in tqdm.tqdm(r.iter_content(block_size), unit='MB', unit_scale=True,
total=math.ceil(int(r.headers.get('content-length', 0))//block_size)):
f.write(data)

# %%
# ***
# Parsing the data
# ------------------
# Now that we have the file, let's load the data
data = nap.load_file(path)
print(data)

# %%
# Let's save the RoiResponseSeries as a variable called 'transients' and print it
transients = data['RoiResponseSeries']
print(transients)

# %%
# ***
# Plotting the activity of one neuron
# -----------------------------------
# Our transients are saved as a (35757, 65) TsdFrame. Looking at the printed object, you can see that we have 35757 data points for each of our 65 regions of interest. We want to see which of these are head-direction cells, so we need to plot a tuning curve of fluorescence vs head-direction of the animal.
#

plt.figure(figsize=(6, 2))
plt.plot(transients[0:2000,0], linewidth=5)
plt.xlabel("Time (s)")
plt.ylabel("Fluorescence")
plt.show()


# %%
# Here we extract the head-direction as a variable called angle

angle = data['ry']
print(angle)

# %%
#As you can see, we have a longer recording for our tracking of the animal's head than we do for our calcium imaging - something to keep in mind.

print(transients.time_support)
print(angle.time_support)

# %%
# ***
# Calcium tuning curves
# ---------------------
# Here we compute the tuning curves of all the neurons

tcurves = nap.compute_1d_tuning_curves_continous(transients, angle, nb_bins = 120)

print(tcurves)

# %%
# We now have a DataFrame, where our index is the angle of the animal's head in radians, and each column represents the tuning curve of each region of interest. We can plot one neuron.

plt.figure()
plt.plot(tcurves[4])
plt.xlabel("Angle")
plt.ylabel("Fluorescence")
plt.show()

# %%
# It looks like this could be a head-direction cell. One important property of head-directions cells however, is that their firing with respect to head-direction is stable. To check for their stability, we can split our recording in two and compute a tuning curve for each half of the recording.
#
# We start by finding the midpoint of the recording, using the function `get_intervals_center`. Using this, then create one new IntervalSet with two rows, one for each half of the recording.

center = transients.time_support.get_intervals_center()

halves = nap.IntervalSet(
start = [transients.time_support.start[0], center.t[0]],
end = [center.t[0], transients.time_support.end[0]]
)

# %%
# Now we can compute the tuning curves for each half of the recording and plot the tuning curves for the fifth region of interest.

half1 = nap.compute_1d_tuning_curves_continous(transients, angle, nb_bins = 120, ep = halves.loc[[0]])
half2 = nap.compute_1d_tuning_curves_continous(transients, angle, nb_bins = 120, ep = halves.loc[[1]])

plt.figure(figsize=(12, 5))
plt.subplot(1,2,1)
plt.plot(half1[4])
plt.title("First half")
plt.xlabel("Angle")
plt.ylabel("Fluorescence")
plt.subplot(1,2,2)
plt.plot(half2[4])
plt.title("Second half")
plt.show()

6 changes: 4 additions & 2 deletions docs/gallery_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
# @Author: Guillaume Viejo
# @Date: 2023-08-04 11:37:58
# @Last Modified by: Guillaume Viejo
# @Last Modified time: 2023-08-04 11:40:07
# @Last Modified time: 2023-11-07 14:23:36
from mkdocs_gallery.gen_gallery import DefaultResetArgv

conf = {
"filename_pattern": "/tutorial"
"reset_argv": DefaultResetArgv(),
"filename_pattern": "/tutorial",
}
25 changes: 19 additions & 6 deletions docs/gen_ref_pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@
"""

from pathlib import Path

import sys
import mkdocs_gen_files
nav = mkdocs_gen_files.Nav()



for path in sorted(Path("pynapple").rglob("*.py")):
print(path)
module_path = path.relative_to("pynapple").with_suffix("")
doc_path = path.relative_to("pynapple").with_suffix(".md")
full_doc_path = Path("reference", doc_path)
Expand All @@ -24,13 +23,27 @@
elif parts[-1] == "__main__":
continue


# parts = tuple(["pynapple"] + list(parts))
if len(parts):
nav[parts] = doc_path.as_posix()
# if the md file name is `module.md`, generate documentation from docstrings
if full_doc_path.name != 'index.md':
# sys.exit()
with mkdocs_gen_files.open(full_doc_path, "w") as fd:
ident = "pynapple." + ".".join(parts)
ident = "pynapple."+".".join(parts)
fd.write(f"::: {ident}")
# if the md file name is `index.md`, add the list of modules with hyperlinks
else:
this_module_path = Path("pynapple") / path.parent.name
module_index = ""
for module_scripts in sorted(this_module_path.rglob("*.py")):
if "__init__" in module_scripts.name:
continue
module_index += f"* [{module_scripts.name.replace('.py', '')}]" \
f"({module_scripts.name.replace('.py', '.md')})\n"

with mkdocs_gen_files.open(full_doc_path, "w") as fd:
fd.write(module_index)


mkdocs_gen_files.set_edit_path(full_doc_path, path)

Expand Down
8 changes: 0 additions & 8 deletions docs/pynacollada.md

This file was deleted.

78 changes: 78 additions & 0 deletions tests/test_abstract_tsd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# -*- coding: utf-8 -*-
# @Author: Guillaume Viejo
# @Date: 2023-09-25 11:53:30
# @Last Modified by: Guillaume Viejo
# @Last Modified time: 2023-09-25 12:37:45

import pynapple as nap
import numpy as np
import pandas as pd
import pytest
from pynapple.core.time_series import _AbstractTsd


class MyClass(_AbstractTsd):

def __getitem__(self, key):
return key

def __setitem__(self, key, value):
pass

def __getitem__(self, key):
return key

def __str__(self):
return "In str"

def __repr__(self):
return "In repr"


def test_create_atsd():
a = MyClass()

assert hasattr(a, "rate")
assert hasattr(a, "index")
assert hasattr(a, "values")
assert hasattr(a, "time_support")

assert a.rate is np.NaN
assert isinstance(a.index, nap.TsIndex)
assert isinstance(a.values, np.ndarray)
assert isinstance(a.time_support, nap.IntervalSet)

assert hasattr(a, "t")
assert hasattr(a, "d")
assert hasattr(a, "start")
assert hasattr(a, "end")
assert hasattr(a, "__array__")
np.testing.assert_array_equal(a.values, np.empty(0))
np.testing.assert_array_equal(a.__array__(), np.empty(0))

assert len(a) == 0

assert a.__repr__() == "In repr"
assert a.__str__() == "In str"

assert hasattr(a, "__getitem__")
assert hasattr(a, "__setitem__")
assert a[0] == 0


def test_methods():
a = MyClass()

np.testing.assert_array_equal(a.times(), np.empty(0))
np.testing.assert_array_equal(a.as_array(), np.empty(0))
np.testing.assert_array_equal(a.data(), np.empty(0))
np.testing.assert_array_equal(a.to_numpy(), np.empty(0))

assert a.start_time() is None
assert a.end_time() is None

assert hasattr(a, "value_from")
assert hasattr(a, "count")
assert hasattr(a, "restrict")


57 changes: 57 additions & 0 deletions tests/test_time_index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
# @Author: gviejo
# @Date: 2022-11-30 09:29:21
# @Last Modified by: Guillaume Viejo
# @Last Modified time: 2023-09-25 11:28:01
"""Tests of time units for `pynapple` package."""

import pynapple as nap
import numpy as np
import pandas as pd
import pytest
# from pynapple.core.time_units import format_timestamps, return_timestamps, sort_timestamps
# from pynapple.core.time_index import TsIndex
import warnings

def test_format_timestamps():
t = np.random.rand(100)

np.testing.assert_array_almost_equal(t, nap.TsIndex.format_timestamps(t))
np.testing.assert_array_almost_equal(t/1e3, nap.TsIndex.format_timestamps(t, 'ms'))
np.testing.assert_array_almost_equal(t/1e6, nap.TsIndex.format_timestamps(t, 'us'))

with pytest.raises(ValueError, match=r"unrecognized time units type"):
nap.TsIndex.format_timestamps(t, 'aaaa')

def test_return_timestamps():
t = np.random.rand(100)

np.testing.assert_array_almost_equal(t, nap.TsIndex.return_timestamps(t))
np.testing.assert_array_almost_equal(t/1e3, nap.TsIndex.return_timestamps(t, 'ms'))
np.testing.assert_array_almost_equal(t/1e6, nap.TsIndex.return_timestamps(t, 'us'))

with pytest.raises(ValueError, match=r"unrecognized time units type"):
nap.TsIndex.return_timestamps(t, units='aaaa')

def test_return_timestamps():
t = np.random.rand(100)

np.testing.assert_array_almost_equal(np.sort(t), nap.TsIndex.sort_timestamps(t, False))

with warnings.catch_warnings(record=True) as w:
nap.TsIndex.sort_timestamps(t, True)
assert str(w[0].message) == "timestamps are not sorted"


def test_TsIndex():
a = nap.TsIndex(np.arange(10))
np.testing.assert_array_equal(a, np.arange(10))
np.testing.assert_array_equal(a.values, np.arange(10))
np.testing.assert_array_equal(a.to_numpy(), np.arange(10))

np.testing.assert_array_equal(a.in_units("s"), np.arange(10))
np.testing.assert_array_equal(a.in_units("ms"), np.arange(10)*1e3)
np.testing.assert_array_equal(a.in_units("us"), np.arange(10)*1e6)

with pytest.raises(RuntimeError, match=r"TsIndex object is not mutable."):
a[0] = 1

0 comments on commit c6dd574

Please sign in to comment.