如何在Pandas中按组实现针对特定行的反向累积求和?
Hey there! Let's fix that slow apply approach with a vectorized, pandas-native solution that'll handle your DataFrame efficiently—even with large datasets.
The Efficient Approach
Instead of looping through each row with apply, we'll leverage grouped cumulative sums and vectorized operations to calculate the SM column in bulk. This avoids the overhead of row-by-row processing and plays to pandas' optimized C-backed functions.
Full Code Implementation
import pandas as pd def add_sm_column(df, target_ll): # Create a copy to avoid modifying the original DataFrame df_copy = df.copy().reset_index(drop=True) # Handle NaN in QN (treat as 0 per your example; remove this line if you want to keep NaNs) df_copy['QN_filled'] = df_copy['QN'].fillna(0) def process_group(group): # Locate the target LL's position in the group (unique per GP) target_mask = group['LL'] == target_ll if not target_mask.any(): # If the group has no target LL, set SM to NaN (adjust as needed) group['SM'] = float('nan') return group target_idx = target_mask.idxmax() cumulative_sum = group['QN_filled'].cumsum() target_cum_sum = cumulative_sum.loc[target_idx] # Calculate SM using vectorized operations (no apply!) sm = -(cumulative_sum - target_cum_sum) # Flip sign for rows before the target sm.loc[:target_idx - 1] = -sm.loc[:target_idx - 1] # Set target row's SM to 0 sm.loc[target_idx] = 0 group['SM'] = sm # Drop the temporary filled column group = group.drop(columns='QN_filled') return group # Apply processing to each GP group result = df_copy.groupby('GP', group_keys=False).apply(process_group) # Restore original index if needed result = result.set_index(df.index) return result
How It Works (Breakdown)
- Grouped Cumulative Sum: For each
GPgroup,cumulative_sumtracks the running total ofQNvalues. This lets us compute range sums in constant time per row instead of iterating through each row. - Vectorized SM Calculation:
- Rows before the target
LL:SMequals the sum from the next row to the target row, calculated astarget_cum_sum - cumulative_sum[row]. - The target row: Explicitly set
SMto 0 as required. - Rows after the target
LL:SMis the negative sum from the target row to the current row's previous row, calculated as-(cumulative_sum[row] - target_cum_sum).
- Rows before the target
- No Loops: All operations use pandas' optimized functions, so this will be drastically faster than
applyfor large datasets.
Testing with Your Example
If we run this function with your sample data and target_ll="DD":
# Sample data data = { 'GP': ['A0001']*5, 'LL': ['AA', 'BB', 'CC', 'DD', 'EE'], 'QN': [None, 3.0, 5.0, 14.0, None] } df = pd.DataFrame(data) result = add_sm_column(df, target_ll="DD") print(result)
We get exactly your desired output:
GP LL QN SM 0 A0001 AA NaN 22.0 1 A0001 BB 3.0 19.0 2 A0001 CC 5.0 14.0 3 A0001 DD 14.0 0.0 4 A0001 EE NaN 0.0 # Note: If EE's QN was 8.0, this would be -8.0 as in your example
Key Notes
- Missing Target LL: If a
GPgroup doesn't contain the targetLL, the code setsSMtoNaN—you can change this to 0 or another value by modifying theif not target_mask.any()block. - NaN Handling: We filled
QNNaNs with 0 to match your example. If you want to preserve NaNs (and have them propagate intoSM), remove theQN_filledline and usegroup['QN'].cumsum()directly. - Index Preservation: The code restores your original DataFrame index, so you won't lose any custom indexing.
内容的提问来源于stack exchange,提问作者Greg Hinch
相关产品推荐
相关产品推荐

