当前位置: 首页 > news >正文

【深度学习-Day 7】精通Pandas:从Series、DataFrame入门到数据清洗实战

Langchain系列文章目录

01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南
02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖
03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南
04-玩转 LangChain:从文档加载到高效问答系统构建的全程实战
05-玩转 LangChain:深度评估问答系统的三种高效方法(示例生成、手动评估与LLM辅助评估)
06-从 0 到 1 掌握 LangChain Agents:自定义工具 + LLM 打造智能工作流!
07-【深度解析】从GPT-1到GPT-4:ChatGPT背后的核心原理全揭秘
08-【万字长文】MCP深度解析:打通AI与世界的“USB-C”,模型上下文协议原理、实践与未来

Python系列文章目录

PyTorch系列文章目录

机器学习系列文章目录

深度学习系列文章目录

Java系列文章目录

JavaScript系列文章目录

深度学习系列文章目录

01-【深度学习-Day 1】为什么深度学习是未来?一探究竟AI、ML、DL关系与应用
02-【深度学习-Day 2】图解线性代数:从标量到张量,理解深度学习的数据表示与运算
03-【深度学习-Day 3】搞懂微积分关键:导数、偏导数、链式法则与梯度详解
04-【深度学习-Day 4】掌握深度学习的“概率”视角:基础概念与应用解析
05-【深度学习-Day 5】Python 快速入门:深度学习的“瑞士军刀”实战指南
06-【深度学习-Day 6】掌握 NumPy:ndarray 创建、索引、运算与性能优化指南
07-【深度学习-Day 7】精通Pandas:从Series、DataFrame入门到数据清洗实战


文章目录

  • Langchain系列文章目录
  • Python系列文章目录
  • PyTorch系列文章目录
  • 机器学习系列文章目录
  • 深度学习系列文章目录
  • Java系列文章目录
  • JavaScript系列文章目录
  • 深度学习系列文章目录
  • 前言
  • 一、Pandas 核心数据结构
    • 1.1 Series 数据结构
      • 1.1.1 Series 的创建
        • (1) 从列表创建 Series
        • (2) 从 NumPy 数组创建 Series
        • (3) 从字典创建 Series
      • 1.1.2 Series 的属性与方法
        • (1) 常用属性
        • (2) 常用方法
      • 1.1.3 Series 的索引与切片
        • (1) 标签索引
        • (2) 位置索引
        • (3) 切片
    • 1.2 DataFrame 数据结构
      • 1.2.1 DataFrame 的创建
        • (1) 从字典创建 DataFrame
        • (2) 从列表的列表(或 NumPy 二维数组)创建 DataFrame
        • (3) 从 Series 列表/字典创建 DataFrame
      • 1.2.2 DataFrame 的属性与方法
        • (1) 常用属性
        • (2) 常用方法
      • 1.2.3 DataFrame 的索引与切片
        • (1) 选择列
        • (2) 选择行:`.loc[]` 和 `.iloc[]`
  • 二、数据读取与写入
    • 2.1 读取 CSV 文件
      • 2.1.1 基本读取
      • 2.1.2 常用参数
    • 2.2 写入 CSV 文件
    • 2.3 其他数据格式
  • 三、数据选择与过滤
    • 3.1 基于标签的选择:`.loc[]`
    • 3.2 基于位置的选择:`.iloc[]`
    • 3.3 条件选择
    • 3.4 组合条件
  • 四、基本的数据清洗和转换操作
    • 4.1 处理缺失值
      • 4.1.1 检测缺失值
      • 4.1.2 填充缺失值
      • 4.1.3 删除缺失值
    • 4.2 处理重复值
      • 4.2.1 检测重复值
      • 4.2.2 删除重复值
    • 4.3 数据类型转换
    • 4.4 应用函数
  • 五、实践:加载并简单探索一个表格数据集
    • 5.1 数据集介绍
    • 5.2 加载数据
    • 5.3 查看数据基本信息
    • 5.4 初步探索与清洗
        • (1) 处理缺失值
        • (2) 处理重复值
        • (3) 简单数据查询
        • (4) 计算新列
  • 六、总结


前言

在深度学习的征途中,数据扮演着至关重要的角色。正所谓“工欲善其事,必先利其器”,要高效地处理和分析数据,一个强大的工具必不可少。Pandas 就是这样一把在 Python 数据科学生态系统中熠熠生辉的“瑞士军刀”。它提供了高性能、易用的数据结构和数据分析工具,使得我们能够轻松应对各种结构化数据的挑战。无论是数据清洗、转换、分析还是可视化前的准备,Pandas 都能提供强有力的支持。本篇文章将带您深入了解 Pandas 的核心概念、常用操作以及如何在实际项目中运用它来处理表格数据,为后续的深度学习模型训练打下坚实的数据基础。

一、Pandas 核心数据结构

Pandas 之所以强大,首先归功于其两大核心数据结构:SeriesDataFrame。它们为我们处理不同类型的数据提供了灵活且高效的容器。

1.1 Series 数据结构

可以将 Series 理解为一个一维的、带标签的数组。它能够存储任何数据类型(整数、字符串、浮点数、Python 对象等),并且每个元素都有一个与之关联的标签,称为索引(index)。

1.1.1 Series 的创建

创建 Series 的方式多种多样,最常见的是从列表、NumPy 数组或字典创建。

(1) 从列表创建 Series
import pandas as pd
import numpy as np# 从列表创建 Series,默认索引为 0, 1, 2...
data_list = [10, 20, 30, 40, 50]
s_from_list = pd.Series(data_list)
print("从列表创建的 Series:")
print(s_from_list)# 创建时指定索引
index_labels = ['a', 'b', 'c', 'd', 'e']
s_with_labels = pd.Series(data_list, index=index_labels)
print("\n带指定标签的 Series:")
print(s_with_labels)

代码解释:

  • pd.Series(data): data 可以是列表、NumPy 数组等。
  • index: 可选参数,用于指定 Series 的索引标签。若不指定,则默认为从0开始的整数索引。
(2) 从 NumPy 数组创建 Series
# 从 NumPy 数组创建 Series
data_numpy = np.array([5.0, 6.0, 7.0, 8.0])
s_from_numpy = pd.Series(data_numpy, index=['w', 'x', 'y', 'z'])
print("\n从 NumPy 数组创建的 Series:")
print(s_from_numpy)
(3) 从字典创建 Series

当从字典创建 Series 时,字典的键(keys)会自动成为 Series 的索引,字典的值(values)则成为 Series 的数据。

