机器学习速成·前置(三):Pandas DataFrame 上手——创建、取值与引用
本文参考 Google ML Crash Course 前置工作 - Pandas DataFrame UltraQuick Tutorial 撰写。这篇不是 DataFrames 的完整教程,而是完成 MLCC 后续练习所必需的最小子集。
一、DataFrame 是什么?
DataFrame(数据帧) 是 pandas API 的核心数据结构。你可以把它理解为一块内存里的电子表格:
- 单元格:数据按”单元格”存储;
- 命名列:通常有列名;
- 编号行:行有行号。
就像 Excel 一样,它把”二维表”和强大的分析能力绑在一起,是机器学习里加载、清洗、浏览数据的主力。
二、导入 NumPy 和 pandas
import numpy as np
import pandas as pd
为什么又要 NumPy?因为 DataFrame 的数据很多来自 NumPy 数组,两者常常一起用。
三、创建 DataFrame
用 pd.DataFrame(...) 实例化一个 DataFrame,通常传两个参数:
- 数据源(如一个 NumPy 数组);
- 列名列表。
# 创建并填充一个 5x2 的 NumPy 数组
my_data = np.array([[0, 3], [10, 7], [20, 9], [30, 14], [40, 15]])
# 保存两列的列名
my_column_names = ['temperature', 'activity']
# 创建 DataFrame
my_dataframe = pd.DataFrame(data=my_data, columns=my_column_names)
# 打印整个 DataFrame
print(my_dataframe)
输出后你就得到一个 5 行 2 列、带表头的表格:temperature、activity 两列,行为 0~4 编号。
⚠️ 注意:后续代码会继续使用这些变量,不要在同一 notebook 里重新定义它们。
四、给 DataFrame 新增一列
给已有 DataFrame 加列非常简单:直接给一个新列名赋值即可。
# 新建名为 adjusted 的列,值是 activity 列 + 2
my_dataframe["adjusted"] = my_dataframe["activity"] + 2
print(my_dataframe)
幂等、直观——pandas 会对整列做向量的逐元素运算,一步生成新列。
五、取出 DataFrame 的子集
pandas 提供了多种隔离行/列/切片/单元格的方式:
print("Rows #0, #1, and #2:")
print(my_dataframe.head(3), '\n') # 前 3 行
print("Row #2:")
print(my_dataframe.iloc[[2]], '\n') # 按位置取第 2 行(iloc)
print("Rows #1, #2, and #3:")
print(my_dataframe[1:4], '\n') # 行切片(左闭右开)
print("Column 'temperature':")
print(my_dataframe['temperature']) # 按列名取整列
| 操作 | 写法 | 返回 |
|---|---|---|
| 前 N 行 | df.head(N) | 一个 DataFrame(含表头) |
| 按行号取行 | df.iloc[[i]] | 指定行 |
| 行切片 | df[a:b] | 第 a 到 b−1 行(左闭右开) |
| 取整列 | df['列名'] | 该列数据 |
六、动手练习:建一个随机 DataFrame
按如下要求做一个小任务(用到前述 NumPy 基础):
- 创建 3×4 的 DataFrame,列名是
Eleanor、Chidi、Tahani、Jason,每个单元格填一个 0~100(含) 的随机整数; - 输出:整个 DataFrame,以及 第 1 行
Eleanor列的那个单元格; - 新建第 5 列
Janet,值为Tahani与Jason两列的逐行求和。
参考解法:
my_column_names = ['Eleanor', 'Chidi', 'Tahani', 'Jason']
# 3x4 随机整数(0~100,含)
my_data = np.random.randint(low=0, high=101, size=(3, 4))
df = pd.DataFrame(data=my_data, columns=my_column_names)
print(df)
# 第 1 行 Eleanor 列的值
print("Second row of the Eleanor column: %d\n" % df['Eleanor'][1])
# 新增列:两列逐行求和
df['Janet'] = df['Tahani'] + df['Jason']
print(df)
体会一下:列与列之间可以直接做算术运算,这正是后续特征工程的高频操作。
七、引用 vs 拷贝:一个微妙但重要的区别
复用一个 DataFrame 有两种方式,结果截然不同:
7.1 引用(Reassignment):会”联动”
reference_to_df = df # 只是给同一个对象换了个名字
df.at[1, 'Jason'] = df['Jason'][1] + 5
print(df['Jason'][1]) # 更新后的值
print(reference_to_df['Jason'][1]) # 引用也被改动了!
因为
reference_to_df和df指向同一份内存,改任何一个,另一个都跟着变。
7.2 拷贝(.copy()):互不影响
copy_of_my_dataframe = my_dataframe.copy() # 真正的独立拷贝
my_dataframe.at[1, 'activity'] = my_dataframe['activity'][1] + 3
print(my_dataframe['activity'][1]) # 被更新
print(copy_of_my_dataframe['activity'][1]) # 拷贝仍保持原值
.copy()生成一份完全独立的副本,两者从此互不影响。
| 写法 | 改一人是否影响另一人 | |
|---|---|---|
| 引用 | new_df = df | ✅ 会影响(同一份内存) |
| 拷贝 | new_df = df.copy() | ❌ 不影响(独立副本) |
一句话记住:”=“只是多了个名字,“
.copy()”才是真复制。 处理共享数据时务必分清,否则极易改动到”没想到会被改”的对象。
八、小结
| 能力 | 关键方法 | 注意点 |
|---|---|---|
| 创建 | pd.DataFrame(data=数组, columns=列名) | 列名与数据列数要一一对应 |
| 加列 | df["新列名"] = 表达式 | 支持列间向量运算 |
| 前几行 | df.head(N) | 返回 DataFrame |
| 按行取 | df.iloc[[i]] | 位置索引 |
| 行切片 | df[a:b] | 左闭右开 |
| 取列 | df["列名"] | 返回整列 |
| 复用 | = 是引用、.copy() 是拷贝 | 引用会联动、拷贝独立 |
一句话:DataFrame 就是”内存里的Excel”——用
pd.DataFrame建表、直接赋值df["新列"]加列、head/iloc/切片取任意子集;但要记住=只是多起个名字,想真正复制得用.copy()。