4、Pandas数据结构:Series与DataFrame创建、数据查看与描述性统计、索引操作与层级索引、数据筛选与条件过滤
好,咱们进入第四章。说实话,Pandas 是数据科学家的吃饭家伙,就像厨师的菜刀一样。你想想看,没有它,你连数据长什么样都看不清。这一章我带你亲手把数据结构搭起来,再学会怎么翻来覆去地看它、查它、筛它。
4.1 Series 与 DataFrame 的创建
先聊 Series。它就是一列数据,带个索引。你可以把它想象成 Excel 里的一列,但比那灵活多了。
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,因为键就是索引,直观:
data = {'张三': 85, '李四': 92, '王五': 78}
s = pd.Series(data)
print(s)
再来看 DataFrame。它是二维的,说白了就是一张表。行有行索引,列有列名。
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五'],
'年龄': [25, 30, 28],
'城市': ['北京', '上海', '广州']
})
print(df)
小技巧: 我在项目中遇到过,从 CSV 读数据时经常列名带空格。用
pd.read_csv('file.csv') 后,记得加个 df.columns = df.columns.str.strip(),否则后面筛选会踩坑。
4.2 数据查看与描述性统计
拿到数据第一件事是什么?不是建模,是看数据长什么样。我一般三步走:
- 看头看尾:
df.head()看前5行,df.tail()看后5行。数据量大时别傻乎乎全打印。 - 看基本信息:
df.info()告诉你每列类型、非空数量。嗯,这里要注意,如果某列非空数少于总行数,说明有缺失值。 - 看统计摘要:
df.describe()直接给出数值列的均值、标准差、四分位数等。
print(df.head(3))
print(df.info())
print(df.describe())
为什么会这样设计?因为数据科学家 80% 的时间都在清洗和理解数据,真正建模只占 20%。你想想看,连数据分布都不清楚,模型能靠谱吗?
重点:
describe() 默认只统计数值列。如果想看所有列,加个 include='all' 参数。我曾经因为没加这个参数,漏掉了分类变量的频次信息,导致后续特征工程做错了方向。
4.3 索引操作与层级索引
索引是 Pandas 的灵魂。我刚开始学的时候,总觉得索引不就是行号吗?后来才发现,它远不止如此。
基本索引:
df['列名']取一列,返回 Seriesdf[['列1', '列2']]取多列,返回 DataFramedf.loc[行标签]按标签取行df.iloc[行位置]按位置取行
# 按标签取
print(df.loc[0]) # 取第一行(标签为0的行)
# 按位置取
print(df.iloc[1]) # 取第二行
层级索引(MultiIndex): 这个有点高级,但实战中非常有用。比如你有多个年份、多个产品的销售数据,用层级索引可以轻松分组。
# 创建层级索引
index = pd.MultiIndex.from_tuples([
('2023年', '产品A'),
('2023年', '产品B'),
('2024年', '产品A'),
('2024年', '产品B')
], names=['年份', '产品'])
df_multi = pd.DataFrame({
'销量': [100, 150, 120, 180],
'收入': [10000, 15000, 12000, 18000]
}, index=index)
print(df_multi)
输出:
销量 收入
年份 产品
2023年 产品A 100 10000
产品B 150 15000
2024年 产品A 120 12000
产品B 180 18000
避坑指南: 我曾经在层级索引上栽过跟头。用
df.loc['2023年'] 取数据时,返回的是所有 2023 年的行,但如果你再用 df.loc['2023年', '产品A'],它返回的是单个值。层级索引的切片规则和普通索引不一样,建议先 df.sort_index() 排序,否则切片会报错。
4.4 数据筛选与条件过滤
筛选数据,说白了就是「我要哪些行、哪些列」。Pandas 的布尔索引非常强大,我几乎每天都在用。
单条件筛选:
# 筛选年龄大于25的人
df_filter = df[df['年龄'] > 25]
print(df_filter)
多条件筛选: 用 &(与)、|(或)、~(非)。注意每个条件要加括号。
# 年龄大于25且城市是上海
df_filter = df[(df['年龄'] > 25) & (df['城市'] == '上海')]
print(df_filter)
用 isin 筛选多个值:
# 筛选城市为北京或广州的人
df_filter = df[df['城市'].isin(['北京', '广州'])]
print(df_filter)
用 query 方法: 我个人觉得 query 更简洁,尤其条件多的时候。
df_filter = df.query('年龄 > 25 and 城市 == "上海"')
print(df_filter)
经验之谈: 我建议你养成用
.copy() 的习惯。比如 df_filter = df[df['年龄'] > 25].copy()。为什么?因为直接切片返回的是视图,修改它可能会影响原数据。我曾经因为这个 bug 排查了一下午,后来再也不敢偷懒了。
好了,这一章的内容就这些。你想想看,从创建数据到查看、索引、筛选,是不是已经能处理大部分日常数据操作了?下一章我们聊数据清洗,那才是真正的「脏活累活」。但别怕,有我在前面踩过坑,你只管跟着走就行。