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 结果翻倍的惨案。

知识体系图

数据类型与结构转换 数据类型转换 astype() to_numeric() to_datetime() DataFrame与Series 提取/构建 转置(T) stack/unstack melt/pivot 索引与列名处理 set_index reset_index rename 列名清洗 核心原则 先检查 → 再转换 → 后验证 数据清洗第一步:让数据「听话」

嗯,这张图把本章的核心内容串起来了。数据类型转换是基础,DataFrame 与 Series 操作是日常,索引与列名处理是细节。三者缺一不可。

总结一下:

  • 转类型用 astype()to_numeric(),记得处理异常值
  • DataFrame 和 Series 互转用 to_frame()squeeze()
  • 列名统一规范,索引保持唯一
  • 每次转换后,用 dtypeshead() 验证结果

我的经验:数据清洗没有银弹。每份数据都有自己的脾气。多检查、多验证,慢慢就能摸清规律。

专注资料整理