Pandas 介绍:数据处理与分析必备库 – wiki基地


Pandas 介绍:数据处理与分析必备库

引言:数据时代的基石

在当今这个数据爆炸的时代,从商业智能到科学研究,从金融分析到社交媒体趋势预测,数据无处不在,并且其规模以前所未有的速度增长。有效地收集、清洗、转换、分析和解释这些海量数据,成为了各行各业的核心竞争力。Python 作为一种语法简洁、功能强大且拥有庞大生态系统的编程语言,迅速成为数据科学领域的首选工具之一。而在这个Python数据科学生态中,Pandas 库无疑是最为耀眼和基础性的存在。

Pandas,全称 “Python Data Analysis Library”,由 Wes McKinney 在 AQR Capital Management 工作期间开发,并于 2009 年首次开源。它的诞生,极大地简化了 Python 中处理结构化(表格化)数据的工作,弥补了 Python 在这方面的不足,使得 Python 能够像 R 语言或 Excel 表格那样方便地进行数据操作。如今,Pandas 已经成为了 Python 数据科学栈的核心组件之一,与 NumPy、Matplotlib、SciPy 和 Scikit-learn 等库紧密协作,共同构成了强大的数据处理和分析平台。

本文将带您深入了解 Pandas 库,详细介绍其核心概念、关键功能以及在实际数据处理与分析流程中的应用,揭示为何它被誉为数据科学家的瑞士军刀。

为什么选择 Pandas?

在 Pandas 出现之前,Python 用户处理表格数据通常依赖于列表的列表、字典的列表或者 NumPy 数组。这些方法虽然能够完成任务,但在以下方面存在明显的局限性:

  1. 效率低下: 使用 Python 原生的循环来处理大规模数据通常非常慢。
  2. 代码繁琐: 对于常见的操作,如数据对齐、缺失值处理、分组聚合等,需要编写大量重复且容易出错的代码。
  3. 可读性差: 数据结构不够直观,难以直接对应到表格的概念。
  4. 功能缺失: 缺少内置的、针对表格数据优化的操作,例如时间序列分析、灵活的数据合并等。

Pandas 的出现彻底改变了这一局面。它提供了高性能、易用的数据结构和数据分析工具,其核心优势在于:

  • 直观的数据结构: 引入了 Series 和 DataFrame 两种核心数据结构,它们能够自然地表示一维和二维的表格数据,并提供了丰富的元信息(如索引和列名)。
  • 高效的数据操作: 基于 NumPy 库构建,充分利用了 NumPy 的向量化计算能力,许多操作都经过高度优化,比纯 Python 循环快得多。
  • 丰富的数据处理功能: 内置了大量用于数据清洗、转换、合并、重塑、切片、索引、选择、聚合、分组、时间序列处理等功能,几乎涵盖了数据处理和分析的方方面面。
  • 便捷的数据读写: 支持读取和写入多种数据格式,如 CSV, Excel, SQL 数据库, JSON, HDF5 等。
  • 良好的生态整合: 与 NumPy、Matplotlib、SciPy、Scikit-learn 等库无缝集成,构成了强大的数据科学工作流。
  • 处理真实世界数据的能力: Pandas 特别擅长处理现实世界中常见的不完整、不规则或带有标签的数据。

总而言之,Pandas 让数据处理变得更加快速、便捷和愉快,是任何希望在 Python 中进行数据分析的人员必备的技能。

Pandas 的核心数据结构

Pandas 提供了两种主要的数据结构:Series 和 DataFrame。它们是构建 Pandas 功能的基础,理解它们是掌握 Pandas 的关键。

1. Series (序列)

Series 是一种带标签的一维数组。它可以被看作是电子表格中的单列数据,或者是 SQL 数据库中的一个列。Series 的关键特性是它拥有一个 索引 (index),这个索引为数据提供了标签,允许用户通过标签或整数位置来访问数据。

创建 Series:

Series 可以通过多种方式创建:

  • 从列表创建:
    “`python
    import pandas as pd

    s = pd.Series([1, 3, 5, 7, 9])
    print(s)

    默认索引从 0 开始

    0 1

    1 3

    2 5

    3 7

    4 9

    dtype: int64

    “`

  • 从列表和指定索引创建:
    python
    s_indexed = pd.Series([1, 3, 5, 7, 9], index=['a', 'b', 'c', 'd', 'e'])
    print(s_indexed)
    # a 1
    # b 3
    # c 5
    # d 7
    # e 9
    # dtype: int64

  • 从字典创建: 字典的键将作为索引,值作为数据。
    python
    data_dict = {'a': 10, 'b': 20, 'c': 30}
    s_dict = pd.Series(data_dict)
    print(s_dict)
    # a 10
    # b 20
    # c 30
    # dtype: int64