# 从字典创建 Series
data_dict = {'apple': 3, 'banana': 5, 'cherry': 2, 'date': 7}
s_from_dict = pd.Series(data_dict)
print("\n从字典创建的 Series:")
print(s_from_dict)# 如果同时传递了 index 参数,则会按照 index 中的标签进行选取和排序
s_from_dict_indexed = pd.Series(data_dict, index=['banana', 'apple', 'orange', 'cherry'])
print("\n从字典创建并指定索引的 Series (orange 会是 NaN):")
print(s_from_dict_indexed) # 注意:'orange' 在 data_dict 中不存在,其值为 NaN

关键点: 如果字典的键和指定的 index 不完全匹配,Pandas 会根据 index 来构建 Series,缺失的键对应的值会是 NaN (Not a Number),表示缺失数据。

1.1.2 Series 的属性与方法

Series 对象拥有许多有用的属性和方法,帮助我们理解和操作数据。

(1) 常用属性
  • index: 获取 Series 的索引。
  • values: 获取 Series 的数据,以 NumPy 数组形式返回。
  • dtype: 获取 Series 中数据的数据类型。
  • name: 获取或设置 Series 的名称。
  • size: 获取 Series 中元素的数量。
  • shape: 获取 Series 的形状(元组形式,对于 Series 总是 (n,))。
  • ndim: 获取 Series 的维度数量(总是 1)。
s = pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'], name='MyNumbers')
print(f"\nSeries 名称: {s.name}")
print(f"Series 索引: {s.index}")
print(f"Series 值 (NumPy 数组): {s.values}")
print(f"Series 数据类型: {s.dtype}")
print(f"Series 大小: {s.size}")
print(f"Series 形状: {s.shape}")
print(f"Series 维度: {s.ndim}")
(2) 常用方法
  • head(n): 查看前 n 个元素,默认为 5。
  • tail(n): 查看后 n 个元素,默认为 5。
  • describe(): 生成描述性统计信息(计数、均值、标准差、最小值、最大值、分位数等)。
  • isnull() / notnull(): 检测缺失值,返回布尔型 Series
  • mean(), sum(), min(), max(), std(): 计算统计量。
  • value_counts(): 计算每个唯一值的出现次数。
  • apply(func): 将函数 func 应用于 Series 的每个元素。
  • astype(dtype): 转换 Series 的数据类型。
s_data = pd.Series([10, 20, 10, 30, 20, 20, np.nan, 40])
print("\n示例 Series:")
print(s_data)
print("\n前3个元素 (head(3)):")
print(s_data.head(3))
print("\n描述性统计 (describe()):")
print(s_data.describe())
print("\n是否为空值 (isnull()):")
print(s_data.isnull())
print("\n值计数 (value_counts()):")
print(s_data.value_counts()) # NaN 不会被计算在内
print("\n值计数 (包括 NaN, value_counts(dropna=False)):")
print(s_data.value_counts(dropna=False))
print(f"\n平均值: {s_data.mean()}") # NaN 会被忽略

1.1.3 Series 的索引与切片

Series 的索引方式非常灵活,既可以使用标签索引,也可以使用位置索引(类似 Python 列表)。

(1) 标签索引

使用中括号 [] 配合索引标签。

s = pd.Series([10, 20, 30, 40], index=['a', 'b', 'c', 'd'])
print(f"\n通过标签 'b' 获取元素: {s['b']}") # 输出 20
# 获取多个元素
print("\n通过标签列表 ['a', 'c'] 获取元素:")
print(s[['a', 'c']])
(2) 位置索引

使用中括号 [] 配合整数位置。

s = pd.Series([10, 20, 30, 40], index=['a', 'b', 'c', 'd'])
print(f"\n通过位置 0 获取元素: {s[0]}") # 输出 10
# 获取多个元素(注意:这里使用整数列表作为索引器)
print("\n通过位置列表 [0, 2] 获取元素:")
print(s[[0, 2]])
(3) 切片

切片操作也支持标签和位置。

  • 标签切片: 包含结束标签。
  • 位置切片: 不包含结束位置(与 Python 列表切片行为一致)。
s = pd.Series([10, 20, 30, 40, 50], index=['a', 'b', 'c', 'd', 'e'])
print("\n标签切片 'b':'d':")
print(s['b':'d']) # 包含 'b', 'c', 'd'print("\n位置切片 1:4:")
print(s[1:4]) # 包含位置 1, 2, 3 (对应标签 'b', 'c', 'd')

注意: 当索引是整数时,直接使用 s[整数] 进行切片可能会产生歧义(是标签索引还是位置索引?)。Pandas 推荐使用 .loc[] (基于标签) 和 .iloc[] (基于位置) 来进行明确的索引,这在 DataFrame 中更为常用和重要。

1.2 DataFrame 数据结构

DataFrame 是 Pandas 中最核心的数据结构,它是一个二维的、表格型的数据结构,类似于电子表格、SQL 表或字典对象。DataFrame 既有行索引也有列索引,每列可以包含不同的数据类型。

可以将 DataFrame 理解为共享相同索引的 Series 的集合(或字典)。

1.2.1 DataFrame 的创建

创建 DataFrame 的方式同样很多,常见的方法包括:

(1) 从字典创建 DataFrame

最常用的方式之一是用一个字典来创建 DataFrame,其中字典的键将成为列名,字典的值(通常是列表、NumPy 数组或 Series)将成为列的数据。所有传入的列表/数组长度必须一致。

# 字典的键是列名,值是该列的数据
data_dict_df = {'Name': ['Alice', 'Bob', 'Charlie', 'David'],'Age': [25, 30, 35, 28],'City': ['New York', 'Paris', 'London', 'Tokyo']
}
df_from_dict = pd.DataFrame(data_dict_df)
print("从字典创建的 DataFrame:")
print(df_from_dict)# 创建时指定行索引
df_from_dict_indexed = pd.DataFrame(data_dict_df, index=['id1', 'id2', 'id3', 'id4'])
print("\n从字典创建并指定行索引的 DataFrame:")
print(df_from_dict_indexed)
(2) 从列表的列表(或 NumPy 二维数组)创建 DataFrame

可以提供一个嵌套列表或 NumPy 二维数组作为数据,并可选地指定列名和行索引。

