Pandas DataFrame分组数据首行保留与其余值NaN化处理

Pandas DataFrame分组数据首行保留与其余值NaN化处理

本教程详细阐述了如何在Pandas DataFrame中,针对指定分组键(如列’a’)的每个组,仅保留其首行的特定列数据,而将该组内其余行的这些列值设置为NaN。同时,教程也展示了如何高效地保留其他指定列的原始数据。文章将介绍一种基于where和fillna方法的矢量化解决方案,以避免低效的循环操作,确保处理大规模数据集时的性能和可扩展性。

1. 问题背景与挑战

在数据处理和分析中,我们经常遇到需要对dataframe进行分组操作的场景。一个常见需求是,在每个分组内部,我们可能只关心第一行的某些特定信息,而希望将后续行的这些信息清空(设置为nan),同时保持其他列的数据不变。

例如,给定以下DataFrame:

import pandas as pdimport numpy as npdf = pd.DataFrame(    {        'a': [            'a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b',        ],        'b': [            -20, 20, 20, 20,-70, -70,-11, -100, -1, -1, -100, 100        ],        'c': [            'f', 'f', 'f', 'f', 'f', 'x', 'x', 'k', 'k', 'k', 'k', 'k'        ],        'x': [            'p', 'p', 'p', 'p', 'p', 'x', 'x', 'i', 'i', 'i', 'i', 'i'        ],    })print("原始DataFrame:")print(df)

原始DataFrame:

    a    b  c  x0   a  -20  f  p1   a   20  f  p2   a   20  f  p3   a   20  f  p4   a  -70  f  p5   a  -70  x  x6   b  -11  x  x7   b -100  k  i8   b   -1  k  i9   b   -1  k  i10  b -100  k  i11  b  100  k  i

我们的目标是:

以列a进行分组。对于每个组,保留其第一行中列b和c的值。将每个组中除了第一行以外的行,其列b和c的值设置为NaN。列a和x的值应保持不变,不被NaN化。

期望的输出如下:

    a     b    c  x0   a -20.0    f  p1   a   NaN  NaN  p2   a   NaN  NaN  p3   a   NaN  NaN  p4   a   NaN  NaN  p5   a   NaN  NaN  x6   b -11.0    x  x7   b   NaN  NaN  i8   b   NaN  NaN  i9   b   NaN  NaN  i10  b   NaN  NaN  i11  b   NaN  NaN  i

一个常见的初步尝试是使用groupby().apply()结合iloc和get_loc。虽然这种方法对于少量列可行,但当需要处理数百列时,手动指定每一列或在循环中迭代列会变得非常低效且难以维护。

# 低效的尝试(不推荐用于大量列)def func(g):    # 假设我们知道要处理的列是'b'和'c'    g.iloc[1:, g.columns.get_loc('b')] = np.nan    g.iloc[1:, g.columns.get_loc('c')] = np.nan    return g# df_modified = df.groupby('a', as_index=False).apply(func)# print(df_modified)

这种方法需要为每个需要NaN化的列单独操作,或者在一个循环中完成,这对于大型DataFrame和大量列来说效率不高。

2. 高效的矢量化解决方案

Pandas提供了强大的矢量化操作,可以更高效地解决这类问题。我们将利用df.duplicated()、df.where()和df.fillna()的组合来实现目标。

2.1 核心思路

识别非首行: 使用df[‘a’].duplicated()来标记每个分组中除第一行之外的所有行。条件性NaN化: 使用df.where()结合上述标记,将所有非首行的值(除了分组键本身)设置为NaN。恢复特定列: 使用df.fillna(),根据原始DataFrame中需要保留的列(例如a和x)来填充之前被NaN化的这些列。

2.2 详细步骤与代码实现

import pandas as pdimport numpy as np# 重新创建原始DataFrame以确保操作的独立性df = pd.DataFrame(    {        'a': [            'a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b',        ],        'b': [            -20, 20, 20, 20,-70, -70,-11, -100, -1, -1, -100, 100        ],        'c': [            'f', 'f', 'f', 'f', 'f', 'x', 'x', 'k', 'k', 'k', 'k', 'k'        ],        'x': [            'p', 'p', 'p', 'p', 'p', 'x', 'x', 'i', 'i', 'i', 'i', 'i'        ],    })# 步骤1: 识别每个分组中的重复行(即非首行)# df['a'].duplicated() 会为每个'a'组的第一个出现返回False,后续重复出现返回True# ~df['a'].duplicated() 则会为每个'a'组的第一个出现返回True,后续重复出现返回Falsemask = ~df['a'].duplicated()# print("重复行掩码 (~df['a'].duplicated()):")# print(mask)# 步骤2: 使用df.where()进行条件性替换# df.where(condition) 会保留condition为True的元素,将condition为False的元素替换为NaN# 此时,所有非首行的值(包括'a'、'b'、'c'、'x')都会被替换为NaNdf_temp = df.where(mask)# print("n经过df.where(mask)处理后的DataFrame:")# print(df_temp)# 步骤3: 使用df.fillna()恢复需要保留原始值的列# 我们希望保留'a'和'x'列的原始值。# df.fillna(other_df) 会根据other_df来填充df中的NaN值。# 只有在df中为NaN且other_df中对应位置有非NaN值时,才会进行填充。# 并且,填充操作只针对other_df中存在的列进行。columns_to_preserve = ['a', 'x']df_final = df_temp.fillna(df[columns_to_preserve])print("n最终处理结果:")print(df_final)

