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

Nsetm 2196 expand output #44

Open
wants to merge 66 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
cc9d4f8
pytest posargs
edasubert Jun 20, 2023
6e07720
extract densities adjustments
edasubert Jun 20, 2023
fb408c3
properly config tools
edasubert Jun 21, 2023
33ede88
lint
edasubert Jun 21, 2023
0687aa1
new probability_map
edasubert Jun 23, 2023
b34c563
use regions
edasubert Jun 29, 2023
11b9329
remove "with_region"
edasubert Jun 30, 2023
5322f24
revert test to be closer to original
edasubert Jun 30, 2023
dc75ed0
remove lamp5 synthesis
edasubert Jun 30, 2023
7ab4bdb
Merge branch 'NSETM-2196-update-create-from-probability-map' into NSE…
edasubert Jun 30, 2023
eef4444
add lamp5
edasubert Jun 30, 2023
bb8b4f0
Merge branch 'NSETM-2196-update-create-from-probability-map' into NSE…
edasubert Jun 30, 2023
71370d7
approx_lamp5
edasubert Jun 30, 2023
fcb8aec
switch to region map
edasubert Jul 4, 2023
7d93f22
fixes to probability map
edasubert Jul 10, 2023
32d0be5
add vip to probability map
edasubert Jul 11, 2023
f32b24a
lint
edasubert Jul 11, 2023
a597693
mypy issue
edasubert Jul 11, 2023
803b867
cleanup
edasubert Jul 12, 2023
a9ed2b7
restore a file for a test
edasubert Jul 12, 2023
46f8511
speed up density creation
mgeplf Jul 14, 2023
ad1d531
Stop using as many config files
mgeplf Jul 14, 2023
b00459e
logger
edasubert Jul 14, 2023
f385fc2
lint
edasubert Jul 14, 2023
e55aa22
try parallelizing w/ joblib.
mgeplf Jul 14, 2023
17b9cc5
speed up density creation (#37)
edasubert Jul 17, 2023
067bd19
Stop using as many config files (#38)
edasubert Jul 17, 2023
37b92f1
Merge branch 'NSETM-2196-update-create-from-probability-map' into NSE…
edasubert Jul 17, 2023
118c58b
logger
edasubert Jul 17, 2023
6b57eca
remove logger
edasubert Jul 17, 2023
aca3df1
Merge branch 'NSETM-2196-update-create-from-probability-map-probabili…
edasubert Jul 17, 2023
f04d50d
merge
edasubert Jul 17, 2023
7f14606
parallel
edasubert Jul 18, 2023
03e7c2f
requirements
edasubert Jul 18, 2023
fe4cb84
multiple probability maps
edasubert Jul 19, 2023
fb76b5d
undo shrink
edasubert Jul 19, 2023
107a767
no more 3.7
edasubert Jul 19, 2023
bacfa3f
no more 3.7 - second try
edasubert Jul 19, 2023
5d95653
no more 3.7 - 3rd try
edasubert Jul 19, 2023
b88fdb9
remove joblib name
edasubert Jul 19, 2023
0c1ece1
remove comment
edasubert Jul 19, 2023
53d8436
Merge branch 'NSETM-2196-update-create-from-probability-map-probabili…
edasubert Jul 19, 2023
924ec07
Merge branch 'parallel' into NSETM-2196-multiple-probability-maps
edasubert Jul 19, 2023
9c23b0c
clean tests
edasubert Jul 19, 2023
46ba45a
restore metadata
edasubert Jul 19, 2023
f7d5016
restore metadata
edasubert Jul 20, 2023
2dba865
Merge branch 'NSETM-2196-update-create-from-probability-map-probabili…
edasubert Jul 20, 2023
7cf957f
Merge branch 'parallel' into NSETM-2196-multiple-probability-maps
edasubert Jul 20, 2023
4ffd4bb
restore 3.8
edasubert Jul 20, 2023
f6c4add
metype, | separator
edasubert Jul 21, 2023
01963c1
save after
edasubert Jul 21, 2023
6a2c1fa
tests and logs
edasubert Jul 21, 2023
f2b2eb8
Merge remote-tracking branch 'origin/main' into NSETM-2196-update-output
edasubert Jul 24, 2023
2e191d2
Merge branch 'main' into NSETM-2196-update-output
edasubert Jul 24, 2023
78ad92c
docs
edasubert Jul 24, 2023
519eda3
revert utils
edasubert Jul 24, 2023
a8c48e7
test
edasubert Jul 24, 2023
5317eeb
:-|
edasubert Jul 24, 2023
da13ee8
synapse class
edasubert Jul 27, 2023
153906d
Merge branch 'main' into NSETM-2196-synapse-class
edasubert Jul 27, 2023
e58a5a9
lint
edasubert Jul 27, 2023
3791cd3
update timestamp
edasubert Jul 31, 2023
880321f
include synapse class in metadata
edasubert Jul 31, 2023
3cb73b8
remove all option
edasubert Jul 31, 2023
65f7baf
revert suffix to synapse_class
edasubert Jul 31, 2023
34f2197
expand output metadata
edasubert Aug 2, 2023
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
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ The molecular density of approx_lamp5 was calculated from the other molecular de

which approximates the molecular density of lamp5.

The command outputs the density files in the output-dir and a legend json file:
The command outputs the density files in the output-dir and a metadata json file:

.. code-block:: json

Expand Down
2,184 changes: 1,092 additions & 1,092 deletions atlas_densities/app/data/mtypes/probability_map/probability_map.csv

Large diffs are not rendered by default.

19 changes: 16 additions & 3 deletions atlas_densities/app/mtype_densities.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@
from atlas_densities.densities.mtype_densities_from_map.create import (
create_from_probability_map as create_from_map,
)
from atlas_densities.densities.mtype_densities_from_map.utils import check_probability_map_sanity
from atlas_densities.densities.mtype_densities_from_map.utils import (
SYNAPSE_CLASSES,
check_probability_map_sanity,
)
from atlas_densities.densities.mtype_densities_from_profiles import DensityProfileCollection
from atlas_densities.exceptions import AtlasDensitiesError

Expand Down Expand Up @@ -213,6 +216,12 @@ def _check_config_sanity(config: dict) -> None:
required=True,
help="Name and path to marker: ex: --marker pv path/pv.nrrd",
)
@click.option(
"--synapse-class",
type=click.Choice(list(SYNAPSE_CLASSES), case_sensitive=False),
required=True,
help="Target synapse class, the other will be skipped.",
)
@click.option(
"--output-dir",
required=True,
Expand All @@ -226,11 +235,12 @@ def _check_config_sanity(config: dict) -> None:
help="Number of jobs to run in parallel.",
)
@log_args(L)
def create_from_probability_map(
def create_from_probability_map( # pylint: disable=too-many-arguments
annotation_path,
hierarchy_path,
probability_map,
marker,
synapse_class,
output_dir,
n_jobs,
): # pylint: disable=too-many-locals
Expand All @@ -251,7 +261,9 @@ def create_from_probability_map(
for probability_map_path in probability_map:
L.info("Loading probability map %s", probability_map_path)
loaded_probability_map = pd.read_csv(probability_map_path)
loaded_probability_map.set_index(["region", "molecular_type"], inplace=True)
loaded_probability_map.set_index(
["region", "molecular_type", "synapse_class"], inplace=True
)
check_probability_map_sanity(loaded_probability_map)
probability_maps.append(loaded_probability_map)

Expand Down Expand Up @@ -279,6 +291,7 @@ def create_from_probability_map(
for (molecular_type, density) in molecular_type_densities.items()
},
probability_maps,
synapse_class,
output_dir,
n_jobs,
)
Expand Down
50 changes: 39 additions & 11 deletions atlas_densities/densities/mtype_densities_from_map/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
_check_probability_map_consistency,
_merge_probability_maps,
)
from atlas_densities.exceptions import AtlasDensitiesError

if TYPE_CHECKING: # pragma: no cover
import pandas as pd
Expand All @@ -33,11 +34,12 @@
L = logging.getLogger(__name__)


def create_from_probability_map(
def create_from_probability_map( # pylint: disable=too-many-arguments
annotation: "VoxelData",
region_map: "RegionMap",
molecular_type_densities: Dict[str, FloatArray],
probability_maps: List["pd.DataFrame"],
synapse_class: str,
output_dirpath: str,
n_jobs: int,
) -> None:
Expand All @@ -56,12 +58,13 @@ def create_from_probability_map(
and whose values are 3D float arrays holding the density fields of the cells of the
corresponding types (i.e., those cells reacting to the corresponding gene markers).
Example: {"pv": "pv.nrrd", "sst": "sst.nrd", "vip": "vip.nrrd", "gad67": "gad67.nrrd"}
probability_map:
data frame whose rows are labeled by regions and molecular types and whose columns are
labeled by metypes ('|' separated).
probability_maps: list of data frames whose rows are labeled by regions and molecular
types and whose columns are labeled by metypes ('|' separated).
synapse_class: synapse class to use for density calculation
output_dirpath: path of the directory where to save the volumetric density nrrd files.
It will be created if it doesn't exist already. It will contain a volumetric density
file of each metype appearing as column label of `probability_map`.
n_jobs: number of jobs to run in parallel

Raises:
AtlasBuildingTools error if
Expand All @@ -72,6 +75,20 @@ def create_from_probability_map(
# pylint: disable=too-many-locals
probability_map = _merge_probability_maps(probability_maps)

_check_probability_map_consistency(probability_map, set(molecular_type_densities.keys()))

# filter by synapse class
probability_map = probability_map[
probability_map.index.get_level_values("synapse_class") == synapse_class
]
if probability_map.empty:
raise AtlasDensitiesError(
f"Filtering probability map by requested synapse_class {synapse_class} "
"resulted in empty probability map."
)
probability_map.index = probability_map.index.droplevel("synapse_class")

# get info on regions
region_info = (
region_map.as_dataframe()
.reset_index()
Expand All @@ -81,13 +98,12 @@ def create_from_probability_map(
)
region_acronyms = set(region_info.region)

_check_probability_map_consistency(probability_map, set(molecular_type_densities.keys()))

region_masks = {
region_acronym: annotation.raw == region_id
for _, region_acronym, region_id in region_info.itertuples()
}

# ensure output directory exists
Path(output_dirpath).mkdir(exist_ok=True, parents=True)

def _create_densities_for_metype(metype: str) -> Optional[Tuple[str, str]]:
Expand All @@ -110,7 +126,7 @@ def _create_densities_for_metype(metype: str) -> Optional[Tuple[str, str]]:

if np.any(metype_density):
# save density file
metype_filename = f"{metype}_densities.nrrd"
metype_filename = f"{metype}_{synapse_class}_densities.nrrd"
filepath = str(Path(output_dirpath) / metype_filename)
annotation.with_data(metype_density).save_nrrd(filepath)

Expand All @@ -121,15 +137,27 @@ def _create_densities_for_metype(metype: str) -> Optional[Tuple[str, str]]:
returns = Parallel(n_jobs=n_jobs, return_as="generator")(
delayed(_create_densities_for_metype)(metype) for metype in probability_map.columns
)

# construct metadata
output_legend: Dict[str, Dict[str, str]] = defaultdict(dict)
for return_value in tqdm(returns, total=len(probability_map.columns)):
if return_value is not None:
metype, filepath = return_value
mtype, etype = metype.split(SEPARATOR)
output_legend[mtype][etype] = filepath

L.info("Saving output legend.")
output_legend_filename = "output_legend.json"
filepath = str(Path(output_dirpath) / output_legend_filename)
metadata = {
"synapse_class": synapse_class,
"metype2file": output_legend,
"file2metype": {
lecriste marked this conversation as resolved.
Show resolved Hide resolved
filename: (mtype, etype)
for mtype, etypes in output_legend.items()
for etype, filename in etypes.items()
},
}

L.info("Saving metadata.")
metadata_filename = "metadata.json"
filepath = str(Path(output_dirpath) / metadata_filename)
with open(filepath, "w", encoding="utf8") as file:
json.dump(output_legend, file, indent=4)
json.dump(metadata, file, indent=4)
12 changes: 11 additions & 1 deletion atlas_densities/densities/mtype_densities_from_map/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@

from atlas_densities.exceptions import AtlasDensitiesError

SYNAPSE_CLASS_INH = "INH"
SYNAPSE_CLASS_EXC = "EXC"
SYNAPSE_CLASSES = {SYNAPSE_CLASS_INH, SYNAPSE_CLASS_EXC}

L = logging.getLogger(__name__)


Expand All @@ -23,6 +27,7 @@ def check_probability_map_sanity(probability_map: "pd.DataFrame") -> None:
Raises:
AtlasDensitiesError if the sum of each row is not (close to) 1.0 or if `probability_map`
holds a negative value.
AtlasDensitiesError if the synapse_class is not just "INH" or "EXC".
"""
if not np.all(probability_map >= 0.0):
raise AtlasDensitiesError("The probability map has negative values.")
Expand All @@ -32,6 +37,11 @@ def check_probability_map_sanity(probability_map: "pd.DataFrame") -> None:
"The sum of each row is not 1.0. Consider renormalizing your input data frame with:\n"
"df = df.div(np.sum(df, axis=1), axis=0)"
)
if not {synapse_class for _, _, synapse_class in probability_map.index} <= SYNAPSE_CLASSES:
raise AtlasDensitiesError(
"The probability map has invalid value for synapse class."
f"Only {SYNAPSE_CLASSES} are allowed."
)


def _check_probability_map_consistency(
Expand All @@ -49,7 +59,7 @@ def _check_probability_map_consistency(
"""
check_probability_map_sanity(probability_map)

_, df_molecular_types = zip(*probability_map.index)
_, df_molecular_types, _ = zip(*probability_map.index)
molecular_types_diff = molecular_types - set(df_molecular_types)
if molecular_types_diff:
L.info(
Expand Down
15 changes: 10 additions & 5 deletions tests/app/test_mtype_densities.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os
from copy import deepcopy
from pathlib import Path
from unittest.mock import patch

import numpy as np
import numpy.testing as npt
Expand Down Expand Up @@ -117,6 +118,8 @@ def get_result_from_probablity_map_(runner):
"--marker",
"approx_lamp5",
"approx_lamp5.nrrd",
"--synapse-class",
"EXC",
"--output-dir",
"output_dir",
],
Expand Down Expand Up @@ -148,14 +151,16 @@ def test_output(self):
result = get_result_from_probablity_map_(runner)
assert result.exit_code == 0

BPbAC = VoxelData.load_nrrd(str(Path("output_dir") / "BP|bAC_densities.nrrd"))
BPbAC = VoxelData.load_nrrd(str(Path("output_dir") / "BP|bAC_EXC_densities.nrrd"))
assert BPbAC.raw.dtype == float
npt.assert_array_equal(BPbAC.voxel_dimensions, self.data["annotation"].voxel_dimensions)

with open(str(Path("output_dir") / "output_legend.json"), "r") as file:
output_legend = json.load(file)
assert "BP" in output_legend
assert "bAC" in output_legend["BP"]
with open(str(Path("output_dir") / "metadata.json"), "r") as file:
metadata = json.load(file)
assert "BP" in metadata["metype2file"]
assert "bAC" in metadata["metype2file"]["BP"]
assert metadata["file2metype"][metadata["metype2file"]["BP"]["bAC"]] == ["BP", "bAC"]
assert "EXC" == metadata["synapse_class"]

class Test_mtype_densities_from_composition:
@pytest.fixture(scope="session")
Expand Down
Loading