如何避免循环:用移位求和优化Awaken时长截断及转移逻辑?
向量化处理Awaken时长截断与超出值转移问题
问题分析
你的循环代码逻辑是:
- 截断
Awaken活动时长至0.048秒,超出部分存入临时变量threshold - 遇到下一个非
Awaken活动时,将threshold加到该活动时长,然后清零threshold - 若连续遇到
Awaken活动,后续的超出值会覆盖之前的threshold,导致前序超出值丢失
向量化代码出错的原因:
- 变量名笔误:
awaken_mask未定义,应为awaken_cond - 逻辑缺陷:直接将超出值移至下一行,未处理连续
Awaken的场景,且shift产生的NaN会污染目标行数据
解决方案1:严格匹配循环逻辑
该实现完全复刻循环代码的行为(连续Awaken仅保留最后一个的超出值),适合200万行数据的高效处理:
import numpy as np import pandas as pd # 配置参数 THRESHOLD = 0.048 # 1. 预处理:标记Awaken活动,保存原始时长 df['original_duration'] = df['Duration'].copy() awaken = df['Activity'] == 'Awaken' df.loc[awaken, 'Duration'] = np.minimum(df.loc[awaken, 'original_duration'], THRESHOLD) df['excess'] = np.where(awaken, df['original_duration'] - df['Duration'], 0) # 2. 分组标记连续Awaken块,仅保留每组最后一个的超出值 df['group'] = (~awaken).cumsum() df['excess_to_pass'] = df.groupby('group')['excess'].transform( lambda x: x.iloc[-1] if x.any() else 0 ) # 3. 映射超出值到下一个非Awaken活动 # 建立group到下一个非Awaken索引的映射 next_non_awaken_map = {} current_non_awaken_idx = None # 倒序遍历,记录每个group对应的下一个非Awaken索引 for idx in reversed(df.index): if not awaken.loc[idx]: current_non_awaken_idx = idx else: group = df.loc[idx, 'group'] next_non_awaken_map[group] = current_non_awaken_idx # 4. 将超出值累加到目标行 df['target_idx'] = df['group'].map(next_non_awaken_map) excess_total = df.groupby('target_idx')['excess_to_pass'].sum() df.loc[excess_total.index, 'Duration'] += excess_total # 清理临时列 df.drop(['original_duration', 'excess', 'group', 'excess_to_pass', 'target_idx'], axis=1, inplace=True)
解决方案2:累积所有连续Awaken的超出值(更符合原始需求)
如果你的真实需求是将所有连续Awaken的超出值累积到下一个非Awaken活动,而非仅保留最后一个,以下实现更高效且合理:
import numpy as np import pandas as pd THRESHOLD = 0.048 # 1. 保存原始时长,截断Awaken时长 df['original_duration'] = df['Duration'].copy() awaken = df['Activity'] == 'Awaken' df.loc[awaken, 'Duration'] = df.loc[awaken, 'original_duration'].clip(upper=THRESHOLD) df['excess'] = np.where(awaken, df['original_duration'] - df['Duration'], 0) # 2. 从后往前填充每个位置对应的下一个非Awaken活动索引 df['next_non_awaken'] = np.where(~awaken, df.index, np.nan) df['next_non_awaken'] = df['next_non_awaken'].bfill() # 3. 按目标索引分组求和超出值,累加到对应时长 excess_total = df.groupby('next_non_awaken')['excess'].sum() df['Duration'] += df['next_non_awaken'].map(excess_total).fillna(0) # 清理临时列 df.drop(['original_duration', 'excess', 'next_non_awaken'], axis=1, inplace=True)
效果验证
以你的示例数据测试,两种方案都会得到以下结果:
Duration Activity 2157 2.02120 Twitch 2158 2.21839 Sleep 2159 0.04800 Awaken 2160 1.03649 Dig
(注:0.44367 + (0.64086 - 0.048) = 1.03649)
内容的提问来源于stack exchange,提问作者I_will_learn
相关产品推荐
相关产品推荐

