pandas 数据分析库基础介绍 – wiki基地


数据分析利器:Pandas 基础介绍

在当今数据爆炸的时代,数据分析能力成为了许多领域不可或缺的技能。Python 凭借其简洁的语法和强大的生态系统,成为了最受欢迎的数据分析语言之一。而在 Python 的数据分析工具箱中,pandas 无疑是最核心、最强大的库之一。它提供了高效、灵活的数据结构,使得处理结构化数据变得轻松快捷。

本文将带你深入了解 pandas 的基础知识,包括其核心数据结构、数据的创建、读取、选择、处理缺失值以及一些基本的数据操作。无论你是数据科学的初学者,还是希望提升数据处理效率的开发者,掌握 pandas 都将为你打开数据世界的大门。

1. 什么是 Pandas?为什么选择 Pandas?

Pandas 是一个开源的 Python 库,提供了高性能、易用的数据结构以及数据分析工具。它的名字来源于 “Panel Data”(面板数据),这是一个计量经济学中的术语,也与 “Python Data Analysis” 紧密相关。

Pandas 的核心目标是成为 Python 中进行实际数据分析的最高级、基础的构建模块。它在数据清洗、转换、聚合、筛选等方面表现出色,能够处理各种类型的数据,包括表格数据(如 SQL 表或 Excel 电子表格)、有序或无序的时间序列数据、以及带有行/列标签的任意矩阵数据。

为什么选择 Pandas?

  1. 强大的数据结构: Pandas 引入了 Series 和 DataFrame 这两个核心数据结构,它们能够优雅地处理带有标签的数据,比 Python 内置的列表或字典更适合进行结构化数据操作。
  2. 高效的数据操作: Pandas 底层使用了 NumPy,许多操作都在 C 语言级别进行优化,因此处理大量数据时效率很高。
  3. 丰富的功能: Pandas 提供了丰富的数据操作函数,包括数据对齐、缺失数据处理、数据重塑、数据切片、聚合、分组等,覆盖了数据分析的整个流程。
  4. 便捷的数据 I/O: 可以轻松读取各种数据格式的文件,如 CSV、Excel、SQL 数据库、HDF5 等,并将数据写入这些格式。
  5. 与生态系统的集成: Pandas 与 NumPy、Matplotlib、Scikit-learn 等其他科学计算库紧密集成,形成了一个强大的数据科学生态系统。

简而言之,如果你需要在 Python 中进行数据清洗、预处理、探索性分析或数据转换,pandas 是你的首选工具。

2. 安装 Pandas

如果你已经安装了 Anaconda 或 Miniconda,那么 pandas 已经默认安装好了。如果没有,可以通过 pip 安装:

bash
pip install pandas

安装完成后,你可以在 Python 环境中导入 pandas:

python
import pandas as pd

通常我们将 pandas 别名为 pd,这是一种约定俗成的做法,方便后续代码编写。

3. Pandas 的核心数据结构:Series 和 DataFrame

Pandas 主要提供了两种核心数据结构:Series 和 DataFrame。理解它们是掌握 pandas 的关键。

3.1 Series(系列)

Series 是一种一维带标签的数组。它可以被看作是带有索引(标签)的 NumPy 数组。

  • 一维: 它只有一个维度,类似于 Python 的列表或 NumPy 的一维数组。
  • 带标签: 它的每个元素都有一个关联的标签,称为索引(index)。这个索引可以是数字,也可以是字符串或其他可哈希的对象。
  • 同构数据类型: 默认情况下,Series 中的所有元素都具有相同的数据类型(如整数、浮点数、字符串等),尽管 pandas 在处理不同类型数据时会尝试找到一个兼容的类型(如 object)。

创建 Series:

可以从多种来源创建 Series:

  1. 从列表或 NumPy 数组创建:

    “`python
    import pandas as pd
    import numpy as np

    从列表创建

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

    输出:

    0 1.0

    1 3.0

    2 5.0

    3 NaN

    4 6.0

    5 8.0

    dtype: float64

    从 NumPy 数组创建

    arr = np.array([10, 20, 30, 40])
    s_from_arr = pd.Series(arr)
    print(s_from_arr)

    输出:

    0 10

    1 20

    2 30

    3 40

    dtype: int64

    “`
    在上面的例子中,如果没有指定索引,pandas 会自动创建一个默认的整数索引(从 0 开始)。

  2. 指定索引创建:

    “`python
    s_indexed = pd.Series([10, 20, 30, 40], index=[‘a’, ‘b’, ‘c’, ‘d’])
    print(s_indexed)

    输出:

    a 10

    b 20

    c 30

    d 40

    dtype: int64

    “`
    现在,我们可以使用自定义的索引标签来访问元素。

  3. 从字典创建:

    “`python
    data_dict = {‘北京’: 1500, ‘上海’: 1800, ‘广州’: 1200, ‘深圳’: 1600}
    s_from_dict = pd.Series(data_dict)
    print(s_from_dict)

    输出:

    北京 1500

    上海 1800

    广州 1200

    深圳 1600

    dtype: int64

    “`
    字典的键会成为 Series 的索引,值会成为 Series 的数据。

Series 的属性:

常用的 Series 属性包括:

  • .values: 获取 Series 的数据(以 NumPy 数组形式)。
  • .index: 获取 Series 的索引。
  • .dtype: 获取 Series 的数据类型。
  • .shape: 获取 Series 的形状(元素数量)。
  • .name: 获取或设置 Series 的名称。

