高效利用Pandas与NumPy根据键值条件映射DataFrame多列数据


高效利用Pandas与NumPy根据键值条件映射DataFrame多列数据

本教程探讨了如何高效地根据dataframe中“键”列的值,有条件地映射和修改多列数据。针对重复使用`numpy.select`的低效性,文章提供了两种优化的矢量化解决方案:一是利用`pandas.get_dummies`创建布尔掩码并结合`dataframe.mask`进行批量替换;二是采用数据重塑(`melt`、`merge`、`unstack`)的方法实现灵活的数据过滤与填充,旨在提升数据处理性能和代码可读性。

在数据分析和处理中,我们经常需要根据某一“键”列的值,有选择性地更新或保留DataFrame中其他多列的数据。例如,如果key列的值是'key1',我们可能只关心colA和colD的值,而其他列则应被标记为'NA'。传统上,这可能通过为每个目标列单独调用numpy.select来实现,但这在处理大量列时效率低下且代码冗余。本教程将介绍两种更高效、更具Pythonic风格的矢量化方法来解决这一问题。

问题场景概述

假设我们有一个DataFrame,其中包含一个key列和若干数据列(如colA到colD)。我们的目标是:

  • 对于每一行,如果key列的值与特定条件匹配,则保留某些指定列的原始值。
  • 如果key列的值不匹配,则将这些列的值设置为'NA'(或任何其他默认值)。
  • 一个key值可能对应多个需要保留的列。

以下是原始的低效实现示例:

import pandas as pd
import numpy as np

# 创建示例DataFrame
data = {
    'key': ['key1', 'key2', 'key3', 'key1', 'key2'],
    'colA': ['value1A', 'value2A', 'value3A', 'value4A', 'value5A'],
    'colB': ['value1B', 'value2B', 'value3B', 'value4B', 'value5B'],
    'colC': ['value1C', 'value2C', 'value3C', 'value4C', 'value5C'],
    'colD': ['value1D', 'value2D', 'value3D', 'value4D', 'value5D']
}
df = pd.DataFrame(data)

# 低效的重复调用 numpy.select
df['colA'] = np.select([df['key'] == 'key1'], [df['colA']], default='NA')
df['colD'] = np.select([df['key'] == 'key1'], [df['colD']], default='NA')
df['colB'] = np.select([df['key'] == 'key2'], [df['colB']], default='NA')
df['colC'] = np.select([df['key'] == 'key3'], [df['colC']], default='NA')

print("原始DataFrame和低效处理结果:")
print(df)

这种方法的问题在于,每当需要处理一个新列或新的key-column映射时,都需要添加一行新的np.select代码,这在列数很多时难以维护且效率低下。

解决方案一:利用 get_dummies 和 mask 创建布尔掩码

此方法的核心思想是首先构建一个布尔掩码,该掩码指示了DataFrame中每个单元格是否应该保留其原始值。然后,使用DataFrame.mask方法根据此掩码批量替换不符合条件的值。

1. 定义键与列的映射关系

首先,我们需要一个字典来明确每个key值对应哪些列应该被保留。

d = {'key1': ['colA', 'colD'],
     'key2': ['colB'],
     'key3': ['colC']}

2. 生成布尔掩码

接下来,我们将这个字典转换为一个布尔DataFrame,其中行索引是key值,列是数据列名。True表示该key值对应的行,该列应保留数据;False则表示应替换为'NA'。

# 将字典转换为Series并展开
s = pd.Series(d).explode()
# 使用get_dummies创建布尔矩阵,指示每个key对应哪些列
mask_df = pd.get_dummies(s, dtype=bool).groupby(level=0).max()

mask_df的结构将如下所示:

NoCode NoCode

美团推出的零代码应用生成平台

NoCode 180 查看详情 NoCode
       colA   colB   colC   colD
key1   True  False  False   True
key2  False   True  False  False
key3  False  False   True  False

3. 应用掩码到DataFrame

