
在Pandas DataFrame中对包含混合数据类型的行进行求和时,直接使用`df.sum(axis=1, numeric_only=True)`可能因列中存在非纯数值类型而返回0。本文将详细介绍如何通过`pd.to_numeric(errors=’coerce’)`将非数值条目转换为`NaN`,然后进行精确求和,并进一步展示如何将总秒数格式化为更易读的时间间隔。
理解numeric_only参数的局限性
当我们在Pandas DataFrame中尝试对行进行求和,并期望只计算数值列时,一个常见的做法是使用df.sum(axis=1, numeric_only=True)。然而,如果DataFrame中的某些列虽然包含看起来像数字的字符串(例如’29160’),但也混杂了其他非数值字符串(例如’No hours logged’)甚至列表(例如[10:02, Odd number: missing entry]),那么numeric_only=True参数的行为可能与预期不符。
根据Pandas的官方文档,numeric_only=True的含义是“仅包含浮点、整数、布尔列”。这意味着,如果一整列中哪怕只有一个非数值类型的值(例如字符串),Pandas就会将该列视为非数值列,从而在求和时完全排除该列。在本例中,所有日期列都包含至少一个字符串值(如’No hours logged’),因此它们都被numeric_only=True排除,导致最终的total_hours列全部为0.0。
正确处理混合数据类型并进行行求和
要解决这个问题,我们需要在求和之前,显式地将目标列中的非数值条目转换为可计算的数值类型。
步骤1:数据预处理——将非数值转换为NaN
Pandas提供了pd.to_numeric()函数,它能够尝试将Series或DataFrame中的值转换为数值类型。结合errors=’coerce’参数,我们可以优雅地处理转换失败的情况:任何无法转换为数字的值都将被替换为NaN(Not a Number)。
import pandas as pd# 示例数据准备 (与原文提供的数据一致)data = { 'id': {0: 514, 1: 2414, 2: 3225, 3: 3434, 4: 3864, 5: 4716, 6: 5793}, 'name': {0: 'alexis', 1: 'donald', 2: 'mackenzie', 3: 'louisa', 4: 'olga', 5: 'rick', 6: 'roberta'}, '2023-11-28': {0: 'No hours logged', 1: '29160', 2: '28500', 3: '25380', 4: '15600', 5: '30180', 6: '29220'}, '2023-11-29': {0: 'No hours logged', 1: '29160', 2: '28620', 3: '18840', 4: '19080', 5: '28800', 6: '29220'}, '2023-11-30': {0: 'No hours logged', 1: '28860', 2: '28560', 3: '26040', 4: '14400', 5: '28740', 6: '29460'}, '2023-12-01': {0: 'No hours logged', 1: '28620', 2: '28620', 3: 'No hours logged', 4: '13800', 5: '28620', 6: '29280'}, '2023-12-02': {0: 'No hours logged', 1: '[10:02, Odd number: missing entry]', 2: '28980', 3: '25560', 4: '5220', 5: '28680', 6: '29340'}, '2023-12-03': {0: 'No hours logged', 1: 'No hours logged', 2: 'No hours logged', 3: 'No hours logged', 4: '17820', 5: 'No hours logged', 6: 'No hours logged'}, '2023-12-04': {0: 'No hours logged', 1: 'No hours logged', 2: 'No hours logged', 3: 'No hours logged', 4: 'No hours logged', 5: 'No hours logged', 6: 'No hours logged'}, '2023-12-05': {0: 'No hours logged', 1: '28860', 2: '28740', 3: '24900', 4: '14400', 5: '28680', 6: '29040'},}df = pd.DataFrame(data)# 假设daily_logs列已经展开,如果未展开,需要先执行类似 df = pd.concat([df,pd.DataFrame(list(df['daily_logs']))],axis=1).drop(columns='daily_logs') 的操作# 在本示例中,我们直接使用已展开的df# 选择需要求和的列(从索引为2的列开始,即跳过'id'和'name')# 对这些列应用pd.to_numeric,并将无法转换的值设为NaN# 然后对转换后的结果进行行求和df['total_hours'] = ( df.iloc[:, 2:] .apply(lambda x: pd.to_numeric(x, errors='coerce')) .sum(axis=1))print(df[['id', 'name', 'total_hours']])
输出结果:
id name total_hours0 514 alexis 0.01 2414 donald 144660.02 3225 mackenzie 172020.03 3434 louisa 120720.04 3864 olga 100320.05 4716 rick 173700.06 5793 roberta 175560.0
现在,total_hours列正确地显示了每行中所有可转换数值的总和。sum()函数在默认情况下会忽略NaN值,这正是我们想要的行为。
关键点:
df.iloc[:, 2:]: 确保只选择包含工作时间数据的列。iloc是基于整数位置的索引,[:, 2:]表示选择所有行,并从第3列(索引为2)开始到最后一列。请根据实际DataFrame的结构调整起始列索引。.apply(lambda x: pd.to_numeric(x, errors=’coerce’)): 这会遍历选定DataFrame中的每一列(x代表一个Series),并对该列应用pd.to_numeric函数。errors=’coerce’是这里的关键,它将所有无法解析为数字的条目(如’No hours logged’或'[10:02, Odd number: missing entry]’)转换为NaN。.sum(axis=1): 在所有相关列都被成功转换为数值类型(或NaN)之后,对这些列进行行方向(axis=1)的求和。
优化时间格式显示
原始数据中的工作时间以秒为单位记录。为了提高可读性,我们可以将总秒数转换为更友好的时间格式,例如HH:MM:SS。Pandas的pd.to_timedelta()函数非常适合这个任务。
# 将总秒数转换为timedelta对象df['total_duration'] = pd.to_timedelta(df['total_hours'], unit='s')print(df[['id', 'name', 'total_hours', 'total_duration']])
输出结果:
id name total_hours total_duration0 514 alexis 0.0 0 days 00:00:001 2414 donald 144660.0 1 days 16:11:002 3225 mackenzie 172020.0 1 days 23:47:003 3434 louisa 120720.0 1 days 09:32:004 3864 olga 100320.0 1 days 03:52:005 4716 rick 173700.0 2 days 00:15:006 5793 roberta 175560.0 2 days 00:46:00
total_duration列现在以timedelta64[ns]类型存储,并以天、小时、分钟和秒的格式显示,极大地提升了数据的可读性。
完整的解决方案函数
将上述步骤封装到一个函数中,可以方便地应用于任何类似的DataFrame:
def calculate_total_working_hours(df_input): """ 计算DataFrame中每行的总工作时间(秒),并添加一个格式化的时间持续时间列。 参数: df_input (pd.DataFrame): 包含工作时间数据的DataFrame。 假设工作时间数据从第三列(索引2)开始。 返回: pd.DataFrame: 包含 'total_hours' (总秒数) 和 'total_duration' (时间持续时间) 列的DataFrame。 """ df = df_input.copy() # 避免修改原始DataFrame # 1. 选择需要求和的列并转换为数值,将非数值强制转换为NaN # 注意:这里假设 'id' 和 'name' 是前两列,实际使用时请根据DataFrame结构调整列索引 df['total_hours'] = ( df.iloc[:, 2:] .apply(lambda x: pd.to_numeric(x, errors='coerce')) .sum(axis=1) ) # 2. 将总秒数转换为timedelta格式 df['total_duration'] = pd.to_timedelta(df['total_hours'], unit='s') return df# 使用示例df_results = calculate_total_working_hours(df.copy()) # 传入原始df的副本print(df_results[['id', 'name', 'total_hours', 'total_duration']])
注意事项
列索引的准确性: 在使用df.iloc[:, 2:]时,请务必确认2是您希望开始求和的列的正确索引。如果您的DataFrame结构不同,例如有更多的非数值前置列,您需要调整这个索引。errors=’coerce’的副作用: errors=’coerce’会将所有无法转换为数值的值替换为NaN。这意味着,如果您的数据中存在其他有意义的非数值字符串(例如,表示特殊状态的文本,而非简单的“未记录”),并且您希望保留这些信息或以不同方式处理它们,那么这种方法可能需要进一步细化。但对于本例中的“No hours logged”和格式错误的列表,这是一种非常有效的处理方式。数据类型一致性: 理想情况下,数据在加载到DataFrame之前就应该进行清理,确保数值列只包含数值。但在面对真实世界中的混合数据时,上述方法提供了强大的弹性。
总结
在Pandas中对DataFrame行进行求和时,如果遇到因混合数据类型导致numeric_only=True失效并返回0的情况,核心解决方案是:
精确选择需要求和的列。使用pd.to_numeric(errors=’coerce’)将这些列中的非数值条目统一转换为NaN。然后对转换后的结果执行sum(axis=1),Pandas会自动忽略NaN值。对于时间数据,可以进一步利用pd.to_timedelta()进行格式化,提升数据可读性。
掌握这些技巧,将使您在处理真实世界中复杂且不一致的数据时,能够更高效、更准确地进行数据分析和计算。
以上就是解决Pandas DataFrame行求和为0的常见问题:混合数据类型处理指南的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1381156.html
微信扫一扫
支付宝扫一扫