You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Pandas fillna后数据类型变为object,如何保留原类型?

问题描述

我正在使用LBNL建筑自动化故障检测数据集,原始数据包含Int64、Float64和datetime64[ns]类型。但在使用Pandas的fillna方法后,所有列的数据类型都变成了object。

初始代码(此时数据类型正常):

import pandas as pd
import datetime as dt
%matplotlib inline
df = pd.read_csv('RTU.csv') 
df['Timestamp'] = pd.to_datetime(df['Timestamp'])

执行以下代码后,所有列数据类型全部变为object:

df['Timestamp'] = pd.to_datetime(df['Timestamp'])
def fault_mapper_FD(faultDate):
    if pd.Timestamp(2017, 8, 27, 0) <= faultDate <= pd.Timestamp(2017, 8, 28, 0):
        return 0
    if pd.Timestamp(2017, 8, 29, 0) <= faultDate <= pd.Timestamp(2017, 8, 29, 23, 59):
        return 0
    if pd.Timestamp(2017, 12, 1, 0) <= faultDate <= pd.Timestamp(2017, 12, 1, 23, 59):
        return 0
    if pd.Timestamp(2017, 12, 3, 0) <= faultDate <= pd.Timestamp(2017, 12, 3, 23, 59):
        return 0
    if pd.Timestamp(2017, 12, 7, 0) <= faultDate <= pd.Timestamp(2017, 12, 8, 0):
        return 0
    if pd.Timestamp(2017, 12, 14, 0) <= faultDate <= pd.Timestamp(2017, 12, 14, 23, 59):
        return 0
    if pd.Timestamp(2018, 2, 7, 0) <= faultDate <= pd.Timestamp(2018, 2, 7, 23, 59):
        return 0
    if pd.Timestamp(2018, 2, 9, 0) <= faultDate <= pd.Timestamp(2018, 2, 9, 23, 59):
        return 0
    if pd.Timestamp(2017, 12, 20, 0) <= faultDate <= pd.Timestamp(2017, 12, 20, 23, 59):
        return 0
    if pd.Timestamp(2018, 2, 18, 0) <= faultDate <= pd.Timestamp(2018, 2, 18, 23, 59):
        return 0
    if pd.Timestamp(2018, 2, 1, 0) <= faultDate <= pd.Timestamp(2018, 2, 1, 23, 59):
        return 0
    if pd.Timestamp(2018, 1, 31, 0) <= faultDate <= pd.Timestamp(2018, 1, 31, 23, 59):
        return 0
    if pd.Timestamp(2018, 1, 28, 0) <= faultDate <= pd.Timestamp(2018, 1, 28, 23, 59):
        return 0
    if pd.Timestamp(2018, 1, 27, 0) <= faultDate <= pd.Timestamp(2018, 1, 27, 23, 59):
        return 0
    if (pd.Timestamp(2017, 9, 1, 0) <= faultDate <= pd.Timestamp(2017, 9, 1, 23, 59) or 
    pd.Timestamp(2017, 11, 30, 0) <= faultDate <= pd.Timestamp(2017, 11, 30, 23, 59) or 
    pd.Timestamp(2017, 12, 9, 0) <= faultDate <= pd.Timestamp(2017, 12, 9, 23, 59) or 
    pd.Timestamp(2017, 12, 10, 0) <= faultDate <= pd.Timestamp(2017, 12, 11, 0) or 
    pd.Timestamp(2017, 12, 24, 0) <= faultDate <= pd.Timestamp(2017, 12, 24, 23, 59) or 
    pd.Timestamp(2018, 2, 4, 0) <= faultDate <= pd.Timestamp(2018, 2, 4, 23, 59) or 
    pd.Timestamp(2018, 2, 5, 0) <= faultDate <= pd.Timestamp(2018, 2, 6, 0)):
        return 1

df['FD'] = df['Timestamp'].apply(lambda fault_date: fault_mapper_FD(fault_date))

cond = (df.Timestamp.dt.time > dt.time(22,0)) | ((df.Timestamp.dt.time < dt.time(7,0)))
df[cond] = df[cond].fillna(0,axis=1)