“`python
print(s_indexed.values)

输出: [10 20 30 40]

print(s_indexed.index)

输出: Index([‘a’, ‘b’, ‘c’, ‘d’], dtype=’object’)

print(s_indexed.dtype)

输出: int64

print(s_indexed.shape)

输出: (4,)

s_indexed.name = ‘城市人口’
print(s_indexed)

输出:

a 10

b 20

c 30

d 40

Name: 城市人口, dtype: int64

“`

3.2 DataFrame(数据框)

DataFrame 是 pandas 中最常用的数据结构,它可以被看作是一个二维带标签的数据结构,或者是一个带有行索引和列索引的表格。它类似于电子表格或 SQL 数据库中的表。

  • 二维: 它有行和列两个维度。
  • 带标签: 它同时拥有行标签(行索引)和列标签(列名)。
  • 异构数据类型: DataFrame 的不同列可以包含不同的数据类型(例如,一列是整数,另一列是字符串,再一列是浮点数)。每列可以看作是一个 Series。

创建 DataFrame:

DataFrame 的创建方式更加多样:

  1. 从字典创建(最常用): 字典的键作为列名,字典的值作为列的数据(通常是列表或 Series)。

    “`python
    data = {
    ‘城市’: [‘北京’, ‘上海’, ‘广州’, ‘深圳’],
    ‘人口’: [1500, 1800, 1200, 1600],
    ‘面积’: [16410, 6340, 7434, 1997]
    }
    df = pd.DataFrame(data)
    print(df)

    输出:

    城市 人口 面积

    0 北京 1500 16410

    1 上海 1800 6340

    2 广州 1200 7434

    3 深圳 1600 1997

    “`
    默认情况下,行索引是整数 0, 1, 2, …。

    你也可以指定索引:

    “`python
    df_indexed = pd.DataFrame(data, index=[‘A’, ‘B’, ‘C’, ‘D’])
    print(df_indexed)

    输出:

    城市 人口 面积

    A 北京 1500 16410

    B 上海 1800 6340

    C 广州 1200 7434

    D 深圳 1600 1997

    “`

  2. 从列表的列表创建: 列表的每个子列表代表一行数据。你需要同时提供列名。

    “`python
    data_list = [
    [‘北京’, 1500, 16410],
    [‘上海’, 1800, 6340],
    [‘广州’, 1200, 7434],
    [‘深圳’, 1600, 1997]
    ]
    df_from_list = pd.DataFrame(data_list, columns=[‘城市’, ‘人口’, ‘面积’])
    print(df_from_list)

    输出:

    城市 人口 面积

    0 北京 1500 16410

    1 上海 1800 6340

    2 广州 1200 7434

    3 深圳 1600 1997

    “`

  3. 从字典的 Series 创建:

    “`python
    data_series = {
    ‘城市’: pd.Series([‘北京’, ‘上海’, ‘广州’, ‘深圳’]),
    ‘人口’: pd.Series([1500, 1800, 1200, 1600]),
    ‘面积’: pd.Series([16410, 6340, 7434, 1997])
    }
    df_from_series = pd.DataFrame(data_series)
    print(df_from_series)

    输出: (与从字典创建类似,如果 Series 有不同的索引,DataFrame 会合并索引)

    城市 人口 面积

    0 北京 1500 16410

    1 上海 1800 6340

    2 广州 1200 7434

    3 深圳 1600 1997

    “`

DataFrame 的属性:

常用的 DataFrame 属性包括:

  • .values: 获取 DataFrame 的数据(以 NumPy 二维数组形式)。
  • .index: 获取 DataFrame 的行索引。
  • .columns: 获取 DataFrame 的列索引(列名)。
  • .dtypes: 获取每列的数据类型。
  • .shape: 获取 DataFrame 的形状(行数,列数)。
  • .info(): 打印 DataFrame 的摘要信息,包括索引 dtype、列 dtype 和非空值的数量。
  • .describe(): 生成描述性统计信息,如计数、均值、标准差、最小值、最大值等,仅针对数值列。

“`python
print(df.values)

输出:

[[‘北京’ 1500 16410]

[‘上海’ 1800 6340]

[‘广州’ 1200 7434]

[‘深圳’ 1600 1997]]

print(df.index)

输出: RangeIndex(start=0, stop=4, step=1)

print(df.columns)

输出: Index([‘城市’, ‘人口’, ‘面积’], dtype=’object’)

print(df.dtypes)

输出:

城市 object

人口 int64

面积 int64

dtype: object

print(df.shape)

输出: (4, 3)

df.info()

输出类似:

RangeIndex: 4 entries, 0 to 3

Data columns (total 3 columns):

# Column Non-Null Count Dtype

— —— ————– —–

0 城市 4 non-null object

1 人口 4 non-null int64

2 面积 4 non-null int64

dtypes: int64(2), object(1)

memory usage: 272.0+ bytes

print(df.describe())

输出:

人口 面积

count 4.000000 4.000000

mean 1525.000000 8045.250000

std 250.000000 6152.952037

min 1200.000000 1997.000000

25% 1425.000000 5255.750000

50% 1550.000000 6887.000000

75% 1650.000000 9676.500000

max 1800.000000 16410.000000

“`

4. 数据加载与读取 (I/O)

实际的数据分析往往从文件读取数据开始。Pandas 提供了强大的 I/O API,支持多种文件格式。最常见的是 CSV 文件。

4.1 读取 CSV 文件

使用 pd.read_csv() 函数:

“`python

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

内容可能是:

姓名,年龄,性别

张三,25,男

李四,30,女

王五,22,男

创建一个模拟的 CSV 文件

csv_data = “””姓名,年龄,性别
张三,25,男
李四,30,女
王五,22,男
“””
with open(‘data.csv’, ‘w’, encoding=’utf-8′) as f:
f.write(csv_data)

读取 CSV 文件

try:
df_csv = pd.read_csv(‘data.csv’)
print(“从 CSV 文件读取的数据:”)
print(df_csv)
except FileNotFoundError:
print(“错误:data.csv 文件未找到!”)

输出:

从 CSV 文件读取的数据:

姓名 年龄 性别

0 张三 25 男

1 李四 30 女

2 王五 22 男

“`

pd.read_csv() 有许多有用的参数,例如:

  • sep: 指定分隔符(默认为逗号)。
  • header: 指定哪一行作为列名(默认为 0,即第一行)。
  • index_col: 指定哪一列作为行索引。
  • names: 如果文件没有列名,可以手动指定列名。
  • skiprows: 跳过文件开头的行。
  • nrows: 只读取文件的前 n 行。
  • encoding: 指定文件编码(如 ‘utf-8’, ‘gbk’)。
  • parse_dates: 将指定的列解析为日期时间类型。

4.2 读取 Excel 文件

使用 pd.read_excel() 函数:

“`python

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

需要安装 openpyxl 或 xlrd 库来读写 Excel 文件

pip install openpyxl xlrd

创建一个模拟的 Excel 文件

df_to_excel = pd.DataFrame({
‘产品’: [‘A’, ‘B’, ‘C’],
‘价格’: [100, 150, 200]
})
try:
df_to_excel.to_excel(‘data.xlsx’, index=False)
print(“\n模拟 Excel 文件创建成功。”)

# 读取 Excel 文件
df_excel = pd.read_excel('data.xlsx')
print("从 Excel 文件读取的数据:")
print(df_excel)

except Exception as e:
print(f”\n处理 Excel 文件时发生错误: {e}”)
print(“请确保已安装 openpyxl 和 xlrd 库。”)

输出类似:

模拟 Excel 文件创建成功。

从 Excel 文件读取的数据:

产品 价格

0 A 100

1 B 150

2 C 200

“`

pd.read_excel() 也有类似的参数,如 sheet_name(指定读取哪个工作表)。

4.3 写入文件

Pandas 对象也有相应的 .to_csv(), .to_excel(), .to_sql(), .to_json() 等方法来将数据写入文件或数据库。

“`python

将 DataFrame 写入 CSV 文件

df.to_csv(‘output.csv’, index=False) # index=False 不写入行索引

将 DataFrame 写入 Excel 文件

df.to_excel(‘output.xlsx’, sheet_name=’Sheet1′, index=False)

print(“\n数据已写入 output.csv 和 output.xlsx”)
“`

5. 数据检查与探索

加载数据后,通常需要先快速浏览一下数据,了解其结构、内容和基本统计特征。

  • df.head(n=5): 显示 DataFrame 的前 n 行(默认为 5)。
  • df.tail(n=5): 显示 DataFrame 的后 n 行(默认为 5)。
  • df.info(): 提供 DataFrame 的简要信息,包括列名、非空值数量、数据类型和内存使用情况。
  • df.describe(): 生成数值列的描述性统计信息(计数、均值、标准差、最小值、25%分位数、中位数、75%分位数、最大值)。对于非数值列,它会计算非空值的数量、唯一值数量、出现频率最高的词及其频率。
  • df.shape: 返回 DataFrame 的维度(行数,列数)。
  • df.columns: 返回列名列表。
  • df.index: 返回行索引。
  • df['列名'].value_counts(): 计算某个列中各个唯一值出现的次数,常用于类别型数据。
  • df.isnull().sum(): 计算每列中缺失值的数量。

“`python

使用之前创建的 df

print(“\n— 数据检查 —“)
print(“前两行:”)
print(df.head(2))

print(“\n后两行:”)
print(df.tail(2))

print(“\n信息概览:”)
df.info()

print(“\n描述性统计:”)
print(df.describe())

print(“\n形状:”)
print(df.shape)

print(“\n列名:”)
print(df.columns)

print(“\n行索引:”)
print(df.index)

假设df有一个’城市’列

print(“\n’城市’列值计数:”)
print(df[‘城市’].value_counts())

添加一些缺失值以演示isnull()

df_with_nan = df.copy()
df_with_nan.loc[1, ‘人口’] = np.nan
df_with_nan.loc[3, ‘面积’] = np.nan
print(“\n包含缺失值的DataFrame:”)
print(df_with_nan)
print(“\n缺失值计数:”)
print(df_with_nan.isnull().sum())
“`

6. 数据选择与切片

从 DataFrame 中选择特定的行、列或单元格是数据分析中最频繁的操作之一。Pandas 提供了多种灵活的方式来实现。

6.1 选择列

可以直接通过列名来选择一列或多列。

  • 选择单列: 返回一个 Series。

    “`python
    print(“\n— 数据选择 —“)
    print(“选择单列 ‘人口’:”)
    population_series = df[‘人口’]
    print(population_series)
    print(type(population_series))

    输出:

    “`

  • 选择多列: 使用一个列名组成的列表,返回一个 DataFrame。

    “`python
    print(“\n选择多列 [‘城市’, ‘面积’]:”)
    city_area_df = df[[‘城市’, ‘面积’]]
    print(city_area_df)
    print(type(city_area_df))

    输出:

    ``
    注意,选择单列时,
    df.列名语法也是可以的 (df.人口),但这种方式在列名包含空格或特殊字符时不适用,且容易与对象方法混淆,因此推荐使用df[‘列名’]` 语法。

6.2 选择行