data_list_of_lists = [['Alice', 25, 'New York'],['Bob', 30, 'Paris'],['Charlie', 35, 'London']
]
df_from_lol = pd.DataFrame(data_list_of_lists, columns=['Name', 'Age', 'City'], index=['p1', 'p2', 'p3'])
print("\n从列表的列表创建的 DataFrame:")
print(df_from_lol)data_numpy_2d = np.array([[1.1, 1.2, 1.3],[2.1, 2.2, 2.3],[3.1, 3.2, 3.3]
])
df_from_numpy_2d = pd.DataFrame(data_numpy_2d, columns=['A', 'B', 'C'])
print("\n从 NumPy 二维数组创建的 DataFrame:")
print(df_from_numpy_2d)
(3) 从 Series 列表/字典创建 DataFrame
s1 = pd.Series([1, 2, 3], name='Col1')
s2 = pd.Series([4, 5, 6], name='Col2')
# 使用字典,键是列名
df_from_series_dict = pd.DataFrame({'A': s1, 'B': s2})
print("\n从 Series 字典创建的 DataFrame:")
print(df_from_series_dict)

1.2.2 DataFrame 的属性与方法

Series 类似,DataFrame 也有一系列属性和方法。

(1) 常用属性
  • index: 获取行索引。
  • columns: 获取列索引(列名)。
  • dtypes: 获取每列的数据类型(返回一个 Series)。
  • values: 获取 DataFrame 的数据,以 NumPy 二维数组形式返回。
  • shape: 获取 DataFrame 的形状(行数, 列数)。
  • size: 获取 DataFrame 中元素的总数(行数 * 列数)。
  • ndim: 获取 DataFrame 的维度数量(总是 2)。
  • T: 转置 DataFrame(行和列互换)。
df = pd.DataFrame({'A': [1, 2, 3],'B': [4.0, 5.5, 6.1],'C': ['x', 'y', 'z']
})
print("\n示例 DataFrame:")
print(df)
print(f"行索引: {df.index}")
print(f"列索引: {df.columns}")
print(f"每列数据类型:\n{df.dtypes}")
print(f"数据 (NumPy 数组):\n{df.values}")
print(f"形状: {df.shape}")
print(f"大小: {df.size}")
print(f"维度: {df.ndim}")
print(f"转置:\n{df.T}")
(2) 常用方法

许多 Series 的方法也适用于 DataFrame,通常会按列进行操作或返回一个 DataFrame

  • head(n), tail(n): 查看头部/尾部数据。
  • describe(): 生成描述性统计(默认只对数值列)。
  • info(): 提供 DataFrame 的简洁摘要,包括索引类型、列类型、非空值数量和内存使用情况。
  • isnull(), notnull(): 检测缺失值,返回布尔型 DataFrame
  • sum(), mean(), min(), max(), std(): 默认按列计算统计量(可通过 axis=1 按行计算)。
  • apply(func, axis=0): 将函数 func 应用于每列 (axis=0) 或每行 (axis=1)。
  • astype(dtype_dict): 转换列的数据类型,dtype_dict 是一个列名到目标类型的映射。
  • sort_values(by, ascending=True): 按指定列的值排序。
  • sort_index(axis=0, ascending=True): 按索引排序。
  • drop(labels, axis=0): 删除指定的行 (axis=0) 或列 (axis=1)。
df_example = pd.DataFrame({'col1': [1, 2, np.nan, 4, 2],'col2': [5.0, 6.1, 7.3, 5.0, 6.1],'col3': ['a', 'b', 'a', 'c', 'b']
})
print("\n示例 DataFrame for methods:")
print(df_example)
print("\nDataFrame 信息 (info()):")
df_example.info()
print("\n描述性统计 (describe()):")
print(df_example.describe())
print("\n按 col3 排序:")
print(df_example.sort_values(by='col3'))
print("\n删除 col1 (drop('col1', axis=1)):")
print(df_example.drop('col1', axis=1)) # 注意:drop 默认不修改原 DataFrame,除非 inplace=True

1.2.3 DataFrame 的索引与切片

DataFrame 的索引比 Series 更为复杂,因为它涉及行和列。

(1) 选择列
  • 使用中括号 [] 加列名: df['column_name'] (返回一个 Series)。
  • 使用点号 . 加列名: df.column_name (仅当列名是有效的 Python 标识符且不与 DataFrame 方法名冲突时可用,返回一个 Series)。
  • 使用中括号 [] 加列名列表: df[['col1', 'col2']] (返回一个新的 DataFrame)。
df = pd.DataFrame({'Name': ['Alice', 'Bob', 'Charlie'],'Age': [25, 30, 35],'Score': [88, 92, 78]
})
print("\n选择 'Age' 列 (Series):")
print(df['Age'])
print("\n选择 'Name' 和 'Score' 列 (DataFrame):")
print(df[['Name', 'Score']])
(2) 选择行:.loc[].iloc[]

为了避免混淆并进行精确的行选择(以及行列组合选择),Pandas 强烈推荐使用 .loc[].iloc[]

  • .loc[] (Label-based selection): 基于标签(行索引名和列索引名)进行选择。

    • df.loc[row_label]:选择单行 (返回 Series)。
    • df.loc[[row_label1, row_label2]]:选择多行 (返回 DataFrame)。
    • df.loc[row_label_slice]:行标签切片 (包含结束标签)。
    • df.loc[row_selection, column_selection]:同时选择行和列。
  • .iloc[] (Integer-location based selection): 基于整数位置进行选择。

    • df.iloc[row_position]:选择单行 (返回 Series)。
    • df.iloc[[row_pos1, row_pos2]]:选择多行 (返回 DataFrame)。
    • df.iloc[row_pos_slice]:行位置切片 (不包含结束位置)。
    • df.iloc[row_pos_selection, col_pos_selection]:同时选择行和列。
df_indexed = pd.DataFrame({'A': [10, 20, 30, 40],'B': [50, 60, 70, 80],'C': [90, 100, 110, 120]
}, index=['r1', 'r2', 'r3', 'r4'])print("\n使用 .loc[] 选择行 'r2':")
print(df_indexed.loc['r2'])print("\n使用 .loc[] 选择行 'r1' 到 'r3':")
print(df_indexed.loc['r1':'r3']) # 包含 'r3'print("\n使用 .loc[] 选择 'r2' 行的 'A' 和 'C' 列:")
print(df_indexed.loc['r2', ['A', 'C']])print("\n使用 .iloc[] 选择第 0 行 (位置):")
print(df_indexed.iloc[0])print("\n使用 .iloc[] 选择第 1 到 3 行 (位置, 不含3):")
print(df_indexed.iloc[1:3])print("\n使用 .iloc[] 选择第 0 行的第 0 和第 2 列 (位置):")
print(df_indexed.iloc[0, [0, 2]])print("\n使用 .iloc[] 选择所有行的第 1 列 (位置):")
print(df_indexed.iloc[:, 1])

