Pandas 数据选择与定位 (Selection & Indexing) 速查表

Pandas 数据选择与定位 (Selection & Indexing) 速查表

适用版本: Pandas 1.x / 2.x
核心概念:

  • Selection (选择): 基于标签或布尔条件筛选数据。
  • Indexing (索引): 基于位置或标签快速访问数据。
  • View vs Copy: 链式赋值(Chained Assignment)可能导致 SettingWithCopyWarning,推荐使用 .loc/.iloc

1. 读取数据与基础查看

目的: 加载数据并了解其结构,为选择做准备。

import pandas as pd
import numpy as np

# --- 创建示例 DataFrame ---
data = {
    'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eva'],
    'age': [25, 30, 35, 40, 30],
    'city': ['NYC', 'LA', 'NYC', 'SF', 'LA'],
    'salary': [70000, 80000, 120000, 90000, 75000]
}
df = pd.DataFrame(data)
df.set_index('name', inplace=True) # 将 name 设为索引

# --- 基础查看 ---
df.head()          # 查看前5行
df.tail(3)         # 查看后3行
df.shape           # (行数, 列数)
df.columns         # 列名列表
df.index           # 索引对象
df.info()          # 数据类型和内存占用
df.describe()      # 数值列统计摘要

2. 选择列 (Selecting Columns)

目的: 提取单个或多个列。

操作 代码示例 返回类型 说明
单列 df['age'] pd.Series 最常用方式
单列 (属性式) df.age pd.Series 仅在列名无空格/特殊字符时可用
多列 df[['age', 'salary']] pd.DataFrame 传入列名列表
列名过滤 df.filter(like='a') pd.DataFrame 选择列名包含 'a' 的列
按正则选列 df.filter(regex='^s') pd.DataFrame 选择以 's' 开头的列

3. 基于标签选择 (.loc)

目的: 通过行标签 (Index Label)列名 进行选择。推荐用法

# 语法: df.loc[row_label(s), column_label(s)]

# --- 行选择 ---
df.loc['Alice']              # 选择单行 (Series)
df.loc[['Alice', 'Bob']]     # 选择多行 (DataFrame)

# --- 行列同时选择 ---
df.loc['Alice', 'age']       # 标量值 (Scalar)
df.loc['Alice', ['age', 'city']] # 单行多列 (Series)

# --- 切片 (包含末端) ---
df.loc['Alice':'David', 'age':'city'] 

# --- 布尔索引 (Boolean Indexing) ---
df.loc[df['age'] > 30]                  # 年龄大于30的所有行
df.loc[df['city'] == 'NYC', 'salary']   # NYC员工的薪水
df.loc[(df['age'] > 30) & (df['salary'] < 100000)] # 多条件 (&, |, ~)

4. 基于位置选择 (.iloc)

目的: 通过整数位置 (Integer Position) 进行选择(从 0 开始)。

# 语法: df.iloc[row_position(s), column_position(s)]

# --- 行选择 ---
df.iloc[0]            # 第一行 (Series)
df.iloc[[0, 2]]       # 第1和第3行 (DataFrame)

# --- 行列同时选择 ---
df.iloc[0, 1]         # 第1行第2列的值
df.iloc[0:2, 1:3]     # 行切片(不含2)和列切片(不含3)

# --- 使用列表/数组 ---
df.iloc[np.arange(3), [0, 2]] 

# --- 负索引 ---
df.iloc[-1]           # 最后一行

5. .loc 与 .iloc 对比 (Cheat Sheet)

特性 .loc .iloc
访问方式 标签 (Label-based) 位置 (Position-based)
切片行为 包含末端 ('A':'C') 不包含末端 (0:2)
索引示例 df.loc['Alice', 'age'] df.iloc[0, 1]
布尔数组 支持 (df.loc[bool_series]) 不支持
推荐度 ⭐⭐⭐⭐⭐ (首选,更安全) ⭐⭐⭐⭐ (处理位置数据时用)

6. 条件筛选与布尔索引 (Boolean Mask)

目的: 根据复杂逻辑过滤数据。

# --- 基本比较 ---
df[df['age'] == 30]
df[df['salary'].between(70000, 90000)]

# --- isin() 成员检查 ---
df[df['city'].isin(['NYC', 'LA'])]

# --- 字符串操作 (str accessor) ---
df[df['city'].str.startswith('N')]
df[df['city'].str.contains('Y', na=False)] # na=False 处理 NaN

# --- 缺失值筛选 ---
df[df['salary'].isna()]    # 选择 salary 为空的行
df[df['salary'].notna()]   # 选择 salary 非空的行

# --- 组合条件 ---
# 注意:每个条件必须用括号括起来,并用 & (AND), | (OR), ~ (NOT)
condition = (df['age'] >= 30) & (df['city'] != 'SF')
df.loc[condition]

7. 赋值与修改数据 (Assignment)

目的: 安全地修改 DataFrame 中的值。

# --- 直接赋值 (广播机制) ---
df['bonus'] = 1000                 # 新增一列,所有值为1000
df['total_comp'] = df['salary'] + df['bonus']

# --- 使用 .loc 赋值 (推荐) ---
df.loc['Alice', 'salary'] = 75000  # 修改单个值
df.loc[df['age'] > 35, 'bonus'] = 5000 # 条件更新

# --- 避免链式赋值 (Chained Assignment) ---
# ❌ 不推荐 (可能产生 Warning 或不生效):
df[df['age'] > 30]['bonus'] = 3000 

# ✅ 推荐:
df.loc[df['age'] > 30, 'bonus'] = 3000

8. 其他高级选择技巧

# --- at / iat (快速标量访问) ---
df.at['Alice', 'age']    # 等同于 df.loc['Alice', 'age'],但速度更快
df.iat[0, 1]             # 等同于 df.iloc[0, 1],但速度更快

# --- query() 方法 (类 SQL 语法) ---
df.query("age > 30 and city == 'NYC'")
df.query("age in [30, 35]")

# --- where() 方法 (保留原结构) ---
# 不满足条件的值会被替换为 NaN (或指定值)
df.where(df['age'] > 30, other=0) 

# --- lookup() (已弃用,建议用 .loc) ---
# df.lookup(row_labels, col_labels) -> 使用 df.loc[row_labels, col_labels]

9. 常见错误与解决方案 (Troubleshooting)

问题现象 原因分析 解决方案
SettingWithCopyWarning 使用了链式索引 (e.g., df[df.A>0]['B']=1) 改用 .loc: df.loc[df.A>0, 'B']=1
KeyError: 'column_name' 列名不存在或拼写错误 检查 df.columns.tolist()
切片结果不符合预期 混淆了 .loc (含尾) 和 .iloc (不含尾) 明确使用场景,优先用 .loc
条件过滤失效 忘记给多条件加括号 ( ) (cond1) & (cond2)
修改了原始数据 Pandas 默认是 View 还是 Copy 不明确 如需副本,显式使用 df.copy()

💡 最佳实践总结 (Best Practices)

  1. 首选 .loc: 除非明确需要按位置索引,否则一律使用 .loc 进行读写,避免歧义。
  2. 警惕链式赋值: 永远不要在一个语句中同时使用 [] 两次进行修改。
  3. 使用副本: 如果需要修改数据的子集而不影响原 DataFrame,请先 sub_df = df.loc[...].copy()
  4. 性能: 对于循环内的单个元素访问,使用 at/iat 代替 loc/iloc
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容