Series 的属性和操作:

Series 拥有许多实用的属性,例如:

  • .values: 获取 Series 中的数据,返回一个 NumPy 数组。
  • .index: 获取 Series 的索引对象。
  • .dtype: 获取 Series 中数据的类型。
  • .shape: 获取 Series 的形状 (元素个数)。
  • .name: 可以给 Series 起一个名字。
  • .index.name: 可以给 Series 的索引起一个名字。

“`python
print(s_indexed.values) # [1 3 5 7 9]
print(s_indexed.index) # Index([‘a’, ‘b’, ‘c’, ‘d’, ‘e’], dtype=’object’)
print(s_indexed.dtype) # int64
print(s_indexed.shape) # (5,)

s_indexed.name = ‘Odd Numbers’
s_indexed.index.name = ‘Letter Index’
print(s_indexed)

Letter Index

a 1

b 3

c 5

d 7

e 9

Name: Odd Numbers, dtype: int64

“`

Series 支持基于索引的元素访问、切片以及向量化操作:

“`python
print(s_indexed[‘c’]) # 5
print(s_indexed[[‘a’, ‘e’]]) # 通过索引列表访问多个元素

Letter Index

a 1

e 9

Name: Odd Numbers, dtype: int64

print(s_indexed[1:4]) # 通过整数位置切片

Letter Index

b 3

c 5

d 7

Name: Odd Numbers, dtype: int64

print(s_indexed[s_indexed > 5]) # 布尔索引:选择值大于 5 的元素

Letter Index

d 7

e 9

Name: Odd Numbers, dtype: int64

print(s_indexed * 2) # 向量化乘法

Letter Index

a 2

b 6

c 10

d 14

e 18

Name: Odd Numbers, dtype: int64

``
注意,Series 在进行算术运算时,如果索引不一致,会自动进行数据对齐,缺失的位置会用
NaN` (Not a Number) 表示,这是 Pandas 非常重要的特性之一。

2. DataFrame (数据框)

DataFrame 是 Pandas 中最重要、最常用的数据结构,它是一个二维的表格型数据结构,可以看作是 Series 的容器,即多个拥有相同索引的 Series 组合在一起。它具有行索引和列标签(列名),非常类似于关系型数据库的表或电子表格。

创建 DataFrame:

DataFrame 可以通过多种方式创建,最常用的是从字典创建:

  • 从字典创建: 字典的键作为列名,值通常是列表、NumPy 数组或 Series。
    python
    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]}
    frame = pd.DataFrame(data)
    print(frame)
    # 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

    默认情况下,DataFrame 会为行自动生成一个从 0 开始的整数索引。列的顺序会按照字典键的字母顺序排列(Python 3.7+ 可能保留插入顺序),但可以通过指定 columns 参数来控制列的顺序。

  • 指定列顺序和索引:
    python
    frame2 = pd.DataFrame(data, columns=['year', 'state', 'pop', 'debt'],
    index=['one', 'two', 'three', 'four', 'five', 'six'])
    print(frame2)
    # year state pop debt
    # one 2000 Ohio 1.5 NaN
    # two 2001 Ohio 1.7 NaN
    # three 2002 Ohio 3.6 NaN
    # four 2001 Nevada 2.4 NaN
    # five 2002 Nevada 2.9 NaN
    # six 2003 Nevada 3.2 NaN

    这里我们指定了列的顺序,并增加了一个不存在的 ‘debt’ 列,Pandas 会自动用 NaN (表示缺失值) 填充。我们也指定了自定义的行索引。

  • 从字典的字典创建: 外层字典的键作为列名,内层字典的键作为行索引,值作为数据。
    python
    pop = {'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6},
    'Nevada': {2001: 2.4, 2002: 2.9, 2003: 3.2}}
    frame3 = pd.DataFrame(pop)
    print(frame3)
    # Ohio Nevada
    # 2000 1.5 NaN
    # 2001 1.7 2.4
    # 2002 3.6 2.9
    # 2003 NaN 3.2

    Pandas 会自动将所有内层字典的键(这里是年份)合并作为行索引,并将缺失的值填充为 NaN

DataFrame 的属性和操作:

DataFrame 也拥有许多重要的属性:

  • .values: 获取 DataFrame 中的所有数据,返回一个二维 NumPy 数组。
  • .index: 获取 DataFrame 的行索引对象。
  • .columns: 获取 DataFrame 的列索引对象。
  • .dtypes: 获取每列的数据类型。
  • .shape: 获取 DataFrame 的形状 (行数, 列数)。
  • .info(): 打印 DataFrame 的简洁摘要,包括索引、列、非空值计数和内存使用情况。
  • .describe(): 生成描述性统计信息,包括计数、均值、标准差、最小值、最大值以及四分位数等,对于数值列非常有用。

“`python
print(frame2.columns) # Index([‘year’, ‘state’, ‘pop’, ‘debt’], dtype=’object’)
print(frame2.index) # Index([‘one’, ‘two’, ‘three’, ‘four’, ‘five’, ‘six’], dtype=’object’)
print(frame2.dtypes)

year int64

state object

pop float64

debt float64

dtype: object

frame2.info()

Index: 6 entries, one to six

Data columns (total 4 columns):

# Column Non-Null Count Dtype

— —— ————– —–

0 year 6 non-null int64

1 state 6 non-null object

2 pop 6 non-null float64

3 debt 0 non-null float64

dtypes: float64(2), int64(1), object(1)

memory usage: 248.0+ bytes

print(frame2.describe())

year pop debt

count 6.000000 6.000000 0.0

mean 2001.500000 2.550000 NaN

std 1.048809 0.836062 NaN

min 2000.000000 1.500000 NaN

25% 2001.000000 1.875000 NaN

50% 2001.500000 2.650000 NaN

75% 2002.000000 3.125000 NaN

max 2003.000000 3.600000 NaN

“`

DataFrame 提供了多种灵活的数据访问和切片方式:

  • 通过列名访问列: 返回一个 Series。
    python
    print(frame2['state']) # 或 frame2.state (如果列名是有效的 Python 变量名)
    # one Ohio
    # two Ohio
    # three Ohio
    # four Nevada
    # five Nevada
    # six Nevada
    # Name: state, dtype: object
  • 通过列名列表访问多列: 返回一个 DataFrame。
    python
    print(frame2[['year', 'pop']])
    # year pop
    # one 2000 1.5
    # two 2001 1.7
    # three 2002 3.6
    # four 2001 2.4
    # five 2002 2.9
    # six 2003 3.2
  • 通过行索引访问行 (使用 .loc.iloc):

    • .loc:基于标签(索引值)进行索引。
    • .iloc:基于整数位置进行索引。
      “`python
      print(frame2.loc[‘three’]) # 按标签获取行 (返回 Series)

    year 2002

    state Ohio

    pop 3.6

    debt NaN

    Name: three, dtype: object

    print(frame2.iloc[2]) # 按整数位置获取行 (返回 Series)

    year 2002

    state Ohio

    pop 3.6

    debt NaN

    Name: three, dtype: object

    print(frame2.loc[‘two’:’four’, [‘year’, ‘pop’]]) # 按标签进行切片 (行和列)

    year pop

    two 2001 1.7

    three 2002 3.6

    four 2001 2.4

    print(frame2.iloc[1:4, [0, 2]]) # 按整数位置进行切片 (行和列)

    year pop

    two 2001 1.7

    three 2002 3.6

    four 2001 2.4

    * **布尔索引/过滤:** 根据条件选择行。python
    print(frame2[frame2[‘state’] == ‘Ohio’]) # 过滤出 state 为 ‘Ohio’ 的行

    year state pop debt

    one 2000 Ohio 1.5 NaN

    two 2001 Ohio 1.7 NaN

    three 2002 Ohio 3.6 NaN

    print(frame2[(frame2[‘year’] >= 2002) & (frame2[‘pop’] > 3)]) # 组合条件过滤

    year state pop debt

    three 2002 Ohio 3.6 NaN

    six 2003 Nevada 3.2 NaN

    “`

DataFrame 还支持增加、删除列,以及列之间的算术运算:

“`python

添加新列

frame2[‘debt’] = 16.5 # 为所有行设置一个标量值
print(frame2)

year state pop debt

one 2000 Ohio 1.5 16.5

two 2001 Ohio 1.7 16.5

three 2002 Ohio 3.6 16.5

four 2001 Nevada 2.4 16.5

five 2002 Nevada 2.9 16.5

six 2003 Nevada 3.2 16.5

frame2[‘debt’] = pd.Series([2.4, 2.9, 3.2], index=[‘four’, ‘five’, ‘six’]) # 使用 Series 赋值,会按索引对齐
print(frame2)

year state pop debt

one 2000 Ohio 1.5 NaN

two 2001 Ohio 1.7 NaN

three 2002 Ohio 3.6 NaN

four 2001 Nevada 2.4 2.4

five 2002 Nevada 2.9 2.9

six 2003 Nevada 3.2 3.2

删除列

del frame2[‘debt’]
print(frame2.columns) # Index([‘year’, ‘state’, ‘pop’], dtype=’object’)

列之间的算术运算

frame2[‘pop_per_year’] = frame2[‘pop’] / frame2[‘year’] # 假设年份作为基数 (实际中不太合理,仅为示例)
print(frame2)

year state pop pop_per_year

one 2000 Ohio 1.5 0.000750

two 2001 Ohio 1.7 0.000849

three 2002 Ohio 3.6 0.001798

four 2001 Nevada 2.4 0.001199

five 2002 Nevada 2.9 0.001449

six 2003 Nevada 3.2 0.001598

“`

数据导入与导出

将数据加载到 DataFrame 中是数据分析的第一步。Pandas 提供了强大的 I/O API,支持多种文件格式。

常用数据读取函数:

  • pd.read_csv(): 读取 CSV 文件。这是最常用的函数之一,支持众多参数来处理分隔符、文件编码、列名、索引列、缺失值标记等。
  • pd.read_excel(): 读取 Excel 文件。
  • pd.read_sql(): 从 SQL 数据库读取数据。
  • pd.read_json(): 读取 JSON 文件。
  • pd.read_html(): 读取 HTML 表格。
  • pd.read_clipboard(): 读取剪贴板中的数据。

示例:读取 CSV 文件
假设我们有一个名为 data.csv 的文件:
csv
id,name,value,category
1,A,10.5,X
2,B,NaN,Y
3,C,12.1,X
4,D,8.8,Z
5,E,NaN,Y

“`python

读取 CSV 文件

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

id name value category

0 1 A 10.5 X

1 2 B NaN Y

2 3 C 12.1 X

3 4 D 8.8 Z

4 5 E NaN Y

指定某一列作为索引

df_indexed = pd.read_csv(‘data.csv’, index_col=’id’)
print(df_indexed)

name value category

id

1 A 10.5 X

2 B NaN Y

3 C 12.1 X

4 D 8.8 Z

5 E NaN Y

“`

常用数据写入函数:

  • df.to_csv(): 写入 CSV 文件。
  • df.to_excel(): 写入 Excel 文件。
  • df.to_sql(): 写入 SQL 数据库。
  • df.to_json(): 写入 JSON 文件。
  • df.to_html(): 写入 HTML 文件。

示例:写入 CSV 文件
“`python

将 DataFrame 写入新的 CSV 文件

df.to_csv(‘new_data.csv’, index=False) # index=False 表示不写入行索引
“`

数据清洗与预处理

真实世界的数据往往是混乱的、不完整的或不一致的。Pandas 提供了强大的工具来处理这些问题,这是数据分析流程中至关重要的一步。

1. 处理缺失值 (Missing Data)

缺失值是数据中常见的问题,通常表示为 NaN (Not a Number)。Pandas 提供了一套灵活的方法来检测、删除和填充缺失值。

  • isnull()isna(): 返回一个布尔型的 DataFrame,指示每个位置是否为缺失值。
  • notnull()notna(): isnull() 的反向操作。
  • dropna(): 删除含有缺失值的行或列。
    • axis=0 (默认): 删除含有缺失值的行。
    • axis=1: 删除含有缺失值的列。
    • how='any' (默认): 删除 任何 缺失值的行/列。
    • how='all': 删除 所有 值都为缺失值的行/列。
    • thresh=n: 删除非缺失值少于 n 个的行/列。
  • fillna(): 填充缺失值。
    • 使用常数填充:df.fillna(0)
    • 使用统计量填充:df.fillna(df.mean()) (按列计算均值填充)
    • 使用前向填充:df.fillna(method='ffill')df.fillna(method='pad') (用前一个非缺失值填充)
    • 使用后向填充:df.fillna(method='bfill')df.fillna(method='backfill') (用后一个非缺失值填充)
    • 可以指定 axislimit (限制填充的连续缺失值数量)。