场景驱动: .loc[] 非常适合当你知道行或列的名称时使用,而 .iloc[] 则在你需要基于它们的数字位置来选择数据时非常有用,例如在循环中。

二、数据读取与写入

Pandas 提供了非常方便的函数来读取和写入多种格式的数据文件,其中最常用的是 CSV 文件。

2.1 读取 CSV 文件

逗号分隔值(CSV)文件是一种非常常见的纯文本数据存储格式。Pandas 的 read_csv() 函数可以轻松地将 CSV 文件加载到 DataFrame 中。

2.1.1 基本读取

假设我们有一个名为 data.csv 的文件,内容如下:

Name,Age,City
Alice,25,New York
Bob,30,Paris
Charlie,,London
David,28,Tokyo
Eve,22,

我们可以这样读取它:

# 为了演示,我们先用代码创建一个示例 CSV 文件
csv_content = """Name,Age,City
Alice,25,New York
Bob,30,Paris
Charlie,,London
David,28,Tokyo
Eve,22,""" # Eve 的 City 是空字符串with open('example_data.csv', 'w') as f:f.write(csv_content)# 基本读取
df_csv = pd.read_csv('example_data.csv')
print("从 CSV 文件读取的 DataFrame:")
print(df_csv)

观察: 注意 CharlieAgeEveCity 在 DataFrame 中显示为 NaN(如果CSV中是空字段)或空字符串(如果CSV中是 "")。read_csv 会尝试智能地推断数据类型。

2.1.2 常用参数

pd.read_csv() 函数有很多可选参数,可以帮助我们更精确地控制读取过程。

  • filepath_or_buffer: 文件路径、URL 或任何带有 read() 方法的对象。
  • sep (或 delimiter): 字段间的分隔符,默认为 ,。如果文件使用其他分隔符(如制表符 \t、分号 ;),需要指定。
  • header: 指定哪一行作为列名。默认为 0(第一行)。如果文件没有列名,可以设置为 None,Pandas 会自动生成数字列名。
  • index_col: 指定某一列或多列作为行索引。
  • names: 当 header=None 时,用于提供列名列表。
  • usecols: 一个列表或可调用对象,用于指定要读取的列。例如 ['Name', 'City'] 只读取这两列。
  • dtype: 一个字典,用于指定某些列的数据类型,例如 {'Age': int, 'City': str}。这在自动类型推断不准确时很有用。
  • na_values: 一个标量、字符串、列表或字典,指定哪些值应被视作 NaN(缺失值)。
  • skiprows: 一个整数或列表,表示跳过文件顶部的指定行数或特定行号。
  • nrows: 只读取文件的前 n 行,对于快速检查大文件非常有用。
# 示例:使用不同参数读取
# 假设有一个 tab 分隔的文件 (data.tsv) 且没有 header
tsv_content = """Alice\t25\tNew York
Bob\t30\tParis"""
with open('example_data.tsv', 'w') as f:f.write(tsv_content)df_tsv_no_header = pd.read_csv('example_data.tsv', sep='\t', header=None, names=['PersonName', 'Years', 'Location'])
print("\n读取TSV文件,无表头,自定义列名:")
print(df_tsv_no_header)# 读取 example_data.csv,并将 'Name' 列作为行索引
df_csv_indexed = pd.read_csv('example_data.csv', index_col='Name')
print("\n读取CSV,并将 'Name' 列作为行索引:")
print(df_csv_indexed)# 读取 example_data.csv,只读取 'Name' 和 'City' 列
df_csv_usecols = pd.read_csv('example_data.csv', usecols=['Name', 'City'])
print("\n读取CSV,只选择 'Name' 和 'City' 列:")
print(df_csv_usecols)# 读取 example_data.csv,并将空字符串视为空值
df_csv_na_values = pd.read_csv('example_data.csv', na_values=['']) # 将 Charlie 的 Age 识别为 NaN
print("\n读取CSV,并将空字符串视为空值:")
print(df_csv_na_values)

2.2 写入 CSV 文件

DataFrame 保存到 CSV 文件同样简单,使用 to_csv() 方法。

df_to_save = pd.DataFrame({'Product': ['Apple', 'Banana', 'Orange'],'Price': [1.0, 0.5, 0.75],'Quantity': [100, 150, 200]
})# 基本写入
df_to_save.to_csv('output_data.csv')
print("\nDataFrame 已保存到 output_data.csv (包含行索引和表头)")# 不保存行索引
df_to_save.to_csv('output_data_no_index.csv', index=False)
print("DataFrame 已保存到 output_data_no_index.csv (不包含行索引)")# 使用不同的分隔符,并指定编码
df_to_save.to_csv('output_data_custom.tsv', sep='\t', encoding='utf-8', index=False)
print("DataFrame 已保存到 output_data_custom.tsv (制表符分隔,UTF-8编码,无索引)")

常用参数:

  • path_or_buf: 文件路径或类文件对象。
  • sep: 分隔符,默认为 ,
  • na_rep: 用于替换缺失值的字符串,默认为空字符串。
  • columns: 要写入的列的序列,默认为所有列。
  • header: 是否写入列名,默认为 True
  • index: 是否写入行索引,默认为 True
  • mode: 写入模式,'w' 为覆盖,'a' 为追加。
  • encoding: 文件编码。

2.3 其他数据格式

Pandas 不仅仅能处理 CSV 文件,它还支持多种其他数据格式的读写:

  • Excel 文件: pd.read_excel()df.to_excel() (通常需要 openpyxlxlrd 库)。
  • SQL 数据库: pd.read_sql_query(), pd.read_sql_table()df.to_sql() (需要 SQLAlchemy 等库)。
  • JSON 文件: pd.read_json()df.to_json()
  • HTML 表格: pd.read_html() (可以读取网页中的表格)。
  • HDF5 文件: pd.read_hdf()df.to_hdf() (高效存储大型数据集)。
  • Parquet 文件: pd.read_parquet()df.to_parquet() (列式存储,高效且压缩好)。
