本章学习目标:
- 理解数据类型错误会导致哪些分析错误
- 掌握最常见的类型问题:数字存成文本、日期存成文本
- 学会用
info()快速发现类型问题- 知道每类问题应该如何转换、转换成什么
- 不需要记住代码,只需要知道"有什么问题、转成什么、怎么告诉AI"
一、为什么数据类型转换如此重要?
1.1 核心认知:类型决定操作
数据类型决定了你能做什么操作,也决定了不能做什么操作。
| 你想做的事 | 数字类型(int/float) | 文本类型(object/string) | 日期类型(datetime) |
|---|---|---|---|
| 计算平均值 | ✅ 可以 | ❌ 不行(会报错或得到无意义结果) | ❌ 不行 |
| 比较大小 | ✅ 可以 | ❌ 不行(按字典序,结果怪异) | ✅ 可以 |
| 加减运算 | ✅ 可以 | ❌ 不行 | ✅ 可以(算天数差) |
| 提取年月日 | ❌ 不行 | ❌ 不行 | ✅ 可以 |
| 判断是否相等 | ✅ 可以 | ✅ 可以 | ✅ 可以 |
| 分组统计 | ✅ 可以 | ✅ 可以 | ✅ 可以 |
1.2 类型错误的后果
| 错误类型 | 现象 | 严重后果 |
|---|---|---|
| 数字存成文本 | 无法求和、求平均 | 统计数字全是错的 |
| 数字存成文本 | 排序结果异常("10"排在"2"前面) | 排名错误 |
| 日期存成文本 | 无法计算时间差 | 无法计算用户生命周期 |
| 日期存成文本 | 无法按年月聚合 | 时间趋势分析做不了 |
| ID存成数字 | 不知不觉对ID求了平均 | 得到完全无意义的结果 |
| 分类存成数字 | 模型误认为有大小顺序 | 模型效果变差 |
1.3 一个真实的教训
场景:某公司分析用户年龄分布
数据:年龄列看起来是数字,但实际是文本类型
错误的操作:
python
# 用户以为年龄是数字,直接求平均
df['age'].mean()
# 报错!因为文本类型不能求平均
即使没报错,也可能是错的:
python
# 如果年龄列混入了"Unknown"、"30岁"等文本
# mean() 会报错,但 sum() 可能不会报错却给出错误结果
核心原则:类型对了,分析才可能对;类型错了,结果一定是错的。
二、常见类型问题与场景
2.1 问题类型总览
| 问题类型 | 当前类型 | 应有的类型 | 典型场景 |
|---|---|---|---|
| 数字存成文本 | object/string | int/float | 从Excel导入时,数字被当作文本 |
| 日期存成文本 | object/string | datetime | 从CSV导入时,日期被读成普通文本 |
| ID存成数字 | int64 | string | 用户ID、订单号不应参与数学运算 |
| 分类存成数字 | int64 | category | 性别编码为1/2,但不应有大小关系 |
| 文本应该拆分 | object | 多列 | 姓名"张三"应该拆成姓"张"名"三" |
| 数字精度错误 | int64 | float | 价格需要小数却存成了整数 |
2.2 为什么会出现类型问题?
| 原因 | 举例 | 说明 |
|---|---|---|
| CSV或Excel导入时未指定类型 | 手机号18812345678被读成整数1.88e10 | 需要提前告诉工具"这是文本" |
| 数据中包含非数字字符 | "30岁"、"$100" | 混入了文字或符号 |
| 日期格式不标准 | 2024年1月15日 vs 2024-01-15 | 不同格式识别率不同 |
| 空值或占位符 | 用"-"或"N/A"表示缺失 | 导致整列变成文本 |
| 前导零问题 | "00123"变成了123 | Excel自动去掉了前导零 |
2.3 具体例子对比
正常情况(类型正确):
| user_id (数字) | name (文本) | age (数字) | salary (数字) | join_date (日期) |
|---|---|---|---|---|
| 101 | 张三 | 28 | 8000.5 | 2024-01-15 |
有问题的情况:
| user_id (数字) | name (文本) | age (文本) | salary (文本) | join_date (文本) |
|---|---|---|---|---|
| 101 | 张三 | 28岁 | 8,000.5 | 2024年1月15日 |
问题分析:
age:文本类型,且包含"岁"字 → 无法计算平均年龄salary:文本类型,且包含逗号 → 无法求和join_date:文本类型,格式不标准 → 无法计算工龄
三、怎么发现类型问题?
3.1 主力工具:info()
info() 是最快发现类型问题的工具。它一次性显示所有列的类型。
输出示例:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000 entries, 0 to 999
Data columns (total 6 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 user_id 1000 non-null int64
1 name 1000 non-null object
2 age 1000 non-null object ← 问题1:年龄应该是数字,却是object
3 salary 998 non-null object ← 问题2:薪资应该是数字,却是object
4 join_date 1000 non-null object ← 问题3:日期应该是datetime,却是object
5 is_vip 1000 non-null int64
一眼看出问题:
age是object(文本)→ 应该是int或floatsalary是object(文本)→ 应该是floatjoin_date是object(文本)→ 应该是datetime
3.2 辅助方法:查看唯一值
当info()显示类型异常时,用unique()或head()查看具体内容,确认问题原因。
查看年龄列的唯一值:
python
df['age'].unique()
# 输出:['28', '30', '25岁', 'unknown', '29']
→ 发现问题:有"25岁"(带单位)、"unknown"(文本)混入,导致整列变文本
查看薪资列的前几行:
python
df['salary'].head()
# 输出:['8,000', '10,500', '7,000', '12,000', '9,500']
→ 发现问题:数字中带了逗号分隔符
查看日期列的前几行:
python
df['join_date'].head()
# 输出:['2024年1月15日', '2023-05-20', '2024/03/10', '2022.12.01', '2024-01-01']
→ 发现问题:日期格式不统一
3.3 业务直觉
有时候,info()不报错,但业务逻辑告诉你类型不对:
| 情况 | info显示 | 业务直觉 | 应该做什么 |
|---|---|---|---|
| 用户ID是int64 | 数字类型 | "用户ID不应该用来求平均" | 转为文本 |
| 性别编码是int64 | 数字类型 | "1和2没有大小的意义" | 转为分类 |
| 评分是object | 文本类型 | "评分应该是1-5的数字" | 清理后转数字 |
3.4 发现问题的检查清单
| 检查项 | 问自己 | 异常信号 |
|---|---|---|
| ID列 | 这个ID有算术意义吗? | 没有 → 应该转文本 |
| 数字列 | 类型是int或float吗? | 是object → 需要转换 |
| 日期列 | 类型是datetime吗? | 是object → 需要转换 |
| 分类列 | 类型是category吗? | 是int且取值少 → 可能需要转分类 |
| 金额列 | 需要小数吗? | 是int → 可能需要转float |
四、类型转换对照表
4.1 目标类型与用途
| 目标类型 | 中文名 | 用途 | 典型列 |
|---|---|---|---|
int |
整数 | 计数、整数运算 | 年龄、数量 |
float |
浮点数 | 带小数的数值运算 | 价格、百分比 |
str / object |
文本 | 比较、匹配、拆分 | 姓名、地址 |
datetime |
日期时间 | 时间差、提取年月 | 注册日期、订单时间 |
category |
分类 | 节省内存、分组聚合 | 性别、省份、状态 |
4.2 常见转换对照表
| 当前状态 | 表现 | 应转为 | 处理方式 |
|---|---|---|---|
| 数字文本 | "123" |
int/float |
直接转换 |
| 带单位的数字 | "25岁"、"$100" |
int/float |
先提取数字,再转换 |
| 带千位分隔符的数字 | "10,000" |
int/float |
先去掉逗号,再转换 |
| 带空格的文本 | "北京 " |
str |
去除空格 |
| 标准日期文本 | "2024-01-15" |
datetime |
直接转换 |
| 非标准日期文本 | "2024年1月15日" |
datetime |
指定格式转换 |
| 纯数字日期 | 20240115 |
datetime |
先转文本,再按格式转日期 |
| 文本型ID | "00123"(显示为123) |
str |
读取时指定为文本,或填充前导零 |
五、各类问题的解决方案
5.1 问题一:数字存成了文本
如何发现:
info()显示类型为object- 但实际内容应该能进行数学运算
常见原因与解决:
| 原因 | 例子 | 怎么告诉AI |
|---|---|---|
| 完全干净的数字文本 | "123" |
"把年龄列从文本转成整数" |
| 包含空格 | " 123 " |
"先把年龄列去除空格,再转成整数" |
| 包含单位 | "25岁" |
"从年龄列中提取数字部分,再转成整数" |
| 包含千位分隔符 | "10,000" |
"去掉薪资列的逗号,再转成浮点数" |
| 混合了文本占位符 | "unknown"、"-" |
"把年龄列中的unknown替换成空,再转成数字" |
5.2 问题二:日期存成了文本
如何发现:
info()显示类型为object- 列名包含
date、time、日期、注册、订单等关键词
常见原因与解决:
| 原因 | 例子 | 怎么告诉AI |
|---|---|---|
| 标准格式 | "2024-01-15" |
"把订单日期列转成日期格式" |
| 斜杠分隔 | "2024/01/15" |
"把日期列从文本转成日期格式,原格式是年/月/日" |
| 中文格式 | "2024年1月15日" |
"把中文日期列转成标准日期格式" |
| 纯数字 | 20240115 |
"把注册日期列从整数转成日期格式,原格式是YYYYMMDD" |
| 月日年 | "01/15/2024" |
"把日期列转成日期格式,原格式是月/日/年" |
| 带时间 | "2024-01-15 14:30:00" |
"把订单时间列转成日期时间格式" |
5.3 问题三:ID存成了数字
为什么这是问题:
- 用户ID
00123被读成123,丢失了前导零 - 你不小心对ID列求了平均,得到了一个无意义的数字
如何发现:
- 列名包含
id、code、编号、用户等关键词 info()显示为int64
解决方法:
| 场景 | 怎么告诉AI |
|---|---|
| 读取时就指定类型 | "读取CSV时,把用户ID列当成字符串读" |
| 已经读成了数字 | "把用户ID列从整数转成字符串,注意要补全前导零到5位" |
5.4 问题四:分类存成了数字
为什么这是问题:
- 某些模型(如线性回归)会误认为
1<2<3有意义 - 但实际上"华为"、"小米"、"苹果"的大小顺序毫无意义
如何发现:
- 列取值是有限的几个数字(如
1,2,3) - 但业务含义是分类(品牌、车型、颜色)
解决方法:
| 场景 | 怎么告诉AI |
|---|---|
| 转换为分类类型(节省内存) | "把品牌编码列转成category类型" |
| 保留数字但告知业务含义 | 建模时独热编码,保持分类含义 |
| 映射为有意义的标签 | "把性别列:1映射为男性、2映射为女性" |
5.5 问题五:文本应该拆分(组合型数据)
为什么这是问题:
- 一列里包含了多个信息,无法直接分析
例子:
| 当前列 | 应拆分为 |
|---|---|
"张三,28,北京" |
姓名、年龄、城市 |
"192.168.1.1" |
四段独立IP可能无用,但有时需拆分 |
解决方法:
| 场景 | 怎么告诉AI |
|---|---|
| 按分隔符拆分 | "把姓名和年龄列按逗号拆分成两列" |
| 提取部分信息 | "从身份证号列中提取出生年月日" |
| 拆分同时保留原列 | "拆分地址列为省、市、区三列,保留原地址列" |
5.6 问题六:数字精度错误
为什么这是问题:
- 价格需要小数(
199.99)却存成了整数(199) → 丢失精度
如何发现:
- 列名包含
price、amount、ratio等关键词 info()显示为int64
解决方法:
| 场景 | 怎么告诉AI |
|---|---|
| 整数转浮点 | "把价格列从整数转成浮点数" |
| 进位处理 | "把价格列从整数转成浮点数,除以100得到真实价格" |
六、类型转换的注意事项
6.1 转换可能失败
当列中包含无法转换的值时,转换会报错。
| 问题 | 例子 | 解决方案 |
|---|---|---|
| 包含非数字字符 | "25岁" |
先清理,再转换 |
| 包含空值/占位符 | "-"、"unknown" |
先处理缺失值,再转换 |
| 混合格式 | "2024-01-15" 和 "2024年1月15日" |
先统一格式,再转换 |
告诉AI的方式:
"把年龄列转成数字时,遇到无法转换的设为空值,然后告诉我哪些行有问题"
6.2 转换顺序很重要
对于复杂情况,可能需要多步处理:
原始数据:"25岁"
↓ 第一步:提取数字 → "25"
↓ 第二步:转成整数 → 25
↓ 第三步:确认类型 → int64
告诉AI的方式:
"从年龄列中提取数字部分,然后转成整数类型"
6.3 原始数据最好保留
在清洗过程中,建议保留原始列,创建新列存放转换后的数据。
原因:
- 万一转换逻辑错了,可以重新来过
- 可以对比原始和转换后的结果,验证正确性
告诉AI的方式:
"创建一个新列'age_clean',从'age'列提取数字作为新列的值"
七、实战案例:二手车数据集类型转换
7.1 发现类型问题
使用info()检查:
Data columns (total 30 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 SaleID 150000 non-null int64
1 name 150000 non-null int64
2 regDate 150000 non-null int64 ← 应该是日期
3 model 149999 non-null float64
4 brand 150000 non-null int64
5 bodyType 145000 non-null float64
6 fuelType 141000 non-null float64
7 gearbox 144000 non-null float64
8 power 150000 non-null int64
9 kilometer 150000 non-null float64
10 notRepairedDamage 120000 non-null object ← 应该是0/1
11 regionCode 150000 non-null int64
12 seller 150000 non-null int64
13 offerType 150000 non-null int64
14 creatDate 150000 non-null int64 ← 应该是日期
15 price 150000 non-null int64
...
发现的问题:
| 列名 | 当前类型 | 应有类型 | 优先级 |
|---|---|---|---|
regDate |
int64 | datetime | 高 |
creatDate |
int64 | datetime | 高 |
notRepairedDamage |
object | int/float | 高 |
SaleID |
int64 | string | 低(但转更好) |
7.2 处理决策
| 列名 | 处理方式 | 怎么告诉AI |
|---|---|---|
regDate |
整数 → 日期 | "把regDate从整数转成日期格式,原格式是YYYYMMDD" |
creatDate |
整数 → 日期 | "把creatDate从整数转成日期格式,原格式是YYYYMMDD" |
notRepairedDamage |
清理 → 数字 | "把notRepairedDamage列中的'-'替换为空,然后转成数字类型" |
SaleID |
整数 → 文本 | "把SaleID转成字符串类型" |
7.3 转换后的验证
告诉AI验证:
"转换完成后,再用info确认一下类型是否正确"
预期结果:
regDate和creatDate显示为datetime64notRepairedDamage显示为float64SaleID显示为object或string
八、如何向AI描述类型转换需求
8.1 基础转换
| 你的需求 | 你应该这样告诉AI |
|---|---|
| 查看所有类型 | "用info查看每列的数据类型" |
| 文本转数字 | "把年龄列从文本转成整数" |
| 文本转小数 | "把价格列从文本转成浮点数" |
| 文本转日期 | "把订单日期列转成日期格式" |
| 数字转文本 | "把用户ID列从整数转成字符串" |
| 数字转分类 | "把性别编码列转成category类型" |
8.2 有问题的转换
| 你的需求 | 你应该这样告诉AI |
|---|---|
| 带单位 | "从年龄列中提取数字部分,再转成整数" |
| 带千位分隔符 | "去掉薪资列的逗号,再转成浮点数" |
| 千位分隔符+小数点 | "把金额列中的逗号去掉,然后转成浮点数" |
| 中文日期 | "把注册日期从'2024年1月15日'格式转成标准日期" |
| 纯数字日期 | "把regDate从整数转成日期,格式是YYYYMMDD" |
| 混合格式 | "尝试把日期列转成日期格式,无法解析的设为空" |
| 处理占位符 | "把年龄列中的unknown替换为空,然后转成数字" |
8.3 验证与调试
| 你的需求 | 你应该这样告诉AI |
|---|---|
| 验证转换结果 | "转换完成后,用info确认类型是否正确" |
| 找出转换失败的行 | "年龄列转数字时,哪些行转换失败了?显示出来" |
| 安全转换(不报错) | "把年龄列转成数字,遇到错误就设为空值" |
九、本章总结
核心知识点回顾
- 为什么重要:类型决定操作,类型错了分析全错
- 怎么发现 :
info()是主力工具,业务直觉辅助 - 常见问题 :
- 数字存成文本 → 转数字
- 日期存成文本 → 转日期
- ID存成数字 → 转文本
- 分类存成数字 → 转分类
- 怎么转换:告诉AI"把X列从A类型转成B类型"
快速诊断卡
| 观察 | 判断 | 怎么告诉AI |
|---|---|---|
info()显示数字列为object |
数字存成了文本 | "把XX列转成数字" |
info()显示日期列为object |
日期存成了文本 | "把XX列转成日期" |
| 列名含id,类型是数字 | ID不该是数字 | "把XX列转成字符串" |
| 列名含price,类型是整数 | 价格需要小数 | "把XX列转成浮点数" |
核心心法
"类型对了,分析才可能对。拿到数据的第一件事:用info()扫一眼类型,感觉不对就转。"
十、思考题
-
你用
info()发现age列是object类型,且包含"25岁"、"30"、"unknown"三种内容。你会怎么处理?分几步? -
日期列存成了整数
20240115格式。你想转成真正的日期。你会怎么告诉AI? -
交易金额列
amount显示为object,前几行是"1,200.50"、"2,000.00"、"500"。这是什么问题?怎么处理? -
用户ID列
user_id在Excel里是00123,读进Python后变成了123。为什么?怎么解决? -
一个"用户评分"列,理论上应该是1-5的数字。
info()显示是int64,看起来没问题。但你发现用户对评分的排序不对:"10"排在了"2"前面。问题出在哪里?