From 52ad50b26abaf8b3f0cc8ccc476127bb0e189bce Mon Sep 17 00:00:00 2001 From: kathryn1995 Date: Fri, 21 Nov 2025 18:26:47 -0800 Subject: [PATCH 1/2] Add function to count targets per panel --- src/pmotools/pmo_engine/pmo_processor.py | 21 ++++ tests/test_pmo_engine/test_pmo_processor.py | 118 ++++++++++++++++++++ 2 files changed, 139 insertions(+) diff --git a/src/pmotools/pmo_engine/pmo_processor.py b/src/pmotools/pmo_engine/pmo_processor.py index 44288e6..cf90d20 100644 --- a/src/pmotools/pmo_engine/pmo_processor.py +++ b/src/pmotools/pmo_engine/pmo_processor.py @@ -402,6 +402,27 @@ def count_library_samples_per_target( drop=True ) + @staticmethod + def count_targets_per_panel(pmodata) -> pd.DataFrame: + """ + Count the targets per panel. + + :param pmodata: the pmo to count from + :return: counts for each panel + """ + # how many targets in each panel + panels = [] + target_count = [] + for panel in pmodata["panel_info"]: + panel_targets = 0 + panels.append(panel["panel_name"]) + for reaction in panel["reactions"]: + panel_targets += len(reaction["panel_targets"]) + target_count.append(panel_targets) + return pd.DataFrame( + data={"panel_name": panels, "panel_target_count": target_count} + ) + @staticmethod def list_library_sample_names_per_specimen_name( pmodata, diff --git a/tests/test_pmo_engine/test_pmo_processor.py b/tests/test_pmo_engine/test_pmo_processor.py index b6e0fba..4d03a6e 100755 --- a/tests/test_pmo_engine/test_pmo_processor.py +++ b/tests/test_pmo_engine/test_pmo_processor.py @@ -873,6 +873,124 @@ def test_get_panel_names(self): names = PMOProcessor.get_panel_names(pmo_data_combined) self.assertEqual(["heomev1"], names) + def test_count_targets_per_panel_single_panel_single_reaction(self): + """Test count_targets_per_panel with a single panel containing one reaction""" + # Create a simple PMO with one panel and one reaction + test_pmo = { + "panel_info": [ + { + "panel_name": "test_panel_1", + "reactions": [ + { + "reaction_name": "reaction_1", + "panel_targets": [0, 1, 2, 3, 4], + } + ], + } + ] + } + + result = PMOProcessor.count_targets_per_panel(test_pmo) + + expected_data = {"panel_name": ["test_panel_1"], "panel_target_count": [5]} + expected_df = pd.DataFrame(expected_data) + + pd.testing.assert_frame_equal(result, expected_df) + + def test_count_targets_per_panel_single_panel_multiple_reactions(self): + """Test count_targets_per_panel with a single panel containing multiple reactions""" + # Create a PMO with one panel and multiple reactions + test_pmo = { + "panel_info": [ + { + "panel_name": "test_panel_1", + "reactions": [ + {"reaction_name": "reaction_1", "panel_targets": [0, 1, 2]}, + {"reaction_name": "reaction_2", "panel_targets": [3, 4, 5, 6]}, + {"reaction_name": "reaction_3", "panel_targets": [7, 8]}, + ], + } + ] + } + + result = PMOProcessor.count_targets_per_panel(test_pmo) + + expected_data = { + "panel_name": ["test_panel_1"], + "panel_target_count": [9], # 3 + 4 + 2 = 9 total targets + } + expected_df = pd.DataFrame(expected_data) + + pd.testing.assert_frame_equal(result, expected_df) + + def test_count_targets_per_panel_multiple_panels(self): + """Test count_targets_per_panel with multiple panels""" + # Create a PMO with multiple panels + test_pmo = { + "panel_info": [ + { + "panel_name": "panel_A", + "reactions": [ + {"reaction_name": "reaction_1", "panel_targets": [0, 1, 2, 3]} + ], + }, + { + "panel_name": "panel_B", + "reactions": [ + {"reaction_name": "reaction_1", "panel_targets": [4, 5]}, + { + "reaction_name": "reaction_2", + "panel_targets": [6, 7, 8, 9, 10], + }, + ], + }, + { + "panel_name": "panel_C", + "reactions": [ + {"reaction_name": "reaction_1", "panel_targets": [11]} + ], + }, + ] + } + + result = PMOProcessor.count_targets_per_panel(test_pmo) + + expected_data = { + "panel_name": ["panel_A", "panel_B", "panel_C"], + "panel_target_count": [4, 7, 1], # 4, (2+5), 1 + } + expected_df = pd.DataFrame(expected_data) + + pd.testing.assert_frame_equal(result, expected_df) + + def test_count_targets_per_panel_with_real_data_structure(self): + """Test count_targets_per_panel with realistic PMO data structure""" + # Create a realistic PMO structure similar to the real data + # Based on the real data structure with one panel containing 100 targets + test_pmo = { + "panel_info": [ + { + "panel_name": "heomev1", + "reactions": [ + { + "panel_targets": list(range(100)), # targets 0-99 + "reaction_name": "full", + } + ], + } + ] + } + + result = PMOProcessor.count_targets_per_panel(test_pmo) + + expected_data = { + "panel_name": ["heomev1"], + "panel_target_count": [100], # targets 0-99 = 100 total + } + expected_df = pd.DataFrame(expected_data) + + pd.testing.assert_frame_equal(result, expected_df) + if __name__ == "__main__": unittest.main() From 1fcc76d5288fc37b3c7d196a2d79d6d2508ca222 Mon Sep 17 00:00:00 2001 From: kathryn1995 Date: Mon, 24 Nov 2025 13:54:53 -0800 Subject: [PATCH 2/2] length of unique targets in panel incase duplicates across reactions --- src/pmotools/pmo_engine/pmo_processor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pmotools/pmo_engine/pmo_processor.py b/src/pmotools/pmo_engine/pmo_processor.py index cf90d20..07ab55b 100644 --- a/src/pmotools/pmo_engine/pmo_processor.py +++ b/src/pmotools/pmo_engine/pmo_processor.py @@ -414,11 +414,11 @@ def count_targets_per_panel(pmodata) -> pd.DataFrame: panels = [] target_count = [] for panel in pmodata["panel_info"]: - panel_targets = 0 + panel_targets = [] panels.append(panel["panel_name"]) for reaction in panel["reactions"]: - panel_targets += len(reaction["panel_targets"]) - target_count.append(panel_targets) + panel_targets.extend(reaction["panel_targets"]) + target_count.append(len(set(panel_targets))) return pd.DataFrame( data={"panel_name": panels, "panel_target_count": target_count} )