# 示例:写入和读取 Excel (需要安装 openpyxl: pip install openpyxl)
# df_to_save.to_excel('output_data.xlsx', sheet_name='Sheet1', index=False)
# df_excel_read = pd.read_excel('output_data.xlsx', sheet_name='Sheet1')
# print("\n从 Excel 文件读取的 DataFrame:")
# print(df_excel_read)

实践建议: 根据你的数据来源和存储需求,选择合适的读写函数。对于大型数据集,Parquet 或 HDF5 通常是更好的选择。

三、数据选择与过滤

DataFrame 中准确地选择和过滤出你需要的数据子集是数据分析中的核心操作。Pandas 提供了多种强大的方式来实现这一点。

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

如前所述,.loc[] 主要用于基于行和列的标签(名称)进行选择。

  • 选择单行: df.loc[row_label]
  • 选择多行: df.loc[[row_label1, row_label2]]
  • 选择行切片: df.loc[start_label:end_label] (包含 end_label)
  • 选择特定行和列: df.loc[row_selection, column_selection]
  • 选择所有行和特定列: df.loc[:, column_selection]
  • 选择特定行和所有列: df.loc[row_selection, :] (或者直接 df.loc[row_selection])
data = {'col1': [1, 2, 3, 4], 'col2': [5, 6, 7, 8], 'col3': [9, 10, 11, 12]}
df = pd.DataFrame(data, index=['a', 'b', 'c', 'd'])
print("\n示例 DataFrame for selection:")
print(df)# 选择 'b' 行
print("\n.loc['b']:")
print(df.loc['b'])# 选择 'a' 和 'd' 行
print("\n.loc[['a', 'd']]:")
print(df.loc[['a', 'd']])# 选择从 'b' 到 'd' 的行
print("\n.loc['b':'d']:")
print(df.loc['b':'d'])# 选择 'c' 行的 'col2' 列
print("\n.loc['c', 'col2']:")
print(df.loc['c', 'col2']) # 返回一个标量# 选择 'a' 和 'c' 行的 'col1' 和 'col3' 列
print("\n.loc[['a', 'c'], ['col1', 'col3']]:")
print(df.loc[['a', 'c'], ['col1', 'col3']])# 选择所有行的 'col2' 列
print("\n.loc[:, 'col2']:")
print(df.loc[:, 'col2']) # 返回一个 Series

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

.iloc[] 用于基于整数位置(从0开始)进行选择。

  • 选择单行: df.iloc[row_position]
  • 选择多行: df.iloc[[row_pos1, row_pos2]]
  • 选择行切片: df.iloc[start_pos:end_pos] (不包含 end_pos)
  • 选择特定行和列: df.iloc[row_pos_selection, col_pos_selection]
  • 选择所有行和特定列: df.iloc[:, col_pos_selection]
  • 选择特定行和所有列: df.iloc[row_pos_selection, :] (或者直接 df.iloc[row_pos_selection])
print("\n示例 DataFrame (same as above):")
print(df)# 选择位置为 1 的行 (即第二行,索引 'b')
print("\n.iloc[1]:")
print(df.iloc[1])# 选择位置为 0 和 2 的行
print("\n.iloc[[0, 2]]:")
print(df.iloc[[0, 2]])# 选择位置从 1 到 3 的行 (不包括位置3)
print("\n.iloc[1:3]:")
print(df.iloc[1:3]) # 行 'b' 和 'c'# 选择位置为 2 的行的位置为 1 的列 (即 'c' 行的 'col2' 列)
print("\n.iloc[2, 1]:")
print(df.iloc[2, 1]) # 返回标量 11# 选择位置为 0 和 3 的行的位置为 0 和 2 的列
print("\n.iloc[[0, 3], [0, 2]]:")
print(df.iloc[[0, 3], [0, 2]])# 选择所有行的位置为 1 的列
print("\n.iloc[:, 1]:")
print(df.iloc[:, 1]) # 返回 Series col2

重要提示: 同时使用标签和位置进行混合索引是不推荐的,容易出错。坚持使用 .loc[] 进行纯标签索引,使用 .iloc[] 进行纯位置索引。

3.3 条件选择

这是数据分析中最常用的功能之一:根据一个或多个条件来筛选数据。这通常会返回一个布尔型 Series,然后用这个布尔 Series 来索引 DataFrame

data_people = {'Name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],'Age': [25, 30, 22, 30, 28],'Score': [88, 92, 75, 92, 85]
}
df_people = pd.DataFrame(data_people)
print("\n人员数据 DataFrame:")
print(df_people)# 条件:年龄大于 25
condition_age_gt_25 = df_people['Age'] > 25
print("\n年龄大于 25 的布尔 Series:")
print(condition_age_gt_25)# 应用条件选择
df_age_gt_25 = df_people[condition_age_gt_25]
print("\n年龄大于 25 的人员:")
print(df_age_gt_25)# 直接在索引中写条件
df_score_lt_90 = df_people[df_people['Score'] < 90]
print("\n分数小于 90 的人员:")
print(df_score_lt_90)

原理透彻: 当你写 df[boolean_series] 时,Pandas 会取出 boolean_series 中为 True 的那些行对应的原始 DataFrame 中的数据。

3.4 组合条件

你可以使用逻辑运算符 & (与, AND)、| (或, OR) 和 ~ (非, NOT) 来组合多个条件。非常重要的是,当组合多个条件时,每个条件都应该用括号 () 括起来,因为这些逻辑运算符的优先级问题。

print("\n人员数据 DataFrame (same as above):")
print(df_people)# 条件1: Age >= 28
cond1 = df_people['Age'] >= 28
# 条件2: Score > 90
cond2 = df_people['Score'] > 90# 组合条件:年龄大于等于28 且 分数大于90
df_combined_and = df_people[(cond1) & (cond2)] # 注意括号
print("\n年龄 >= 28 且 分数 > 90 的人员:")
print(df_combined_and)# 组合条件:年龄小于25 或 分数等于92
df_combined_or = df_people[(df_people['Age'] < 25) | (df_people['Score'] == 92)]
print("\n年龄 < 25 或 分数 == 92 的人员:")
print(df_combined_or)# 条件:Name 不是 'Alice'
df_not_alice = df_people[~(df_people['Name'] == 'Alice')]
print("\n不是 Alice 的人员:")
print(df_not_alice)

常见问题:

  • 忘记给每个条件加括号,例如 df_people[df_people['Age'] >= 28 & df_people['Score'] > 90] 会因为运算符优先级导致错误。正确写法是 df_people[(df_people['Age'] >= 28) & (df_people['Score'] > 90)]
  • 使用 Python 的 and, or, not 关键字代替 &, |, ~。在 Pandas 条件表达式中,必须使用位运算符。

