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 数据查看与描述性统计

拿到数据第一件事是什么?不是建模,是看数据长什么样。我一般三步走:

  1. 看头看尾df.head() 看前5行,df.tail() 看后5行。数据量大时别傻乎乎全打印。
  2. 看基本信息df.info() 告诉你每列类型、非空数量。嗯,这里要注意,如果某列非空数少于总行数,说明有缺失值。
  3. 看统计摘要df.describe() 直接给出数值列的均值、标准差、四分位数等。
print(df.head(3))
print(df.info())
print(df.describe())

为什么会这样设计?因为数据科学家 80% 的时间都在清洗和理解数据,真正建模只占 20%。你想想看,连数据分布都不清楚,模型能靠谱吗?

重点: describe() 默认只统计数值列。如果想看所有列,加个 include='all' 参数。我曾经因为没加这个参数,漏掉了分类变量的频次信息,导致后续特征工程做错了方向。

4.3 索引操作与层级索引

索引是 Pandas 的灵魂。我刚开始学的时候,总觉得索引不就是行号吗?后来才发现,它远不止如此。

基本索引:

  • df['列名'] 取一列,返回 Series
  • df[['列1', '列2']] 取多列,返回 DataFrame
  • df.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 排查了一下午,后来再也不敢偷懒了。

好了,这一章的内容就这些。你想想看,从创建数据到查看、索引、筛选,是不是已经能处理大部分日常数据操作了?下一章我们聊数据清洗,那才是真正的「脏活累活」。但别怕,有我在前面踩过坑,你只管跟着走就行。