示例:处理缺失值
继续使用上面的 df (包含 NaN 的 DataFrame)
“`python
print(df.isnull())

id name value category

0 False False False False

1 False False True False

2 False False False False

3 False False False False

4 False False True False

删除含有缺失值的行

df_dropped_rows = df.dropna()
print(df_dropped_rows)

id name value category

0 1 A 10.5 X

2 3 C 12.1 X

3 4 D 8.8 Z

删除含有缺失值的列 (在这个例子中,因为 value 列有缺失,会删除 value)

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

id name category

0 1 A X

1 2 B Y

2 3 C X

3 4 D Z

4 5 E Y

用均值填充 ‘value’ 列的缺失值

mean_value = df[‘value’].mean()
df_filled = df.fillna({‘value’: mean_value}) # 使用字典可以指定不同列的填充值
print(df_filled)

id name value category

0 1 A 10.5 X

1 2 B 10.4 Y (NaN 填充为均值 10.4)

2 3 C 12.1 X

3 4 D 8.8 Z

4 5 E 10.4 Y (NaN 填充为均值 10.4)

“`

2. 处理重复值 (Duplicate Data)

重复的行可能会扭曲分析结果。Pandas 提供了检测和删除重复行的方法。

  • duplicated(): 返回一个布尔型 Series,指示每一行是否是重复的(与之前的行相比)。
    • keep='first' (默认): 标记除第一次出现外的所有重复项为 True
    • keep='last': 标记除最后一次出现外的所有重复项为 True
    • keep=False: 标记所有重复项为 True
  • drop_duplicates(): 删除重复的行。
    • 参数与 duplicated() 类似。
    • 可以指定 subset 参数,只考虑 DataFrame 的子集列来检测重复。

示例:处理重复值
“`python
data_dup = {‘col1’: [‘A’, ‘B’, ‘A’, ‘C’, ‘B’],
‘col2’: [1, 2, 1, 3, 2]}
df_dup = pd.DataFrame(data_dup)
print(df_dup)

col1 col2

0 A 1

1 B 2

2 A 1

3 C 3

4 B 2

print(df_dup.duplicated()) # 检测重复行

0 False

1 False

2 True (行 2 与行 0 重复)

3 False

4 True (行 4 与行 1 重复)

dtype: bool

df_unique = df_dup.drop_duplicates() # 删除重复行 (默认 keep=’first’)
print(df_unique)

col1 col2

0 A 1

1 B 2

3 C 3

只考虑 ‘col1’ 列来检测重复

print(df_dup.duplicated(subset=[‘col1’]))

0 False

1 False

2 True

3 False

4 True

dtype: bool

“`

3. 数据类型转换 (Type Conversion)

有时数据会被误读为错误的类型(例如,数字被读作字符串)。astype() 方法可以用来强制转换数据类型。

“`python

创建一个包含看起来像数字的字符串列的 DataFrame

df_str_num = pd.DataFrame({‘col_str’: [‘1’, ‘2’, ‘3’, ‘4’]})
print(df_str_num.dtypes)

col_str object

dtype: object

将 ‘col_str’ 转换为整数类型

df_str_num[‘col_int’] = df_str_num[‘col_str’].astype(int)
print(df_str_num.dtypes)

col_str object

col_int int64

dtype: object

对于日期字符串,使用 pd.to_datetime() 更合适

df_date_str = pd.DataFrame({‘date_str’: [‘2023-01-01’, ‘2023-01-15’, ‘2023-02-10’]})
df_date_str[‘date_dt’] = pd.to_datetime(df_date_str[‘date_str’])
print(df_date_str)

date_str date_dt

0 2023-01-01 2023-01-01

1 2023-01-15 2023-01-15

2 2023-02-10 2023-02-10

print(df_date_str.dtypes)

date_str object

date_dt datetime64[ns]

dtype: object

“`

4. 重命名轴索引 (Renaming Index)

可以使用 rename() 方法修改行索引或列标签。

“`python
df_rename = pd.DataFrame({‘A’: [1, 2], ‘B’: [3, 4]}, index=[‘row1’, ‘row2’])
print(df_rename)

A B

row1 1 3

row2 2 4

重命名列

df_rename_cols = df_rename.rename(columns={‘A’: ‘col_A’, ‘B’: ‘col_B’})
print(df_rename_cols)

col_A col_B

row1 1 3

row2 2 4

重命名行索引

df_rename_index = df_rename.rename(index={‘row1’: ‘first’, ‘row2’: ‘second’})
print(df_rename_index)

A B

first 1 3

second 2 4

链式操作,同时重命名行和列

df_renamed_all = df_rename.rename(columns={‘A’: ‘col_A’}, index={‘row2’: ‘row_B’})
print(df_renamed_all)

col_A B

row1 1 3

row_B 2 4

“`

数据转换与操作

