From cef4232ccf7c0368d59fbde4edd36c22a464bb85 Mon Sep 17 00:00:00 2001 From: Chloe-Thangavelu <83045403+Chloe-Thangavelu@users.noreply.github.com> Date: Fri, 2 May 2025 00:01:29 -0700 Subject: [PATCH 1/3] Added Cell Counts to Dendrogram Labels In the hierarchical heatmap function, Cell Counts for each cluster were added to dendrogram y-axis labels --- src/spac/visualization.py | 23 +++++++++++++++++++ .../test_hierarchical_heatmap.py | 20 ++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/spac/visualization.py b/src/spac/visualization.py index ef46f7c5..005bdd42 100644 --- a/src/spac/visualization.py +++ b/src/spac/visualization.py @@ -910,6 +910,29 @@ def hierarchical_heatmap(adata, annotation, features=None, layer=None, 'col_linkage': dendro_col_data } + # Add cell counts to labels + # Retrieve the number of cells in each group + cell_counts = labels.value_counts() + cell_counts = dict(cell_counts) + + # Retrieve the cluster labels from the heatmap + cluster_labels = clustergrid.ax_heatmap.get_yticklabels() + + # Append the cell number to each cluster + numbered_labels = [] + for x in cluster_labels: + key = int(x.get_text()) + value = cell_counts[key] + numbered_label = f'cluster {key}\n{value} cells' + numbered_labels.append(numbered_label) + + # add updated labels with cell counts to the heatmap + clustergrid.ax_heatmap.set_yticklabels(numbered_labels) + plt.setp(clustergrid.ax_heatmap.get_yticklabels(), rotation=0) + + # adjust so labels don't get cut off + clustergrid.ax_heatmap.figure.subplots_adjust(right=0.9, left=0.1) + return mean_intensity, clustergrid, dendrogram_data diff --git a/tests/test_visualization/test_hierarchical_heatmap.py b/tests/test_visualization/test_hierarchical_heatmap.py index 4b2b4863..c25ea713 100644 --- a/tests/test_visualization/test_hierarchical_heatmap.py +++ b/tests/test_visualization/test_hierarchical_heatmap.py @@ -118,6 +118,26 @@ def test_axes_switching(self): mean_intensity_swapped.shape ) + def test_cell_count_labels(self): + '''This test confirms the cell count labels are correct''' + # Set up AnnData object with known cell counts (3 cells in 1 cluster) + X_data = pd.DataFrame( + {'gene1': [1, 2, 3], 'gene2': [1, 2, 3], 'gene3': [1, 2, 3]} + ) + obs_data = pd.DataFrame({'cluster': [1, 1, 1]}) + self.adata = anndata.AnnData(X=X_data, obs=obs_data) + + # Use hierarchical_heatmap function on AnnData object and get y-axis labels + _, clustergrid, _ = hierarchical_heatmap(self.adata, annotation='cluster') + actual_labels = [] + for label in clustergrid.ax_heatmap.get_yticklabels(): + actual_labels.append(label.get_text()) + + # Confirm actual labels are in the expected labels + # dendrogram order varies if multiple clusters + expected_labels = ['cluster 1\n3 cells'] + self.assertIn(expected_labels[0], actual_labels) + if __name__ == "__main__": unittest.main() From 3f8bc40bc58a55f88b3ef6a2cae626c2c3c6efbb Mon Sep 17 00:00:00 2001 From: Chloe-Thangavelu <83045403+Chloe-Thangavelu@users.noreply.github.com> Date: Thu, 8 May 2025 11:22:47 -0700 Subject: [PATCH 2/3] Cell Count Labeling Function Argument Added MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Made including cell counts optional: Wrapped cell count labeling code with an argument in the function called “Show counts” --- src/spac/visualization.py | 44 ++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/spac/visualization.py b/src/spac/visualization.py index 005bdd42..e08bfaa4 100644 --- a/src/spac/visualization.py +++ b/src/spac/visualization.py @@ -704,7 +704,8 @@ def heatmap(adata, column, layer=None, **kwargs): def hierarchical_heatmap(adata, annotation, features=None, layer=None, cluster_feature=False, cluster_annotations=False, standard_scale=None, z_score="annotation", - swap_axes=False, rotate_label=False, **kwargs): + swap_axes=False, rotate_label=False, + show_counts=False, **kwargs): """ Generates a hierarchical clustering heatmap and dendrogram. @@ -745,6 +746,9 @@ def hierarchical_heatmap(adata, annotation, features=None, layer=None, axis (rows) and features are on the horizontal axis (columns). When set to True, features will be on the vertical axis and annotations on the horizontal axis. Default is False. + show_counts : bool, optional + If True, adds the amount of cells to each cluster in the heatmap. + Default is False. rotate_label : bool, optional If True, rotate x-axis labels by 45 degrees. Default is False. **kwargs: @@ -910,28 +914,30 @@ def hierarchical_heatmap(adata, annotation, features=None, layer=None, 'col_linkage': dendro_col_data } - # Add cell counts to labels - # Retrieve the number of cells in each group - cell_counts = labels.value_counts() - cell_counts = dict(cell_counts) + if show_counts: + + # Add cell counts to labels + # Retrieve the number of cells in each group + cell_counts = labels.value_counts() + cell_counts = dict(cell_counts) - # Retrieve the cluster labels from the heatmap - cluster_labels = clustergrid.ax_heatmap.get_yticklabels() + # Retrieve the cluster labels from the heatmap + cluster_labels = clustergrid.ax_heatmap.get_yticklabels() - # Append the cell number to each cluster - numbered_labels = [] - for x in cluster_labels: - key = int(x.get_text()) - value = cell_counts[key] - numbered_label = f'cluster {key}\n{value} cells' - numbered_labels.append(numbered_label) + # Append the cell number to each cluster + numbered_labels = [] + for x in cluster_labels: + key = int(x.get_text()) + value = cell_counts[key] + numbered_label = f'cluster {key}\n{value} cells' + numbered_labels.append(numbered_label) - # add updated labels with cell counts to the heatmap - clustergrid.ax_heatmap.set_yticklabels(numbered_labels) - plt.setp(clustergrid.ax_heatmap.get_yticklabels(), rotation=0) + # add updated labels with cell counts to the heatmap + clustergrid.ax_heatmap.set_yticklabels(numbered_labels) + plt.setp(clustergrid.ax_heatmap.get_yticklabels(), rotation=0) - # adjust so labels don't get cut off - clustergrid.ax_heatmap.figure.subplots_adjust(right=0.9, left=0.1) + # adjust so labels don't get cut off + clustergrid.ax_heatmap.figure.subplots_adjust(right=0.9, left=0.1) return mean_intensity, clustergrid, dendrogram_data From 7048b302a8efb9caa8cff11eb588fc507bc6ee5d Mon Sep 17 00:00:00 2001 From: Chloe-Thangavelu <83045403+Chloe-Thangavelu@users.noreply.github.com> Date: Mon, 9 Jun 2025 16:12:23 -0700 Subject: [PATCH 3/3] fix(test_hierarchical_heatmap): fix cell count test failure Updates the unit test for hierarchical_heatmap. show_counts argument in the hierarchical_heatmap function is set to True so that the y-axis labels including cell counts are enabled. Previously, the test relied on the default show_counts=False, resulting in labels without cell counts which caused the test to fail. --- tests/test_visualization/test_hierarchical_heatmap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_visualization/test_hierarchical_heatmap.py b/tests/test_visualization/test_hierarchical_heatmap.py index c25ea713..de320d89 100644 --- a/tests/test_visualization/test_hierarchical_heatmap.py +++ b/tests/test_visualization/test_hierarchical_heatmap.py @@ -128,7 +128,7 @@ def test_cell_count_labels(self): self.adata = anndata.AnnData(X=X_data, obs=obs_data) # Use hierarchical_heatmap function on AnnData object and get y-axis labels - _, clustergrid, _ = hierarchical_heatmap(self.adata, annotation='cluster') + _, clustergrid, _ = hierarchical_heatmap(self.adata, annotation='cluster', show_counts=True) actual_labels = [] for label in clustergrid.ax_heatmap.get_yticklabels(): actual_labels.append(label.get_text())