@@ -34,6 +34,11 @@ def analyze_alignments(path_prefix):
3434 dict: Dictionary containing analysis results and raw data
3535 """
3636 read_df = pd .read_csv (f"{ path_prefix } .per_read.tsv.gz" , sep = "\t " )
37+ alignments_df = pd .read_csv (f"{ path_prefix } .alignments.tsv.gz" , sep = "\t " )
38+
39+ # Pre-aggregate the alignment data by read_id (sum map_len)
40+ map_len_df = alignments_df [alignments_df ["is_mapped" ] == "Y" ].groupby ("read_id" )["map_len" ].sum ().reset_index ()
41+
3742 # Filter to vector-only reads
3843 read_vector = read_df [read_df ["reference_label" ] == "vector" ]
3944
@@ -46,10 +51,10 @@ def analyze_alignments(path_prefix):
4651
4752 result = {
4853 "agg_ref_type" : get_ref_type_agg (
49- read_df , total_read_count_all , total_read_count_vector , total_read_count_lambda
54+ read_df , map_len_df , total_read_count_all , total_read_count_vector , total_read_count_lambda
5055 ),
5156 "agg_subtype" : get_subtype_agg (
52- read_vector , total_read_count_all , total_read_count_vector , total_read_count_lambda
57+ read_vector , map_len_df , total_read_count_all , total_read_count_vector , total_read_count_lambda
5358 ),
5459 }
5560
@@ -62,15 +67,26 @@ def analyze_alignments(path_prefix):
6267 return result
6368
6469
65- def get_ref_type_agg (read_df , total_read_count_all , total_read_count_vector , total_read_count_lambda ):
70+ def get_ref_type_agg (read_df , map_len_df , total_read_count_all , total_read_count_vector , total_read_count_lambda ):
6671 """Counts and percentages of reference labels and types."""
67- df = (
68- read_df . groupby ([ "reference_label" , "assigned_type" ], dropna = False )[
69- "effective_count"
70- ]
71- . sum ()
72- . reset_index ( name = "effective_count" )
72+ # Merge read_df with pre-aggregated map_len data
73+ merged_df = pd . merge (
74+ read_df [[ "read_id" , "reference_label" , "assigned_type" , "effective_count" ]],
75+ map_len_df ,
76+ on = "read_id" ,
77+ how = "left" # Keep all reads, even if they don't have alignments
7378 )
79+ merged_df ["map_len" ] = merged_df ["map_len" ].fillna (0 )
80+
81+ # Do a single groupby to get both effective_count and base
82+ df = merged_df .groupby (["reference_label" , "assigned_type" ], dropna = False ).agg ({
83+ "effective_count" : "sum" ,
84+ "map_len" : "sum"
85+ }).reset_index ()
86+
87+ # Rename map_len to base
88+ df = df .rename (columns = {"map_len" : "base" })
89+
7490 df = df .sort_values (
7591 ["reference_label" , "effective_count" ], ascending = [False , False ]
7692 )
@@ -86,7 +102,7 @@ def get_ref_type_agg(read_df, total_read_count_all, total_read_count_vector, tot
86102 return df
87103
88104
89- def get_subtype_agg (read_vector , total_read_count_all , total_read_count_vector , total_read_count_lambda ):
105+ def get_subtype_agg (read_vector , map_len_df , total_read_count_all , total_read_count_vector , total_read_count_lambda ):
90106 """Counts and percentages of assigned types and subtypes."""
91107 # Handle case where there are no vector reads
92108 if read_vector .empty :
@@ -96,16 +112,30 @@ def get_subtype_agg(read_vector, total_read_count_all, total_read_count_vector,
96112 "assigned_type" : [pd .NA ],
97113 "assigned_subtype" : [pd .NA ],
98114 "effective_count" : [0 ], # Use 0 so R sum() works correctly
115+ "base" : [0 ],
99116 "pct_vector" : [0.0 ],
100117 "pct_total" : [0.0 ],
101118 "pct_wo_lambda" : [0.0 ]
102119 })
103120
104- df = (
105- read_vector .groupby (["assigned_type" , "assigned_subtype" ])["effective_count" ]
106- .sum ()
107- .reset_index (name = "effective_count" )
121+ # Merge read_vector with pre-aggregated map_len data
122+ merged_df = pd .merge (
123+ read_vector [["read_id" , "assigned_type" , "assigned_subtype" , "effective_count" ]],
124+ map_len_df ,
125+ on = "read_id" ,
126+ how = "left"
108127 )
128+ merged_df ["map_len" ] = merged_df ["map_len" ].fillna (0 )
129+
130+ # Group by to get both effective_count and base
131+ df = merged_df .groupby (["assigned_type" , "assigned_subtype" ]).agg ({
132+ "effective_count" : "sum" ,
133+ "map_len" : "sum"
134+ }).reset_index ()
135+
136+ # Rename map_len to base
137+ df = df .rename (columns = {"map_len" : "base" })
138+
109139 df = df .sort_values (["assigned_type" , "effective_count" ], ascending = [False , False ])
110140 df ["pct_vector" ] = round (df ["effective_count" ] * 100 / total_read_count_vector , 2 )
111141 df ["pct_total" ] = round (df ["effective_count" ] * 100 / total_read_count_all , 2 )
@@ -115,11 +145,17 @@ def get_subtype_agg(read_vector, total_read_count_all, total_read_count_vector,
115145
116146def get_flipflop_agg (ff_df ):
117147 """Counts of flip-flop configurations."""
118- df = (
119- ff_df .groupby (["type" , "subtype" , "leftITR" , "rightITR" ])
120- .size ()
121- .reset_index (name = "count" )
122- )
148+ # Calculate length for each record
149+ ff_df ["length" ] = ff_df ["end" ] - ff_df ["start" ]
150+
151+ # Group by required columns and calculate count and sum of lengths
152+ df = ff_df .groupby (["type" , "subtype" , "leftITR" , "rightITR" ]).agg ({
153+ "name" : "count" , # This gives us the count
154+ "length" : "sum" # This gives us the base (sum of all lengths)
155+ }).reset_index ()
156+
157+ # Rename columns
158+ df = df .rename (columns = {"name" : "count" , "length" : "base" })
123159 return df
124160
125161
0 commit comments