数据清洗完成后,通常需要对数据进行转换或计算新的特征。

1. 应用函数 (Applying Functions)

Pandas 的 apply() 方法非常强大,可以将函数应用到 Series 或 DataFrame 的行或列。

  • 应用到 Series (例如某一列):
    python
    df['pop_double'] = df['pop'].apply(lambda x: x * 2) # 对 'pop' 列每个元素乘以 2
    # 或者使用自定义函数
    def categorize_pop(pop_value):
    if pop_value > 3:
    return 'High'
    elif pop_value > 1.5:
    return 'Medium'
    else:
    return 'Low'
    frame2['pop_category'] = frame2['pop'].apply(categorize_pop)
    print(frame2)
    # year state pop pop_per_year pop_category
    # one 2000 Ohio 1.5 0.000750 Low
    # two 2001 Ohio 1.7 0.000849 Medium
    # three 2002 Ohio 3.6 0.001798 High
    # four 2001 Nevada 2.4 0.001199 Medium
    # five 2002 Nevada 2.9 0.001449 Medium
    # six 2003 Nevada 3.2 0.001598 High
  • 应用到 DataFrame (按行或按列):
    • axis=0 (默认): 将函数应用到每一列 (函数的输入是 Series)。
    • axis=1: 将函数应用到每一行 (函数的输入是 Series)。
      “`python

    计算每一行的总和 (假设所有列都是数值型)

    df_filled[‘row_sum’] = df_filled.apply(lambda row: row[‘id’] + row[‘value’], axis=1)
    print(df_filled)

    id name value category row_sum

    0 1 A 10.5 X 11.5

    1 2 B 10.4 Y 12.4

    2 3 C 12.1 X 15.1

    3 4 D 8.8 Z 12.8

    4 5 E 10.4 Y 15.4

    “`

2. 数据聚合 (Aggregation)

聚合是将多个数值计算为一个摘要统计量的过程,例如求和、平均值、计数、最小值、最大值等。Pandas 的 Series 和 DataFrame 都有内置的聚合方法。

“`python
print(df_filled[‘value’].sum()) # Series 求和
print(df_filled[‘value’].mean()) # Series 求均值
print(df_filled[‘value’].count()) # Series 非空值计数
print(df_filled.sum()) # DataFrame 按列求和 (对非数值列无效或特殊处理)

id 15

name ABCDE

value 52.2

category XYZYZ

row_sum 67.2

dtype: object

print(df_filled.mean(numeric_only=True)) # DataFrame 对数值列求均值

id 3.00

value 10.44

row_sum 13.44

dtype: float64

“`

3. 分组与聚合 (GroupBy)

GroupBy 是 Pandas 中最强大和常用的功能之一,它实现了数据处理中常见的 “split-apply-combine” (拆分-应用-合并) 范式:

  1. Split (拆分): 根据某个(或某几个)键将数据拆分成组。
  2. Apply (应用): 对每个组独立地应用一个函数,如聚合、转换或过滤。
  3. Combine (合并): 将不同组产生的结果合并成一个统一的数据结构。

groupby() 方法返回一个 GroupBy 对象,它并不会立即计算,而是在调用聚合、转换或过滤方法时才进行计算。

示例:分组聚合
“`python

根据 ‘category’ 列进行分组,并计算每个分组的 ‘value’ 的均值

grouped = df_filled.groupby(‘category’)
print(grouped[‘value’].mean())

category

X 11.30

Y 10.40

Z 8.80

Name: value, dtype: float64

根据多个列进行分组 (例如 ‘category’ 和 ‘state’),并计算 ‘pop’ 的总和

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

state year

Nevada 2001 2.4

2002 2.9

2003 3.2

Ohio 2000 1.5

2001 1.7

2002 3.6

Name: pop, dtype: float64

返回一个 MultiIndex Series

对分组应用多种聚合函数

grouped3 = frame2.groupby(‘state’)
print(grouped3[‘pop’].agg([‘mean’, ‘sum’, ‘count’]))

mean sum count

state

Nevada 2.8 8.5 3

Ohio 2.2 6.8 3

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

print(grouped3.agg({‘pop’: ‘mean’, ‘year’: ‘min’}))

pop year

state

Nevada 2.8 2001

Ohio 2.2 2000

“`

GroupBy 对象除了支持聚合方法,还支持转换 (transform) 和过滤 (filter) 操作。

  • transform(): 对每个组应用函数,返回一个与原 DataFrame 大小相同的 DataFrame 或 Series,用于标准化或填充组内数据。
  • filter(): 对每个组应用函数,返回一个布尔值,用于过滤整个组。

