0

0

Pandas高级数据填充:基于多列‘1’s的条件性前向填充策略

碧海醫心

碧海醫心

发布时间:2025-10-20 10:13:27

|

222人浏览过

|

来源于php中文网

原创

Pandas高级数据填充:基于多列'1's的条件性前向填充策略

本教程探讨如何在pandas dataframe中实现复杂的条件性前向填充。针对根据多列中特定值(如'1')的位置来定义填充范围的需求,文章详细介绍了利用布尔索引、`diff()`、`shift()`、`where()`和`ffill()`等pandas核心功能构建解决方案的步骤。通过实例代码,读者将学习如何精确控制数据填充的起始与结束点,从而实现灵活高效的数据转换。

在数据分析和处理中,我们经常需要根据某些条件来填充数据。一种常见的场景是,我们需要在一个DataFrame列中进行前向填充(forward fill),但这个填充的范围并非全局的,而是由其他一列或多列中的特定标记(例如数字'1')所限定。例如,当某一列出现'1'时,我们希望从该点开始进行前向填充,直到另一列出现'1',或者直到下一个“起始点”出现。本文将详细介绍如何使用Pandas的高级功能来实现这种基于多列条件的精确前向填充。

问题描述与初始尝试

假设我们有一个DataFrame prac,其中包含两列 'A' 和 'B',以及一个期望结果 DesiredResult。我们的目标是根据 'A' 或 'B' 列中 '1' 的位置来生成 DesiredResult 列。具体来说,当 'A' 或 'B' 中出现 '1' 时,我们希望从该位置开始将结果标记为 '1',并向前填充,直到下一个 '0' 出现,或者直到某个逻辑上的“结束点”。

考虑以下示例数据:

import pandas as pd

