pandas 提供了多套相互独立又互补的索引系统。本节涵盖最基础的索引方式。


1. 方括号 []

Series 的方括号索引

Series 的方括号行为取决于索引类型:

  • 整数索引下 s[0] 按位置取值
  • 标签索引下 s['a'] 按标签取值
  • 混合情况可能产生歧义
import pandas as pd
 
s_label = pd.Series([10, 20, 30], index=['a', 'b', 'c'])
s_label['a']    # 10(标签取值)
s_label[0]      # 10(位置取值)
 
s_int = pd.Series([10, 20, 30], index=[1, 2, 3])
s_int[1]        # 10(索引为 1 的标签值?还是位置 0?)

注意事项

当 Series 索引为整型时,s[1] 按标签解释而非位置。建议明确使用 .loc / .iloc。

Series 方括号支持:

  • 单值:s['a']
  • 标签列表:s[['a', 'c']]
  • 位置切片:s[0:2](不包含端点)
  • 布尔数组:s[[True, False, True]]

DataFrame 的方括号索引

DataFrame 的方括号主要用于列选择:

df = pd.DataFrame({'A': [1, 2], 'B': [3, 4], 'C': [5, 6]})
 
df['A']          # 选择单列,返回 Series
df[['A', 'C']]   # 选择多列,返回 DataFrame

DataFrame 方括号也支持行切片(位置语义):

df[0:1]          # 选择第一行(位置切片,不含端点)
df[::2]          # 隔行选择
df[df['A'] > 1]  # 布尔索引行过滤

不要使用 df['A', 'B']

逗号括号在 DataFrame 中会产生错误,需要传递列表:df[['A', 'B']]。


2. 属性访问 .

df = pd.DataFrame({'name': ['张三', '李四'], 'age': [25, 30]})
df.name      # 等效于 df['name'],返回 Series
df.age       # 等效于 df['age']

属性访问的限制

  • 列名必须以 Python 标识符规范命名(字母/下划线开头,不含空格)
  • 列名可能与 DataFrame 方法/属性冲突(如 df.columns、df.index、df.shape)
  • pandas 2.x 起,若属性与 DataFrame 方法同名,会抛出警告
  • 建议仅在交互式探索时使用属性访问,正式代码使用 df['col']
# 列名为非标识符或与内置属性冲突时必须使用方括号
df['first name']   # 必须使用方括号
df['columns']      # 不能使用 df.columns(这是属性)

3. 标签索引 .loc 与 .at

.loc 语法

.loc[] 按标签(或标签列表、标签切片、布尔数组)选择数据,切片的结束标签包含在内。

df = pd.DataFrame(
    {'A': [1, 2, 3, 4], 'B': [5, 6, 7, 8]},
    index=['w', 'x', 'y', 'z']
)
语法结果
df.loc['x']选择标签为 'x' 的行(Series)
df.loc[['x', 'z']]按标签列表选行
df.loc['x':'y']标签切片(包含 'y')
df.loc['x', 'B']单个元素(标量)
df.loc['x':'z', 'A']行切片 + 单列
df.loc[:, 'B']所有行 + B 列
df.loc[['x', 'z'], ['A', 'B']]行列均为标签列表
df.loc[df['A'] > 2]布尔条件筛选行
df.loc[df['A'] > 2, 'B']布尔条件 + 指定列
# Series 的 .loc
s = pd.Series([10, 20, 30], index=['a', 'b', 'c'])
s.loc['b']             # 20
s.loc['a':'b']         # a    10
                       # b    20(含端点)
s.loc[['a', 'c']]      # 标签列表

无匹配项处理

  • 标签不存在:抛出 KeyError
  • 使用 reindex() 可容忍不存在的标签

.at 语法

.at 定位单个标量(按标签),比 .loc 更快:

df.at['x', 'B']       # 6
df.at[df.index[0], 'A']  # 1
 
# 赋值
df.at['x', 'B'] = 100

.at 的限制

  • 只能访问单个元素,不能用于切片或列表
  • 标签不存在时抛出 KeyError
  • 在 MultiIndex 上需传入完整元组

4. 位置索引 .iloc 与 .iat

.iloc 语法

.iloc[] 按整数位置选择(从 0 开始),切片不包含结束位置。

