Pandas 库使用教程:面向 Python 数据科学 – wiki基地


Pandas 库使用教程:面向 Python 数据科学

引言:为什么选择 Pandas?

在 Python 数据科学领域,Pandas 库是无可争议的基石。它提供了一系列高性能、易于使用的数据结构和数据分析工具,使得处理、清洗、转换、分析和可视化结构化数据(如表格数据、时间序列数据)变得极其高效和直观。无论您是数据分析师、数据科学家、机器学习工程师,还是任何需要处理数据的研究人员或开发人员,掌握 Pandas 都将极大地提升您的工作效率。

Pandas 这个名字来源于“Panel Data”(面板数据),这是一个计量经济学中用于描述多维结构化数据集的术语。该库最初由 Wes McKinney 在 2008 年开发,旨在为 Python 提供一个强大且灵活的数据分析框架,弥补当时 Python 在数据处理方面相较于 R 等语言的不足。如今,Pandas 已成为 Python 生态系统中最受欢迎和最重要的库之一。

Pandas 的核心优势:

  1. 强大的数据结构: 核心是 Series(一维带标签数组)和 DataFrame(二维带标签表格),它们能够高效地处理不同类型的数据(整数、浮点数、字符串、布尔值、Python 对象等),并带有灵活的索引系统。
  2. 丰富的数据操作功能: 提供大量用于数据清洗、转换、合并、重塑、切片、索引、分组、聚合等操作的内置函数。
  3. 高效的数据读写: 支持从多种文件格式(如 CSV、Excel、JSON、SQL 数据库、HDF5 等)读取数据,并将数据写回这些格式。
  4. 灵活的数据处理: 轻松处理缺失数据(NaN),支持时间序列数据的专门处理,能够进行向量化操作以提高性能。
  5. 与其他库的集成: 与 NumPy(底层依赖)、Matplotlib/Seaborn(可视化)、Scikit-learn(机器学习)等 Python 数据科学生态系统中的其他核心库无缝集成。

本教程将带您从 Pandas 的基础知识开始,逐步深入到更高级的数据操作和分析技巧,旨在为您打下坚实的 Pandas 应用基础。

一、 安装与基础概念

1.1 安装 Pandas

安装 Pandas 非常简单。如果您已经安装了 Anaconda 发行版,那么 Pandas 通常已经预装好了。如果没有,或者您使用的是标准的 Python 环境,可以通过 pip 进行安装:

bash
pip install pandas

为了充分利用 Pandas 的功能,通常还会一起安装 NumPy(Pandas 的底层依赖)和 Matplotlib(用于可视化):

bash
pip install numpy matplotlib

在 Python 脚本或 Jupyter Notebook 中,我们通常按以下约定导入 Pandas 和 NumPy:

python
import pandas as pd
import numpy as np

1.2 核心数据结构:Series 与 DataFrame

Pandas 的两大核心数据结构是 SeriesDataFrame

1.2.1 Series

Series 是一种一维带标签的数组,类似于 NumPy 的 ndarray,但它有一个额外的索引(Index)。索引可以是数字、字符串或其他 Python 对象,为每个数据点提供了标签。

  • 创建 Series:

“`python

从列表创建,默认数字索引

s1 = pd.Series([1, 3, 5, np.nan, 6, 8])
print(s1)

输出:

0 1.0

1 3.0

2 5.0

3 NaN

4 6.0

5 8.0

dtype: float64

从列表创建,并指定索引

s2 = pd.Series([10, 20, 30], index=[‘a’, ‘b’, ‘c’])
print(s2)

输出:

a 10

b 20

c 30

dtype: int64

从字典创建,字典的键成为索引

data_dict = {‘Ohio’: 35000, ‘Texas’: 71000, ‘Oregon’: 16000, ‘Utah’: 5000}
s3 = pd.Series(data_dict)
print(s3)

输出:

Ohio 35000

Texas 71000

Oregon 16000

Utah 5000

dtype: int64

“`

  • Series 的属性与操作:
    • s.values: 返回包含 Series 数据的 NumPy 数组。
    • s.index: 返回 Series 的索引。
    • s.dtype: 返回数据的类型。
    • s.shape: 返回 Series 的形状(元组)。
    • s.size: 返回元素的数量。
    • s.name: 给 Series 本身命名。
    • s.index.name: 给索引命名。

Series 支持类似 NumPy 数组的向量化操作和索引方式,也支持基于标签的索引。