prac = pd.DataFrame(
    {"A": [0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
     "B": [0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0],
     "DesiredResult": [0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0]}
)
print("原始DataFrame:")
print(prac)

期望结果 DesiredResult 显示,当 'A' 或 'B' 中任一列出现 '1' 时,结果列会从该位置开始变为 '1',并持续到下一个 '0' 或下一个独立 '1' 块的起始位置。例如,prac.loc[1, 'A'] 是 '1',所以 DesiredResult 从索引 1 变为 '1'。prac.loc[3, 'B'] 是 '1',它延续了前一个 '1' 的填充。prac.loc[6] 的 'A' 和 'B' 都是 '0',所以 DesiredResult 变为 '0'。

用户最初的尝试可能包括使用 mask() 将 '0' 替换为 NaN,然后使用 combine_first() 合并两列,最后应用 ffill()。

# 用户的初始尝试
newDf = prac[['A','B']].mask(prac==0)
newDf['buySell'] =  newDf['A'].combine_first(newDf['B'])
newDf['buySell'].ffill(inplace=True)
print("\n用户初始尝试的结果:")
print(newDf)

这种方法的问题在于,ffill() 会在遇到 NaN 时一直向前填充,而无法识别 '0' 作为填充的“停止点”或“重置点”。它只是简单地填充了所有 NaN,直到遇到下一个非 NaN 值。为了实现更精确的条件填充,我们需要一种更复杂的逻辑来定义填充的起始和结束范围。

高级条件前向填充策略

解决此类问题的关键在于精确识别出所有需要进行前向填充的“起始点”。一旦这些起始点被标记,我们就可以利用 ffill() 来完成填充。

以下是实现期望结果的解决方案:

# 核心解决方案
s = prac['A'].eq(1)
e = prac['B'].eq(1)

result = s.where(s | (e.diff(-1).ne(0) & e).shift()).ffill().fillna(0).astype(int)

print("\n最终计算结果:")
print(result)

输出结果:

0     0
1     1
2     1
3     1
4     1
5     1
6     0
7     1
8     1
9     1
10    1
11    1
12    1
13    1
14    1
15    0

这个结果与 DesiredResult 完全一致。现在,我们来详细解析这个解决方案的每一步。

1. 识别起始点和结束点标记

首先,我们需要将列 'A' 和 'B' 中的 '1' 转换为布尔序列,以便于进行逻辑操作。

s = prac['A'].eq(1) # 's' 代表 'A' 列中 '1' 的位置
e = prac['B'].eq(1) # 'e' 代表 'B' 列中 '1' 的位置

print("\n布尔序列 s (A==1):")
print(s)
print("\n布尔序列 e (B==1):")
print(e)

s 和 e 现在是布尔序列,True 表示原位置为 '1',False 表示原位置为 '0'。

2. 处理 'B' 列作为潜在的“延续”或“新起始”

这是解决方案中最巧妙的部分。我们不仅要考虑 'A' 列中的 '1' 作为起始点,还要考虑 'B' 列中的 '1'。特别是,如果 'B' 列中的 '1' 能够独立开启一个新的填充范围,或者在 'A' 列的 '1' 之后延续填充,我们需要识别它。

表达式 (e.diff(-1).ne(0) & e).shift() 的作用是找出 'B' 列中那些“独立”的 '1' 或“新开始”的 '1'。

  • e.diff(-1):计算 e 序列中当前元素与其后一个元素的差值。
    • 如果 e 是 [False, True, True, False],那么 e.diff(-1) 会是 [NaN, True, False, False]。
    • True 表示 False 后面是 True(从0到1的跳变)。
    • False 表示 True 后面是 True(保持1)。
    • False 表示 True 后面是 False(从1到0的跳变)。
  • .ne(0):将非零值(即 True)标记为 True。这会识别出从 False 到 True 的跳变。
  • & e:与原始的 e 序列进行按位与操作。这确保我们只考虑那些本身就是 True 的位置。
  • .shift():将结果向下移动一个位置。这是关键一步,它将识别到的“B列中一个'1'块的起始点”向前移动,使其对齐到该块的第一个 '1' 的位置。

让我们逐步看 (e.diff(-1).ne(0) & e).shift() 的结果:

print("\ne.diff(-1):")
print(e.diff(-1))

print("\ne.diff(-1).ne(0):")
print(e.diff(-1).ne(0))

print("\n(e.diff(-1).ne(0) & e):")
print((e.diff(-1).ne(0) & e))

print("\n(e.diff(-1).ne(0) & e).shift():")
print((e.diff(-1).ne(0) & e).shift())

通过 shift() 操作,我们有效地捕获了 'B' 列中每个 '1' 连续块的起始位置。

3. 组合所有起始条件

现在,我们将 'A' 列的起始点 s 和 'B' 列中经过处理的起始点 (e.diff(-1).ne(0) & e).shift() 进行逻辑或(|)操作。这会生成一个布尔序列,其中 True 表示任何一个有效的填充起始点。

combined_starts = s | (e.diff(-1).ne(0) & e).shift()
print("\n组合后的所有填充起始点 (s | (e.diff(-1).ne(0) & e).shift()):")
print(combined_starts)

这个 combined_starts 序列现在包含了所有我们希望开始前向填充的位置。

4. 应用 where() 和 ffill()

接下来,我们使用 s.where(combined_starts)。where() 方法根据条件选择值:如果 combined_starts 中的值为 True,则保留 s 中对应位置的值;如果为 False,则替换为 NaN。

masked_series = s.where(combined_starts)
print("\n应用 where() 后的序列:")
print(masked_series)

现在,masked_series 中只有那些被 combined_starts 标记为 True 的位置保留了 s 的值(即 True 或 False),其他位置都变成了 NaN。这正是我们进行前向填充的理想输入:True 表示填充的起始,NaN 表示需要填充或跳过。

然后,我们对 masked_series 应用 ffill()。ffill() 会将 NaN 值替换为其前一个非 NaN 值。

filled_series = masked_series.ffill()
print("\n应用 ffill() 后的序列:")
print(filled_series)

此时,filled_series 已经包含了大部分我们期望的 '1' 序列。

5. 清理和类型转换

最后一步是处理可能存在的 NaN 值(例如,如果序列开头就没有 '1',那么 ffill() 无法填充这些初始的 NaN)并将其转换为整数类型。

  • .fillna(0):将所有剩余的 NaN 替换为 '0'。
  • .astype(int):将布尔值 True/False 转换为整数 1/0。
final_result = filled_series.fillna(0).astype(int)
print("\n最终结果 (fillna(0).astype(int)):")
print(final_result)

这个 final_result 就是我们 DesiredResult 所期望的输出。

总结与注意事项

通过结合使用 eq() 进行布尔索引、diff() 识别变化、shift() 调整位置、where() 进行条件选择以及 ffill() 执行前向填充,我们能够灵活地处理复杂的条件性数据填充需求。这种方法的核心在于精确构造一个布尔掩码,该掩码能够识别所有有效的填充起始点。

关键概念回顾:

  • 布尔索引 (.eq()): 将数值列转换为布尔序列,便于逻辑操作。
  • 差分 (.diff()): 计算序列中元素之间的差值,常用于识别变化点。
  • 移位 (.shift()): 将序列中的元素向上或向下移动,对于处理时间序列或前后依赖关系非常有用。
  • 条件选择 (.where()): 根据布尔条件保留或替换Series/DataFrame中的值。
  • 前向填充 (.ffill()): 将 NaN 值替换为前一个非 NaN 值。

这种方法不仅适用于 '1',也可以推广到其他特定值或更复杂的条件。理解每一步操作的逻辑,特别是 diff() 和 shift() 的组合使用,是掌握Pandas高级数据处理能力的关键。在实际应用中,根据具体业务逻辑,可能需要调整 diff() 的参数(如 periods)或 shift() 的方向和步长,以适应不同的条件填充模式。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Python 时间序列分析与预测
Python 时间序列分析与预测

本专题专注讲解 Python 在时间序列数据处理与预测建模中的实战技巧,涵盖时间索引处理、周期性与趋势分解、平稳性检测、ARIMA/SARIMA 模型构建、预测误差评估,以及基于实际业务场景的时间序列项目实操,帮助学习者掌握从数据预处理到模型预测的完整时序分析能力。

54

2025.12.04

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

401

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

543

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

73

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

197

2025.08.29

C++类型转换方式
C++类型转换方式

本专题整合了C++类型转换相关内容,想了解更多相关内容,请阅读专题下面的文章。

299

2025.07.15

数据分析的方法
数据分析的方法

数据分析的方法有:对比分析法,分组分析法,预测分析法,漏斗分析法,AB测试分析法,象限分析法,公式拆解法,可行域分析法,二八分析法,假设性分析法。php中文网为大家带来了数据分析的相关知识、以及相关文章等内容。

471

2023.07.04

数据分析方法有哪几种
数据分析方法有哪几种

数据分析方法有:1、描述性统计分析;2、探索性数据分析;3、假设检验;4、回归分析;5、聚类分析。本专题为大家提供数据分析方法的相关的文章、下载、课程内容,供大家免费下载体验。

280

2023.08.07

拼多多赚钱的5种方法 拼多多赚钱的5种方法
拼多多赚钱的5种方法 拼多多赚钱的5种方法

在拼多多上赚钱主要可以通过无货源模式一件代发、精细化运营特色店铺、参与官方高流量活动、利用拼团机制社交裂变,以及成为多多进宝推广员这5种方法实现。核心策略在于通过低成本、高效率的供应链管理与营销,利用平台社交电商红利实现盈利。

24

2026.01.26

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
誉天教育RHCE视频教程
誉天教育RHCE视频教程

共9课时 | 1.4万人学习

尚观Linux RHCE视频教程(二)
尚观Linux RHCE视频教程(二)

共34课时 | 5.8万人学习

尚观RHCE视频教程(一)
尚观RHCE视频教程(一)

共28课时 | 4.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号