选择行可以通过多种方式实现,最常用的是基于索引标签的 .loc[] 和基于整数位置的 .iloc[]。理解这两者的区别至关重要。

  • 基于标签的选择:.loc[]

    .loc[] 主要通过行索引标签和列索引标签(列名)来选择数据。

    • 选择单行(按行索引标签):

      “`python
      print(“\n用 .loc[] 选择索引标签为 1 的行:”) # 默认索引是整数 0,1,2,3…
      print(df.loc[1]) # 返回一个 Series

      输出:

      城市 上海

      人口 1800

      面积 6340

      Name: 1, dtype: object

      print(“\n用 .loc[] 选择索引标签为 ‘B’ 的行 (如果索引是自定义标签):”)
      print(df_indexed.loc[‘B’])

      输出:

      城市 上海

      人口 1800

      面积 6340

      Name: B, dtype: object

      “`

    • 选择多行(按行索引标签列表):

      “`python
      print(“\n用 .loc[] 选择索引标签为 [0, 2] 的行:”)
      print(df.loc[[0, 2]]) # 返回一个 DataFrame

      输出:

      城市 人口 面积

      0 北京 1500 16410

      2 广州 1200 7434

      “`

    • 按行索引标签范围切片(包含起始和结束标签):

      “`python
      print(“\n用 .loc[] 按标签范围切片 (从索引 1 到 3, 包含 3):”)
      print(df.loc[1:3]) # 返回一个 DataFrame

      输出:

      城市 人口 面积

      1 上海 1800 6340

      2 广州 1200 7434

      3 深圳 1600 1997

      ``
      注意:与 Python 列表切片不同,使用
      .loc[]` 进行标签切片是 包含 结束标签的。

    • 同时选择行和列(按标签):

      “`python
      print(“\n用 .loc[] 选择索引标签为 0 和 2 的行的 ‘人口’ 列:”)
      print(df.loc[[0, 2], ‘人口’]) # 返回一个 Series

      输出:

      0 1500

      2 1200

      Name: 人口, dtype: int64

      print(“\n用 .loc[] 选择索引标签从 1 到 3 的行的 [‘城市’, ‘面积’] 列:”)
      print(df.loc[1:3, [‘城市’, ‘面积’]]) # 返回一个 DataFrame

      输出:

      城市 面积

      1 上海 6340

      2 广州 7434

      3 深圳 1997

      使用 `:` 表示选择所有行或所有列:python
      print(“\n用 .loc[] 选择所有行的 ‘城市’ 列:”)
      print(df.loc[:, ‘城市’]) # 相当于 df[‘城市’]

      输出:

      0 北京

      1 上海

      2 广州

      3 深圳

      Name: 城市, dtype: object

      “`

  • 基于位置的选择:.iloc[]

    .iloc[] 主要通过行和列的整数位置来选择数据(类似于 NumPy 数组的索引方式)。位置从 0 开始。

    • 选择单行(按行位置):

      “`python
      print(“\n用 .iloc[] 选择位置为 1 的行:”)
      print(df.iloc[1]) # 返回一个 Series

      输出:

      城市 上海

      人口 1800

      面积 6340

      Name: 1, dtype: object

      “`

    • 选择多行(按行位置列表):

      “`python
      print(“\n用 .iloc[] 选择位置为 [0, 2] 的行:”)
      print(df.iloc[[0, 2]]) # 返回一个 DataFrame

      输出:

      城市 人口 面积

      0 北京 1500 16410

      2 广州 1200 7434

      “`

    • 按行位置范围切片(不包含结束位置):

      “`python
      print(“\n用 .iloc[] 按位置范围切片 (从位置 1 到 3, 不包含 3):”)
      print(df.iloc[1:3]) # 返回一个 DataFrame

      输出:

      城市 人口 面积

      1 上海 1800 6340

      2 广州 1200 7434

      ``
      注意:与 Python 列表切片一样,使用
      .iloc[]` 进行位置切片是 不包含 结束位置的。

    • 同时选择行和列(按位置):

      “`python
      print(“\n用 .iloc[] 选择位置为 0 和 2 的行的 位置为 1 的列:”)
      print(df.iloc[[0, 2], 1]) # 返回一个 Series

      输出:

      0 1500

      2 1200

      Name: 人口, dtype: int64

      print(“\n用 .iloc[] 选择位置从 1 到 3 的行的 位置为 0 到 2 的列:”)
      print(df.iloc[1:4, 0:2]) # 返回一个 DataFrame

      输出:

      城市 人口

      1 上海 1800

      2 广州 1200

      3 深圳 1600

      使用 `:` 表示选择所有行或所有列:python
      print(“\n用 .iloc[] 选择所有行的 位置为 0 的列:”)
      print(df.iloc[:, 0]) # 相当于 df[‘城市’]

      输出:

      0 北京

      1 上海

      2 广州

      3 深圳

      Name: 城市, dtype: object

      “`

总结 .loc[].iloc[] 的区别:

  • .loc[]: 基于 标签索引名称。用于按名称选择行和列。切片时包含结束标签。
  • .iloc[]: 基于 整数位置。用于按位置选择行和列。切片时不包含结束位置。

在大多数情况下,当你知道行或列的名称时使用 .loc;当你知道行或列在 DataFrame 中的位置时使用 .iloc。避免在可能的情况下混合使用整数和标签索引,明确使用 .loc.iloc 可以提高代码的可读性和避免潜在的错误。

6.3 布尔索引(条件选择)