python
print(s2['b']) # 输出: 20 (标签索引)
print(s2[1]) # 输出: 20 (位置索引,有时不推荐与标签混用)
print(s2[['a', 'c']]) # 选择多个标签
print(s2 * 2) # 向量化乘法
print(s2 > 15) # 向量化比较

1.2.2 DataFrame

DataFrame 是一个二维带标签的数据结构,可以看作是一个共享相同索引的 Series 的集合,或者是一个带有行索引和列索引的表格。它是 Pandas 中最常用的数据结构。

  • 创建 DataFrame:

“`python

从字典创建(常用方式),字典的键成为列名,值可以是列表、数组或 Series

data = {‘state’: [‘Ohio’, ‘Ohio’, ‘Ohio’, ‘Nevada’, ‘Nevada’, ‘Nevada’],
‘year’: [2000, 2001, 2002, 2001, 2002, 2003],
‘pop’: [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]}
df = pd.DataFrame(data)
print(df)

输出:

state year pop

0 Ohio 2000 1.5

1 Ohio 2001 1.7

2 Ohio 2002 3.6

3 Nevada 2001 2.4

4 Nevada 2002 2.9

5 Nevada 2003 3.2

指定列顺序和索引

df2 = pd.DataFrame(data, columns=[‘year’, ‘state’, ‘pop’],
index=[‘one’, ‘two’, ‘three’, ‘four’, ‘five’, ‘six’])
print(df2)

输出:

year state pop

one 2000 Ohio 1.5

two 2001 Ohio 1.7

three 2002 Ohio 3.6

four 2001 Nevada 2.4

five 2002 Nevada 2.9

six 2003 Nevada 3.2

从 NumPy 数组创建,指定列名和索引

dates = pd.date_range(‘20230101’, periods=6)
df_np = pd.DataFrame(np.random.randn(6, 4), index=dates, columns=list(‘ABCD’))
print(df_np)

输出: (随机数每次不同)

A B C D

2023-01-01 -0.876543 1.234567 -0.123456 0.987654

2023-01-02 0.111111 -0.222222 0.333333 -0.444444

… (共6行)

“`

  • DataFrame 的基本属性与方法:
    • df.index: 返回行索引。
    • df.columns: 返回列索引(列名)。
    • df.dtypes: 返回每列的数据类型。
    • df.shape: 返回 DataFrame 的形状(行数, 列数)。
    • df.size: 返回总元素数量。
    • df.values: 返回 DataFrame 的 NumPy 表示形式(可能包含多种数据类型)。
    • df.head(n): 显示前 n 行数据(默认 5 行)。
    • df.tail(n): 显示后 n 行数据(默认 5 行)。
    • df.info(): 打印 DataFrame 的简明摘要,包括索引类型、列信息(非空值数量、数据类型)和内存使用情况。
    • df.describe(): 生成描述性统计数据(计数、均值、标准差、最小值、分位数、最大值),主要针对数值列。

二、 数据加载与存储

Pandas 提供了方便的函数来读写各种格式的数据文件,最常用的是 CSV 和 Excel。

2.1 读取 CSV 文件

使用 pd.read_csv() 函数。它有许多参数可以控制读取过程。

“`python

假设有一个名为 ‘data.csv’ 的文件

df = pd.read_csv(‘data.csv’)

常用参数示例:

df = pd.read_csv(‘data.csv’,

sep=’,’, # 指定分隔符,默认为逗号

header=0, # 指定哪一行作为列名,默认第一行 (0)

index_col=0, # 指定哪一列作为行索引

usecols=[‘col1’, ‘col2’], # 只读取指定的列

nrows=100, # 只读取前 100 行

na_values=[‘NA’, ‘?’], # 指定哪些值应被视为空值 (NaN)

encoding=’utf-8′ # 指定文件编码,处理中文时常用 ‘gbk’ 或 ‘utf-8’

)

“`

2.2 读取 Excel 文件

使用 pd.read_excel() 函数。需要安装额外的库 openpyxl(用于 .xlsx)或 xlrd(用于旧版 .xls)。

bash
pip install openpyxl xlrd

“`python

读取 Excel 文件的第一个工作表

df_excel = pd.read_excel(‘data.xlsx’)

读取指定的工作表

df_sheet2 = pd.read_excel(‘data.xlsx’, sheet_name=’Sheet2′)

df_sheet_by_index = pd.read_excel(‘data.xlsx’, sheet_name=1) # 按索引读取

读取所有工作表到一个字典,键是工作表名,值是 DataFrame

all_sheets = pd.read_excel(‘data.xlsx’, sheet_name=None)

“`