四、基本的数据清洗和转换操作

真实世界的数据往往是不完美的:可能包含缺失值、重复值,或者数据类型不正确。Pandas 提供了强大的工具来进行数据清洗和转换,这是数据预处理的关键步骤。

4.1 处理缺失值

缺失值(通常表示为 NaN, NoneNaT (for datetime))在数据集中很常见。

4.1.1 检测缺失值

  • isnull(): 返回一个布尔型的 DataFrameSeriesTrue 表示对应位置是缺失值。
  • notnull(): 与 isnull() 相反。
  • any(axis=...): 在指定轴上检查是否有任何 True
  • all(axis=...): 在指定轴上检查是否所有都为 True
data_missing = {'A': [1, 2, np.nan, 4, 5],'B': [np.nan, 7, 8, np.nan, 10],'C': [11, 12, 13, 14, np.nan],'D': [16, 17, 18, 19, 20] # 无缺失值
}
df_missing = pd.DataFrame(data_missing)
print("\n包含缺失值的 DataFrame:")
print(df_missing)print("\n检测缺失值 (isnull()):")
print(df_missing.isnull())print("\n每列是否有缺失值 (isnull().any()):")
print(df_missing.isnull().any()) # 默认 axis=0,即按列print("\n每行是否有缺失值 (isnull().any(axis=1)):")
print(df_missing.isnull().any(axis=1))print("\n缺失值总数 (isnull().sum().sum()):")
print(df_missing.isnull().sum().sum())print("\n每列的缺失值数量 (isnull().sum()):")
print(df_missing.isnull().sum())

4.1.2 填充缺失值

使用 fillna() 方法可以用指定的值或策略来填充 NaN

  • df.fillna(value): 用标量 value 填充所有 NaN
  • df.fillna(method='ffill'): 前向填充(用前一个非缺失值填充)。
  • df.fillna(method='bfill'): 后向填充(用后一个非缺失值填充)。
  • 可以为不同列指定不同的填充值:df.fillna({'A': 0, 'B': df['B'].mean()})
  • inplace=True: 修改原始 DataFrame
print("\n用 0 填充所有缺失值:")
print(df_missing.fillna(0))print("\n用每列的平均值填充 (只对数值列B有效,A因为有NaN无法直接求mean, C不能求mean):")
# 注意: Series.mean() 会自动忽略 NaN
df_filled_mean = df_missing.copy() # 创建副本以避免修改原始 df_missing
df_filled_mean['A'] = df_filled_mean['A'].fillna(df_filled_mean['A'].mean())
df_filled_mean['B'] = df_filled_mean['B'].fillna(df_filled_mean['B'].mean())
df_filled_mean['C'] = df_filled_mean['C'].fillna('Unknown') # 假设 C 是类别,用特定字符串填充
print(df_filled_mean)print("\n前向填充 (ffill):")
print(df_missing.fillna(method='ffill'))print("\n后向填充 (bfill):")
print(df_missing.fillna(method='bfill'))

场景驱动: 填充策略的选择取决于数据的特性和分析的目的。例如,时间序列数据常用 ffill。对于数值特征,可以用均值、中位数填充。对于类别特征,可以用众数或一个特定的占位符填充。

4.1.3 删除缺失值

使用 dropna() 方法可以删除包含缺失值的行或列。

  • df.dropna()df.dropna(axis=0): 删除任何包含 NaN 的行 (默认行为)。
  • df.dropna(axis=1): 删除任何包含 NaN 的列。
  • how='all': 只删除所有值都为 NaN 的行/列。默认 how='any' (只要有一个 NaN 就删除)。
  • thresh=N: 保留至少有 N 个非 NaN 值的行/列。
  • subset=['col1', 'col2']: 只在指定的列子集中检查 NaN 来决定是否删除行。
  • inplace=True: 修改原始 DataFrame
print("\n删除任何包含 NaN 的行:")
print(df_missing.dropna())print("\n删除任何包含 NaN 的列 (列D会被保留):")
print(df_missing.dropna(axis=1))print("\n删除所有值都为 NaN 的行 (本例中无此类行):")
df_all_nan_row = df_missing.append(pd.Series([np.nan, np.nan, np.nan, np.nan], index=df_missing.columns, name='new_row')) # 添加一行全是NaN
print(df_all_nan_row.dropna(how='all'))print("\n只在 'A' 和 'B' 列检查 NaN,并删除对应行:")
print(df_missing.dropna(subset=['A', 'B'])) # 只要A或B是NaN,该行就被删print("\n保留至少有3个非NaN值的行:")
print(df_missing.dropna(thresh=3))

实用建议: 删除数据是一种简单粗暴的方法,可能会丢失有价值的信息。除非缺失比例非常高,或者特定分析不容许缺失值,否则优先考虑填充。

4.2 处理重复值

数据集中可能存在完全重复的行。

4.2.1 检测重复值

duplicated() 方法返回一个布尔型 Series,标记出重复的行。默认情况下,第一次出现的行不被标记为重复。

  • df.duplicated(): 标记除了第一次出现之外的重复行。
  • df.duplicated(keep='last'): 标记除了最后一次出现之外的重复行。
  • df.duplicated(keep=False): 标记所有重复的行。
  • subset=['col1', 'col2']: 只考虑指定列的组合来判断是否重复。
data_duplicates = {'ID': [1, 2, 1, 3, 2, 4],'Name': ['A', 'B', 'A', 'C', 'B', 'D'],'Value': [100, 200, 100, 300, 200, 400]
}
df_duplicates = pd.DataFrame(data_duplicates)
print("\n包含重复值的 DataFrame:")
print(df_duplicates)print("\n检测重复行 (默认 keep='first'):")
print(df_duplicates.duplicated()) # 行索引2和4 (0-based) 是重复的print("\n检测重复行 (keep='last'):")
print(df_duplicates.duplicated(keep='last')) # 行索引0和1 是重复的print("\n检测所有重复的行 (keep=False):")
print(df_duplicates.duplicated(keep=False))print("\n基于 'ID' 列检测重复:")
print(df_duplicates.duplicated(subset=['ID']))

4.2.2 删除重复值

drop_duplicates() 方法用于删除重复行。

  • df.drop_duplicates(): 删除重复行,保留第一次出现的 (默认 keep='first')。
  • df.drop_duplicates(keep='last'): 删除重复行,保留最后一次出现的。
  • df.drop_duplicates(keep=False): 删除所有重复的行。
  • subset=['col1', 'col2']: 基于指定列删除重复。
  • inplace=True: 修改原始 DataFrame