输出结果:

最终处理结果:    a     b    c  x0   a -20.0    f  p1   a   NaN  NaN  p3   a   NaN  NaN  p2   a   NaN  NaN  p4   a   NaN  NaN  p5   a   NaN  NaN  x6   b -11.0    x  x7   b   NaN  NaN  i8   b   NaN  NaN  i9   b   NaN  NaN  i10  b   NaN  NaN  i11  b   NaN  NaN  i

注意:输出的行索引顺序可能与原始示例略有不同,这是因为Pandas在处理过程中可能会调整索引,但这不影响数据的逻辑对应关系。如果需要严格的索引顺序,可以在操作后进行sort_index()或reset_index()。

2.3 关键概念解析

df[‘a’].duplicated():此方法用于标记DataFrame中a列的重复值。默认情况下(keep=’first’),它会为每个分组中的第一个出现返回False,而为后续的重复出现返回True。例如,对于a列中的第一个’a’,返回False;对于第二个’a’,返回True,以此类推。~df[‘a’].duplicated():~是逻辑非操作符。它将duplicated()的结果取反。因此,它会为每个分组中的第一个出现返回True,而为后续的重复出现返回False。这个布尔Series就是我们用来识别首行的掩码。df.where(condition):where()方法是一个强大的条件选择工具。它会根据condition(一个布尔Series或DataFrame)来选择数据。当condition中的值为True时,df.where()保留DataFrame中对应位置的原始值。当condition中的值为False时,df.where()会将DataFrame中对应位置的值替换为NaN(默认行为)。在本例中,df.where(~df[‘a’].duplicated())会保留每个分组的第一行,并将所有后续行的所有列(包括a、b、c、x)都设置为NaN。df.fillna(other_df):fillna()用于填充DataFrame中的NaN值。当other_df是一个DataFrame时,fillna()会尝试根据other_df中对应列和索引的值来填充当前DataFrame中的NaN。具体来说,它会查找当前DataFrame中为NaN的位置,如果other_df在相同列和索引位置有非NaN值,则用other_df的值进行填充。在本例中,df_temp在非首行的所有列都变成了NaN。df_temp.fillna(df[[‘a’, ‘x’]])会查看df_temp中的NaN。对于a列和x列中的NaN,它会用原始df中对应a列和x列的值来填充。而b列和c列的NaN不会被填充,因为df[[‘a’, ‘x’]]中不包含b和c列。

3. 注意事项与拓展

列的选择: columns_to_preserve = [‘a’, ‘x’]这一步非常关键。它明确指定了哪些列在非首行时也应该保留其原始值。如果你希望除了分组列之外的所有列在非首行时都变为NaN,那么columns_to_preserve就只包含分组列即可(例如[‘a’])。性能: 这种基于where和fillna的矢量化方法在处理大型DataFrame时比groupby().apply()结合行迭代的方式效率高得多,因为它利用了Pandas底层的优化C/Cython实现。通用性: 这种模式可以轻松推广到任何分组键和任意数量的需要保留或NaN化的列。数据类型: 当将数值列中的值替换为NaN时,如果该列原本是整数类型,Pandas会自动将其转换为浮点数类型(因为NaN在Pandas中通常表示为浮点数)。例如,b列从int64变为float64。这是预期行为。

4. 总结

通过巧妙地结合duplicated()、where()和fillna()这三个Pandas函数,我们能够高效且灵活地实现DataFrame分组数据的首行保留与其余值NaN化处理。这种方法不仅代码简洁,而且在处理大规模数据集时表现出卓越的性能,是Pandas数据操作中值得掌握的实用技巧。

以上就是Pandas DataFrame分组数据首行保留与其余值NaN化处理的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1369197.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
Python字典键值对齐输出:利用f-string实现动态宽度格式化
上一篇 2025年12月14日 09:27:02
Python 二维数组元素修改:避免引用陷阱
下一篇 2025年12月14日 09:27:21