2.3 存储数据

使用 df.to_csv()df.to_excel() 方法。

“`python

保存为 CSV 文件

df.to_csv(‘output.csv’,

index=False, # 通常不保存 DataFrame 的索引到文件中

sep=’;’, # 指定分隔符

encoding=’utf-8-sig’ # 使用 ‘utf-8-sig’ 可以在 Excel 中正确显示中文

)

保存为 Excel 文件

df.to_excel(‘output.xlsx’,

sheet_name=’MyData’,

index=False # 不保存索引

)

将多个 DataFrame 保存到同一个 Excel 文件的不同工作表

with pd.ExcelWriter(‘output_multi.xlsx’) as writer:

df1.to_excel(writer, sheet_name=’Sheet1′, index=False)

df2.to_excel(writer, sheet_name=’Sheet2′, index=False)

“`

Pandas 还支持 JSON (pd.read_json, df.to_json)、SQL (pd.read_sql, df.to_sql)、HTML (pd.read_html) 等多种格式。

三、 数据选择与索引 (Indexing & Selection)

这是 Pandas 操作的核心部分,有多种方式可以选择和访问数据。

3.1 选择列

“`python

选择单列,返回一个 Series

state_col = df[‘state’]

或者使用属性访问(仅当列名是有效 Python 标识符且不与 DataFrame 方法冲突时)

state_col_attr = df.state

选择多列,返回一个新的 DataFrame

subset = df[[‘year’, ‘pop’]]
“`

3.2 选择行

主要使用 .loc.iloc

  • .loc:基于标签(索引名和列名)的选择
    • df.loc[label]: 选择单行(基于行索引标签)。
    • df.loc[[label1, label2]]: 选择多行。
    • df.loc[start_label:end_label]: 选择行切片(包含结束标签)。
    • df.loc[row_labels, col_labels]: 同时选择行和列。

python
print(df2.loc['two']) # 选择索引为 'two' 的行
print(df2.loc[['one', 'three']]) # 选择索引为 'one' 和 'three' 的行
print(df2.loc['two':'four']) # 选择从 'two' 到 'four' 的行(包含 'four')
print(df2.loc['one', 'state']) # 选择 'one' 行 'state' 列的值
print(df2.loc[['one', 'five'], ['year', 'pop']]) # 选择特定行和列

  • .iloc:基于整数位置(从 0 开始)的选择
    • df.iloc[pos]: 选择单行(基于行位置)。
    • df.iloc[[pos1, pos2]]: 选择多行。
    • df.iloc[start_pos:end_pos]: 选择行切片(不包含结束位置,类似 Python 列表切片)。
    • df.iloc[row_positions, col_positions]: 同时选择行和列。

python
print(df.iloc[0]) # 选择第一行
print(df.iloc[[1, 3, 5]]) # 选择第 2, 4, 6 行
print(df.iloc[0:3]) # 选择前三行(位置 0, 1, 2)
print(df.iloc[0, 1]) # 选择第一行第二列的值
print(df.iloc[[0, 2], [0, 2]]) # 选择第 1, 3 行的第 1, 3 列

注意:
* 直接使用 df[] 进行索引比较复杂:
* df['col_name'] 选择列。
* df[start:end] 选择行切片(基于位置,但有时会因索引类型而异,不推荐)。
* df[boolean_array] 进行布尔索引(常用)。
* 强烈推荐使用 .loc.iloc 进行显式索引,避免混淆和潜在错误。

3.3 布尔索引 (Boolean Indexing)

这是非常强大和常用的功能,允许根据条件选择数据。

“`python

选择 ‘year’ 大于 2001 的行

print(df[df[‘year’] > 2001])

选择 ‘state’ 为 ‘Ohio’ 的行

print(df[df[‘state’] == ‘Ohio’])

组合多个条件(使用 & 表示 AND,| 表示 OR,~ 表示 NOT)

注意:每个条件必须用括号括起来

print(df[(df[‘year’] > 2001) & (df[‘state’] == ‘Nevada’)])

使用 isin() 选择特定值的行

print(df[df[‘year’].isin([2000, 2003])])
“`

3.4 设置值

可以通过上述选择方法定位到数据,然后进行赋值。