print("\n删除重复行 (保留第一个):")
print(df_duplicates.drop_duplicates())print("\n基于 'ID' 列删除重复行 (保留第一个):")
print(df_duplicates.drop_duplicates(subset=['ID']))print("\n删除 'ID' 和 'Name' 都重复的行,保留最后一个,并原地修改:")
df_temp = df_duplicates.copy()
df_temp.drop_duplicates(subset=['ID', 'Name'], keep='last', inplace=True)
print(df_temp)

4.3 数据类型转换

有时,数据被加载后类型不正确(例如,数字被当作字符串)。astype() 方法用于转换 DataFrameSeries 的数据类型。

data_types = {'ProductID': [1, 2, 3],'Price': ['10.5', '20.0', '5.75'], # Price 是字符串'Quantity': [100, '50', 120]      # Quantity 部分是字符串
}
df_types = pd.DataFrame(data_types)
print("\n原始数据类型 DataFrame:")
print(df_types)
print(df_types.dtypes)# 转换 'Price' 为 float
df_types['Price'] = df_types['Price'].astype(float)# 转换 'Quantity' 为 int (如果包含不能转为int的,如 '50.0' 或非数字字符串,会报错)
# 为了安全,可以先处理可能存在的非整数字符串
df_types['Quantity'] = pd.to_numeric(df_types['Quantity'], errors='coerce').fillna(0).astype(int) # errors='coerce' 会将无法转换的变为NaNprint("\n转换后数据类型 DataFrame:")
print(df_types)
print(df_types.dtypes)# 也可以使用字典一次转换多列
# df_types = df_types.astype({'Price': float, 'Quantity': int})

注意: 如果转换不兼容(例如,将包含字母的字符串转换为 int),astype() 会引发错误。使用 pd.to_numeric() 配合 errors='coerce' 参数可以在转换失败时将其设为 NaN,之后再进行处理。

4.4 应用函数

Pandas 允许你对数据应用自定义函数或已有的 Python 函数。

  • Series.apply(func)Series.map(func_or_dict): 对 Series 的每个元素应用函数。map 也可以接受字典或 Series 来进行元素映射。
  • DataFrame.apply(func, axis=0): 将函数 func 应用于 DataFrame 的每列 (axis=0,默认) 或每行 (axis=1)。传递给 func 的是 Series 对象。
  • DataFrame.applymap(func): 将函数 func 应用于 DataFrame 的每个元素。
df_apply = pd.DataFrame({'A': [1, 2, 3, 4],'B': [10, 20, 30, 40],'C': ['foo', 'bar', 'baz', 'qux']
})
print("\n示例 DataFrame for apply/map:")
print(df_apply)# Series.apply
print("\n对 'A' 列每个元素平方:")
print(df_apply['A'].apply(lambda x: x**2))# Series.map (常用于基于字典的替换)
category_map = {'foo': 1, 'bar': 2, 'baz': 3, 'qux': 4}
print("\n用字典映射 'C' 列:")
print(df_apply['C'].map(category_map))# DataFrame.apply (对每列求和)
print("\n对 DataFrame 每列求和 (axis=0):")
print(df_apply[['A', 'B']].apply(sum, axis=0)) # 或者 df_apply[['A','B']].sum()# DataFrame.apply (对每行计算 A+B)
print("\n对 DataFrame 每行计算 A+B (axis=1):")
print(df_apply.apply(lambda row: row['A'] + row['B'], axis=1))# DataFrame.applymap (对所有数值元素 +5,非数值元素会报错,需小心使用)
df_numeric = df_apply[['A', 'B']]
print("\n对 DataFrame 所有数值元素 +5 (applymap):")
print(df_numeric.applymap(lambda x: x + 5))

场景驱动:

  • 当需要对 Series 的每个值进行转换时,applymap 非常有用。
  • 当需要对 DataFrame 的行或列进行聚合或复杂转换时,apply 是强大的工具。
  • applymap 适用于对 DataFrame 中所有元素执行相同的元素级操作。

五、实践:加载并简单探索一个表格数据集

理论学习后,实践是巩固知识的最佳方式。让我们加载一个简单的数据集并进行初步探索。

5.1 数据集介绍

我们将创建一个简单的学生分数 CSV 文件 students_scores.csv

StudentID,Name,Math,Science,English,History
S001,Alice,85,92,78,88
S002,Bob,90,88,92,85
S003,Charlie,72,65,80,70
S004,David,95,98,90,92
S005,Eve,60,70,65,75
S006,Frank,88,np.nan,85,90
S007,Grace,78,82,79,
S008,Alice,85,92,78,88

注意:

  • FrankScience 成绩缺失。
  • GraceHistory 成绩为空(将被 read_csv 读为 NaN)。
  • Alice 有两条重复记录。

5.2 加载数据

首先,创建这个 CSV 文件,然后用 Pandas 加载它。

# 创建示例 CSV 文件
students_csv_content = """StudentID,Name,Math,Science,English,History
S001,Alice,85,92,78,88
S002,Bob,90,88,92,85
S003,Charlie,72,65,80,70
S004,David,95,98,90,92
S005,Eve,60,70,65,75
S006,Frank,88,,85,90
S007,Grace,78,82,79,
S008,Alice,85,92,78,88
"""
with open('students_scores.csv', 'w') as f:f.write(students_csv_content)# 加载数据
students_df = pd.read_csv('students_scores.csv')
print("加载的学生分数 DataFrame:")
print(students_df)

5.3 查看数据基本信息

加载数据后,通常第一步是了解数据的概况。

print("\n--- 数据基本信息 ---")
# 查看前几行
print("\n前3行 (head(3)):")
print(students_df.head(3))# 查看后几行
print("\n后2行 (tail(2)):")
print(students_df.tail(2))# 查看 DataFrame 的简要信息
print("\nDataFrame 信息 (info()):")
students_df.info()
# 从 info() 输出可以看到:
# - 行数和列数
# - 每列的非空值数量 (可以发现 Science 和 History 有缺失)
# - 每列的数据类型 (Science 和 History 因为有空值可能被读为 float)# 查看数值列的描述性统计
print("\n数值列描述性统计 (describe()):")
print(students_df.describe())
# describe() 会给出 count, mean, std, min, 25%, 50%, 75%, max
# 对于非数值列,可以指定 include='object' 或 include='all'
print("\n所有列描述性统计 (describe(include='all')):")
print(students_df.describe(include='all'))
# 对于对象类型 (object) 或类别类型 (category) 的列,会给出 count, unique, top, freq