df = pd.DataFrame(
    {'A': [1, 2, 3, 4], 'B': [5, 6, 7, 8]},
    index=['w', 'x', 'y', 'z']
)
语法结果
df.iloc[0]第一行(Series)
df.iloc[1:3]第 1~2 行(不含 3)
df.iloc[0, 1]第一行第二列(标量)
df.iloc[:, 0]所有行第一列
df.iloc[[0, 2], :]按位置列表选行
df.iloc[-1]最后一行(负数从末尾计数)
df.iloc[::2]隔行选择
# Series 的 .iloc
s = pd.Series([10, 20, 30], index=['a', 'b', 'c'])
s.iloc[0]       # 10
s.iloc[1:3]     # b    20
                # c    30
s.iloc[[0, 2]]  # 位置列表

.iat 语法

.iat 通过整数位置快速访问标量:

df.iat[0, 1]   # 5
df.iat[2, 0]   # 3
 
# 赋值
df.iat[1, 1] = 50

性能对比

  • 对于单个标量,at / iat 比 loc / iloc 更快
  • 但差距通常很小,优先选择语义清晰的 .loc / .iloc

5. 布尔索引

基本用法

df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
 
mask = df['A'] > 1
df[mask]                    # 过滤 A > 1 的行
df[df['B'] < 6]             # 直接表达式
df.loc[df['A'] > 1]         # 推荐写法

复合条件

# 使用 &(与)、|(或)、~(非)
df[(df['A'] > 1) & (df['B'] < 6)]    # 两个条件都满足
df[(df['A'] > 1) | (df['B'] == 4)]   # 任一条件满足
df[~(df['A'] > 1)]                   # 取反

运算符优先级

  • 必须使用 &、|、~,而不是 Python 的 and、or、not
  • 每个布尔条件必须用括号包裹:(df['A'] > 1) & (df['B'] < 6)
  • and / or 会抛出 ValueError: The truth value of a Series is ambiguous

常用布尔条件构造

方法说明
df['col'] == value等于某个值
df['col'] != value不等于
df['col'].isin(values)在指定集合中
~df['col'].isin(values)不在集合中
df['col'].between(a, b)在闭区间 [a, b] 内
df['col'].isna()缺失值
df['col'].notna()非缺失值
df['str'].str.contains(...)字符串包含
df['str'].str.startswith(...)字符串开头
mask = (
    (df['城市'].isin(['北京', '上海'])) &
    (df['年龄'].between(18, 35)) &
    (df['姓名'].str.startswith('张'))
)
df.loc[mask]

布尔索引与 .loc 的区别

# df[boolean] 与 df.loc[boolean] 在筛选行时基本等效
df[mask]                      # 行过滤
df.loc[mask]                  # 行过滤
df.loc[df['A'] > 1, 'B']      # 同时过滤行并选择列
 
# 不要使用这种写法(错误)
# df[df['A'] > 1, 'B']

6. 切片

Series 切片

s = pd.Series([10, 20, 30, 40], index=['a', 'b', 'c', 'd'])
 
# 位置切片(不包含结束位置)
s[0:2]           # a    10
                 # b    20
 
# 标签切片(包含结束位置)
s['a':'c']       # a    10
                 # b    20
                 # c    30
 
# 步长切片
s[::2]           # a    10
                 # c    30

DataFrame 切片

df = pd.DataFrame({'A': range(6), 'B': range(100, 106)})
 
# 位置切片(行)
df[1:4]          # 第 1~3 行(不含第 4)
 
# 步长
df[::2]          # 偶数行
 
# .loc 标签切片(行,含端点)
df.loc[1:4]      # 索引 1~4 全部包含
 
# .iloc 位置切片(不含端点)
df.iloc[1:4]     # 第 1~3 行

切片关键区别

方式端点语义示例
df[1:4]不含端点(位置)行 1~3
df.loc[1:4]包含端点(标签)行 1~4
df.iloc[1:4]不含端点(位置)行 1~3
s['a':'c']包含端点(标签)标签 a~c
s[0:2]不含端点(位置)位置 0~1

警告

  • 当 DataFrame 索引不是顺序整数时,df[start:stop] 使用位置语义,容易混淆。
  • 如需标签语义切片,始终使用 .loc。

小结

  • 方括号 []:最基础的列选择与位置切片
  • 属性访问 .:快速上手但不适合正式代码
  • .loc / .at:标签定位,.at 为单标量快速访问
  • .iloc / .iat:位置定位,.iat 为单标量快速访问
  • 布尔索引:&、|、~ 组合条件,需要括号包裹
  • 切片:.loc 含端点、.iloc 不含端点