示例:分组转换与过滤
“`python

将每个 ‘category’ 分组的 ‘value’ 列转换为标准化分数 (z-score)

mean_val = grouped[‘value’].transform(‘mean’)

std_val = grouped[‘value’].transform(‘std’)

df_filled[‘value_zscore’] = (df_filled[‘value’] – mean_val) / std_val

print(df_filled[[‘category’, ‘value’, ‘value_zscore’]])

(注意:由于Y和Z分组只有一个值,std会是NaN,此处省略计算,实际应用中需注意)

过滤出每个分组中 ‘value’ 的均值大于 10 的组

df_filtered_groups = df_filled.groupby(‘category’).filter(lambda x: x[‘value’].mean() > 10)
print(df_filtered_groups)

id name value category row_sum

0 1 A 10.5 X 11.5

1 2 B 10.4 Y 12.4

2 3 C 12.1 X 15.1

4 5 E 10.4 Y 15.4

(过滤掉了 Z 分组,因为其均值 8.8 不大于 10)

“`

4. 数据合并与连接 (Merging, Joining, Concatenating)

在实际数据分析中,常常需要将来自不同源或不同结构的数据整合在一起。Pandas 提供了 merge(), join()concat() 函数来完成这些任务。

  • pd.merge(): 用于通过一个或多个键将 DataFrame 的行连接起来,类似于数据库中的 JOIN 操作。
    • left, right: 要合并的 DataFrame。
    • on: 用于连接的列名(如果两边列名相同)。
    • left_on, right_on: 如果两边连接列名不同。
    • left_index, right_index: 使用 DataFrame 的索引作为连接键。
    • how: 指定连接类型 (‘inner’, ‘left’, ‘right’, ‘outer’)。
  • df.join(): 是 merge() 的便捷方法,默认使用索引进行连接。
  • pd.concat(): 用于沿着某个轴(行或列)堆叠或绑定对象,类似于数据库中的 UNION 操作。
    • objs: 要连接的 Pandas 对象(通常是 DataFrame 或 Series)列表。
    • axis=0 (默认): 沿着行轴连接(垂直堆叠)。
    • axis=1: 沿着列轴连接(水平绑定)。

示例:数据合并
“`python
df1 = pd.DataFrame({‘key’: [‘K0’, ‘K1’, ‘K2’, ‘K3’],
‘A’: [‘A0’, ‘A1’, ‘A2’, ‘A3’],
‘B’: [‘B0’, ‘B1’, ‘B2’, ‘B3’]})
df2 = pd.DataFrame({‘key’: [‘K0’, ‘K1’, ‘K4’, ‘K5’],
‘C’: [‘C0’, ‘C1’, ‘C4’, ‘C5’],
‘D’: [‘D0’, ‘D1’, ‘D4’, ‘D5’]})

内连接 (默认)

merged_inner = pd.merge(df1, df2, on=’key’)
print(merged_inner)

key A B C D

0 K0 A0 B0 C0 D0

1 K1 A1 B1 C1 D1

左连接

merged_left = pd.merge(df1, df2, on=’key’, how=’left’)
print(merged_left)

key A B C D

0 K0 A0 B0 C0 D0

1 K1 A1 B1 C1 D1

2 K2 A2 B2 NaN NaN

3 K3 A3 B3 NaN NaN

外连接

merged_outer = pd.merge(df1, df2, on=’key’, how=’outer’)
print(merged_outer)

key A B C D

0 K0 A0 B0 C0 D0

1 K1 A1 B1 C1 D1

2 K2 A2 B2 NaN NaN

3 K3 A3 B3 NaN NaN

4 K4 NaN NaN C4 D4

5 K5 NaN NaN C5 D5

“`

示例:数据连接 (Concatenating)
“`python
df_concat1 = pd.DataFrame({‘A’: [‘A0’, ‘A1’], ‘B’: [‘B0’, ‘B1’]})
df_concat2 = pd.DataFrame({‘A’: [‘A2’, ‘A3’], ‘B’: [‘B2’, ‘B3’]})

按行连接 (默认)

concat_rows = pd.concat([df_concat1, df_concat2])
print(concat_rows)

A B

0 A0 B0

1 A1 B1

0 A2 B2 # 默认保留原索引

按行连接并忽略原索引

concat_rows_ignore_index = pd.concat([df_concat1, df_concat2], ignore_index=True)
print(concat_rows_ignore_index)

A B

0 A0 B0

1 A1 B1

2 A2 B2

3 A3 B3

按列连接

concat_cols = pd.concat([df_concat1, df_concat2], axis=1)
print(concat_cols)

A B A B

0 A0 B0 A2 B2

1 A1 B1 A3 B3

“`

