Skip to content

Commit

Permalink
Merge pull request #90 from PixelgenTechnologies/feature/exe-1354
Browse files Browse the repository at this point in the history
Feature/exe 1354
  • Loading branch information
ptajvar authored Feb 21, 2024
2 parents cb2304e + 1a5a015 commit dce9955
Show file tree
Hide file tree
Showing 23 changed files with 1,078 additions and 67 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
src/pixelator/report/webreport/template.html linguist-vendored
tests/snapshots/test_plot/*.png filter=lfs diff=lfs merge=lfs -text
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ coverage.xml
*.cover
.hypothesis/
.pytest_cache/
tests/reports_mpl/*

# Translations
*.mo
Expand Down Expand Up @@ -113,3 +114,4 @@ ENV/
pytest-results
# pixelator file
tests/data/ten_of_each.h5ad
.DS_Store
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

* Add QC plot showing UMIs per UPIA vs Tau.
* Add plot functions showing edge rank and cell counts.
* Add 2D and 3D graph plot functions.
* Add plot functions showing colocalization and differential colocalization.
* Performance improvements and reduced bundle size in QC report.
* Improved console output in verbose mode.
* Improved logging from multiprocessing jobs.
Expand Down
54 changes: 53 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ importlib-resources = "^5.12.0"
fsspec = "^2023.12.2"
fastparquet = "^2023.8.0"
graspologic = "^3.3.0"
plotly = "*"

[tool.poetry.group.dev.dependencies]
ruff = "*"
Expand All @@ -66,6 +67,7 @@ invoke = "*"
isort = "*"
pylint = "*"
pytest = "^7.0.0"
pytest-mpl = "^0.17.0"
sphinx = "*"
tox = "*"
tox-current-env = "^0.0.11"
Expand Down Expand Up @@ -98,7 +100,9 @@ markers = [
"integration_test: Marks a test as an integration test, which is often slow (deselect with '-m \"not integration_test\"')",
"workflow_test: Marks a test as a complete pixelator workflow, which is extremely slow (deselect with '-m \"not workflow_test\"')"]

addopts = ["-p no:pytest-workflow", "-m not workflow_test", "--benchmark-disable"]
mpl-results-path = "tests/reports_mpl"
mpl-generate-summary = "html"
addopts = ["-p no:pytest-workflow", "-m not workflow_test", "--benchmark-disable", "--mpl"]
filterwarnings = ["ignore::DeprecationWarning",]

[tool.ruff]
Expand Down
48 changes: 44 additions & 4 deletions src/pixelator/analysis/colocalization/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,7 @@
TransformationTypes,
)
from pixelator.graph.utils import Graph
from pixelator.statistics import (
correct_pvalues,
log1p_transformation,
)
from pixelator.statistics import correct_pvalues, log1p_transformation

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -288,3 +285,46 @@ def data():

logger.debug("Colocalization scores for dataset computed")
return scores


def get_differential_colocalization(
colocalization_data_frame: pd.DataFrame,
target: str,
reference: str,
contrast_column: str = "sample",
use_z_score: bool = True,
) -> pd.DataFrame:
"""Calculate the differential colocalization.
:param colocalization_data_frame: The colocalization data frame.
:param target: The label for target components in the contrast_column.
:param reference: The label for reference components in the contrast_column.
:param contrast_column: The column to use for the contrast. Defaults to "sample".
:param use_z_score: Whether to use the z-score. Defaults to True.
:return: The differential colocalization.
:rtype: pd.DataFrame
"""
if use_z_score:
value_column = "pearson_z"
else:
value_column = "pearson"

same_marker_mask = (
colocalization_data_frame["marker_1"] == colocalization_data_frame["marker_2"]
)
data_frame = colocalization_data_frame[~same_marker_mask]
colocalization_scores_source = data_frame[data_frame[contrast_column] == reference]
colocalization_scores_target = data_frame[data_frame[contrast_column] == target]
differential_colocalization = colocalization_scores_source.groupby(
["marker_1", "marker_2"]
)[[value_column]].median().astype(float) - colocalization_scores_target.groupby(
["marker_1", "marker_2"]
)[
[value_column]
].median().astype(
float
)
differential_colocalization = differential_colocalization.reset_index()

return differential_colocalization
20 changes: 7 additions & 13 deletions src/pixelator/graph/backends/implementations/_networkx.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,18 +352,16 @@ def _layout_coordinates(
if layout_algorithm == "fruchterman_reingold_3d":
layout_inst = nx.spring_layout(raw, dim=3, seed=random_seed)
if layout_algorithm == "pmds":
return pmds_layout(raw, seed=random_seed, **kwargs)
layout_inst = pmds_layout(raw, seed=random_seed, **kwargs)
if layout_algorithm == "pmds_3d":
return pmds_layout(raw, dim=3, seed=random_seed, **kwargs)
layout_inst = pmds_layout(raw, dim=3, seed=random_seed, **kwargs)

coordinates = pd.DataFrame.from_dict(
layout_inst,
orient="index",
columns=["x", "y"] if len(layout_inst[0]) == 2 else ["x", "y", "z"],
)
coordinates.index = [
str(raw.nodes[node_idx]["name"]) for node_idx in layout_inst.keys()
]

return coordinates

@staticmethod
Expand Down Expand Up @@ -425,7 +423,7 @@ def layout_coordinates(
# Added here to avoid circular imports
from pixelator.graph.utils import create_node_markers_counts

node_marker_counts = create_node_markers_counts(self) # type: ignore
node_marker_counts = create_node_markers_counts(self, name_as_index=False) # type: ignore
df = pd.concat([coordinates, node_marker_counts], axis=1)
else:
df = coordinates
Expand Down Expand Up @@ -752,7 +750,8 @@ def pmds_layout(
pivs = np.random.choice(g.nodes, pivots, replace=False)

# Calculate the shortest path length from the pivots to all other nodes
A = nx.to_numpy_array(g, weight=None)
nodelist = list(g.nodes)
A = nx.to_numpy_array(g, weight=None, nodelist=nodelist)
D = sp.sparse.csgraph.shortest_path(A, directed=False, unweighted=True)
D_pivs = D[:, np.where(np.isin(g.nodes, pivs))[0]]

Expand All @@ -765,12 +764,7 @@ def pmds_layout(
# in an abstract cartesian space
_, _, Vh = np.linalg.svd(D_pivs_centered)
coordinates = D_pivs_centered @ np.transpose(Vh)[:, :dim]
coordinates = pd.DataFrame(
coordinates, columns=["x", "y"] if dim == 2 else ["x", "y", "z"]
)

coordinates.index = [
str(g.nodes[node_idx]["name"]) for node_idx in coordinates.index
]
coordinates = {nodelist[i]: coordinates[i, :] for i in range(coordinates.shape[0])}

return coordinates
10 changes: 6 additions & 4 deletions src/pixelator/graph/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ def create_node_markers_counts(
graph: Graph,
k: int = 0,
normalization: Optional[Literal["mean"]] = None,
name_as_index: bool = True,
) -> pd.DataFrame:
"""Create a matrix of marker counts for each in the graph.
Expand Down Expand Up @@ -180,16 +181,17 @@ def create_node_markers_counts(
node_marker_counts = pd.DataFrame.from_records(
list(graph.vs.get_attribute("markers")),
columns=markers,
index=list(graph.vs.get_attribute("name")),
index=list(graph.raw.nodes),
)
node_marker_counts = node_marker_counts.reindex(
sorted(node_marker_counts.columns), axis=1
)
node_marker_counts.columns.name = "markers"
node_marker_counts.columns = node_marker_counts.columns.astype("string[pyarrow]")
node_marker_counts.index = pd.Index(
list(graph.vs.get_attribute("name")), dtype="string[pyarrow]", name="node"
)
if name_as_index:
node_marker_counts.index = pd.Index(
list(graph.vs.get_attribute("name")), dtype="string[pyarrow]", name="node"
)
if k == 0:
return node_marker_counts

Expand Down
Loading

0 comments on commit dce9955

Please sign in to comment.