有了mask_df,我们可以将其重新索引到原始DataFrame的key列,生成一个与原始DataFrame数据部分形状相同的布尔数组。然后,使用DataFrame.mask方法,它会根据布尔条件替换值为True的位置上的数据(注意:mask方法默认替换True,where方法默认替换False)。为了达到我们的目的,即替换不符合条件(False)的值,我们可以直接使用where方法,或者对mask_df取反后使用mask。这里我们直接使用where方法,它在条件为True时保留原始值,条件为False时替换为指定值。

# 筛选出需要处理的数据列
cols_to_process = df.columns.difference(['key'])

# 根据df['key']重新索引mask_df,生成与df数据部分形状一致的布尔数组
# .to_numpy() 转换为NumPy数组以提高性能
aligned_mask = mask_df.reindex(df['key']).to_numpy()

# 使用where方法进行条件替换
df[cols_to_process] = df[cols_to_process].where(aligned_mask, 'NA')

完整代码示例:

import pandas as pd
import numpy as np

data = {
    'key': ['key1', 'key2', 'key3', 'key1', 'key2'],
    'colA': ['value1A', 'value2A', 'value3A', 'value4A', 'value5A'],
    'colB': ['value1B', 'value2B', 'value3B', 'value4B', 'value5B'],
    'colC': ['value1C', 'value2C', 'value3C', 'value4C', 'value5C'],
    'colD': ['value1D', 'value2D', 'value3D', 'value4D', 'value5D']
}
df = pd.DataFrame(data)

d = {'key1': ['colA', 'colD'],
     'key2': ['colB'],
     'key3': ['colC']}

# 1. 创建键与列的映射Series
s = pd.Series(d).explode()

# 2. 生成布尔掩码DataFrame
# get_dummies将s转换为one-hot编码形式的DataFrame
# groupby(level=0).max() 合并相同key的行,确保所有对应列都为True
mask_df = pd.get_dummies(s, dtype=bool).groupby(level=0).max()

# 3. 筛选出需要处理的数据列
cols_to_process = df.columns.difference(['key'])

# 4. 根据df['key']对mask_df进行reindex,使其与原始DataFrame的行对齐
# to_numpy() 转换为NumPy数组,提高后续操作效率
aligned_mask = mask_df.reindex(df['key']).to_numpy()

# 5. 使用where方法进行条件替换:
# 当aligned_mask为True时,保留df[cols_to_process]的原始值
# 当aligned_mask为False时,替换为'NA'
df[cols_to_process] = df[cols_to_process].where(aligned_mask, 'NA')

print("\n解决方案一结果:")
print(df)

解决方案二:利用数据重塑(melt, merge, unstack)

第二种方法通过将数据从宽格式(wide format)转换为长格式(long format),进行过滤,然后再转换回宽格式来实现。这种方法在处理更复杂的数据过滤和聚合场景时非常强大。

1. 定义键与列的映射关系

与方法一相同,我们首先定义映射字典:

d = {'key1': ['colA', 'colD'],
     'key2': ['colB'],
     'key3': ['colC']}

2. 数据重塑为长格式并合并过滤

  • melt: 将原始DataFrame的数据列转换为行,创建variable(列名)和value列。同时保留原始索引和key列。
  • 创建映射DataFrame: 将映射字典d也转换为长格式,包含key和variable。
  • merge: 将熔化后的原始数据与映射DataFrame合并。只有当原始数据的key和variable(列名)组合在映射字典中存在时,数据才会被保留。
  • set_index: 设置新的索引,为后续的unstack做准备。
# 1. 准备映射数据
map_df = pd.Series(d).explode().rename_axis('key').reset_index(name='variable')

# 2. 熔化原始DataFrame,保留'index'和'key'作为id_vars
melted_df = df.reset_index().melt(['index', 'key'])

# 3. 将熔化后的数据与映射数据合并,实现过滤
# 只有在map_df中存在的(key, variable)组合才会被保留
filtered_df = melted_df.merge(map_df)

# 4. 设置索引并堆叠,将'value'列重新转换为宽格式
result_df = filtered_df.set_index(['index', 'key', 'variable'])['value'] \
                       .unstack('variable', fill_value='NA') \
                       .reset_index('key') \
                       .rename_axis(index=None, columns=None)