布尔索引是一种非常强大的数据筛选方式,可以根据某个条件选择数据。

  • 基本布尔索引:

    “`python
    print(“\n— 布尔索引 —“)

    选择人口大于 1300 的城市数据

    condition = df[‘人口’] > 1300
    print(“人口大于 1300 的条件 Series:”)
    print(condition)

    输出:

    0 True

    1 True

    2 False

    3 True

    Name: 人口, dtype: bool

    print(“\n人口大于 1300 的城市数据:”)
    print(df[condition]) # 将布尔 Series 直接传入 DataFrame

    输出:

    城市 人口 面积

    0 北京 1500 16410

    1 上海 1800 6340

    3 深圳 1600 1997

    “`

  • 使用逻辑运算符组合条件:

    可以使用 & (AND), | (OR), ~ (NOT) 运算符组合多个条件。每个条件表达式需要用括号括起来。

    “`python

    选择人口大于 1300 且面积小于 10000 的城市

    condition1 = df[‘人口’] > 1300
    condition2 = df[‘面积’] < 10000
    print(“\n人口大于 1300 且面积小于 10000 的城市数据:”)
    print(df[condition1 & condition2])

    输出:

    城市 人口 面积

    1 上海 1800 6340

    3 深圳 1600 1997

    “`

  • 使用 isin() 方法:

    用于判断某一列的值是否在指定的列表或 Series 中。

    “`python

    选择城市为 ‘北京’ 或 ‘广州’ 的数据

    cities_to_select = [‘北京’, ‘广州’]
    print(“\n城市为 ‘北京’ 或 ‘广州’ 的数据:”)
    print(df[df[‘城市’].isin(cities_to_select)])

    输出:

    城市 人口 面积

    0 北京 1500 16410

    2 广州 1200 7434

    “`

布尔索引返回的结果始终是 DataFrame 的一个副本(view 或 copy 取决于 pandas 版本和操作的复杂性),对其进行修改可能不会影响原 DataFrame。如果需要修改,最好使用 .loc.iloc 结合布尔条件。

“`python

使用 .loc 结合布尔条件修改数据

df_copy = df.copy() # 创建副本以避免修改原始 df
df_copy.loc[df_copy[‘人口’] > 1500, ‘人口’] = df_copy[‘人口’] * 1.1 # 人口大于1500的增长10%
print(“\n人口大于 1500 的增长 10% 后:”)
print(df_copy)

输出:

城市 人口 面积

0 北京 1500.0 # 北京人口不大于1500

1 上海 1980.0 # 上海人口增长10%

2 广州 1200.0

3 深圳 1760.0 # 深圳人口增长10%

“`

7. 处理缺失数据

现实世界中的数据常常是不完整的,处理缺失值是数据清洗的重要步骤。Pandas 使用 NaN (Not a Number) 来表示浮点数和某些数值类型的缺失值,对于对象类型,也可能使用 None

  • 检查缺失值:

    • df.isnull()df.isna(): 返回一个与 DataFrame 形状相同的布尔型 DataFrame,元素为 True 表示该位置是缺失值。
    • df.notnull()df.notna(): 返回布尔型 DataFrame,元素为 True 表示该位置不是缺失值。
    • df.isnull().sum(): 计算每列的缺失值数量。
    • df.isnull().sum().sum(): 计算整个 DataFrame 的缺失值总数。

    “`python
    print(“\n— 处理缺失数据 —“)
    print(“DataFrame 包含缺失值:”)
    print(df_with_nan)

    print(“\n是否缺失 (isnull):”)
    print(df_with_nan.isnull())

    print(“\n每列缺失值数量:”)
    print(df_with_nan.isnull().sum())

    输出:

    城市 0

    人口 1

    面积 1

    dtype: int64

    “`

  • 删除缺失值:

    • df.dropna(): 删除包含缺失值的行或列。

      “`python

      删除包含缺失值的行

      df_dropped_rows = df_with_nan.dropna()
      print(“\n删除包含缺失值的行:”)
      print(df_dropped_rows)

      输出:

      城市 人口 面积

      0 北京 1500.0 16410.0

      2 广州 1200.0 7434.0

      删除包含缺失值的列 (axis=1)

      df_dropped_cols = df_with_nan.dropna(axis=1)
      print(“\n删除包含缺失值的列:”)
      print(df_dropped_cols)

      输出:

      城市

      0 北京

      1 上海

      2 广州

      3 深圳

      ``dropna()默认删除包含 *任何* 缺失值的行或列 (how=’any’)。你也可以指定how=’all’,只删除全部为缺失值的行或列。thresh` 参数可以指定至少有多少个非缺失值才保留该行/列。

  • 填充缺失值:

    • df.fillna(value): 用指定的值填充缺失值。
    • df.fillna(method='ffill'): 使用前一个非缺失值填充(向前填充)。
    • df.fillna(method='bfill'): 使用后一个非缺失值填充(向后填充)。
    • df.fillna(value=...) 结合其他方法,如均值、中位数等。

    “`python

    用特定值填充

    df_filled_value = df_with_nan.fillna(0)
    print(“\n用 0 填充缺失值:”)
    print(df_filled_value)

    输出:

    城市 人口 面积

    0 北京 1500.0 16410.0

    1 上海 0.0 6340.0

    2 广州 1200.0 7434.0

    3 深圳 1600.0 0.0

    用列的均值填充 (通常是更好的做法)

    mean_population = df_with_nan[‘人口’].mean()
    df_filled_mean = df_with_nan.copy()
    df_filled_mean[‘人口’].fillna(mean_population, inplace=True) # inplace=True 直接修改原DataFrame
    print(“\n用 ‘人口’ 列均值填充缺失值:”)
    print(df_filled_mean)

    输出:

    城市 人口 面积

    0 北京 1500.0 16410.0

    1 上海 1433.333 6340.0 # (1500+1200+1600)/3 = 1433.333…

    2 广州 1200.0 7434.0

    3 深圳 1600.0 NaN # ‘面积’列的缺失值未被填充

    使用向前填充

    df_filled_ffill = df_with_nan.fillna(method=’ffill’)
    print(“\n使用向前填充 (ffill):”)
    print(df_filled_ffill)

    输出:

    城市 人口 面积

    0 北京 1500.0 16410.0

    1 上海 1500.0 6340.0 # 人口被前一行的1500填充

    2 广州 1200.0 7434.0

    3 深圳 1600.0 7434.0 # 面积被前一行的7434填充

    ``fillna()默认返回一个新的 DataFrame,使用inplace=True` 可以直接修改原 DataFrame。