现在执行df.dtypes显示所有列均为object类型,请问如何操作才能在Pandas处理后保留原数据类型?


解决方案

问题根源

问题出在df[cond] = df[cond].fillna(0,axis=1)这一行:

  • 对筛选后的子DataFrame填充0并赋值回原DataFrame时,Pandas会自动将列类型转换为object——因为填充的0是整数类型,与原列的浮点/日期类型混合,触发类型降级。
  • Timestamp列被错误包含在填充范围内,用整数0覆盖了日期值,直接导致该列类型变为object。

修复步骤

1. 精准选择填充列(推荐)

只对数值列填充,排除Timestamp和FD列:

# 筛选出所有数值类型的列
numeric_cols = df.select_dtypes(include=['int64', 'float64']).columns
# 仅对符合条件的行中的数值列填充0
df.loc[cond, numeric_cols] = df.loc[cond, numeric_cols].fillna(0)

2. 已执行错误操作后的恢复

如果已经完成错误填充,可手动将各列转换回原类型:

# 恢复Timestamp列的日期类型
df['Timestamp'] = pd.to_datetime(df['Timestamp'])
# 恢复数值列类型
for col in numeric_cols:
    if df[col].dtype == 'int64':
        df[col] = df[col].astype('int64')
    elif df[col].dtype == 'float64':
        df[col] = df[col].astype('float64')
# 恢复FD列的整数类型
df['FD'] = df['FD'].astype('int64')

3. 优化故障标记函数(可选)

简化原函数的大量if判断,同时确保返回值类型一致:

# 定义正常日期区间
normal_intervals = [
    (pd.Timestamp(2017,8,27), pd.Timestamp(2017,8,28)),
    (pd.Timestamp(2017,8,29), pd.Timestamp(2017,8,29,23,59)),
    (pd.Timestamp(2017,12,1), pd.Timestamp(2017,12,1,23,59)),
    (pd.Timestamp(2017,12,3), pd.Timestamp(2017,12,3,23,59)),
    (pd.Timestamp(2017,12,7), pd.Timestamp(2017,12,8)),
    (pd.Timestamp(2017,12,14), pd.Timestamp(2017,12,14,23,59)),
    (pd.Timestamp(2018,2,7), pd.Timestamp(2018,2,7,23,59)),
    (pd.Timestamp(2018,2,9), pd.Timestamp(2018,2,9,23,59)),
    (pd.Timestamp(2017,12,20), pd.Timestamp(2017,12,20,23,59)),
    (pd.Timestamp(2018,2,18), pd.Timestamp(2018,2,18,23,59)),
    (pd.Timestamp(2018,2,1), pd.Timestamp(2018,2,1,23,59)),
    (pd.Timestamp(2018,1,31), pd.Timestamp(2018,1,31,23,59)),
    (pd.Timestamp(2018,1,28), pd.Timestamp(2018,1,28,23,59)),
    (pd.Timestamp(2018,1,27), pd.Timestamp(2018,1,27,23,59))
]

# 定义故障日期区间
fault_intervals = [
    (pd.Timestamp(2017,9,1), pd.Timestamp(2017,9,1,23,59)),
    (pd.Timestamp(2017,11,30), pd.Timestamp(2017,11,30,23,59)),
    (pd.Timestamp(2017,12,9), pd.Timestamp(2017,12,9,23,59)),
    (pd.Timestamp(2017,12,10), pd.Timestamp(2017,12,11)),
    (pd.Timestamp(2017,12,24), pd.Timestamp(2017,12,24,23,59)),
    (pd.Timestamp(2018,2,4), pd.Timestamp(2018,2,4,23,59)),
    (pd.Timestamp(2018,2,5), pd.Timestamp(2018,2,6))
]

def fault_mapper_FD(faultDate):
    for start, end in normal_intervals:
        if start <= faultDate <= end:
            return 0
    for start, end in fault_intervals:
        if start <= faultDate <= end:
            return 1
    return None

# 使用Int64类型支持缺失值
df['FD'] = df['Timestamp'].apply(fault_mapper_FD).astype('Int64')

内容的提问来源于stack exchange,提问作者arash

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 11:36:14