数据类型转换是数据清洗的关键步骤。pandas 提供多种方式:强制转换(
astype)、解析函数(pd.to_*)、自动推断(convert_dtypes)。
1. astype()
astype() 强制将 Series 或 DataFrame 转换为指定类型,是使用频率最高的类型转换方法。
Series 转换
import pandas as pd
import numpy as np
s = pd.Series([1, 2, 3])
# 转浮点
s.astype('float64')
# 0 1.0
# 1 2.0
# 2 3.0
# 转字符串
s.astype('str')
# 0 '1'
# 1 '2'
# 2 '3'
# 转可空整数
s.astype('Int64')
# 转分类类型
s.astype('category')DataFrame 转换
df = pd.DataFrame({
'A': [1, 2, 3],
'B': ['1.1', '2.2', '3.3'],
'C': [True, False, True]
})
# 统一转换所有列
df.astype('object')
# 按列指定类型
df.astype({'A': 'int64', 'B': 'float64', 'C': 'bool'})
# 转换为可空类型
df.astype({'A': 'Int64', 'B': 'Float64', 'C': 'boolean'})
# 转 string 扩展类型
df.astype({'B': 'string'})参数详解
| 参数 | 说明 |
|---|---|
dtype | 目标类型(字符串、NumPy dtype、pandas dtype)或字典(按列指定) |
copy | 是否复制数据(默认 True) |
errors | 'raise'(默认,转换失败抛错)或 'ignore'(忽略错误返回原对象) |
# 转换失败时忽略错误
s.astype('int64', errors='ignore')常见陷阱
- 字符串转数值时,若含无法解析的字符(如
'abc'),会抛出ValueErrorNone/NaN在astype('int64')时会失败,需先填补或使用可空类型astype('str')转为 object 类型,字符串方法使用.str;若需string扩展类型,使用astype('string')
2. pd.to_numeric()
pd.to_numeric() 将参数转换为数值类型,可处理字符串、列表、Series 等。
参数详解
| 参数 | 说明 |
|---|---|
arg | 待转换的对象(标量、列表、Series、数组) |
errors | 'raise'(默认)、'coerce'(无效值转为 NaN)、'ignore'(无效时返回原对象) |
downcast | 降级到更小的类型:'integer'、'signed'、'unsigned'、'float' |
示例
s = pd.Series(['1', '2.5', '3', 'abc', None])
# 默认行为:遇到无法解析的字符串会报错
pd.to_numeric(s)
# ValueError: Unable to parse string "abc"
# coerce:无法解析转为 NaN
pd.to_numeric(s, errors='coerce')
# 0 1.0
# 1 2.5
# 2 3.0
# 3 NaN
# 4 NaN
# dtype: float64
# ignore:无法转换时保留原样
pd.to_numeric(s, errors='ignore')
# 降级类型
pd.to_numeric(['100', '200'], errors='coerce')
# 0 100
# 1 200
# dtype: int64
# 强制降级为整数
pd.to_numeric(['1', '2'], downcast='integer')
# dtype: int8应用到 DataFrame
df = pd.DataFrame({'A': ['1', '2'], 'B': ['3.4', '5.6']})
# 将指定列转换为数值
df['A'] = pd.to_numeric(df['A'])
df['B'] = pd.to_numeric(df['B'])
# 多列批量处理
for col in ['A', 'B']:
df[col] = pd.to_numeric(df[col], errors='coerce')优势
to_numeric比astype更能容忍错误(配合errors='coerce'),且在解析数字字符串时更智能。
3. pd.to_datetime()
pd.to_datetime() 将字符串、整数、列表等转换为 datetime64 类型。
参数详解
| 参数 | 说明 |
|---|---|
arg | 待转换对象(标量、列表、Series、DataFrame、字典等) |
errors | 'raise'(默认)、'coerce'(无效转 NaT)、'ignore'(返回原对象) |
format | 指定解析格式(如 '%Y-%m-%d'),可加速解析 |
unit | 时间戳单位(如 's'、'ms'、'ns'),用于数字转日期 |
dayfirst | 优先解释为“日/月/年”(默认 False) |
yearfirst | 优先解释为“年/月/日” |
utc | 是否将结果统一为 UTC |
exact | format 是否要求严格匹配(默认 True) |
cache | 是否使用缓存(默认 True) |
示例
# 字符串列表
pd.to_datetime(['2024-01-01', '2024-02-01'])
# DatetimeIndex(['2024-01-01', '2024-02-01'], dtype='datetime64[ns]')
# 混合格式 + 错误处理
pd.to_datetime(['2024-01-01', 'invalid'], errors='coerce')
# DatetimeIndex(['2024-01-01', 'NaT'], dtype='datetime64[ns]')
# 指定格式
pd.to_datetime(['2024/01/01', '2024/02/01'], format='%Y/%m/%d')
# 时间戳整数(单位秒)
pd.to_datetime([1609459200, 1609545600], unit='s')
# 时间戳毫秒
pd.to_datetime([1609459200000], unit='ms')
# 拆分的年月日列合并
df = pd.DataFrame({'year': [2024, 2025], 'month': [1, 6], 'day': [15, 30]})
pd.to_datetime(df)
# 日期列转换
df['日期'] = pd.to_datetime(df['日期'], format='%Y-%m-%d')常见格式参考
| 格式码 | 含义 | 示例 |
|---|---|---|
%Y | 4 位年份 | 2024 |
%y | 2 位年份 | 24 |
%m | 2 位月份 | 01 |
%d | 2 位日期 | 15 |
%H | 24 小时制小时 | 13 |
%M | 分钟 | 45 |
%S | 秒 | 30 |
%f | 微秒 | 123456 |
%Z | 时区 | UTC+08:00 |
# 解析带时分秒的字符串
pd.to_datetime('2024-03-15 13:45:30', format='%Y-%m-%d %H:%M:%S')注意
- 混合格式字符串建议使用
errors='coerce'并检查NaT- 解析大文件日期列时,用
format参数可显著提升性能- 时区转换请使用
tz_localize()/tz_convert(),to_datetime本身不处理时区
4. pd.to_timedelta()
pd.to_timedelta() 将字符串、数字等转换为 timedelta64 类型。
参数详解
| 参数 | 说明 |
|---|---|
arg | 待转换对象(字符串、数字、列表、Series) |
unit | 数字的默认为单位(如 's'、'ms'、'm'、'h'、'D') |
errors | 'raise'(默认)、'coerce'(无效转 NaT)、'ignore' |
示例
# 字符串形式
pd.to_timedelta('1 days')
# Timedelta('1 days 00:00:00')
pd.to_timedelta('2 hours 30 minutes')
# Timedelta('0 days 02:30:00')
# 列表
pd.to_timedelta(['1 day', '2 days', '3 days'])
# 数字 + 单位
pd.to_timedelta([1, 2, 3], unit='h')
# TimedeltaIndex(['0 days 01:00:00', '0 days 02:00:00', '0 days 03:00:00'])
# 错误处理
pd.to_timedelta(['1 day', 'bad'], errors='coerce')
# TimedeltaIndex(['1 days', 'NaT'], dtype='timedelta64[ns]')与数值列运算
df = pd.DataFrame({'秒数': [3600, 7200, 10800]})
df['时长'] = pd.to_timedelta(df['秒数'], unit='s')
# 秒数 时长
# 0 3600 0 days 01:00:00
# 1 7200 0 days 02:00:00
# 2 10800 0 days 03:00:005. convert_dtypes()
convert_dtypes() 自动将每列转换为“最佳”的可空数据类型,是 pandas 推荐的新方法。
参数详解
| 参数 | 说明 |
|---|---|
infer_objects | 是否推断 object 类型(默认 True) |
convert_string | 是否转为 string 扩展类型(默认 True) |
convert_integer | 是否转为可空整数(默认 True) |
convert_boolean | 是否转为可空布尔(默认 True) |
convert_floating | 是否转为可空浮点(默认 True) |
dtype_backend | 'numpy_nullable'(默认)或 'pyarrow' |
示例
df = pd.DataFrame({
'A': [1, 2, None], # 含缺失的整数
'B': [1.5, 2.5, None], # 含缺失的浮点
'C': ['a', 'b', None], # 含缺失的字符串
'D': [True, False, None] # 含缺失的布尔
})
print(df.dtypes)
# A float64
# B float64
# C object
# D object
# dtype: object
df2 = df.convert_dtypes()
print(df2.dtypes)
# A Int64
# B Float64
# C string
# D boolean
# dtype: object应用到读取数据
# read_csv 时配合 dtype_backend
df = pd.read_csv('data.csv', dtype_backend='pyarrow')
# 读取后统一转换
df = pd.read_csv('data.csv').convert_dtypes()优势
- 自动将含缺失的数值列转换为可空扩展类型,解决
NaN破坏整数的问题- 自动将 object 字符串转为
string扩展类型,更高效- 与 pandas 2.x 的 PyArrow 后端无缝集成
注意
convert_dtypes()是启发式推断,可能对部分列产生意外结果。转换后务必检查df.dtypes是否符合预期。
6. 类型转换实践
综合示例
df = pd.read_csv('乱数据.csv')
# 1. 统一缺失值标记
df = df.replace(['NULL', 'N/A', '?'], pd.NA)
# 2. 自动推断类型
df = df.convert_dtypes()
# 3. 手动修正特殊列
df['日期'] = pd.to_datetime(df['日期'], errors='coerce')
df['价格'] = pd.to_numeric(df['价格'], errors='coerce')
df['数量'] = pd.to_numeric(df['数量'], errors='coerce', downcast='integer')
df['时长'] = pd.to_timedelta(df['时长'], errors='coerce')
# 4. 检查结果
print(df.dtypes)
print(df.isna().sum())转换时常见的坑
| 场景 | 问题 | 解法 |
|---|---|---|
| 字符串含逗号 | '1,000' 无法转数字 | 先 str.replace(',', '') |
| 日期格式多样 | '2024/1/1' 与 '01-02-2024' | errors='coerce' 后检查 NaT |
| 整数列含 NaN | astype('int64') 报错 | 使用 Int64 或先填充 |
| 布尔字符串 | 'Y' / 'N' | replace({'Y': True, 'N': False}) |
| 百分比字符串 | '10%' | str.rstrip('%') 后转浮点除以 100 |