跳到主要内容

Pandas 基础操作

问题

Pandas 是什么?DataFrame 和 Series 有什么区别?如何进行数据读取、索引和选取?

答案

Pandas 是 Python 数据分析的核心库,提供了高效的数据结构 DataFrameSeries,几乎是数据分析师日常工作中使用最频繁的工具。

核心数据结构

对比SeriesDataFrame
维度一维二维
类比SQL 的一列 / Excel 的一列SQL 的一张表 / Excel 的一个 Sheet
创建pd.Series([1,2,3])pd.DataFrame({'a':[1,2],'b':[3,4]})
索引单个 Index行 Index + 列 Columns

数据读写

数据读取与写入
import pandas as pd

# 读取 CSV
df = pd.read_csv('data.csv', encoding='utf-8')

# 读取 Excel
df = pd.read_excel('data.xlsx', sheet_name='Sheet1')

# 从 SQL 读取(需要 sqlalchemy)
from sqlalchemy import create_engine
engine = create_engine('mysql+pymysql://user:pwd@host/db')
df = pd.read_sql('SELECT * FROM orders', engine)

# 读取 JSON
df = pd.read_json('data.json')

# 读取 Parquet(高性能列式格式)
df = pd.read_parquet('data.parquet')

# 写出为 CSV
df.to_csv('output.csv', index=False)

# 写出为 Excel
df.to_excel('output.xlsx', index=False)

基本信息查看

快速了解数据
# 前/后 N 行
df.head(5)
df.tail(5)

# 形状(行数, 列数)
df.shape # (10000, 12)

# 列名和数据类型
df.dtypes
df.info() # 综合信息:列名、非空数、类型、内存占用

# 描述性统计
df.describe() # 数值列的 count/mean/std/min/max 等
df.describe(include='all') # 包含分类列

索引与选取

这是面试高频考点,需掌握 [].loc.iloc 三种方式的区别:

方式说明示例
df['col']选取单列,返回 Seriesdf['name']
df[['col1','col2']]选取多列,返回 DataFramedf[['name','age']]
df[条件]布尔索引,筛选行df[df['age'] > 18]
.loc[行标签, 列标签]标签选取df.loc[0:5, 'name':'age']
.iloc[行位置, 列位置]位置选取(整数索引)df.iloc[0:5, 0:3]
loc vs iloc 的切片区别
  • .loc 的切片包含终点df.loc[0:5] 返回第 0~5 行(6 行)
  • .iloc 的切片不包含终点df.iloc[0:5] 返回第 0~4 行(5 行)

这是面试中最常见的 Pandas 陷阱之一!

索引与选取示例
import pandas as pd

df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Charlie', 'David'],
'age': [25, 30, 35, 28],
'city': ['北京', '上海', '广州', '深圳'],
'salary': [15000, 20000, 25000, 18000]
})

# 1. 选取单列
df['name'] # 返回 Series
df[['name', 'age']] # 返回 DataFrame

# 2. 布尔索引(条件筛选)
df[df['age'] > 28] # 年龄大于 28
df[(df['age'] > 25) & (df['city'] == '上海')] # 多条件筛选(& 且 | 或 ~ 非)

# 3. loc - 按标签索引
df.loc[0:2, 'name':'city'] # 第 0~2 行(含),name~city 列(含)
df.loc[df['age'] > 28, ['name', 'salary']] # 条件 + 列选取

# 4. iloc - 按位置索引
df.iloc[0:2, 0:3] # 第 0~1 行,第 0~2 列
df.iloc[-1] # 最后一行

常用列操作

列的增删改
# 新增列
df['bonus'] = df['salary'] * 0.1

# 条件赋值(类似 SQL CASE WHEN)
import numpy as np
df['level'] = np.where(df['salary'] >= 20000, '高薪', '普通')

# 多条件赋值
conditions = [
df['salary'] >= 25000,
df['salary'] >= 18000,
]
choices = ['高级', '中级']
df['grade'] = np.select(conditions, choices, default='初级')

# 删除列
df = df.drop(columns=['bonus'])

# 重命名列
df = df.rename(columns={'name': '姓名', 'age': '年龄'})

# 修改数据类型
df['age'] = df['age'].astype(float)

排序