8. 基本数据操作

Pandas 提供了丰富的函数进行数据的计算、统计和转换。

8.1 数学运算和统计

DataFrame 和 Series 支持各种数学运算,并且会自动进行基于索引的对齐。

“`python
print(“\n— 基本数据操作 —“)
print(“原始 DataFrame:”)
print(df)

计算人口密度 (假设面积单位一致)

df[‘人口密度’] = df[‘人口’] / df[‘面积’]
print(“\n添加 ‘人口密度’ 列:”)
print(df)

输出:

城市 人口 面积 人口密度

0 北京 1500 16410 0.091408

1 上海 1800 6340 0.283912

2 广州 1200 7434 0.161421

3 深圳 1600 1997 0.801202

计算列的统计量

print(“\n’人口’ 列的均值:”, df[‘人口’].mean())
print(“‘面积’ 列的总和:”, df[‘面积’].sum())
print(“最大人口:”, df[‘人口’].max())
print(“最小面积:”, df[‘面积’].min())

计算整个 DataFrame 的统计量 (忽略非数值列)

print(“\nDataFrame 均值 (仅数值列):”)
print(df.mean(numeric_only=True)) # pandas 2.0+ 需要 numeric_only=True

输出:

人口 1525.000000

面积 8045.250000

人口密度 0.334486

dtype: float64

按行计算总和

print(“\n按行计算总和 (axis=1):”)
print(df[[‘人口’, ‘面积’]].sum(axis=1))

输出:

0 17910

1 8140

2 8634

3 3597

dtype: int64

``
常用的统计方法包括
count(),mean(),median(),sum(),std()(标准差),var()(方差),min(),max(),quantile()(分位数),cumsum()(累积和),cumprod()` (累积积) 等。

8.2 应用函数

Pandas 提供了 apply() 方法,可以将一个函数应用到 Series 或 DataFrame 的行或列上。

  • 应用于 Series:

    “`python

    将函数应用于 ‘人口’ 列,判断人口是否大于 1500

    def is_large_population(pop):
    return pop > 1500

    df[‘人口多’] = df[‘人口’].apply(is_large_population)
    print(“\n应用函数创建新列 ‘人口多’:”)
    print(df)

    输出:

    城市 人口 面积 人口密度 人口多

    0 北京 1500 16410 0.091408 False

    1 上海 1800 6340 0.283912 True

    2 广州 1200 7434 0.161421 False

    3 深圳 1600 1997 0.801202 True

    lambda 函数也很常用:python
    df[‘面积等级’] = df[‘面积’].apply(lambda x: ‘大’ if x > 10000 else ‘小’)
    print(“\n应用 lambda 函数创建新列 ‘面积等级’:”)
    print(df)

    输出:

    城市 人口 面积 人口密度 人口多 面积等级

    0 北京 1500 16410 0.091408 False 大

    1 上海 1800 6340 0.283912 True 小

    2 广州 1200 7434 0.161421 False 小

    3 深圳 1600 1997 0.801202 True 小

    “`

  • 应用于 DataFrame 的行或列:

    默认情况下,apply() 应用于每一列(axis=0)。使用 axis=1 应用于每一行。

    “`python

    计算每行的 人口+面积

    df[‘人口+面积’] = df[[‘人口’, ‘面积’]].apply(sum, axis=1)
    print(“\n计算每行 人口+面积:”)
    print(df)

    输出:

    城市 人口 面积 人口密度 人口多 面积等级 人口+面积

    0 北京 1500 16410 0.091408 False 大 17910

    1 上海 1800 6340 0.283912 True 小 8140

    2 广州 1200 7434 0.161421 False 小 8634

    3 深圳 1600 1997 0.801202 True 小 3597

    “`

8.3 排序

可以按索引或按值对 DataFrame 进行排序。

  • 按索引排序:

    “`python
    df_sorted_index = df.sort_index(axis=0, ascending=False) # ascending=False 为降序
    print(“\n按行索引降序排序:”)
    print(df_sorted_index)

    输出:

    城市 人口 面积 人口密度 人口多 面积等级 人口+面积

    3 深圳 1600 1997 0.801202 True 小 3597

    2 广州 1200 7434 0.161421 False 小 8634

    1 上海 1800 6340 0.283912 True 小 8140

    0 北京 1500 16410 0.091408 False 大 17910

    “`

  • 按值排序:

    “`python

    按 ‘人口’ 列升序排序

    df_sorted_population = df.sort_values(by=’人口’)
    print(“\n按 ‘人口’ 列升序排序:”)
    print(df_sorted_population)

    输出:

    城市 人口 面积 人口密度 人口多 面积等级 人口+面积

    2 广州 1200 7434 0.161421 False 小 8634

    0 北京 1500 16410 0.091408 False 大 17910

    3 深圳 1600 1997 0.801202 True 小 3597

    1 上海 1800 6340 0.283912 True 小 8140

    按多个列排序 (先按人口降序,再按面积升序)

    df_sorted_multi = df.sort_values(by=[‘人口’, ‘面积’], ascending=[False, True])
    print(“\n按 [‘人口’, ‘面积’] 排序 (人口降序,面积升序):”)
    print(df_sorted_multi)

    输出:

    城市 人口 面积 人口密度 人口多 面积等级 人口+面积

    1 上海 1800 6340 0.283912 True 小 8140

    3 深圳 1600 1997 0.801202 True 小 3597

    0 北京 1500 16410 0.091408 False 大 17910

    2 广州 1200 7434 0.161421 False 小 8634

    “`