“`python

修改单个值

df.loc[0, ‘pop’] = 1.6

修改整列的值

df[‘pop’] = df[‘pop’] * 1000000

根据条件修改值

df.loc[df[‘year’] == 2000, ‘state’] = ‘OHIO_Updated’

添加新列

df[‘debt’] = np.random.rand(len(df)) * 1000 # 添加基于计算的新列
df[‘eastern’] = df[‘state’].isin([‘Ohio’, ‘OHIO_Updated’]) # 添加基于条件的新列
“`

四、 数据清洗与准备

真实世界的数据往往不规整,包含缺失值、重复值、错误类型等。Pandas 提供了强大的工具来处理这些问题。

4.1 处理缺失数据 (NaN)

Pandas 使用 NaN (Not a Number) 来表示缺失值。

  • 检测缺失值:
    • pd.isnull(obj)obj.isnull(): 返回一个布尔型的对象,指示哪些值是缺失的。
    • pd.notnull(obj)obj.notnull(): 与 isnull 相反。
    • df.isnull().sum(): 计算每列的缺失值数量(非常常用)。

“`python

创建一个带有缺失值的 DataFrame

data_missing = {‘A’: [1, 2, np.nan, 4],
‘B’: [5, np.nan, np.nan, 8],
‘C’: [9, 10, 11, 12]}
df_miss = pd.DataFrame(data_missing)
print(df_miss.isnull())

输出:

A B C

0 False False False

1 False True False

2 True True False

3 False False False

print(df_miss.isnull().sum())

输出:

A 1

B 2

C 0

dtype: int64

“`

  • 处理缺失值:
    • 删除: df.dropna(axis=0, how='any', thresh=None, subset=None)
      • axis=0: 删除包含 NaN 的行(默认)。
      • axis=1: 删除包含 NaN 的列。
      • how='any': 只要有一个 NaN 就删除(默认)。
      • how='all': 只有当所有值都为 NaN 时才删除。
      • thresh=N: 保留至少有 N 个非 NaN 值的行/列。
      • subset=['col1', 'col2']: 只在指定的列中检查 NaN。
    • 填充: df.fillna(value=None, method=None, axis=None, limit=None)
      • value: 用于填充 NaN 的标量值或字典/Series(指定不同列填充不同值)。
      • method='ffill': 前向填充(用前一个有效值填充)。
      • method='bfill': 后向填充(用后一个有效值填充)。

“`python

删除包含任何 NaN 的行

df_dropped_rows = df_miss.dropna()
print(df_dropped_rows)

删除包含任何 NaN 的列

df_dropped_cols = df_miss.dropna(axis=1)
print(df_dropped_cols)

用 0 填充所有 NaN

df_filled_zero = df_miss.fillna(0)
print(df_filled_zero)

用每列的平均值填充 NaN

df_filled_mean = df_miss.fillna(df_miss.mean())
print(df_filled_mean)

使用前向填充

df_filled_ffill = df_miss.fillna(method=’ffill’)
print(df_filled_ffill)
“`

4.2 处理重复数据

  • 检测重复行: df.duplicated(subset=None, keep='first')
    • 返回一个布尔 Series,指示哪些行是重复的。
    • subset: 指定要考虑判断重复的列,默认所有列。
    • keep='first': 标记除第一个出现之外的重复项为 True。
    • keep='last': 标记除最后一个出现之外的重复项为 True。
    • keep=False: 标记所有重复项为 True。
  • 删除重复行: df.drop_duplicates(subset=None, keep='first', inplace=False)
    • 参数与 duplicated 类似。
    • inplace=True: 直接修改原 DataFrame。

“`python
data_dup = {‘col1’: [‘A’, ‘B’, ‘C’, ‘A’, ‘B’, ‘D’],
‘col2’: [1, 2, 3, 1, 2, 4]}
df_dup = pd.DataFrame(data_dup)
print(df_dup.duplicated())

输出:

0 False

1 False

2 False

3 True

4 True

5 False

dtype: bool

print(df_dup.drop_duplicates())

输出:

col1 col2

0 A 1

1 B 2

2 C 3

5 D 4

print(df_dup.drop_duplicates(subset=[‘col1′], keep=’last’))

输出:

col1 col2

2 C 3

3 A 1

4 B 2

5 D 4

“`

4.3 数据类型转换

使用 astype() 方法。

