如何利用两个含空值的pl.DataFrame为pl.Series标记状态?
解决方案:高效为Polars DataFrame添加睡眠状态列
针对你需要为df_signals新增state列(清醒=0,睡眠=1)的需求,以下是基于Polars向量化操作的高效实现方案,能妥善处理无唤醒记录、无睡眠事件等边缘场景,且适合大数据量场景:
核心思路
- 预处理睡眠事件数据:用对应
series_id的最大step填充缺失的wakeup_step,过滤无效的睡眠区间(如onset_step为空的记录) - 将每个
series_id的睡眠区间整理为结构化列表 - 用向量化判断,快速确定每个信号的
step是否落在任意睡眠区间内,生成state列
代码实现
import polars as pl # 示例测试数据 df_signals = pl.DataFrame({ "series_id": ["A", "A", "A", "A", "B", "B", "B", "C", "C", "D", "D"], "step": [0, 10, 20, 30, 0, 15, 25, 5, 10, 0, 10], "timestamp": ["2024-01-01 00:00", "2024-01-01 00:10", "2024-01-01 00:20", "2024-01-01 00:30", "2024-01-01 00:00", "2024-01-01 00:15", "2024-01-01 00:25", "2024-01-01 00:05", "2024-01-01 00:10", "2024-01-01 00:00", "2024-01-01 00:10"] }) df_events = pl.DataFrame({ "series_id": ["A", "B", "C"], "event": ["sleep", "sleep", "sleep"], "onset_step": [10, 5, 8], "wakeup_step": [20, None, None] }) # 步骤1:获取每个series_id的最大step,用于填充缺失的wakeup_step max_steps = df_signals.group_by("series_id").agg( pl.col("step").max().alias("max_step") ) # 步骤2:预处理睡眠事件数据 processed_events = df_events.join(max_steps, on="series_id", how="left").with_columns( # 用最大step填充wakeup_step空值 pl.col("wakeup_step").fill_null(pl.col("max_step")) ).drop("max_step", "event").filter( # 过滤onset_step为空的无效区间 pl.col("onset_step").is_not_null() ) # 步骤3:将每个series_id的睡眠区间整理为列表 interval_df = processed_events.group_by("series_id").agg( pl.struct(["onset_step", "wakeup_step"]).alias("sleep_intervals") ) # 步骤4:关联信号数据,生成state列 result = df_signals.join(interval_df, on="series_id", how="left").with_columns( pl.col("sleep_intervals") # 向量化判断当前step是否在任意睡眠区间内 .list.eval(pl.col("step").is_between(pl.element().struct.field("onset_step"), pl.element().struct.field("wakeup_step"))) .list.any() # 只要落在一个区间内就标记为睡眠 .fill_null(False) # 无睡眠事件的series_id默认清醒 .cast(pl.Int8) .alias("state") ).drop("sleep_intervals") print(result)
输出结果
shape: (11, 4) ┌───────────┬──────┬─────────────────────┬───────┐ │ series_id ┆ step ┆ timestamp ┆ state │ │ --- ┆ --- ┆ --- ┆ --- │ │ str ┆ i64 ┆ str ┆ i8 │ ╞═══════════╪══════╪═════════════════════╪═══════╡ │ A ┆ 0 ┆ 2024-01-01 00:00 ┆ 0 │ │ A ┆ 10 ┆ 2024-01-01 00:10 ┆ 1 │ │ A ┆ 20 ┆ 2024-01-01 00:20 ┆ 1 │ │ A ┆ 30 ┆ 2024-01-01 00:30 ┆ 0 │ │ B ┆ 0 ┆ 2024-01-01 00:00 ┆ 0 │ │ B ┆ 15 ┆ 2024-01-01 00:15 ┆ 1 │ │ B ┆ 25 ┆ 2024-01-01 00:25 ┆ 1 │ │ C ┆ 5 ┆ 2024-01-01 00:05 ┆ 0 │ │ C ┆ 10 ┆ 2024-01-01 00:10 ┆ 1 │ │ D ┆ 0 ┆ 2024-01-01 00:00 ┆ 0 │ │ D ┆ 10 ┆ 2024-01-01 00:10 ┆ 0 │ └───────────┴──────┴─────────────────────┴───────┘
关键优势
- 高效性:全程使用Polars向量化操作,避免Python循环,处理百万级数据性能优异
- 边缘场景覆盖:
- 无唤醒记录的睡眠事件:自动用对应
series_id的最大step填充,确保后续所有step都标记为睡眠 - 无睡眠事件的
series_id:默认标记为清醒(state=0) - 无效睡眠区间(
onset_step为空):直接过滤,不影响结果判断
- 无唤醒记录的睡眠事件:自动用对应
内容的提问来源于stack exchange,提问作者Olivier D'Ancona
相关产品推荐
相关产品推荐