完整代码示例:

import pandas as pd
import numpy as np

data = {
    'key': ['key1', 'key2', 'key3', 'key1', 'key2'],
    'colA': ['value1A', 'value2A', 'value3A', 'value4A', 'value5A'],
    'colB': ['value1B', 'value2B', 'value3B', 'value4B', 'value5B'],
    'colC': ['value1C', 'value2C', 'value3C', 'value4C', 'value5C'],
    'colD': ['value1D', 'value2D', 'value3D', 'value4D', 'value5D']
}
df = pd.DataFrame(data)

d = {'key1': ['colA', 'colD'],
     'key2': ['colB'],
     'key3': ['colC']}

# 1. 将原始DataFrame的索引重置,并将其和'key'列作为标识符,将其他数据列“熔化”为长格式
# 'index'列用于后续重构原始DataFrame的顺序
melted_df = df.reset_index().melt(['index', 'key'])

# 2. 将映射字典d转换为一个DataFrame,其中包含'key'和'variable'(列名)
map_df = pd.Series(d).explode().rename_axis('key').reset_index(name='variable')

# 3. 将熔化后的数据与映射DataFrame合并
# 只有当melted_df中的(key, variable)组合在map_df中存在时,该行才会被保留
merged_df = melted_df.merge(map_df, on=['key', 'variable'])

# 4. 设置新的多级索引,然后使用unstack将'variable'列重新转换为列
# fill_value='NA'用于填充那些没有匹配到的单元格
# reset_index('key') 将key列从索引中移回普通列
# rename_axis(index=None, columns=None) 清理索引和列的名称,使其更美观
result_df = merged_df.set_index(['index', 'key', 'variable'])['value'] \
                     .unstack('variable', fill_value='NA') \
                     .reset_index('key') \
                     .rename_axis(index=None, columns=None)

# 5. 将处理后的数据合并回原始DataFrame,或者直接使用result_df
# 为了保持原始DataFrame的结构,这里可以将key列也考虑进去
final_df = df[['key']].merge(result_df, left_index=True, right_index=True, how='left')
# 确保列顺序与原始问题一致,并且没有重复的key列
final_df = final_df[['key'] + [col for col in df.columns if col not in ['key']]]

print("\n解决方案二结果:")
print(final_df)

注意:在实际应用中,如果只是需要最终结果,可以直接使用result_df。如果需要确保原始key列的位置和所有列的顺序与原始df完全一致,可能需要额外的列重排操作。上述代码中,为了保持与原始df的key列和列顺序一致,进行了一次merge和列重排。

总结与注意事项

这两种矢量化方法都比重复调用numpy.select更高效、更简洁,尤其是在处理大量列和复杂映射关系时。

  • 方法一(get_dummies + mask)
    • 优点:代码相对直观,直接构建布尔掩码进行条件替换。对于只需要根据条件替换现有DataFrame中值的情况,效率很高。
    • 适用场景:当你需要基于key列的值,有条件地保留或替换DataFrame中现有列的值时。
  • 方法二(melt + merge + unstack)
    • 优点:非常灵活,通过将数据重塑为长格式,可以更容易地进行过滤、聚合和更复杂的条件操作。
    • 适用场景:当你需要执行更复杂的数据转换,例如不仅是替换值,还可能涉及到根据key进行分组计算、聚合,或者从外部源合并数据来决定哪些值应该被保留时。它提供了一种更通用的数据操作范式。

在选择哪种方法时,可以根据具体需求和个人偏好来决定。通常,如果任务只是简单的条件替换,get_dummies和mask的组合可能更直接。如果数据操作涉及到更复杂的重组或与外部数据源的交互,melt/merge/unstack的管道会更具优势。无论选择哪种,都应优先考虑使用Pandas和NumPy提供的矢量化操作,以最大化数据处理的效率和可维护性。

以上就是高效利用Pandas与NumPy根据键值条件映射DataFrame多列数据的详细内容,更多请关注其它相关文章!