“`python

假设 df[‘year’] 是 int64,想转为字符串

df[‘year_str’] = df[‘year’].astype(str)
print(df.dtypes)

假设有一列 ‘price’ 是 object 类型(字符串),包含 ‘$’ 符号,需要转为 float

df[‘price’] = df[‘price’].str.replace(‘$’, ”).astype(float) # 示例,需先处理非数字字符

转为 Category 类型(适用于低基数(少量唯一值)的列,可以节省内存和加速计算)

df[‘state_cat’] = df[‘state’].astype(‘category’)
print(df[‘state_cat’].cat.categories) # 查看类别
print(df[‘state_cat’].cat.codes) # 查看类别的整数编码
“`

4.4 重命名索引和列名

使用 rename() 方法。

“`python
print(df.rename(columns={‘year’: ‘YEAR’, ‘pop’: ‘Population’},
index={0: ‘zero’, 1: ‘one’})) # 不会修改原 df

使用 inplace=True 修改原 df

df.rename(columns={‘year’: ‘YEAR’}, inplace=True)

也可以直接赋值给 .columns 或 .index 属性(需要提供完整的列表)

df.columns = [‘State’, ‘Year’, ‘Population’, …]

df.index = [ … new index labels … ]

“`

4.5 应用函数 (apply, map, applymap)

  • map() 用于 Series,对每个元素应用一个函数或映射(字典)。
  • apply() 用于 DataFrame 的行或列。
    • df.apply(func, axis=0): 将函数 func 应用于每列(每列作为 Series 传入 func)。
    • df.apply(func, axis=1): 将函数 func 应用于每行(每行作为 Series 传入 func)。
  • applymap() 用于 DataFrame,对每个元素应用一个函数。

“`python

map 示例 (Series)

s = pd.Series([1, 2, 3])
print(s.map(lambda x: x * 100))
print(s.map({1: ‘one’, 2: ‘two’})) # 使用字典映射

apply 示例 (DataFrame)

df_apply = df[[‘year’, ‘pop’]]
def min_max_diff(x): # x 是一个 Series (一列或一行)
return x.max() – x.min()

print(df_apply.apply(min_max_diff, axis=0)) # 应用于每列

输出:

year 3.0

pop 2100000.0

dtype: float64

print(df_apply.apply(min_max_diff, axis=1)) # 应用于每行

applymap 示例 (DataFrame, 应用于每个元素)

print(df_apply.applymap(lambda x: f'{x:.2f}’)) # 格式化每个元素为字符串
“`

五、 数据分组与聚合 (Group By)

groupby 操作是数据分析的核心,遵循 “split-apply-combine”(拆分-应用-合并)的模式。

  1. Split: 根据某些标准将数据拆分成组。
  2. Apply: 对每个组独立应用一个函数(如聚合、转换、过滤)。
  3. Combine: 将结果合并成一个新的数据结构。

“`python

准备数据

data_group = {‘Company’: [‘GOOG’, ‘GOOG’, ‘MSFT’, ‘MSFT’, ‘FB’, ‘FB’],
‘Person’: [‘Sam’, ‘Charlie’, ‘Amy’, ‘Vanessa’, ‘Carl’, ‘Sarah’],
‘Sales’: [200, 120, 340, 124, 243, 350]}
df_group = pd.DataFrame(data_group)

按 ‘Company’ 分组

by_comp = df_group.groupby(‘Company’)

Apply: 计算每个公司的平均 Sales (聚合)

print(by_comp.mean())

输出:

Sales

Company

FB 296.5

GOOG 160.0

MSFT 232.0

其他常用聚合函数: count(), sum(), std(), min(), max(), describe()

print(by_comp.sum())
print(by_comp.describe()) # 对每个组进行描述性统计
print(by_comp.describe().transpose()) # 转置结果更易读

按多列分组

by_comp_person = df_group.groupby([‘Company’, ‘Person’])
print(by_comp_person.sum())

使用 agg() 进行更复杂的聚合

print(by_comp[‘Sales’].agg([‘sum’, ‘mean’, ‘std’, ‘count’]))

输出:

sum mean std count

Company

FB 593.0 296.5 75.660426 2

GOOG 320.0 160.0 56.568542 2

MSFT 464.0 232.0 152.735065 2

对不同列应用不同聚合函数

print(by_comp.agg({‘Sales’: ‘sum’, ‘Person’: ‘count’})) # 注意这里对 Person count 没太大意义,仅作示例

Sales Person

Company

FB 593 2

GOOG 320 2

MSFT 464 2

迭代分组

for name, group in by_comp:

print(f”Company: {name}”)

print(group)

print(“-” * 20)

“`

