Pandas入门:Series、DataFrame、文件读取与缺失值处理

做数据分析绕不开 Pandas,这玩意儿说白了就是 Python 里的 Excel 增强版。我刚接触时觉得它不就一个表格库么?后来在项目中处理百万行日志,才知道没有 Pandas 我连数据都打不开。

这一章我们集中攻克四个核心:

  • 两种基本结构 —— Series 和 DataFrame
  • 从 CSV / Excel 把数据拽进来
  • 处理最头疼的缺失值
学习建议:跟着代码敲一遍,别光看。我当年就是只看不敲,结果面试让写个 groupby 卡了十分钟。
Pandas 核心知识结构 Pandas Series(一维) DataFrame(二维) 文件读取 索引操作 向量化 列选择 过滤 CSV Excel 缺失值处理

1. Series 与 DataFrame:两种武器

Series 是一维的,就像 Excel 里的一列(带行号)。DataFrame 是二维的,像一张完整的表。

直接看代码:

import pandas as pd

# 创建 Series
s = pd.Series([10, 20, 30, 40], index=['a', 'b', 'c', 'd'])
print(s)
# a    10
# b    20
# c    30
# d    40
# dtype: int64

Series 的索引可以自定义,不一定是连续整数。我自己在项目中经常用它来存时间序列的一列数据,比如某个传感器的每小时读数。

DataFrame 更常用:

data = {
    '城市': ['北京', '上海', '广州', '深圳'],
    '温度': [12, 15, 20, 22],
    '湿度': [60, 70, 80, 75]
}
df = pd.DataFrame(data)
print(df)
#    城市  温度  湿度
# 0  北京   12   60
# 1  上海   15   70
# 2  广州   20   80
# 3  深圳   22   75

嗯,这里要注意:DataFrame 的列名来自字典的 key,行索引默认从 0 开始。

我整理了一张对比表,方便你快速区分:

特性 Series DataFrame
维度 一维 二维
数据结构 带标签的数组 带标签的表格(多列 Series)
索引方式 loc 按标签,iloc 按位置 loc 按行列标签,iloc 按行列位置
常见创建 pd.Series(list, index=) pd.DataFrame(dict) 或读取文件
典型用途 单列数据 + 统计分析 多列关联数据 + 清洗建模

有个实用技巧:DataFrame 里每一列其实都是一个 Series。你取 df['温度'] 得到的就是 Series,可以继续用 Series 的方法。

实操经验:我在做房价预测项目时,把每个特征列转成 Series 做缺失值填充,然后再拼回 DataFrame。熟悉这两种结构的互相转换,能省不少代码。

2. 读取 CSV / Excel:数据入口

现实中的数据很少是靠手动敲字典创建的,更多是从文件读入。Pandas 的 read_csv 和 read_excel 就是大门。

先看 CSV:

# 最简单的读取
df = pd.read_csv('data.csv')
print(df.head())   # 默认前5行

# 如果文件没有列名(header=None)
df = pd.read_csv('data.csv', header=None)

# 指定分隔符为制表符
df = pd.read_csv('data.tsv', sep='\t')

# 只读取前100行,适合大文件测试
df = pd.read_csv('big.csv', nrows=100)

我记得刚入行时,遇到一个 CSV 文件里面有几万行乱码,就是因为编码问题。后来我养成习惯:

# 遇到中文文件,指定 encoding='utf-8' 或 'gbk'
df = pd.read_csv('数据.csv', encoding='gbk')
踩坑提醒:我曾经因为没指定 encoding,读出来的全是乱码,排查了半小时才发现是文件本身是 GBK 编码。CSV 文件最好先用记事本打开确认编码。

读取 Excel 文件要复杂一丢丢,需要安装 openpyxl 或 xlrd:

# 默认读取第一个 sheet
df = pd.read_excel('报表.xlsx')

# 指定 sheet 名
df = pd.read_excel('报表.xlsx', sheet_name='销售数据')

# 跳过前2行,从第3行开始读(表头在第3行)
df = pd.read_excel('报表.xlsx', header=2)

读取后第一件事是什么?我的流程是这样:

  1. df.info() —— 看列名、非空数量、数据类型
  2. df.describe() —— 数值列的统计摘要
  3. df.isnull().sum() —— 查缺失值情况

你想想看,这三个命令下来,数据长什么样基本心里有数了。

3. 缺失值处理:数据清洗必修课

现实数据几乎没有干干净净的。缺一个值、传个 NaN、甚至留个空字符串,都很常见。Pandas 把缺失值统一表示为 NaN(Not a Number)。

怎么发现缺失值?

# 布尔型缺失掩码
df.isna()    # 或 df.isnull()

# 每列缺失数量
df.isnull().sum()

# 有缺失的行
df[df.isnull().any(axis=1)]

处理缺失值通常有两种思路:删掉 或 填充。

3.1 删除缺失行/列

# 删除包含任何缺失值的行
df_clean = df.dropna()

# 只在某列缺失时才删(比如 '价格' 列)
df_clean = df.dropna(subset=['价格'])

# 删除全部是缺失值的行
df_clean = df.dropna(how='all')

# 删除列:缺失比例超过 50% 的列
df_clean = df.dropna(thresh=len(df)*0.5, axis=1)
小经验:别上来就 dropna。如果数据本来就少,删一行就丢一条样本。我一般先看看缺失比例,低于 5% 才考虑直接删。

3.2 填充缺失值

# 用固定值填充
df['温度'].fillna(0, inplace=True)

# 用列平均值填充
df['温度'].fillna(df['温度'].mean(), inplace=True)

# 用前一个有效值填充(适用于时间序列)
df['温度'].fillna(method='ffill', inplace=True)

# 用后一个有效值填充
df['温度'].fillna(method='bfill', inplace=True)

为什么用平均值?对于正态分布的数值数据,平均值是合理的猜测。但如果数据有离群点,用中位数更稳健:

df['收入'].fillna(df['收入'].median(), inplace=True)

类别数据则用众数填充:

df['城市'].fillna(df['城市'].mode()[0], inplace=True)

讲真,有一次我忘了 inplace=True,跑完代码发现 DataFrame 没变,闹了个小乌龙。现在写 fillna 我都习惯先赋值或加 inplace。

综合案例:假设有个销售订单表,缺失了部分金额和客户名。我会这样处理:
1. 金额列:用同产品的平均金额填充
2. 客户名列:缺失比例低就删行,高则统一填 "未知客户"
3. 日期列(如果有缺失):直接删行,因为日期不可推断

小结

这一章我们走了三个台阶:先认识 Series 和 DataFrame,再学会从 CSV/Excel 读取数据,最后处理缺失值。这三步是任何数据分析流程的前置动作,练熟了后面画图建模才不卡壳。

我自己初期在缺失值处理上栽过跟头,比如用 fillna 后没检查是否真的填充成功。推荐你们每次填充后都跑一次 df.isnull().sum() 确认一下。嗯,今天就到这,动手敲吧。