在数据处理流程中,理解并正确转换数据类型是极关键的一环。从 Python 内置类型转换 到 NumPy 类型,再重点展开 Pandas DataFrame 的列类型转换与元素级的值转换。
一、Python 内建数据类型及其转换
1. 基础类型
- 数字:
int、float、complex - 布尔:
bool(True/False,实际上是int的子类) - 文本:
str - 二进制:
bytes、bytearray - 空值:
NoneType(只有None一个值) - 容器:
list、tuple、dict、set、frozenset
2. 类型检查
type(x) # 返回类型对象
isinstance(x, int) # 推荐,支持继承关系3. 显式转换(构造函数)
| 转换目标 | 函数 | 说明 |
|---|---|---|
| 整数 | int(x) | 字符串 "10" → 10;浮点数 3.9 → 3(截断) |
| 浮点数 | float(x) | 整数 10 → 10.0;字符串 "3.14" → 3.14 |
| 布尔值 | bool(x) | 绝大多数非空/非零 → True;0、""、[]、None 等 → False |
| 字符串 | str(x) | 几乎所有对象都有 __str__ |
| 列表/元组 | list(x)/tuple(x) | 从可迭代对象转换 |
常见陷阱:
int("abc")抛出ValueErrorint(None)抛出TypeErrorfloat("3.14.15")抛出ValueErrorbool("False")返回True,因为非空字符串为真
4. 隐式转换
- 整数与浮点数运算:
3 + 4.0→7.0(int 自动转 float) - 字符串与数字拼接:
"value: " + 5报错,需显式"value: " + str(5) - 布尔值参与算术:
True + 1→2(True 视为 1)
5. 与空值相关的转换
从 Excel/MySQL 导入的数据常混合 None 和 np.nan,转换前务必用 pd.isna() 或 math.isnan() 判空,参考你前一个问题中的空值判断表。
二、NumPy 数据类型(Pandas 的基石)
Pandas 列底层是 numpy.ndarray 或 ExtensionArray,因此了解 NumPy 类型很重要。
| 类型名 | 描述 | 对应 Python 类型 |
|---|---|---|
int64 | 64 位有符号整数 | int(但不能存 NaN) |
float64 | 双精度浮点 | float(可存 np.nan) |
bool_ | 布尔值 | bool |
object | 任意 Python 对象 | str、list 等混合类型 |
datetime64[ns] | 纳秒精度时间戳 | pd.Timestamp(类似 datetime.datetime) |
timedelta64[ns] | 时间差 | pd.Timedelta |
转换:np.array([1,2,3]).astype('float64')
三、Pandas DataFrame 的列类型转换
这是数据处理中最常用的操作——将整列的数据类型统一调整。
1. 查看列类型
df.dtypes
# 或单列
df['A'].dtype2. 主要转换方法
(1) Series.astype() —— 最通用的方法
# 转换为内置或 numpy 类型
df['int_col'] = df['float_col'].astype(int) # 会截断小数
df['str_col'] = df['num_col'].astype(str) # 数字 -> 字符串
df['bool_col'] = df['flag'].astype(bool)
# 转换为 pandas 可空类型(支持缺失值,不会将 NaN 强转为 float)
df['int_col'] = df['float_col'].astype('Int64') # 注意大写 I
df['str_col'] = df['obj_col'].astype('string')
df['bool_col'] = df['flag'].astype('boolean')
# 转换为 category(节省内存)
df['cat_col'] = df['str_col'].astype('category')
# 转换为 datetime64
df['date_col'] = pd.to_datetime(df['date_str'])
# 再用 astype 统一格式(如有需要)
df['date_col'] = df['date_col'].astype('datetime64[ns]')注意:astype() 会忽略缺失值(NaN),但如果列里原本有 pd.NA,转成 int 会报错,此时应转为 Int64。若列里含有无法转换的字符串,astype(int) 会直接报错。
(2) 专门的安全转换函数
- 转数值:
pd.to_numeric()
# errors='coerce' 将无法转换的值变为 NaN
df['price'] = pd.to_numeric(df['price'], errors='coerce')
# downcast 可缩小内存占用
df['price'] = pd.to_numeric(df['price'], downcast='integer')- 转日期时间:
pd.to_datetime()
df['date'] = pd.to_datetime(df['date_str'], format='%Y-%m-%d', errors='coerce')
# 从 Excel 序列号转换(1899-12-30 为基准)
df['date'] = pd.to_datetime(df['excel_serial'], unit='D', origin='1899-12-30')- 转时间差:
pd.to_timedelta()
df['duration'] = pd.to_timedelta(df['time_str'])(3) 将 object 列中的多种空值占位符统一处理后再转类型
结合你之前的空值判断,典型流程:
# 先将各种空值占位符替换为 NaN
na_vals = ['', 'NA', 'NULL', 'N/A', '-']
df.replace(na_vals, np.nan, inplace=True)
# 再安全转为数值
df['col'] = pd.to_numeric(df['col'], errors='coerce')
# 最后转为可空整型(避免 float)
df['col'] = df['col'].astype('Int64')(4) 多列同时转换
df = df.astype({
'id': 'Int64',
'name': 'string',
'score': 'float',
'active': 'boolean',
'created_at': 'datetime64[ns]'
})四、Pandas DataFrame 值级别的类型转换
有时不是整列转类型,而是对每个单元格进行自定义转换。
1. Series 上的逐元素操作
Series.apply(func):对每个元素应用函数
df['new'] = df['old'].apply(lambda x: x * 2 if isinstance(x, (int,float)) else x)Series.map(func/dict):可用于查找替换或函数变换
# 映射转换
df['code'] = df['status'].map({'pass':1, 'fail':0})2. DataFrame 上的逐元素操作
DataFrame.applymap(func)(新版本推荐用DataFrame.map(func))
# 将所有数字四舍五入到两位小数
df = df.applymap(lambda x: round(x, 2) if isinstance(x, float) else x)- 向量化条件转换:直接用布尔索引或
np.where
df['new'] = np.where(df['score'] > 60, 'pass', 'fail')3. 分箱转换(连续值 → 类别)
df['age_group'] = pd.cut(df['age'], bins=[0,18,35,60,100], labels=['child','youth','adult','senior'])
# 结果自动为 category 类型4. 处理混合类型列的坑
从 Excel 读取时可能一列中混合数字和文本,其 dtype 为 object。直接 .apply() 通常安全,但性能低;若想转为数值,务必先用 pd.to_numeric(errors='coerce') 将非数字转 NaN,否则会报错。
五、实战:Excel / MySQL 数据导入后的类型修正示例
结合你之前询问的 Excel 和 MySQL 类型对应,这里给出几个典型修正场景:
从 Excel 读取后
import pandas as pd
df = pd.read_excel('data.xlsx')
# 1. 日期序列号转日期(如列中为数字)
df['date'] = pd.to_datetime(df['date'], unit='D', origin='1899-12-30', errors='coerce')
# 2. 带有千位分隔符的数字字符串 (如 "1,234") 转为数值
df['amount'] = df['amount'].str.replace(',', '').astype(float)
# 3. 百分比字符串 (如 "85%") 转为浮点数
df['pct'] = df['pct'].str.rstrip('%').astype(float) / 100.0
# 4. 空值占位符清理后转为 Int64
df.replace(['', 'NULL', 'N/A'], pd.NA, inplace=True)
df['id'] = df['id'].astype('Int64')从 MySQL 读取后
from sqlalchemy import create_engine
engine = create_engine('mysql+pymysql://...')
df = pd.read_sql("SELECT * FROM products", engine)
# 1. Decimal 类型列可能被读为 object(字符串或 Decimal 对象)
# 转为 float,注意精度,或保留 Decimal 用于后续精确计算
from decimal import Decimal
df['price'] = df['price'].apply(lambda x: float(x) if x is not None else None)
# 2. TINYINT(1) 布尔标志:pymysql 返回 0/1,转为 bool
df['active'] = df['active'].astype(bool)
# 3. JSON 字符串解析为字典
import json
df['props'] = df['props'].apply(lambda x: json.loads(x) if pd.notna(x) else {})六、总结与最佳实践
- 先诊断,后转换:用
df.dtypes、df.head()、df['col'].unique()了解数据实际类型和异常值。 - 优先使用列级转换:
pd.to_numeric、pd.to_datetime配合errors='coerce',比循环apply快得多。 - 缺失值友好:想让整数列带 NaN,用 pandas 的可空类型
Int64、boolean、string,不要用numpy的int64。 - 精确度控制:金融数据避免
float→Decimal,或读取 MySQL 时直接保留Decimal对象。 - 链式操作:结合
astype、replace、fillna形成清洗流水线,最终得到干净、类型正确的 DataFrame。
掌握了这些,无论数据来自 Excel、MySQL 还是其他源,你都能游刃有余地完成类型整理和后续分析了。