跳转到内容

列的增删改:DataFrame 的形态变换

筛选是从 DataFrame 中"取"数据,而这一节要讲的是如何"改" DataFrame 的结构——新增一列、修改现有列、删除不需要的列。这些操作看似简单,但不同的写法在可读性、性能和副作用上有显著差异。

新增列的四种方式

python
import pandas as pd

df = pd.DataFrame({'x': [1, 2, 3], 'y': [4, 5, 6]})

df['z'] = [7, 8, 9]
df['sum'] = df['x'] + df['y']
df['is_large'] = df['x'] > 1

print(df)

直接用 df['new_col'] = ... 是最常用也最直观的方式。它支持各种右值:Python 列表、NumPy 数组、另一个 Series、标量值,或者基于现有列的计算表达式。

当你需要在一个表达式中新增多个列并且希望链式调用时,assign() 更合适:

python
result = (
    df.assign(
        z=lambda d: d['x'] + d['y'],
        product=lambda d: d['x'] * d['y'],
        ratio=lambda d: d['x'] / d['y'],
    )
)
print(result)

assign() 的两个关键特性:不修改原 DataFrame(返回新对象)支持链式调用。而且它可以在同一个 assign 中引用前面刚创建的列——Pandas 会按顺序依次执行每个赋值。

如果你需要把新列插入到特定位置(而不是默认追加到末尾),用 insert()

python
df.insert(1, 'mid_col', ['a', 'b', 'c'])
print(df)

修改列的三种模式

修改已有列和新增列语法完全一样——区别只在于列名是否已存在:

python
import numpy as np

df = pd.DataFrame({
    'score': [85, 92, 78],
    'grade': ['B', 'A', 'C'],
})

df['score_normalized'] = df['score'] / 100

df.loc[df['score'] >= 90, 'grade'] = 'A+'

df['grade_encoded'] = df['grade'].map({'A+': 4, 'A': 3, 'B': 2, 'C': 1})

三种模式对应三种常见需求:

  • 计算新特征:基于现有列派生出新列(如归一化)
  • 条件更新:满足条件的行修改为特定值(用 loc
  • 映射转换:离散值的编码/解码(用 map()

删除列

python
df2 = df.drop(columns=['score_normalized'])
df3 = df.drop(['grade'], axis=1)

两种写法等价,但 drop(columns=[...]) 更清晰。注意 drop() 默认返回新 DataFrame 而不修改原对象——这是 Pandas 的通用设计哲学(函数式风格)。如果你确实需要原地删除,可以用 inplace=True,但不推荐这样做(原因和前面章节提到的一样:inplace 容易导致链式赋值问题且不利于调试)。

基于 MIT 许可发布