You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将按帧范围记录的动物行为DataFrame扩展为逐帧格式并保留未标注帧的NaN值

如何将按帧范围记录的动物行为DataFrame扩展为逐帧格式并保留未标注帧的NaN值

我完全懂你的需求啦——你手头的动物行为标注数据是按帧范围记录的,但现在需要转换成逐帧的格式,关键是那些没被标注的空白帧(比如例子里的11-15帧)必须保留NaN,不能像之前用ffill那样把前面的行为硬生生填充进去。下面给你两个实用的解决方法:

方法1:用explode展开帧范围(直观易懂)

这个方法的核心是先把每个行为对应的帧范围拆成单独的帧列表,再把列表展开成逐行,最后补全所有帧的索引来保留NaN。

import pandas as pd

# 你的原始数据
data = {
    'Frame start': [0, 2, 5, 9, 16],
    'Frame stop': [2, 5, 9, 11, 20],
    'Behavior': ['turning', 'immobility', 'mild movement', 'immobility', 'jump']}

df = pd.DataFrame(data)
df.set_index('Frame start', inplace=True)

# 步骤1:重置索引,把Frame start变回普通列
df_reset = df.reset_index()

# 步骤2:为每一行生成对应的帧列表(注意Frame stop是不包含的,所以用range(start, stop))
df_reset['Frames'] = df_reset.apply(lambda row: list(range(row['Frame start'], row['Frame stop'])), axis=1)

# 步骤3:展开帧列表,每个帧单独成一行
expanded_df = df_reset.explode('Frames').set_index('Frames')['Behavior']

# 步骤4:重新索引到完整的帧范围(这里是0到19),空白帧自动填充为NaN
full_frames = range(0, 20)
final_df = expanded_df.reindex(full_frames)

# 查看结果
print(final_df)

运行后得到的结果正是你想要的:

Frames
0           turning
1           turning
2        immobility
3        immobility
4        immobility
5     mild movement
6     mild movement
7     mild movement
8     mild movement
9        immobility
10       immobility
11              NaN
12              NaN
13              NaN
14              NaN
15              NaN
16             jump
17             jump
18             jump
19             jump
Name: Behavior, dtype: object

方法2:利用区间索引(适合大数据量)

如果你的标注数据量很大,用区间索引的方式效率会更高,原理是把每个行为的帧范围转换成区间,再匹配每个帧是否属于某个区间。

import pandas as pd

# 原始数据
data = {
    'Frame start': [0, 2, 5, 9, 16],
    'Frame stop': [2, 5, 9, 11, 20],
    'Behavior': ['turning', 'immobility', 'mild movement', 'immobility', 'jump']}

df = pd.DataFrame(data)
df.set_index('Frame start', inplace=True)

# 步骤1:创建包含所有帧的DataFrame
full_df = pd.DataFrame({'Frame': range(0, 20)})

# 步骤2:把原始数据的帧范围转换成左闭右开的区间
df_reset = df.reset_index()
df_reset['Frame Interval'] = pd.IntervalIndex.from_arrays(df_reset['Frame start'], df_reset['Frame stop'], closed='left')

# 步骤3:用merge_asof匹配每个帧对应的区间和行为
merged_df = pd.merge_asof(full_df, df_reset[['Frame Interval', 'Behavior']], 
                          left_on='Frame', right_on='Frame Interval', direction='backward')

# 步骤4:判断帧是否在对应的区间内,不在则设为NaN
merged_df['Behavior'] = merged_df.apply(
    lambda x: x['Behavior'] if x['Frame'] in x['Frame Interval'] else pd.NA, 
    axis=1
)

# 整理成最终格式
final_df = merged_df.set_index('Frame')['Behavior']

print(final_df)

这个方法得到的结果和方法1完全一致,但处理大规模数据时速度会更快。

为什么之前的ffill不行?

你之前尝试的reindex + ffill之所以不符合要求,是因为它会把最后一个非NaN值一直填充到下一个非NaN值出现,而我们需要的是只有标注过的帧才有行为值,未标注的空白帧保持NaN,所以必须先精准展开有标注的帧,再补全空白索引,而不是直接填充。

备注:内容来源于stack exchange,提问作者Albert hiuka fok

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 16:48:01