时间序列处理

Pandas 在时间序列数据处理方面表现出色。它提供了专门的日期和时间类型、频率转换、重采样、时区处理等功能。虽然深入探讨时间序列需要一整篇文章,但了解其基本能力对于处理包含时间信息的数据至关重要。

  • pd.to_datetime(): 将字符串或其他类型转换为 DatetimeIndex 或 Timestamp 对象。
  • 使用 DatetimeIndex 作为 DataFrame 的索引,可以方便地进行基于时间的切片和重采样。
  • resample(): 用于时间序列的频率转换和数据聚合(例如,将每分钟的数据重采样为每小时或每天的数据)。

“`python
dates = pd.to_datetime([‘2023-01-01’, ‘2023-01-01 12:00’, ‘2023-01-02’, ‘2023-01-02 12:00’])
ts = pd.Series([1, 1.5, 2, 2.5], index=dates)
print(ts)

2023-01-01 00:00:00 1.0

2023-01-01 12:00:00 1.5

2023-01-02 00:00:00 2.0

2023-01-02 12:00:00 2.5

dtype: float64

重采样到天频率,并求均值

print(ts.resample(‘D’).mean())

2023-01-01 1.25

2023-01-02 2.25

Freq: D, dtype: float64

“`

与其他库的整合

Pandas 并非孤立存在,它是 Python 数据科学生态系统的一部分。

  • 与 NumPy: Pandas 基于 NumPy 构建,Pandas 对象的底层数据通常是 NumPy 数组。这使得 Pandas 可以轻松地与需要 NumPy 数组作为输入的函数或库交互。
  • 与 Matplotlib/Seaborn: Pandas 对象有内置的 .plot() 方法,可以直接使用 Matplotlib 进行可视化。Seaborn 是基于 Matplotlib 的高级可视化库,与 Pandas DataFrame 结合使用更加便捷。
  • 与 SciPy/Scikit-learn: Pandas DataFrame 和 Series 可以方便地转换为 NumPy 数组,作为 SciPy (科学计算) 或 Scikit-learn (机器学习) 库的输入数据。

性能考虑

对于非常大的数据集,Pandas 的内存使用和处理速度可能会成为瓶颈。虽然 Pandas 已经非常高效,但在处理数 GB 甚至 TB 级别的数据时,可能需要考虑其他解决方案:

  • 优化数据类型: 使用更节省内存的数据类型(如 category 类型代替字符串,适当缩小的数值类型)。
  • 分块读取: 使用 pd.read_csv(..., chunksize=...) 分块处理大型文件。
  • 考虑使用 Dask: Dask 是一个并行计算库,可以与 Pandas (和 NumPy) 无缝集成,提供分布式 DataFrame 和数组,处理超出内存的数据。
  • 考虑使用 PySpark 或 Vaex: 对于分布式计算或需要内存映射处理的大型数据集,这些是更专业的工具。

尽管如此,对于绝大多数中等规模的数据集(几十 GB 以内),Pandas 已经足够高效且易用。

结论

Pandas 库已经成为 Python 数据处理和分析领域的标准工具。其直观的 Series 和 DataFrame 数据结构,结合丰富而高效的函数集,极大地简化了从数据导入、清洗、转换到聚合、分析的整个流程。无论是处理结构化数据、时间序列数据,还是进行探索性数据分析 (EDA),Pandas 都能提供强大而灵活的支持。

掌握 Pandas 是迈入 Python 数据科学世界的坚实一步。它不仅是数据处理的利器,更是连接数据与其他高级分析和机器学习库(如 Scikit-learn)的桥梁。通过熟练运用 Pandas,您将能够更高效地从数据中提取价值,解决实际问题。

本文详细介绍了 Pandas 的核心概念和关键功能,但这仅仅是冰山一角。Pandas 拥有庞大而完善的 API,涵盖了更多高级功能,例如窗口函数、分类数据处理、字符串操作、离散化等等。继续深入学习官方文档、参与实践项目是进一步提升 Pandas 技能的最佳途径。

在数据驱动的未来,Pandas 无疑将继续扮演着 Python 数据科学基础设施中不可或缺的角色。投入时间学习和精通它,将为您的数据处理和分析之旅打下坚实的基础。


发表评论

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

滚动至顶部