新闻中心

Pandas DataFrame中连续行块计数的高效方法

2025-11-24
浏览次数:
返回列表

Pandas DataFrame中连续行块计数的高效方法

本文将深入探讨如何在pandas dataframe中高效地计算某一列连续相同值的行数,并将其作为新列添加。通过结合`shift()`、`cumsum()`和`groupby().transform('size')`等pandas核心函数,我们将展示一种优雅且强大的解决方案,精确识别并统计数据集中所有连续值块的长度,避免了传统`groupby`的局限性,适用于需要精细化连续数据分析的场景。

在数据处理和分析中,我们经常会遇到需要统计DataFrame中某一列连续相同值出现的次数。例如,给定一个序列 ['a', 'a', 'a', 'b', 'b', 'c', 'd', 'e', 'e', 'e'],我们希望得到的结果是 [3, 3, 3, 2, 2, 1, 1, 3, 3, 3],即每个连续块的长度。这与简单地按值分组并计数不同,因为同一个值在DataFrame中可能非连续地出现多次,而我们只关心连续出现的块。

问题场景与常见误区

假设我们有以下DataFrame:

import pandas as pd

data = {
    'class': ['a', 'a', 'a', 'b', 'b', 'c', 'd', 'e', 'e', 'e', 'f', 'a', 'c', 'd', 'd']
}
df = pd.DataFrame(data)
print("原始DataFrame:")
print(df)

输出:

原始DataFrame:
   class
0      a
1      a
2      a
3      b
4      b
5      c
6      d
7      e
8      e
9      e
10     f
11     a
12     c
13     d
14     d

我们期望的结果是为每行添加一个 consecutive_count 列,其值表示该行所属的连续块的长度:

   class  consecutive_count
0      a                  3
1      a                  3
2      a                  3
3      b                  2
4      b                  2
5      c                  1
6      d                  1
7      e                  3
8      e                  3
9      e                  3
10     f                  1
11     a                  1
12     c                  1
13     d                  2
14     d                  2

误区一:使用 groupby().transform('count')

直接按 class 列进行分组并使用 transform('count') 会统计每个 class 值在整个DataFrame中出现的总次数,而不是连续块的次数。

df['total_count'] = df.groupby('class')['class'].transform('count')
print("\n使用 transform('count') 的结果:")
print(df)

输出:

使用 transform('count') 的结果:
   class  total_count
0      a            4
1      a            4
2      a            4
3      b            2
4      b            2
5      c            2
6      d            3
7      e            3
8      e            3
9      e            3
10     f            1
11     a            4
12     c            2
13     d            3
14     d            3

显然,这不符合我们的需求,例如,第一个 'a' 块有3个,但结果显示为4,因为后面还有一个单独的 'a'。

误区二:使用 (df['class'] != df['class'].shift()).cumsum()

这个表达式可以为每个连续块生成一个唯一的标识符,但它本身不提供块的长度。

启科网络PHP商城系统 启科网络PHP商城系统

启科网络商城系统由启科网络技术开发团队完全自主开发,使用国内最流行高效的PHP程序语言,并用小巧的MySql作为数据库服务器,并且使用Smarty引擎来分离网站程序与前端设计代码,让建立的网站可以自由制作个性化的页面。 系统使用标签作为数据调用格式,网站前台开发人员只要简单学习系统标签功能和使用方法,将标签设置在制作的HTML模板中进行对网站数据、内容、信息等的调用,即可建设出美观、个性的网站。

启科网络PHP商城系统 0 查看详情 启科网络PHP商城系统
df['consecutive_group_id'] = (df['class'] != df['class'].shift()).cumsum()
print("\n使用 cumsum() 生成组ID的结果:")
print(df)

输出:

使用 cumsum() 生成组ID的结果:
   class  consecutive_group_id
0      a                     1
1      a                     1
2      a                     1
3      b                     2
4      b                     2
5      c                     3
6      d                     4
7      e                     5
8      e                     5
9      e                     5
10     f                     6
11     a                     7
12     c                     8
13     d                     9
14     d                     9

这里 consecutive_group_id 成功地将不同的连续块区分开来(例如,第一个 'a' 块的ID是1,而第11行的 'a' 块的ID是7)。这是解决问题的关键一步,但还需要进一步处理才能得到块的长度。