六、 数据合并、连接与拼接

Pandas 提供了多种将不同 DataFrame 对象组合在一起的方法。

6.1 拼接 (Concatenating) – pd.concat()

将多个 DataFrame 沿着一个轴(行或列)堆叠起来。

“`python
df1 = pd.DataFrame({‘A’: [‘A0’, ‘A1’], ‘B’: [‘B0’, ‘B1’]}, index=[0, 1])
df2 = pd.DataFrame({‘A’: [‘A2’, ‘A3’], ‘B’: [‘B2’, ‘B3’]}, index=[2, 3])
df3 = pd.DataFrame({‘C’: [‘C0’, ‘C1’], ‘D’: [‘D0’, ‘D1’]}, index=[0, 1])

沿行拼接 (axis=0, 默认)

result_row = pd.concat([df1, df2])
print(result_row)

输出:

A B

0 A0 B0

1 A1 B1

2 A2 B2

3 A3 B3

沿列拼接 (axis=1)

result_col = pd.concat([df1, df3], axis=1)
print(result_col)

输出:

A B C D

0 A0 B0 C0 D0

1 A1 B1 C1 D1

拼接时处理索引冲突

df4 = pd.DataFrame({‘A’: [‘A4’, ‘A5’], ‘B’: [‘B4’, ‘B5’]}, index=[0, 1]) # 与 df1 索引重复
result_ignore = pd.concat([df1, df4], ignore_index=True) # 重置索引
print(result_ignore)

输出:

A B

0 A0 B0

1 A1 B1

2 A4 B4

3 A5 B5

“`

6.2 合并 (Merging) – pd.merge()

类似 SQL 中的 JOIN 操作,根据一个或多个键将不同 DataFrame 的行连接起来。

“`python
left = pd.DataFrame({‘key’: [‘K0’, ‘K1’, ‘K2’, ‘K3’],
‘A’: [‘A0’, ‘A1’, ‘A2’, ‘A3’],
‘B’: [‘B0’, ‘B1’, ‘B2’, ‘B3’]})
right = pd.DataFrame({‘key’: [‘K0’, ‘K1’, ‘K2’, ‘K4’],
‘C’: [‘C0’, ‘C1’, ‘C2’, ‘C4’],
‘D’: [‘D0’, ‘D1’, ‘D2’, ‘D4’]})

内连接 (inner join, 默认): 只保留两个 DataFrame 中键都存在的行

inner_join = pd.merge(left, right, on=’key’, how=’inner’)
print(inner_join)

输出:

key A B C D

0 K0 A0 B0 C0 D0

1 K1 A1 B1 C1 D1

2 K2 A2 B2 C2 D2

左连接 (left join): 保留左 DataFrame 的所有行,匹配右 DataFrame 的行

left_join = pd.merge(left, right, on=’key’, how=’left’)
print(left_join)

输出:

key A B C D

0 K0 A0 B0 C0 D0

1 K1 A1 B1 C1 D1

2 K2 A2 B2 C2 D2

3 K3 A3 B3 NaN NaN

右连接 (right join): 保留右 DataFrame 的所有行

right_join = pd.merge(left, right, on=’key’, how=’right’)
print(right_join)

输出:

key A B C D

0 K0 A0 B0 C0 D0

1 K1 A1 B1 C1 D1

2 K2 A2 B2 C2 D2

3 K4 NaN NaN C4 D4

外连接 (outer join): 保留两个 DataFrame 的所有行

outer_join = pd.merge(left, right, on=’key’, how=’outer’)
print(outer_join)

输出:

key A B C D

0 K0 A0 B0 C0 D0

1 K1 A1 B1 C1 D1

2 K2 A2 B2 C2 D2

3 K3 A3 B3 NaN NaN

4 K4 NaN NaN C4 D4

如果键的列名不同

left_diff = pd.DataFrame({‘key1’: [‘K0’, ‘K1’, ‘K2’], ‘A’: [‘A0’, ‘A1’, ‘A2’]})
right_diff = pd.DataFrame({‘key2’: [‘K0’, ‘K1’, ‘K3’], ‘C’: [‘C0’, ‘C1’, ‘C3′]})
merge_diff_keys = pd.merge(left_diff, right_diff, left_on=’key1′, right_on=’key2′, how=’inner’)
print(merge_diff_keys)

输出:

key1 A key2 C

0 K0 A0 K0 C0

1 K1 A1 K1 C1

“`