5.4 初步探索与清洗

接下来,进行一些初步的数据探索和简单的清洗。

(1) 处理缺失值
print("\n--- 处理缺失值 ---")
print("\n每列的缺失值数量:")
print(students_df.isnull().sum())# 假设我们决定用0填充 Science 的缺失值,用该科目的平均分填充 History 的缺失值
science_mean = students_df['Science'].mean()
print(f"\nScience 平均分 (忽略NaN): {science_mean}")
students_df['Science'].fillna(0, inplace=True) # 用0填充 Frank 的 Science
students_df['History'].fillna(students_df['History'].mean(), inplace=True) # 用平均分填充 Grace 的 Historyprint("\n填充缺失值后的 DataFrame (info()):")
students_df.info() # 再次查看,确认没有缺失值了
print("\n填充缺失值后的前几行:")
print(students_df.head())
(2) 处理重复值
print("\n--- 处理重复值 ---")
print("\n检测重复行:")
print(students_df.duplicated()) # 应该能看到最后一行 Alice 是重复的# 删除重复行,保留第一个出现的
students_df.drop_duplicates(inplace=True)
print("\n删除重复行后的 DataFrame:")
print(students_df)
(3) 简单数据查询
print("\n--- 简单数据查询 ---")
# 查询数学成绩大于90分的学生
math_gt_90 = students_df[students_df['Math'] > 90]
print("\n数学成绩大于 90 分的学生:")
print(math_gt_90)# 查询 Alice 的所有成绩
alice_scores = students_df[students_df['Name'] == 'Alice']
print("\nAlice 的成绩:")
print(alice_scores)# 查询数学和科学都大于85分的学生
high_achievers = students_df[(students_df['Math'] > 85) & (students_df['Science'] > 85)]
print("\n数学和科学都大于 85 分的学生:")
print(high_achievers)
(4) 计算新列
print("\n--- 计算新列 ---")
# 计算总分 (Math, Science, English, History)
subject_cols = ['Math', 'Science', 'English', 'History']
students_df['TotalScore'] = students_df[subject_cols].sum(axis=1)
# 计算平均分
students_df['AverageScore'] = students_df[subject_cols].mean(axis=1)print("\n添加了总分和平均分后的 DataFrame:")
print(students_df.head())

这个实践环节展示了如何将前面学到的知识点(加载数据、查看信息、处理缺失/重复值、条件选择、创建新列)串联起来,完成一个基本的数据探索和预处理流程。

六、总结

本篇文章详细介绍了 Python 数据处理库 Pandas 的核心功能和实际应用。通过学习,我们掌握了:

  1. 核心数据结构:

    • Series: 一维带标签数组,是构建 DataFrame 的基础。理解了其创建方式、常用属性和方法,以及灵活的索引机制。
    • DataFrame: 二维表格型数据结构,是 Pandas 中进行数据分析和处理的主力。学习了其多种创建途径、重要属性和方法,并重点掌握了基于标签 (.loc[]) 和基于位置 (.iloc[]) 的强大索引与切片能力。
  2. 数据读取与写入:

    • 掌握了使用 pd.read_csv() 从 CSV 文件读取数据,并了解了其常用参数以应对不同格式的 CSV 文件。
    • 学会了使用 df.to_csv() 将 DataFrame 写入 CSV 文件,并能控制是否包含索引、表头等。
    • 了解 Pandas 支持读取和写入多种其他数据格式,如 Excel、SQL 数据库等。
  3. 数据选择与过滤:

    • 熟练运用 .loc[].iloc[] 进行精确的行和列选择。
    • 掌握了如何根据单条件或组合条件 (&, |, ~,注意括号使用) 筛选 DataFrame 中的数据子集。
  4. 基本的数据清洗和转换操作:

    • 处理缺失值: 能够检测 (isnull())、填充 (fillna()) 和删除 (dropna()) 数据中的缺失值。
    • 处理重复值: 能够检测 (duplicated()) 和删除 (drop_duplicates()) 重复数据。
    • 数据类型转换: 能够使用 astype()pd.to_numeric() 转换列的数据类型。
    • 应用函数: 能够使用 apply(), map(), applymap() 对数据进行灵活的函数应用和转换。
  5. 实践应用:

    • 通过一个加载并探索表格数据集的实例,将所学知识点融会贯通,体验了从数据加载、信息查看、数据清洗到初步分析的完整流程。

Pandas 是数据科学和深度学习项目进行数据预处理不可或缺的工具。熟练掌握 Pandas,将为我们后续进行更复杂的数据分析、特征工程以及模型训练打下坚实的基础。希望大家通过本文的学习和实践,能够真正领会 Pandas 的强大之处,并将其灵活运用于未来的项目中。


相关文章:

  • C语言——操作符
  • 快速上手SpringBoot开发指南
  • 电子商务商家运营简历模板
  • 操作指南*
  • allegro出gerber时,单击Artwork并没有弹窗的问题
  • Linux 安全加固
  • htop筛选进程时,出现重复进程
  • 浅谈C++的new和delete
  • 端口隔离实验
  • Docker容器网络架构深度解析与技术实践指南——基于Linux内核特性的企业级容器网络实现
  • 如何进行室内VR全景拍摄?
  • 控制mac地址表端口安全
  • 004 Linux基本指令
  • 使用 Selenium 截图功能,截不到原生 JavaScript 弹窗
  • Red Hat linux环境openssh升级到openssh-10.0p1
  • [特殊字符] Milvus + LLM大模型:打造智能电影知识库系统
  • 首屏加载时间优化策略
  • WSL 的 Ubuntu 子系统中启用图形化界面
  • Ollama【部署 05】Linux环境安装、升级、启动脚本(connection reset by peer异常)
  • ISCC 2025练武题 WP部分
  • 西甲上海足球学院揭幕,用“足球方法论”试水中国青训
  • 上汽享道出行完成13亿元C轮融资,已启动港股IPO计划
  • 2025中国南昌国际龙舟赛5月23日启幕,是历年来南昌举办的最高规格龙舟赛事
  • 外交部:习近平主席同普京总统达成许多新的重要共识
  • 数理+AI+工程,上海交大将开首届“笛卡尔班”招生约20名
  • 悬疑推理联合书单|虫神山事件