解决方案:组合动态分组与 transform('size')

解决此问题的核心在于创建一个能够同时识别值和其连续性的分组键。我们可以利用 (df['class'] != df['class'].shift()).cumsum() 生成的连续组ID,并将其与原始的 class 值结合起来作为 groupby 的键。

步骤详解:

  1. 识别连续块的起始点:df['class'].shift() 将 class 列向下移动一行,使得当前行的值可以与上一行的值进行比较。 df['class'] != df['class'].shift() 会生成一个布尔序列,True 表示当前行的值与上一行的值不同(即一个新的连续块开始),False 表示相同。对于第一行,shift() 会产生 NaN,与任何值比较都会是 True,这恰好符合其作为第一个块起始点的逻辑。

  2. 生成唯一的连续块ID: 对上述布尔序列应用 .cumsum()。由于 True 在求和时被视为1,False 被视为0,cumsum() 会为每个新的连续块分配一个递增的唯一整数ID。这样,即使同一个值(如 'a')在DataFrame中多次非连续地出现,它们也会被赋予不同的连续块ID。

  3. 构建复合分组键: 现在我们有了两个关键信息:原始的 class 值,以及它所属的连续块的唯一ID。我们将这两者结合起来作为 groupby 的键: df.groupby(['class', (df['class'] != df['class'].shift()).cumsum()]) 这个 groupby 操作会创建一个组,其中每个组都由一个唯一的 (class值, 连续块ID) 对定义。例如,第一个 'a' 块会形成一个组 ('a', 1),而第11行的 'a' 块会形成另一个组 ('a', 7)。

  4. 计算并广播组大小: 在分组之后,我们使用 .transform('size')。transform('size') 的作用是计算每个组的元素数量(即连续块的长度),然后将这个计算结果广播回原始DataFrame中属于该组的所有行。这样,每个属于同一个连续块的行都会得到该块的正确长度。

完整代码示例:

import pandas as pd

# 原始数据
data = {
    'class': ['a', 'a', 'a', 'b', 'b', 'c', 'd', 'e', 'e', 'e', 'f', 'a', 'c', 'd', 'd']
}
df = pd.DataFrame(data)

# 核心解决方案
df['consecutive_count'] = df.groupby(['class', (df['class'] != df['class'].shift()).cumsum()]).transform('size')

print("\n最终结果DataFrame:")
print(df)

输出:

最终结果DataFrame:
   class  consecutive_count
0      a                  3
1      a                  3
2      a                  3
3      b                  2
4      b                  2
5      c                  1
6      d                  1
7      e                  3
8      e                  3
9      e                  3
10     f                  1
11     a                  1
12     c                  1
13     d                  2
14     d                  2

这个结果与我们最初期望的完全一致。

注意事项与总结

  • shift() 处理 NaN: df['class'].shift() 在DataFrame的第一行会产生 NaN。当与 df['class'] 的第一个元素比较时,'a' != NaN 会被评估为 True(在Pandas和Python中,'a' == NaN 是 False),因此 cumsum() 从1开始计数是正确的。
  • 性能: 这种方法利用了Pandas底层的优化,对于大型DataFrame来说效率很高。
  • 通用性: 这种方法不仅适用于字符串类型,也适用于数值型或其他可比较的数据类型。
  • transform() 的强大: transform() 函数是Pandas中一个非常强大的工具,它允许在 groupby 操作后,将聚合结果(如 size, sum, mean 等)广播回原始DataFrame,保持DataFrame的原始形状,这在许多数据转换场景中都非常有用。

通过理解并运用 shift()、cumsum() 和 groupby().transform('size') 的组合,我们能够优雅且高效地解决Pandas DataFrame中连续行块计数的问题,这在时间序列分析、日志处理等领域具有广泛的应用价值。

以上就是Pandas DataFrame中连续行块计数的高效方法的详细内容,更多请关注其它相关文章!