排序操作
# 按单列排序
df.sort_values('salary', ascending=False) # 降序

# 按多列排序
df.sort_values(['city', 'salary'], ascending=[True, False])

# 按索引排序
df.sort_index()

# 获取排名(类似 SQL RANK)
df['salary_rank'] = df['salary'].rank(ascending=False, method='dense')

字符串操作

Pandas 的 .str 访问器提供了丰富的字符串方法:

字符串方法
# 常用字符串方法
df['name'].str.lower() # 转小写
df['name'].str.upper() # 转大写
df['name'].str.contains('li') # 包含判断(返回布尔)
df['name'].str.startswith('A') # 前缀判断
df['name'].str.len() # 字符串长度
df['name'].str.strip() # 去除首尾空格
df['name'].str.replace('A', 'a') # 替换

# 正则提取
df['email'].str.extract(r'@(\w+)') # 提取邮箱域名

apply 自定义函数

当内置方法满足不了需求时,使用 apply 应用自定义函数:

apply 用法
# 对单列应用函数
df['name_len'] = df['name'].apply(len)

# 带参数的 lambda
df['salary_tax'] = df['salary'].apply(lambda x: x * 0.8 if x > 20000 else x * 0.9)

# 对整行应用函数(axis=1)
def calc_score(row):
if row['age'] < 30 and row['salary'] > 18000:
return 'A'
return 'B'

df['score'] = df.apply(calc_score, axis=1)
apply 性能注意

apply 本质是逐行/逐列循环,大数据量下性能很差。优先使用向量化操作:

  • np.where() 替代条件 apply
  • df['col'].map(dict) 替代映射 apply
  • .str 方法替代字符串 apply

常见面试问题

Q1: DataFrame 和 Series 的区别?

答案

  • Series 是带标签的一维数组,类似于 Python 字典或 SQL 的一列
  • DataFrame 是带行列标签的二维表格,由多个 Series 组成,类似于 SQL 表或 Excel Sheet
  • DataFrame 的每一列是一个 Series,可以通过 df['col'] 获取

Q2: loc 和 iloc 的区别?

答案

对比.loc.iloc
索引方式标签(label)位置(integer)
切片终点包含终点不包含终点
示例df.loc[0:5] → 6 行df.iloc[0:5] → 5 行
支持布尔
支持列名df.loc[:, 'name']❌ 只能用数字

Q3: 如何处理 Pandas 中的条件赋值?

答案

三种常用方式,优先级从高到低:

# 方式一:np.where(二选一条件)
df['level'] = np.where(df['score'] >= 90, '优秀', '普通')

# 方式二:np.select(多条件)
conditions = [df['score'] >= 90, df['score'] >= 60]
choices = ['优秀', '及格']
df['level'] = np.select(conditions, choices, default='不及格')

# 方式三:map(字典映射)
mapping = {'A': '优秀', 'B': '良好', 'C': '一般'}
df['level'] = df['grade'].map(mapping)

Q4: apply 和向量化操作的性能差异?

答案

向量化操作比 apply10~100 倍,因为:

  • 向量化利用 NumPy 的 C 底层计算,批量处理整列数据
  • apply 是 Python 层面的循环,逐行执行

原则:能用向量化就不用 apply,只在逻辑非常复杂时才用 apply。

Q5: 如何读取超大 CSV 文件?

答案

# 方式一:分块读取
chunks = pd.read_csv('big.csv', chunksize=100000)
result = pd.concat([chunk[chunk['col'] > 0] for chunk in chunks])

# 方式二:只读取需要的列
df = pd.read_csv('big.csv', usecols=['col1', 'col2'])

# 方式三:指定数据类型减少内存
df = pd.read_csv('big.csv', dtype={'id': 'int32', 'name': 'category'})

# 方式四:使用 Parquet 格式(推荐)
df = pd.read_parquet('big.parquet') # 比 CSV 快 5~10 倍

Q6: Pandas 中 inplace=True 是好的实践吗?

答案

不推荐使用 inplace=True。原因:

  • 不支持链式操作,降低代码可读性
  • 不一定节省内存,有时反而创建临时副本
  • Pandas 官方已在考虑未来废弃该参数

推荐写法:df = df.drop(columns=['col']) 而非 df.drop(columns=['col'], inplace=True)


相关链接