Pandas 数据类型转换

数据类型的正确性直接影响分析结果和内存效率。Pandas 提供了灵活的类型转换工具,让你能够将数据转换为最适合的格式——无论是数值、字符串、日期还是分类类型。

📌 为什么需要类型转换?

  • 数据分析需求:日期列需要是 datetime 类型才能进行时间序列操作;数值列需要是数字才能计算统计量。
  • 内存优化:将 float64 转为 float32 或 int8 可以大幅减少内存占用。
  • 避免错误:从文件读取时可能将数值列误读为字符串(object),导致后续计算失败。

🔍 查看数据类型

在转换前,先查看当前数据的类型:

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'ID': ['101', '102', '103'],
    '年龄': ['28', '35', '42'],
    '工资': ['8000', '12000', '15000'],
    '入职日期': ['2020-01-15', '2018-06-01', '2015-11-23']
})

print(df.dtypes)
df.info()

你会发现所有列都是 object 类型,因为数据以字符串形式读入。

🔄 使用 astype() 转换

astype() 是最通用的类型转换方法,可以将 Series 或整个 DataFrame 转换为指定类型。

# 单列转换
df['ID'] = df['ID'].astype(int)                 # 转为整数
df['年龄'] = df['年龄'].astype(float)             # 转为浮点数
df['入职日期'] = df['入职日期'].astype('datetime64[ns]')  # 转为日期

# 多列转换(传入字典)
df = df.astype({'ID': int, '工资': float})

# 整个 DataFrame 转换(所有列转为同一类型)
df = df.astype(str)   # 转为字符串

🔢 数值转换:pd.to_numeric()

当数据中包含无法直接转换的值(如空字符串、特殊字符)时,astype() 会报错。pd.to_numeric() 提供了更灵活的错误处理。

s = pd.Series(['1', '2', '3.5', 'NA', '5'])

# 默认遇到错误会抛出异常
# pd.to_numeric(s)  # 报错

# 将无效值转为 NaN
s_num = pd.to_numeric(s, errors='coerce')
print(s_num)

# 忽略错误,保持原样(不推荐)
s_num2 = pd.to_numeric(s, errors='ignore')  # 返回原 Series

# 对 DataFrame 的指定列应用
df['工资'] = pd.to_numeric(df['工资'], errors='coerce')

📅 日期时间转换:pd.to_datetime()

将字符串或数字转换为 Pandas 的 datetime 类型,支持多种格式。

# 基本用法
df['入职日期'] = pd.to_datetime(df['入职日期'])

# 处理不同格式
s = pd.Series(['2024-01-01', '2024/02/01', '01-03-2024', 'invalid'])
dates = pd.to_datetime(s, errors='coerce')  # 无效值转为 NaT
print(dates)

# 指定格式(提升速度,避免歧义)
df['日期'] = pd.to_datetime(df['日期'], format='%Y-%m-%d')

# 处理包含时间的字符串
df['时间戳'] = pd.to_datetime(df['时间戳'], format='%Y-%m-%d %H:%M:%S')

🏷️ 分类类型:astype('category')

对于重复值很多的离散列(如性别、地区),转换为 category 类型可以节省内存并提高性能。

df['部门'] = df['部门'].astype('category')
print(df['部门'].cat.categories)  # 查看所有类别
print(df['部门'].cat.codes)       # 查看编码后的整数

🧩 布尔类型转换

将数值或字符串转换为布尔值。

# 数值转布尔:0 -> False, 非0 -> True
df['是否活跃'] = df['状态'].astype(bool)

# 字符串转布尔(需自定义映射)
df['是否会员'] = df['会员'].map({'是': True, '否': False})

⚙️ 内存优化技巧

通过向下转换数据类型,可以大幅减少内存占用。

# 对于数值列,如果范围合适,可以转为更小的类型
df['年龄'] = pd.to_numeric(df['年龄']).astype('int8')   # 如果年龄 < 127
df['工资'] = pd.to_numeric(df['工资']).astype('float32')

# Pandas 提供了自动向下转换函数
df_int = df.select_dtypes(include=['int']).apply(pd.to_numeric, downcast='unsigned')
df_float = df.select_dtypes(include=['float']).apply(pd.to_numeric, downcast='float')

🧪 综合示例

import pandas as pd
import numpy as np

# 原始数据(模拟读取自 CSV)
df = pd.DataFrame({
    'user_id': ['1001', '1002', '1003', '1004'],
    'age': ['25', '30', 'NaN', '28'],
    'salary': ['5000', '7500', '8200', 'N/A'],
    'join_date': ['2023-01-15', '2022-06-01', '2023-11-20', '2021-03-10'],
    'department': ['IT', 'HR', 'IT', 'Finance']
})

print("原始类型:")
print(df.dtypes)

# 1. 转换数值列,处理无效值
df['age'] = pd.to_numeric(df['age'], errors='coerce')
df['salary'] = pd.to_numeric(df['salary'], errors='coerce')

# 2. 转换日期列
df['join_date'] = pd.to_datetime(df['join_date'])

# 3. 将部门转为分类类型
df['department'] = df['department'].astype('category')

# 4. user_id 应作为字符串(而不是整数),因为可能包含前导0或非数字
df['user_id'] = df['user_id'].astype(str)

# 5. 内存优化:如果年龄范围小,可转为 int8
df['age'] = df['age'].fillna(0).astype('int8')

print("\n转换后类型:")
print(df.dtypes)
print("\n数据预览:")
print(df)

⚠️ 常见问题与注意事项

精度损失:从 float64 转为 float32 可能会损失精度,确保业务可接受。
无效转换:使用 errors='coerce' 会导致无效值变为 NaN/NaT,后续需处理缺失值。
日期格式歧义:例如 "01-02-2024" 可能是1月2日或2月1日,最好指定 format 参数。
inplace 参数:astype() 没有 inplace 参数,必须重新赋值。而 pd.to_numeric 返回新 Series。
最佳实践:
  • 数据加载后立即检查 dtypes,对每列规划合适的类型。
  • 对可能包含脏数据的列,使用 pd.to_numericpd.to_datetime 配合 errors='coerce'
  • 分类变量用 category 类型,可节省内存并加快 groupby 等操作。
  • 处理大型数据时,考虑使用 pd.to_numeric(downcast='integer/float') 或手动指定更小的 dtype。