4. 数据类型与结构转换
数据类型转换和结构操作,说白了就是让数据「听话」。
我刚开始做数据清洗时,经常被各种类型报错搞到头大。明明看着是数字,一求和却报错。后来才明白——数据类型不对,啥都白费。
4.1 数据类型转换
Python 里常见的数据类型有 int、float、str、bool、datetime 等。Pandas 里还有 category、object 这些特殊类型。
为什么要转类型?
- 计算需要:字符串不能做加减乘除
- 存储优化:category 比 object 省内存
- 分析需求:日期需要转成 datetime 才能做时间序列
核心方法:
astype():最通用的类型转换to_numeric():转数字,出错可设 errors='coerce'to_datetime():转日期,自动识别格式
import pandas as pd
# 创建一个有问题的 DataFrame
df = pd.DataFrame({
'价格': ['12.5', '23.8', '9.9', 'error'],
'销量': ['100', '200', '150', '300'],
'日期': ['2024-01-01', '2024-01-02', '2024/01/03', '20240104']
})
# 转数字 - 遇到错误就变成 NaN
df['价格'] = pd.to_numeric(df['价格'], errors='coerce')
# 转日期 - 自动识别多种格式
df['日期'] = pd.to_datetime(df['日期'], errors='coerce')
# 转整数 - 先确保没有 NaN
df['销量'] = df['销量'].astype(int)
我的习惯:用 errors='coerce' 先转一遍,看看哪些数据有问题。比直接报错强多了。
常见坑点:
- 字符串里有空格或特殊字符,转数字会失败
- 日期格式不统一,需要指定 format 参数
- 转整数前如果有 NaN,会报错
我曾经踩过的坑:有一批销售数据,价格列里混了「元」字。直接 astype(float) 报错,排查了半天才发现。后来我养成了习惯——先 unique() 看一眼数据长啥样。
4.2 DataFrame 与 Series 操作
DataFrame 是表格,Series 是列。两者经常需要互相转换。
从 DataFrame 提取 Series:
# 提取单列 - 返回 Series
price_series = df['价格']
# 提取多列 - 返回 DataFrame
subset = df[['价格', '销量']]
从 Series 构建 DataFrame:
# 单列 Series 转 DataFrame
df_from_series = price_series.to_frame('价格')
# 多个 Series 合并
df_new = pd.DataFrame({
'价格': price_series,
'销量': sales_series
})
行列操作:
df.T:转置,行变列、列变行df.stack():列转行(宽表变长表)df.unstack():行转列(长表变宽表)
实战场景:我处理过一份用户行为数据,每个用户一行,行为类型是列名。用 melt() 转成长表后,分析起来方便多了。
# 宽表转长表
df_long = pd.melt(df,
id_vars=['用户ID'],
var_name='行为类型',
value_name='次数')
# 长表转宽表
df_wide = df_long.pivot(index='用户ID',
columns='行为类型',
values='次数')
4.3 索引与列名处理
索引和列名,是数据的「身份证」。处理不好,后面全乱套。
索引操作:
df.set_index('列名'):把某列设为索引df.reset_index():索引重置为数字df.rename(index={旧: 新}):重命名索引
列名操作:
df.columns:查看所有列名df.rename(columns={旧: 新}):重命名列df.columns = ['新列名1', '新列名2']:批量改名
我建议:列名统一用小写+下划线格式。比如 user_name 而不是 UserName 或 用户名称。后期写代码省心很多。
# 列名清洗实战
df.columns = df.columns.str.strip() # 去空格
df.columns = df.columns.str.lower() # 转小写
df.columns = df.columns.str.replace(' ', '_') # 空格换下划线
df.columns = df.columns.str.replace('[^a-z0-9_]', '', regex=True) # 去特殊字符
# 索引重置
df = df.reset_index(drop=True) # drop=True 丢弃旧索引
注意:索引重复会导致很多操作报错。用 df.index.is_unique 检查一下。我遇到过索引重复导致 merge 结果翻倍的惨案。
知识体系图
嗯,这张图把本章的核心内容串起来了。数据类型转换是基础,DataFrame 与 Series 操作是日常,索引与列名处理是细节。三者缺一不可。
总结一下:
- 转类型用
astype()或to_numeric(),记得处理异常值 - DataFrame 和 Series 互转用
to_frame()和squeeze() - 列名统一规范,索引保持唯一
- 每次转换后,用
dtypes和head()验证结果
我的经验:数据清洗没有银弹。每份数据都有自己的脾气。多检查、多验证,慢慢就能摸清规律。