# 编码  # 我们可以  # 当你  # 矢量化  # 两种  # 浮点  # 键值  # 掩码  # 转换为  # 布尔  # red  # 代码可读性  # python  # 才会  # 店铺营销推广是什么岗位  # 安阳专业网站优化公司推荐  # 南昌seo博客  # 上海网站推广营销设计  # 重庆綦江厉害的网站建设  # seo优化阶段  # 网站资源优化与推广试题  # 网站优化步骤  # 定制行业营销推广费用  # 生鲜网站推广图片大全集 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894


相关推荐: 《土豆雅思》修改密码方法  123网页端官方登录页 123邮箱网页版即时通讯服务  苹果SE如何开启单手模式_苹果SE单手操作功能  J*a里如何处理ArithmeticException并防止除零_算术异常防护策略解析  TikTok网页版入口快速访问 TikTok官网账号登录方法  三星A55应用闪退排查步骤_Samsung A55稳定性优化技巧  电脑双系统如何安装和卸载 Windows和Linux双系统安装教程【详解】  iPhone 14 Pro如何更改区域设置_iPhone 14 Pro地区语言修改教程  《淘宝联盟》推广自己的店铺方法  C++如何实现矩阵乘法_C++二维数组矩阵运算代码示例  疯狂小鸟微信小游戏入口 疯狂小鸟网页版秒玩  《tt语音》超级玩家开通方法  AI图层蒙版怎么用_AI图层蒙版应用技巧与设计实例  百度网盘网页入口链接分享 百度网盘官网入口网页登录  微博网页版入口链接 微博网页版在线互动平台  苹果电脑如何快速查看电池状态 苹果电脑电池信息快捷方法  抖音赚钱快速入门_新手必看的抖音赚钱步骤  AO3中文版手机快速通道_AO3最新稳定链接更新  Go Template中优雅处理循环最后一项:自定义函数实践  附近酒吧怎么找?  《sketchbook》选中部分图案移动方法  消除网页顶部意外空白线:CSS布局常见问题与解决方案  edge浏览器怎么修改语言为中文_Edge界面语言切换教程  cad加载的线型看不见怎么办_cad线型不可见问题解决方法  快递优选如何查优选物流_快递优选专属物流渠道查询与配送时效  修复UI元素交互障碍:从“开始”按钮到信息框的平滑过渡实现  响应式设计中动态背景颜色条的实现指南  优化Flask模板中SQLAlchemy查询迭代标签:处理字符串空格问题  sublime如何自定义文件类型图标_AFileIcon插件的主题切换与个性化配置  胃动力不足?试试这5个调理方法  有道AI翻译入口 智能写作官方网站入口  《火花chat》搜索好友方法  CSS如何使用outline-offset与颜色组合突出元素边框  Word如何将文字快速转成表格 Word文本转换成表格功能使用技巧【效率】  《米姆米姆哈》米姆获取及技能攻略  铁路12306入口 铁路12306官网版入口登录网址  Python实时数据流中高效查找最大最小值  iPhone 13 mini如何清理Safari缓存_iPhone 13 mini浏览器缓存清理方法  路由器DNS怎么设置最快 优化DNS提升上网速度教程  《长生:天机降世》火塔小怪大全  口腔诊所管理软件推荐  苹果手机缓存怎么清除_苹果手机缓存如何清除iphone各版本操作步骤  Lar*el Eloquent中通过Join查询关联数据表:解决多行子查询问题  如何定制PrimeNG Sidebar的背景颜色  OPPO手机参数配置如何开启护眼模式_OPPO手机参数配置护眼模式开启指南  Firefox OS应用开发:解决XMLHttpRequest跨域请求阻塞问题  Golang如何使用gRPC拦截器实现日志收集_Golang gRPC拦截器日志收集实践  跨语言测试实践:使用Python Selenium测试现有J*a Web项目  《广发易淘金》国债逆回购操作教程  win11怎么更改账户类型 Win11标准用户和管理员权限切换【教程】 

 2025-11-26

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.