相关推荐

  • composer require-dev和require有什么不同_Composer Require与Require-Dev区别解析

    require用于声明项目运行必需的依赖,如框架、数据库组件和第三方SDK,这些包会随项目部署到生产环境;2. require-dev用于声明仅在开发和测试阶段需要的工具,如PHPUnit、PHPStan、Faker等,不会默认部署到生产环境;3. 安装时composer install根据环境决定…

    2026年5月10日
    1000
  • 利用海象运算符简化条件赋值:Python教程与最佳实践

    本文旨在探讨Python中海象运算符(:=)在条件赋值场景下的应用。通过对比传统if/else语句与海象运算符,以及条件表达式,分析海象运算符在简化代码、提高可读性方面的优势与局限性。并通过具体示例,展示如何在列表推导式等场景下合理使用海象运算符,同时强调其潜在的复杂性及替代方案,帮助开发者更好地掌…

    2026年5月10日
    300
  • Debian syslog性能优化技巧有哪些

    提升Debian系统syslog (通常基于rsyslog)性能,关键在于精简配置和高效处理日志。以下策略能有效优化日志管理,提升系统整体性能: 精简配置,高效加载: 在rsyslog配置文件中,仅加载必要的输入、输出和解析模块。 使用全局指令设置日志级别和格式,避免不必要的处理。 自定义模板: 创…

    2026年5月10日
    000
  • c++中的SFINAE技术是什么_c++模板编程中的SFINAE原理与应用

    SFINAE 是“替换失败不是错误”的原则,指模板实例化时若参数替换导致错误,只要存在其他合法候选,编译器不报错而是继续重载决议。它用于条件启用模板、类型检测等场景,如通过 decltype 或 enable_if 控制函数重载,实现类型特征判断。尽管 C++20 引入 Concepts 简化了部分…

    2026年5月10日
    000
  • Golang goroutine与channel调试技巧

    使用go run -race检测数据竞争,结合runtime.NumGoroutine监控协程数量,通过pprof分析阻塞调用栈,利用select超时避免永久阻塞,有效排查goroutine泄漏、死锁和数据竞争问题。 Go语言的goroutine和channel是并发编程的核心,但它们也带来了调试上…

    2026年5月10日
    000
  • 使用 Jupyter Notebook 进行探索性数据分析

    Jupyter Notebook通过单元格实现代码与Markdown结合,支持数据导入(pandas)、清洗(fillna)、探索(matplotlib/seaborn可视化)、统计分析(describe/corr)和特征工程,便于记录与分享分析过程。 Jupyter Notebook 是进行探索性…

    2026年5月10日
    000
  • 网站标题关键词更新后,搜索引擎为何仍显示旧标题?

    网站标题更新后,搜索引擎为何显示旧标题? 网站SEO优化中,站长常修改网站标题关键词,期望搜索结果显示自定义标题。然而,即使更新标签、meta keywords、meta description和结构化数据中的name属性后,搜索结果仍显示旧标题,这令人费解。本文将对此进行解释。 问题:站长修改了网…

    2026年5月10日
    300
  • Python命令怎样使用profile分析脚本性能 Python命令性能分析的基础教程

    使用Python的cProfile模块分析脚本性能最直接的方式是通过命令行执行python -m cProfile your_script.py,它会输出每个函数的调用次数、总耗时、累积耗时等关键指标,帮助定位性能瓶颈;为进一步分析,可将结果保存为文件python -m cProfile -o ou…

    2026年5月10日
    000
  • 如何插入查询结果数据_SQL插入Select查询结果方法

    如何插入查询结果数据_SQL插入Select查询结果方法如何插入查询结果数据_SQL插入Select查询结果方法如何插入查询结果数据_SQL插入Select查询结果方法如何插入查询结果数据_SQL插入Select查询结果方法

    使用INSERT INTO…SELECT语句可高效插入数据,通过NOT EXISTS、LEFT JOIN、MERGE语句或唯一约束避免重复;表结构不一致时可通过别名、类型转换、默认值或计算字段处理;结合存储过程可提升可维护性,支持参数化与动态SQL。 将查询结果数据插入到另一个表中,可以…

    2026年5月10日 用户投稿
    400
  • python中zip函数详解 python多序列压缩zip函数应用场景

    zip函数的应用场景包括:1) 同时遍历多个序列,2) 合并多个列表的数据,3) 数据分析和科学计算中的元素运算,4) 处理csv文件,5) 性能优化。zip函数是一个强大的工具,能够简化代码并提高处理多个序列时的效率。 在Python中,zip函数是一个非常有用的工具,它能够将多个可迭代对象打包成…

    2026年5月10日
    300
  • 谷歌浏览器如何截图 谷歌浏览器页面截图技巧

    谷歌浏览器如何截图 谷歌浏览器页面截图技巧谷歌浏览器如何截图 谷歌浏览器页面截图技巧谷歌浏览器如何截图 谷歌浏览器页面截图技巧谷歌浏览器如何截图 谷歌浏览器页面截图技巧

    使用谷歌浏览器的开发者工具截图步骤:1. 按ctrl+shift+i(windows/linux)或cmd+option+i(mac)打开开发者工具。2. 点击右上角三个点,选择”更多工具”,再选择”截图”。3. 选择截取整个页面。推荐的谷歌浏览器扩展…

    2026年5月10日 用户投稿
    100
  • Python中怎样使用pymongo?

    在python中使用pymongo可以轻松地与mongodb数据库进行交互。1)安装pymongo:pip install pymongo。2)连接到mongodb:from pymongo import mongoclient; client = mongoclient(‘mongod…

    2026年5月10日
    000
  • JS如何实现迭代器?迭代器协议

    JavaScript中实现迭代器需遵循可迭代协议和迭代器协议,通过定义[Symbol.iterator]方法返回具备next()方法的迭代器对象,从而支持for…of和展开运算符;该机制统一了数据结构的遍历接口,实现惰性求值,适用于自定义对象、树、图及无限序列等复杂场景,提升代码通用性与…

    2026年5月10日
    300
  • JavaScript函数中插入加载动画(Spinner)的正确方法

    本文旨在解决在JavaScript函数中插入加载动画(Spinner)时遇到的异步问题。通过引入async/await和Promise.all,确保在数据处理完成前后正确显示和隐藏加载动画,提升用户体验。我们将提供两种实现方案,并详细解释其原理和优势。 在Web开发中,当执行耗时操作时,显示加载动画…

    2026年5月10日
    500
  • Golang空接口如何应用在项目中

    空接口可用于接收任意类型值,常见于日志函数、通用数据结构、JSON动态解析及配置驱动逻辑,提升代码灵活性,但需配合类型断言确保安全,避免滥用以降低维护成本。 空接口 interface{} 在 Go 语言中是一个非常灵活的类型,它可以存储任何类型的值。虽然它牺牲了一部分类型安全,但在实际项目中合理使…

    2026年5月10日
    300
  • Golang使用Protobuf定义接口与消息格式

    Protobuf通过字段编号实现兼容性,新增字段可忽略、删除字段可保留编号,确保新旧版本互操作,支持服务独立演进。 在Golang项目中,利用Protobuf定义接口和消息格式,本质上是为服务间通信构建了一套高效、类型安全且跨语言的契约。它让数据结构清晰可见,RPC调用标准化,极大地简化了分布式系统…

    2026年5月10日
    000
  • PHP多维数组到复杂XML结构的SOAP序列化实践

    本文旨在解决php多维数组向复杂soap xml结构序列化时遇到的“无法序列化结果”问题。通过深入理解soap xml的结构要求,包括命名空间和类型属性,文章将指导您如何构建符合特定xml schema的php关联数组。我们将利用`spatie/array-to-xml`库,详细演示其安装与使用方法…

    2026年5月10日
    100
  • 使用 Ajax 和 FormData 实现文件上传及文本数据提交的完整教程

    本文旨在解决在使用 Ajax 和 FormData 进行文件上传时,遇到的 $_POST 和 $_FILES 为空的问题。通过详细的代码示例和解释,我们将展示如何正确地构建 FormData 对象,并通过 Ajax 将文件和文本数据发送到服务器端,同时避免常见的错误配置,确保数据能够成功地被 PHP…

    2026年5月10日
    000
  • pycharm解析器怎么添加 解析器添加详细流程

    在pycharm中添加解析器的步骤包括:1) 打开pycharm并进入设置,2) 选择project interpreter,3) 点击齿轮图标并选择add,4) 选择解析器类型并配置路径,5) 点击ok完成添加。添加解析器后,选择合适的类型和版本,配置环境变量,并利用解析器的功能提高开发效率。 在…

    2026年5月10日
    100
  • CSS技巧:在复杂悬停效果中确保图像始终可见

    CSS技巧:在复杂悬停效果中确保图像始终可见CSS技巧:在复杂悬停效果中确保图像始终可见CSS技巧:在复杂悬停效果中确保图像始终可见CSS技巧:在复杂悬停效果中确保图像始终可见

    本教程探讨如何在包含悬停效果的CSS卡片布局中,确保图像始终显示在最顶层而不被裁剪或遮挡。通过调整HTML结构,利用CSS的position和z-index属性,以及引入pointer-events,我们将解决图像被overflow: hidden和扩展叠加层遮盖的问题,实现复杂的视觉交互效果。 在…

    2026年5月10日 用户投稿
    000

发表回复

登录后才能评论
关注微信