From 32c006753c62dd58bf65bd091a9fd5c76fb976ab Mon Sep 17 00:00:00 2001 From: Brian Geuther Date: Tue, 21 Oct 2025 10:12:09 -0400 Subject: [PATCH 1/4] Adjusting outputs to include bout counts. Changing column names to properly indicate sums. --- support_code/behavior_summaries.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/support_code/behavior_summaries.py b/support_code/behavior_summaries.py index 66217670..e76df5c8 100644 --- a/support_code/behavior_summaries.py +++ b/support_code/behavior_summaries.py @@ -5,6 +5,7 @@ from JABS postprocessing summary tables, calculating metrics like time spent in behaviors and distances traveled. """ + import argparse import pandas as pd @@ -129,10 +130,11 @@ def aggregate_data_by_bin_size( time_behavior_col = f"{behavior}_time_behavior" time_not_behavior_col = f"{behavior}_time_not_behavior" behavior_dist_col = f"{behavior}_behavior_dist" + behavior_bout_col = f"{behavior}_bout_behavior" # Calculate time spent in behavior # TODO: Do we need to make `5` a configurable parameter? - aggregated[f"bin_avg_{bin_size*5}.{behavior}_time_secs"] = ( + aggregated[f"bin_sum_{bin_size * 5}.{behavior}_time_secs"] = ( aggregated[time_behavior_col] / (aggregated[time_behavior_col] + aggregated[time_not_behavior_col]) * bin_size @@ -141,9 +143,17 @@ def aggregate_data_by_bin_size( # Calculate average distance (in cm) # TODO: Do we need to make `5` a configurable parameter? - aggregated[f"bin_avg_{bin_size*5}.{behavior}_distance_cm"] = aggregated[ + aggregated[f"bin_avg_{bin_size * 5}.{behavior}_distance_cm"] = aggregated[ behavior_dist_col ] / (bin_size * 5) + aggregated[f"bin_sum_{bin_size * 5}.{behavior}_distance_cm"] = aggregated[ + behavior_dist_col + ] + + # Sum up bout count + aggregated[f"bin_sum_{bin_size * 5}.{behavior}_bout_behavior"] = aggregated[ + behavior_bout_col + ] # Reset index to make MouseID a regular column return aggregated.reset_index() From 3ff975ae4bb4e5752c9a752b6de3f69629472798 Mon Sep 17 00:00:00 2001 From: Brian Geuther Date: Thu, 23 Oct 2025 15:17:21 -0400 Subject: [PATCH 2/4] Cutover from R to python feature extractor. --- nextflow/configs/profiles/sumner2.config | 2 +- nextflow/modules/jabs_classifiers.nf | 4 +-- support_code/behavior_summaries.py | 32 ++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/nextflow/configs/profiles/sumner2.config b/nextflow/configs/profiles/sumner2.config index 49b11d7f..69756060 100644 --- a/nextflow/configs/profiles/sumner2.config +++ b/nextflow/configs/profiles/sumner2.config @@ -182,7 +182,7 @@ process { container = "/projects/kumar-lab/meta/images/JABS-behavior-classifier/headless/v0.36.1/latest.sif" } withLabel: "jabs_postprocess" { - container = "/projects/kumar-lab/meta/images/JABS-postprocess/jabs-postprocess/v0.4.2/latest.sif" + container = "/projects/kumar-lab/meta/images/JABS-postprocess/jabs-postprocess/v0.5.1/latest.sif" } withLabel: "jabs_table_convert" { container = "/projects/kumar-lab/meta/images/mouse-tracking-runtime/RBase/v0.1.4/latest.sif" diff --git a/nextflow/modules/jabs_classifiers.nf b/nextflow/modules/jabs_classifiers.nf index a08ae5fb..a07776f9 100644 --- a/nextflow/modules/jabs_classifiers.nf +++ b/nextflow/modules/jabs_classifiers.nf @@ -171,7 +171,7 @@ process PREDICT_HEURISTICS { * @return features The generated feature file. */ process BEHAVIOR_TABLE_TO_FEATURES { - label "jabs_table_convert" + label "tracking" label "cpu" label "r_jabs_table_convert" @@ -183,7 +183,7 @@ process BEHAVIOR_TABLE_TO_FEATURES { script: """ - Rscript ${params.support_code_dir}behavior_summaries.R -f ${in_summary_table} -b ${bin_size} -o "${in_summary_table.baseName}_features_${bin_size}.csv" + python3 /media/bgeuther/LL3_Internal/mouse-tracking-runtime/support_code/behavior_summaries.py -f ${in_summary_table} -b ${bin_size} -o "${in_summary_table.baseName}_features_${bin_size}.csv" """ } diff --git a/support_code/behavior_summaries.py b/support_code/behavior_summaries.py index e76df5c8..a24a26fa 100644 --- a/support_code/behavior_summaries.py +++ b/support_code/behavior_summaries.py @@ -8,6 +8,7 @@ import argparse +import numpy as np import pandas as pd @@ -101,6 +102,14 @@ def get_columns_to_exclude(behavior: str) -> list: "bout_behavior", "not_behavior_dist", "behavior_dist", + "behavior_dist_threshold", + "behavior_dist_seg", + "avg_bout_duration", + "_stats_sample_count", + "bout_duration_std", + "bout_duration_var", + "latency_to_first_prediction", + "latency_to_last_prediction", ] return [f"{behavior}_{suffix}" for suffix in suffixes] @@ -149,12 +158,35 @@ def aggregate_data_by_bin_size( aggregated[f"bin_sum_{bin_size * 5}.{behavior}_distance_cm"] = aggregated[ behavior_dist_col ] + aggregated[f"bin_sum_{bin_size * 5}.{behavior}_distance_cm_threshold"] = aggregated[ + f"{behavior}_behavior_dist_threshold" + ] + aggregated[f"bin_sum_{bin_size * 5}.{behavior}_distance_cm_seg"] = aggregated[ + f"{behavior}_behavior_dist_seg" + ] # Sum up bout count aggregated[f"bin_sum_{bin_size * 5}.{behavior}_bout_behavior"] = aggregated[ behavior_bout_col ] + # Additional stats + if np.sum(aggregated[f"{behavior}__stats_sample_count"]) == 0: + aggregated[f"bin_avg_{bin_size * 5}.{behavior}_avg_bout_length"] = np.nan + else: + aggregated[f"bin_avg_{bin_size * 5}.{behavior}_avg_bout_length"] = np.average( + aggregated[f"{behavior}_avg_bout_duration"], + weights=aggregated[f"{behavior}__stats_sample_count"], + ) + # TODO: var and std need to be aggregated across bins. + # This is non-trivial because of the partial bouts and their associated weights. + aggregated[f"bin_first_{bin_size * 5}.{behavior}_latency_first_prediction"] = ( + aggregated[f"{behavior}_latency_to_first_prediction"].head(1) + ) + aggregated[f"bin_last_{bin_size * 5}.{behavior}_latency_last_prediction"] = ( + aggregated[f"{behavior}_latency_to_last_prediction"].tail(1) + ) + # Reset index to make MouseID a regular column return aggregated.reset_index() From be2cc2f36fa67fe22d23434e6652c4a8b24ff50e Mon Sep 17 00:00:00 2001 From: Brian Geuther Date: Thu, 23 Oct 2025 15:19:24 -0400 Subject: [PATCH 3/4] Add new heuristic classifiers that combine locomotion with zone --- nextflow/configs/profiles/sumner2.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nextflow/configs/profiles/sumner2.config b/nextflow/configs/profiles/sumner2.config index 69756060..de7d3c04 100644 --- a/nextflow/configs/profiles/sumner2.config +++ b/nextflow/configs/profiles/sumner2.config @@ -90,7 +90,7 @@ params { ], ] - heuristic_classifiers = ["corner", "corner_facing", "freeze", "locomotion", "periphery", "wall_facing"] + heuristic_classifiers = ["corner", "corner_facing", "freeze", "locomotion", "periphery", "wall_facing", "locomotion_corner", "locomotion_periphery"] // Number of 5-minute bins for transforming summary tables into bins // 1 = 5 minutes, 4 = 20 minutes, etc. From 114b0d40aeffd28904081d31f7983b09424a9925 Mon Sep 17 00:00:00 2001 From: Brian Geuther Date: Fri, 24 Oct 2025 11:42:09 -0400 Subject: [PATCH 4/4] Changing path of support code to be within container --- nextflow/configs/profiles/sumner2.config | 2 +- nextflow/modules/jabs_classifiers.nf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nextflow/configs/profiles/sumner2.config b/nextflow/configs/profiles/sumner2.config index de7d3c04..7b1dfdde 100644 --- a/nextflow/configs/profiles/sumner2.config +++ b/nextflow/configs/profiles/sumner2.config @@ -29,7 +29,7 @@ params { tracking_code_dir = "/kumar_lab_models/mouse-tracking-runtime/" gait_code_dir = "/gait-analysis/" vfi_code_dir = "/vfi/Code/" - support_code_dir = "/mouse-tracking-runtime/support_code/" + support_code_dir = "/workspace/support_code/" heuristic_classifier_folder = "/opt/JABS-postprocess/src/jabs_postprocess/heuristic_classifiers/" filter_processed = false model_dir = "/projects/kumar-lab/multimouse-pipeline/nextflow-artifacts/neural_net_models/" diff --git a/nextflow/modules/jabs_classifiers.nf b/nextflow/modules/jabs_classifiers.nf index a07776f9..d53520b5 100644 --- a/nextflow/modules/jabs_classifiers.nf +++ b/nextflow/modules/jabs_classifiers.nf @@ -183,7 +183,7 @@ process BEHAVIOR_TABLE_TO_FEATURES { script: """ - python3 /media/bgeuther/LL3_Internal/mouse-tracking-runtime/support_code/behavior_summaries.py -f ${in_summary_table} -b ${bin_size} -o "${in_summary_table.baseName}_features_${bin_size}.csv" + python3 ${params.support_code_dir}/behavior_summaries.py -f ${in_summary_table} -b ${bin_size} -o "${in_summary_table.baseName}_features_${bin_size}.csv" """ }