8.4 重命名列或索引

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

“`python

重命名列

df_renamed_cols = df.rename(columns={‘城市’: ‘City’, ‘人口’: ‘Population’})
print(“\n重命名列 ‘城市’ 和 ‘人口’:”)
print(df_renamed_cols)

输出:

City Population 面积 人口密度 人口多 面积等级 人口+面积

0 北京 1500 16410 0.091408 False 大 17910

1 上海 1800 6340 0.283912 True 小 8140

2 广州 1200 7434 0.161421 False 小 8634

3 深圳 1600 1997 0.801202 True 小 3597

重命名索引 (使用 df_indexed 演示)

df_renamed_index = df_indexed.rename(index={‘A’: ‘City A’, ‘B’: ‘City B’})
print(“\n重命名索引 ‘A’ 和 ‘B’:”)
print(df_renamed_index)

输出:

城市 人口 面积

City A 北京 1500 16410

City B 上海 1800 6340

C 广州 1200 7434

D 深圳 1600 1997

``rename()方法也默认返回新对象,使用inplace=True` 可以直接修改。

9. 数据分组与聚合 (Groupby)

groupby() 是 pandas 中一个非常强大且常用的操作,它实现了数据分组聚合的“split-apply-combine”(分割-应用-合并)模式:

  1. Split (分割): 根据某个(或多个)键将数据分割成多个组。
  2. Apply (应用): 对每个组独立地应用函数(如聚合、转换、过滤)。
  3. Combine (合并): 将结果组合成一个数据结构。

“`python
print(“\n— 数据分组与聚合 (Groupby) —“)
data_sales = {
‘商品’: [‘A’, ‘B’, ‘A’, ‘C’, ‘B’, ‘C’, ‘A’, ‘B’],
‘地区’: [‘华北’, ‘华东’, ‘华北’, ‘华南’, ‘华东’, ‘华北’, ‘华南’, ‘华北’],
‘销量’: [100, 150, 120, 80, 160, 90, 110, 130]
}
df_sales = pd.DataFrame(data_sales)
print(“原始销售数据:”)
print(df_sales)

按 ‘商品’ 列分组,计算每种商品的平均销量

groupby_product = df_sales.groupby(‘商品’)
print(“\n按 ‘商品’ 分组,计算平均销量:”)
print(groupby_product[‘销量’].mean())

输出:

商品

A 110.0

B 146.666667

C 85.0

Name: 销量, dtype: float64

按 ‘地区’ 列分组,计算总销量

print(“\n按 ‘地区’ 分组,计算总销量:”)
print(df_sales.groupby(‘地区’)[‘销量’].sum())

输出:

地区

华南 190

华东 310

华北 440

Name: 销量, dtype: int64

按多个列分组,计算多个聚合函数

print(“\n按 [‘商品’, ‘地区’] 分组,计算销量总和与数量:”)
groupby_product_region = df_sales.groupby([‘商品’, ‘地区’])
print(groupby_product_region[‘销量’].agg([‘sum’, ‘count’]))

输出:

sum count

商品 地区

A 华北 220 2

华南 110 1

B 华东 310 2

华北 130 1

C 华南 80 1

华北 90 1

“`

聚合函数有很多,如 sum(), mean(), size(), count(), min(), max(), first(), last(), nth(), median(), std(), var(), agg() (用于同时应用多个聚合函数) 等。

10. 数据合并与连接

在实际应用中,数据可能分散在多个表或文件中,需要将它们合并起来进行分析。Pandas 提供了多种合并数据的方式。

10.1 Concatenation (连接)

pd.concat() 函数可以沿着某个轴(行或列)将多个 DataFrame 或 Series 堆叠起来。

“`python
print(“\n— 数据合并与连接 —“)
df1 = pd.DataFrame({‘A’: [‘A0’, ‘A1’], ‘B’: [‘B0’, ‘B1’]}, index=[0, 1])
df2 = pd.DataFrame({‘A’: [‘A2’, ‘A3’], ‘B’: [‘B2’, ‘B3’]}, index=[2, 3])
print(“DataFrame 1:”)
print(df1)
print(“\nDataFrame 2:”)
print(df2)

按行连接 (axis=0, 默认)

result_row = pd.concat([df1, df2])
print(“\n按行连接:”)
print(result_row)

输出:

A B

0 A0 B0

1 A1 B1

2 A2 B2

3 A3 B3

按列连接 (axis=1)

result_col = pd.concat([df1, df2], axis=1)
print(“\n按列连接:”)
print(result_col)

输出:

A B A B

0 A0 B0 NaN NaN # df1 有索引 0,1; df2 有索引 2,3。合并时根据索引对齐,缺失值填充 NaN

1 A1 B1 NaN NaN

2 NaN NaN A2 B2

3 NaN NaN A3 B3

可以选择忽略原索引,重新生成索引

result_ignore_index = pd.concat([df1, df2], ignore_index=True)
print(“\n按行连接 (忽略原索引):”)
print(result_ignore_index)

输出:

A B

0 A0 B0

1 A1 B1

2 A2 B2

3 A3 B3

“`

10.2 Merging (合并/连接)