6.3 连接 (Joining) – df.join()

一个便捷的方法,主要用于基于索引进行合并,可以看作是 merge 的一种特殊情况。

“`python
left_idx = pd.DataFrame({‘A’: [‘A0’, ‘A1’, ‘A2’]}, index=[‘K0’, ‘K1’, ‘K2’])
right_idx = pd.DataFrame({‘B’: [‘B0’, ‘B1’, ‘B2’]}, index=[‘K0’, ‘K1’, ‘K3’]) # 注意索引不同

默认按索引进行左连接

join_result = left_idx.join(right_idx)
print(join_result)

输出:

A B

K0 A0 B0

K1 A1 B1

K2 A2 NaN

外连接

outer_join_result = left_idx.join(right_idx, how=’outer’)
print(outer_join_result)

输出:

A B

K0 A0 B0

K1 A1 B1

K2 A2 NaN

K3 NaN B2

也可以基于列进行 join

left_on_col = pd.DataFrame({‘key’: [‘K0’, ‘K1’, ‘K2’], ‘A’: [‘A0’, ‘A1’, ‘A2’]})
right_on_idx = pd.DataFrame({‘B’: [‘B0’, ‘B1’, ‘B2’]}, index=[‘K0’, ‘K1’, ‘K3′])
join_on_col = left_on_col.join(right_on_idx, on=’key’) # 左 df 的 ‘key’ 列与右 df 的索引匹配
print(join_on_col)

输出:

key A B

0 K0 A0 B0

1 K1 A1 B1

2 K2 A2 NaN

“`

七、 时间序列分析基础

Pandas 最初就是为金融时间序列分析而设计的,因此在处理时间序列数据方面非常强大。

  • 创建时间戳和时间范围:
    • pd.to_datetime(): 将字符串或数字转换为 Timestamp 对象。
    • pd.date_range(): 生成一个固定频率的 DatetimeIndex。

“`python
dates = pd.to_datetime([‘2023-01-01’, ‘2023-01-05’, ‘2023/01/10’])
print(dates)

输出: DatetimeIndex([‘2023-01-01’, ‘2023-01-05’, ‘2023-01-10′], dtype=’datetime64[ns]’, freq=None)

date_idx = pd.date_range(‘20230101′, periods=5, freq=’D’) # ‘D’ 表示天
print(date_idx)

输出: DatetimeIndex([‘2023-01-01’, ‘2023-01-02’, ‘2023-01-03’, ‘2023-01-04’, ‘2023-01-05′], dtype=’datetime64[ns]’, freq=’D’)

创建一个以时间为索引的 Series

ts = pd.Series(np.random.randn(len(date_idx)), index=date_idx)
print(ts)
“`

  • 时间序列索引:
    • 可以使用字符串日期、Timestamp 对象或切片进行索引。

python
print(ts['2023-01-02'])
print(ts['2023-01-01':'2023-01-03'])
print(ts['2023']) # 选择 2023 年的所有数据
print(ts['2023-01']) # 选择 2023 年 1 月的所有数据

  • 重采样 (Resampling):
    • ts.resample(rule): 对时间序列进行频率转换和数据聚合。类似于 groupby 操作。
    • rule: 重采样的频率(如 ‘M’ 月末, ‘Q’ 季末, ‘A’ 年末, ‘5T’ 5分钟, ‘H’ 小时等)。
    • 需要跟上聚合函数,如 .sum(), .mean(), .ohlc() (Open-High-Low-Close)。

“`python

创建一个更高频率的时间序列

longer_ts = pd.Series(np.random.randn(100), index=pd.date_range(‘2023-01-01′, periods=100, freq=’H’))

降采样到每日频率,计算均值

daily_mean = longer_ts.resample(‘D’).mean()
print(daily_mean.head())

升采样(频率变高),通常需要填充

upsampled = daily_mean.resample(‘H’).ffill() # 使用前向填充
print(upsampled.head())
“`

  • 时间窗口 (Rolling Windows):
    • ts.rolling(window): 提供滚动窗口计算。
    • window: 窗口大小(整数表示固定数量,时间偏移字符串如 ‘3D’ 表示时间窗口)。
    • 需要跟上聚合函数,如 .mean(), .sum(), .std()