# 工具  # 第一个  # 适用于  # 布尔  # 解决问题  # 这在  # 结合起来  # 转换为  # 创建一个  # 命令行  # python  # 石家庄网站建设方案维护  # 肇庆网站建设的概要设计  # 天津正规网站优化公司  # 广州网站建设推广好吗  # 衡水网站建设外包服务  # 推广职域营销  # 拉萨网站建设维护  # 百度产品如何做推广网站  # 河南企业建设网站  # seo刷词工  # 如何将 


相关栏目: 【 科技资讯46185 】 【 网络学院92790


相关推荐: 红果短剧网页版官网入口 官方最新网址发布  淘宝支付提示失败如何解决 淘宝支付流程优化方法  c++中的std::forward_list和std::list有什么不同_c++ forward_list与list区别分析  Lar*el的路由模型绑定怎么用_Lar*el Route Model Binding简化控制器逻辑  照顾宝贝2小游戏免费秒玩入口  探索高级语言到原生C/C++的转译:挑战与内存管理策略  Fabric Mod开发:在1.19.3+版本中正确添加自定义物品并管理物品组  CSS Grid如何控制元素对齐_align-items与justify-items组合使用  构建轻量级网站内部消息系统:Formspree 集成指南  文心一言怎样用批量生成做多版文案_文心一言用批量生成做多版文案【批量创作】  在Go开发中优雅管理ListenAndServe进程:GoSublime集成方案  vivo浏览器怎么扫描二维码 vivo浏览器内置扫一扫功能使用方法  J*a TimerTask中HashMap意外清空的深层原因与解决方案  怎么在浏览器上运行HTML文件_浏览器运行HTML文件技巧【技巧】  Win11网速慢怎么解决 Win11网络设置优化解除限速  绝地鸭卫平a核爆刀流玩法攻略  如何使用Rector自动化升级旧代码_通过Composer安装和配置Rector进行代码重构  零跑汽车11月交付量达70327台 实现连续9个月正增长  夸克浏览器图书入口 夸克手机浏览器阅读入口  Go与Ruby之间实现AES加密互通:CFB模式下的密钥长度匹配策略  Lar*el表单中优雅地处理“返回”按钮以规避验证:最佳实践指南  Lar*el 递归关系中排除指定分支的教程  如何优雅地扩展SprykerGlue后端API授权逻辑,使用spryker/glue-backend-api-application-authorization-connector-extension  在J*a项目里如何构建对象之间的契约_接口约束的实际落地  Python中高效且防溢出的双曲正弦计算:基于对数空间的优化策略  C++指针和引用有什么区别_C++内存管理核心概念深度解析  126邮箱账号注册 电脑版登录入口  iCloud登录入口网页版 苹果iCloud官网登录  yy漫画网页版官方入口_yy漫画官网登录页面链接  windows10怎么查看硬盘序列号_windows10硬盘id查询命令  css绝对定位元素脱离父容器怎么办_确保父元素position非static  网易大神账号申诉需要多久_网易大神账号申诉流程说明  J*aScript数据结构转换:将对象数组按类别分组  steam官方入口大全 steam账号注册及操作指南  Windows电脑怎么截图最方便_系统自带截图工具的5种神仙用法【技巧】  优化 Python 函数中的条件逻辑:解决 if-else 嵌套与参数选择问题  在Pyomo中实现基于变量的条件约束:Big-M方法详解  AO3最新可访问网址 Archive of Our Own官方在线入口  PyTorch模型训练准确率不提升:诊断与修复常见指标计算错误  解决Bootstrap卡片顶部边距导致背景图下移的问题  c++20的std::jthread是什么_c++可中断线程与RAII式管理  蛙漫官网漫画入口地址_蛙漫在线畅读无广告弹窗  深入理解rpy2中的类型转换:优化Python对象到R矩阵的映射  QQ邮箱登录官网首页 腾讯QQ邮箱网页入口  Win11怎么开启高性能模式_Windows 11电源计划优化设置  拼多多购物车商品数量无法修改如何处理 拼多多购物车操作优化方法  深入理解Google Cloud Datastore查询:祖先路径与数据一致性  sublime怎么覆盖插件的默认快捷键_sublime快捷键优先级与设置  怎么在mac上运行html代码_mac运行html代码方法【指南】  微信客户端如何收红包_微信客户端接收红包使用教程 

搜索