pd.merge() 函数可以根据一个或多个键将 DataFrame 组合起来,类似于数据库中的 JOIN 操作。

“`python
df_left = pd.DataFrame({‘key’: [‘K0’, ‘K1’, ‘K2’, ‘K3’],
‘A’: [‘A0’, ‘A1’, ‘A2’, ‘A3’],
‘B’: [‘B0’, ‘B1’, ‘B2’, ‘B3’]})

df_right = pd.DataFrame({‘key’: [‘K0’, ‘K1’, ‘K4’, ‘K5’],
‘C’: [‘C0’, ‘C1’, ‘C2’, ‘C3’],
‘D’: [‘D0’, ‘D1’, ‘D2’, ‘D3’]})
print(“DataFrame Left:”)
print(df_left)
print(“\nDataFrame Right:”)
print(df_right)

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

merged_inner = pd.merge(df_left, df_right, on=’key’)
print(“\n内连接 (on=’key’):”)
print(merged_inner)

输出:

key A B C D

0 K0 A0 B0 C0 D0

1 K1 A1 B1 C1 D1

左连接 (left join) – 保留左边 DataFrame 的所有行,右边没有匹配的用 NaN 填充

merged_left = pd.merge(df_left, df_right, on=’key’, how=’left’)
print(“\n左连接 (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 # K2, K3 只在左边,右边没有匹配

3 K3 A3 B3 NaN NaN

外连接 (outer join) – 保留两个 DataFrame 中的所有行,没有匹配的用 NaN 填充

merged_outer = pd.merge(df_left, df_right, on=’key’, how=’outer’)
print(“\n外连接 (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 # K2, K3 只在左边

3 K3 A3 B3 NaN NaN

4 K4 NaN NaN C2 D2 # K4, K5 只在右边

5 K5 NaN NaN C3 D3

右连接 (right join) – 保留右边 DataFrame 的所有行,左边没有匹配的用 NaN 填充

merged_right = pd.merge(df_left, df_right, on=’key’, how=’right’)
print(“\n右连接 (how=’right’):”)
print(merged_right)

输出:

key A B C D

0 K0 A0 B0 C0 D0

1 K1 A1 B1 C1 D1

2 K4 NaN NaN C2 D2 # K4, K5 只在右边

3 K5 NaN NaN C3 D3

``pd.merge()的常用参数包括:on(指定连接键)、left_onright_on(左右 DataFrame 的不同连接键)、left_indexright_index(使用索引作为连接键)、how`(连接类型:’inner’, ‘outer’, ‘left’, ‘right’)。

11. 基础可视化

Pandas 对象集成了 Matplotlib 的绘图功能,可以直接从 DataFrame 或 Series 快速生成图表进行初步的可视化探索。

“`python
print(“\n— 基础可视化 —“)

需要安装 Matplotlib

pip install matplotlib

import matplotlib.pyplot as plt

创建一个包含数值数据的 DataFrame

data_plot = {‘类别’: [‘A’, ‘B’, ‘C’, ‘D’],
‘值1’: [10, 25, 15, 30],
‘值2’: [5, 20, 10, 35]}
df_plot = pd.DataFrame(data_plot)
print(“用于绘图的 DataFrame:”)
print(df_plot)

绘制 ‘值1’ 列的柱状图

df_plot.plot(kind=’bar’, x=’类别’, y=’值1′, title=’类别 vs 值1′)
plt.ylabel(‘值1’)
plt.show() # 显示图表

绘制 ‘值1’ 和 ‘值2’ 的线形图

df_plot.plot(kind=’line’, x=’类别’, y=[‘值1’, ‘值2′], title=’类别 vs 值1 和 值2’)
plt.ylabel(‘值’)
plt.show()

绘制散点图

df_scatter = pd.DataFrame(np.random.rand(50, 2), columns=[‘X’, ‘Y’])
df_scatter.plot(kind=’scatter’, x=’X’, y=’Y’, title=’随机散点图’)
plt.show()
``
Pandas
.plot()方法的kind参数支持多种图表类型,如‘line’,‘bar’,‘hist’(直方图),‘box’(箱线图),‘kde’(核密度估计),‘area’(面积图),‘pie’(饼图),‘scatter’` (散点图)。

12. 总结与展望

恭喜你!走到这里,你已经了解了 pandas 最核心和基础的功能:

  • 理解了 Series 和 DataFrame 这两个基本数据结构。
  • 学会了如何创建和检查 DataFrame。
  • 掌握了如何从 CSV 和 Excel 文件读取数据。
  • 熟悉了使用 .loc.iloc 和布尔索引进行数据选择和筛选。
  • 学会了识别和处理缺失值。
  • 了解了基本的数学运算、统计、函数应用和排序。
  • 初步接触了 groupby 进行数据分组聚合。
  • 了解了使用 concatmerge 合并数据。
  • 进行了简单的可视化。

Pandas 的功能远不止于此。在掌握了这些基础后,你可以继续深入学习:

  • 更高级的 groupby 操作(transform, filter)。
  • 处理时间序列数据。
  • 字符串操作。
  • 高级的数据重塑(pivot, melt, stack, unstack)。
  • MultiIndex(多级索引)。
  • 更复杂的数据合并场景。
  • 与其他库(如 Seaborn, Scikit-learn)的协同使用。

数据分析是一个实践出真知的过程。最好的学习方法是在实际数据集上不断练习。从简单的数据加载和检查开始,逐步尝试更复杂的数据清洗、转换和分析任务。

Pandas 是你进行高效数据分析的强大伙伴,熟练掌握它将极大地提升你的工作效率和数据处理能力。祝你在数据分析的旅程中一切顺利!


发表评论

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

滚动至顶部