“`python

计算 3 个周期的滚动平均值

rolling_mean = ts.rolling(window=3).mean()
print(rolling_mean)
“`

八、 数据可视化

Pandas DataFrame 和 Series 具有内置的 .plot() 方法,可以方便地调用 Matplotlib 进行快速绘图。

“`python
import matplotlib.pyplot as plt

准备数据

ts = pd.Series(np.random.randn(1000), index=pd.date_range(‘2022-01-01’, periods=1000))
ts = ts.cumsum() # 累积和,模拟随机游走

df_plot = pd.DataFrame(np.random.randn(100, 4), columns=[‘A’, ‘B’, ‘C’, ‘D’])
df_plot = df_plot.cumsum()

线图 (默认)

ts.plot(figsize=(10, 6), title=’Random Walk’)
plt.show()

df_plot.plot(figsize=(10, 6))
plt.show()

其他图形类型 (kind 参数)

df = pd.DataFrame(np.random.rand(50, 4), columns=[‘a’, ‘b’, ‘c’, ‘d’])

散点图

df.plot(kind=’scatter’, x=’a’, y=’b’, figsize=(8, 5))
plt.title(‘Scatter Plot a vs b’)
plt.show()

柱状图

df.iloc[5].plot(kind=’bar’) # 绘制第 5 行的数据
plt.title(‘Bar Plot for row 5’)
plt.show()

直方图

df[‘a’].plot(kind=’hist’, bins=20, alpha=0.7)
plt.title(‘Histogram of column a’)
plt.show()

箱线图

df.plot(kind=’box’, figsize=(8, 6))
plt.title(‘Box Plot for all columns’)
plt.show()

面积图

df.plot(kind=’area’, alpha=0.5, figsize=(10, 6))
plt.title(‘Area Plot’)
plt.show()
“`

虽然 Pandas 的内置绘图很方便进行快速探索,但对于更复杂、更具定制性的可视化,通常会直接使用 Matplotlib 或 Seaborn 库。

九、 性能优化与技巧

  • 使用向量化操作: 尽量避免使用 Python 的 for 循环遍历 DataFrame 的行。Pandas 和 NumPy 的底层实现(通常是 C 或 Cython)使得向量化操作(如 df['col1'] + df['col2'])比逐元素操作快得多。
  • 使用 apply 谨慎: apply 比向量化操作慢,但比 Python 循环快。如果可能,优先寻找内置的 Pandas/NumPy 函数。
  • 使用 isin() df[df['col'].isin(values_list)] 通常比用 | 连接多个 == 条件更快。
  • 使用 Category 类型: 对于字符串列,如果唯一值数量有限(低基数),将其转换为 category 类型可以显著减少内存占用并加速某些操作(如 groupby)。
  • 避免链式索引赋值:df[df['A'] > 0]['B'] = 5 这样的操作可能不会按预期工作,并可能引发 SettingWithCopyWarning。应使用 .loc 一次性完成索引和赋值:df.loc[df['A'] > 0, 'B'] = 5
  • 读取大文件时分块处理: 对于非常大的文件,可以在 read_csv 中使用 chunksize 参数,返回一个迭代器,每次处理一个数据块。

总结

Pandas 是 Python 数据科学生态系统中不可或缺的工具。它提供的 SeriesDataFrame 数据结构以及围绕它们构建的丰富功能集,使得数据的导入、清洗、转换、分析和可视化变得高效而直观。

本教程涵盖了 Pandas 的核心概念和常用操作,包括:

  • 安装和基本数据结构(Series, DataFrame)。
  • 数据的读写(CSV, Excel)。
  • 强大的数据选择和索引方法(.loc, .iloc, 布尔索引)。
  • 数据清洗技术(处理缺失值、重复值、类型转换、重命名)。
  • 强大的分组聚合能力 (groupby)。
  • 合并、连接和拼接数据集 (concat, merge, join)。
  • 时间序列处理基础。
  • 内置的数据可视化功能。
  • 性能优化的一些建议。

掌握 Pandas 的关键在于实践。尝试使用真实或模拟的数据集,应用本教程中学到的各种操作。随着经验的积累,您将能够更加熟练地运用 Pandas 来解决各种复杂的数据处理和分析问题,为后续的统计分析、机器学习建模等工作打下坚实的基础。继续探索 Pandas 官方文档和社区资源,您会发现更多高级功能和技巧,让您的数据科学之旅更加顺畅。


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部