Python Pandas 基础教程:数据分析的得力助手
数据是现代世界的基石,而如何高效地处理、清洗、分析和转换数据,是数据科学、机器学习乃至日常工作中不可或缺的技能。在 Python 的数据生态系统中,Pandas 库无疑是处理结构化数据的首选工具。它提供了一系列高性能、易用的数据结构和数据分析工具,极大地简化了数据处理的流程。
本篇文章将带你深入了解 Pandas 的基础知识,包括其核心数据结构、数据创建、数据选择、数据清洗、数据加载与保存等常用操作。我们将通过丰富的代码示例,让你亲手实践,逐步掌握这个强大的工具。
1. 什么是 Pandas?为什么选择 Pandas?
Pandas 是一个开源的 Python 库,专注于提供快速、灵活且富有表现力的数据结构,旨在使处理“关系型”或“带标签”的数据变得简单直观。它是基于 NumPy 构建的,但提供了比 NumPy 更高级的数据处理功能,尤其擅长处理表格化数据(类似于电子表格或数据库中的表格)。
为什么选择 Pandas?
- 强大的数据结构: 提供了
Series
和DataFrame
这两种核心数据结构,能够优雅地处理各种类型的数据。 - 便捷的数据操作: 支持灵活的索引、切片、合并、重塑、分组、聚合等操作。
- 处理缺失数据: 提供了完善的缺失数据处理机制。
- 文件 I/O 支持: 可以轻松读取和写入多种数据格式,如 CSV、Excel、SQL 数据库、JSON 等。
- 集成性好: 与 NumPy、Matplotlib、Scikit-learn 等其他 Python 科学计算库紧密集成。
- 高性能: 底层由高度优化的 C 代码实现,处理大规模数据时效率较高。
可以说,Pandas 是进行数据清洗(Data Cleaning)、数据转换(Data Transformation)、数据探索(Data Exploration)和数据预处理(Data Preprocessing)的瑞士军刀。
2. 安装 Pandas
在开始之前,你需要确保已经安装了 Python 和 pip 包管理器。安装 Pandas 非常简单,只需在终端或命令提示符中运行以下命令:
bash
pip install pandas openpyxl # openpyxl 用于读写 Excel 文件
如果你使用的是 Anaconda Python 发行版,Pandas 通常已经预装好了,你可以直接使用。如果未安装,可以使用 conda 进行安装:
bash
conda install pandas openpyxl
安装完成后,你可以在 Python 脚本或交互式环境中导入 Pandas:
python
import pandas as pd
我们通常使用 pd
作为 Pandas 的别名,这是一个约定俗成的做法。
3. Pandas 的核心数据结构:Series 和 DataFrame
Pandas 主要提供了两种核心数据结构:Series
和 DataFrame
。理解它们是掌握 Pandas 的关键。
3.1 Series (序列)
Series
是一种一维的带标签数组(labeled array)。它可以存储任何数据类型(整数、浮点数、字符串、Python 对象等),并且每个元素都有一个与之关联的标签,称为索引(index)。如果没有显式指定索引,Pandas 会自动创建一个从 0 开始的默认整数索引。
特点:
- 一维结构。
- 带标签的轴(索引)。
- 可以存储同构或异构数据(但在实践中通常存储同构数据)。
创建 Series:
可以通过多种方式创建 Series:
-
从 Python 列表或 NumPy 数组创建:
“`python
import pandas as pd
import numpy as np从列表创建,使用默认整数索引
s1 = pd.Series([10, 20, 30, 40, 50])
print(“Series s1:”)
print(s1)输出:
0 10
1 20
2 30
3 40
4 50
dtype: int64
从 NumPy 数组创建,并指定索引
data = np.array([‘a’, ‘b’, ‘c’, ‘d’])
index_labels = [‘label1’, ‘label2’, ‘label3’, ‘label4’]
s2 = pd.Series(data, index=index_labels)
print(“\nSeries s2:”)
print(s2)输出:
label1 a
label2 b
label3 c
label4 d
dtype: object
从 NumPy 数组创建,使用默认索引
s3 = pd.Series(np.linspace(0, 1, 5)) # 0到1之间等间隔的5个值
print(“\nSeries s3:”)
print(s3)
“` -
从 Python 字典创建:
字典的键将成为 Series 的索引,值将成为 Series 的数据。
“`python
data_dict = {‘apple’: 1.5, ‘banana’: 2.0, ‘cherry’: 3.0}
s4 = pd.Series(data_dict)
print(“\nSeries s4 (from dict):”)
print(s4)输出:
apple 1.5
banana 2.0
cherry 3.0
dtype: float64
从字典创建,但指定索引的顺序(未指定的键将得到 NaN 值)
s5 = pd.Series(data_dict, index=[‘banana’, ‘apple’, ‘grape’])
print(“\nSeries s5 (from dict with specified index order):”)
print(s5)输出:
banana 2.0
apple 1.5
grape NaN
dtype: float64
``
NaN`(Not a Number),它代表缺失值,是 Pandas 中常用的缺失数据标记。
注意这里的
Series 的属性:
一些常用的 Series 属性:
s.values
: 返回 Series 的数据(NumPy 数组)。s.index
: 返回 Series 的索引对象。s.dtype
: 返回 Series 中元素的 数据类型。s.size
: 返回 Series 中元素的数量。s.name
: Series 的名称(可选)。s.index.name
: Series 索引的名称(可选)。
“`python
print(“\ns4 attributes:”)
print(“Values:”, s4.values)
print(“Index:”, s4.index)
print(“Dtype:”, s4.dtype)
print(“Size:”, s4.size)
s4.name = “水果价格”
s4.index.name = “水果名称”
print(“\nSeries s4 after setting names:”)
print(s4)
print(“Name:”, s4.name)
print(“Index Name:”, s4.index.name)
“`
Series 的基本操作(索引与切片):
Series 的索引和切片操作与 NumPy 数组和 Python 列表类似,但可以使用标签或整数位置进行访问。
“`python
访问单个元素 (通过标签或位置)
print(“\nAccessing Series elements:”)
print(“s4[‘apple’]:”, s4[‘apple’]) # 通过标签
print(“s4[0]:”, s4[0]) # 通过整数位置
切片 (通过标签或位置)
print(“\nSlicing Series:”)
print(“s4[‘banana’:’cherry’] (inclusive label slice):”)
print(s4[‘banana’:’cherry’]) # 通过标签切片,右侧标签包含在内
print(“\ns1[1:3] (exclusive integer slice):”)
print(s1[1:3]) # 通过整数位置切片,右侧索引不包含在内
使用列表进行多元素选择
print(“\nSelecting multiple elements with a list:”)
print(“s4[[‘apple’, ‘cherry’]]:”)
print(s4[[‘apple’, ‘cherry’]])
使用布尔数组选择
print(“\nBoolean indexing:”)
print(“s1 > 25:”)
print(s1[s1 > 25]) # 选择值大于25的元素
“`
3.2 DataFrame (数据框)
DataFrame
是 Pandas 中最重要也是最常用的数据结构。它是一个二维的带标签数据结构,可以被看作是 Series 的容器,共享同一个索引。可以理解为一个表格,有行和列,每一列可以是不同的数据类型(但同一列的数据类型通常是相同的)。
特点:
- 二维结构。
- 有行索引(Index)和列索引(Columns)。
- 每列可以存储不同的数据类型。
- 类似于电子表格或 SQL 表。
创建 DataFrame:
DataFrame 的创建方式非常灵活:
-
从 Python 字典创建 (最常见):
字典的键作为列名,值作为列的数据(通常是列表、NumPy 数组或 Series)。
“`python
data = {
‘城市’: [‘北京’, ‘上海’, ‘广州’, ‘深圳’, ‘成都’],
‘人口’: [2154, 2428, 1530, 1303, 1633], # 单位: 万
‘面积’: [16411, 6340, 7434, 1973, 14312], # 单位: 平方公里
‘GDP’: [30320, 32680, 22859, 24221, 15342] # 单位: 亿人民币
}
df = pd.DataFrame(data)
print(“\nDataFrame df (from dict):”)
print(df)输出类似表格的数据:
城市 人口 面积 GDP
0 北京 2154 16411 30320
1 上海 2428 6340 32680
2 广州 1530 7434 22859
3 深圳 1303 1973 24221
4 成都 1633 14312 15342
“`
-
从字典列表创建:
列表中的每个字典代表一行数据。
python
data_list = [
{'Name': 'Alice', 'Age': 25, 'City': 'New York'},
{'Name': 'Bob', 'Age': 30, 'City': 'London'},
{'Name': 'Charlie', 'Age': 35, 'City': 'Paris'}
]
df_list = pd.DataFrame(data_list)
print("\nDataFrame df_list (from list of dicts):")
print(df_list) -
从 NumPy 二维数组创建:
需要指定列名和索引。
python
np_data = np.random.rand(4, 3) # 4行3列的随机数据
df_np = pd.DataFrame(np_data, columns=['col1', 'col2', 'col3'], index=['row1', 'row2', 'row3', 'row4'])
print("\nDataFrame df_np (from NumPy array):")
print(df_np)
DataFrame 的属性:
一些常用的 DataFrame 属性:
df.index
: 返回 DataFrame 的行索引。df.columns
: 返回 DataFrame 的列索引 (列名)。df.values
: 返回 DataFrame 的数据(NumPy 二维数组)。df.shape
: 返回 DataFrame 的形状 (行数, 列数) 元组。df.dtypes
: 返回每列的数据类型。df.info()
: 打印 DataFrame 的简要信息,包括索引、列、非空值数量和内存使用情况。df.describe()
: 生成描述性统计信息,包括计数、均值、标准差、最小值、最大值和四分位数(仅限数值列)。df.head(n=5)
: 显示前 n 行数据 (默认为 5)。df.tail(n=5)
: 显示后 n 行数据 (默认为 5)。
“`python
print(“\ndf attributes:”)
print(“Index:”, df.index)
print(“Columns:”, df.columns)
print(“Values:\n”, df.values)
print(“Shape:”, df.shape)
print(“Dtypes:”, df.dtypes)
print(“\ndf.info():”)
df.info()
print(“\ndf.describe():”)
print(df.describe())
print(“\ndf.head(3):”)
print(df.head(3))
“`
4. 数据选择与索引 (DataFrame)
从 DataFrame 中选择数据是日常操作中最频繁的部分。Pandas 提供了多种强大的方式来选择行、列或单个元素。
4.1 选择列
选择一列会返回一个 Series,选择多列会返回一个 DataFrame。
“`python
选择单列 (返回 Series)
cities = df[‘城市’]
print(“\nSelecting single column ‘城市’:”)
print(cities)
print(type(cities)) #
选择多列 (返回 DataFrame)
city_pop_area = df[[‘城市’, ‘人口’, ‘面积’]]
print(“\nSelecting multiple columns [‘城市’, ‘人口’, ‘面积’]:”)
print(city_pop_area)
print(type(city_pop_area)) #
``
df[‘列名’]
注意,选择单列使用语法,选择多列使用
df[[‘列名1’, ‘列名2’, …]]` 语法,即列名列表作为索引。
4.2 选择行 (loc vs iloc)
这是 Pandas 索引中最容易混淆但也最重要的部分。理解 loc
和 iloc
的区别至关重要。
loc
: 基于标签(label-based) 的索引。使用行索引的标签和列索引的标签来选择数据。iloc
: 基于整数位置(integer-based) 的索引。使用行和列的整数位置(从 0 开始)来选择数据。
使用 loc
(基于标签):
“`python
设置 ‘城市’ 列为索引以便演示标签索引
df_indexed = df.set_index(‘城市’)
print(“\nDataFrame with ‘城市’ as index:”)
print(df_indexed)
选择单行 (通过行标签)
beijing_data = df_indexed.loc[‘北京’]
print(“\nSelecting row with label ‘北京’ using loc:”)
print(beijing_data) # 返回 Series
选择多行 (通过行标签列表)
beijing_shanghai = df_indexed.loc[[‘北京’, ‘上海’]]
print(“\nSelecting rows with labels [‘北京’, ‘上海’] using loc:”)
print(beijing_shanghai) # 返回 DataFrame
选择行标签范围 (包含右边界)
guangzhou_shenzhen = df_indexed.loc[‘广州’:’深圳’]
print(“\nSelecting rows from ‘广州’ to ‘深圳’ using loc (inclusive slice):”)
print(guangzhou_shenzhen) # 返回 DataFrame
选择特定行和特定列 (通过标签)
beijing_pop_gdp = df_indexed.loc[‘北京’, [‘人口’, ‘GDP’]]
print(“\nSelecting row ‘北京’ and columns [‘人口’, ‘GDP’] using loc:”)
print(beijing_pop_gdp) # 返回 Series
选择行标签范围和列标签范围 (包含右边界)
partial_data = df_indexed.loc[‘上海’:’深圳’, ‘人口’:’GDP’]
print(“\nSelecting rows ‘上海’ to ‘深圳’ and columns ‘人口’ to ‘GDP’ using loc:”)
print(partial_data) # 返回 DataFrame
选择所有行和特定列
all_rows_pop_area = df_indexed.loc[:, [‘人口’, ‘面积’]]
print(“\nSelecting all rows and columns [‘人口’, ‘面积’] using loc:”)
print(all_rows_pop_area) # 返回 DataFrame
“`
使用 iloc
(基于整数位置):
iloc
的语法类似于 NumPy 数组的切片,右侧边界是不包含的。
“`python
print(“\nUsing iloc (integer-based indexing) on original df:”)
print(df) # 使用原始的 df,其行索引是默认的整数 0, 1, 2…
选择单行 (通过行整数位置)
first_row = df.iloc[0]
print(“\nSelecting row at integer position 0 using iloc:”)
print(first_row) # 返回 Series (北京的数据)
选择多行 (通过行整数位置列表)
first_two_rows = df.iloc[[0, 1]]
print(“\nSelecting rows at integer positions [0, 1] using iloc:”)
print(first_two_rows) # 返回 DataFrame (北京、上海的数据)
选择行整数位置范围 (不包含右边界)
middle_rows = df.iloc[1:4]
print(“\nSelecting rows from integer position 1 to 3 using iloc (exclusive slice):”)
print(middle_rows) # 返回 DataFrame (上海、广州、深圳的数据)
选择特定行和特定列 (通过整数位置)
guangzhou_pop_area_iloc = df.iloc[2, [1, 2]]
print(“\nSelecting row at integer position 2 and columns at integer positions [1, 2] using iloc:”)
print(guangzhou_pop_area_iloc) # 返回 Series (广州的人口和面积)
选择行整数位置范围和列整数位置范围 (不包含右边界)
partial_data_iloc = df.iloc[1:4, 1:3]
print(“\nSelecting rows from 1 to 3 and columns from 1 to 2 using iloc:”)
print(partial_data_iloc) # 返回 DataFrame (上海、广州、深圳的人口和面积)
选择所有行和特定列
all_rows_pop_area_iloc = df.iloc[:, [1, 2]]
print(“\nSelecting all rows and columns at integer positions [1, 2] using iloc:”)
print(all_rows_pop_area_iloc) # 返回 DataFrame (所有城市的人口和面积)
“`
总结 loc
和 iloc
:
语法 | 索引类型 | 行切片行为 | 列切片行为 | 主要用途 |
---|---|---|---|---|
df.loc[] |
标签 | 包含右边界 | 包含右边界 | 使用明确的行/列标签选择数据 |
df.iloc[] |
整数位置 | 不包含右边界 (同列表) | 不包含右边界 (同列表) | 使用整数位置选择数据 |
4.3 布尔索引 (Filtering)
布尔索引是一种非常强大的数据选择方式,可以根据条件过滤 DataFrame 中的行。
“`python
选择人口大于 2000 万的城市
pop_greater_than_2000 = df[df[‘人口’] > 2000]
print(“\nCities with population > 2000:”)
print(pop_greater_than_2000)
组合条件 (使用 & 表示 AND, | 表示 OR, ~ 表示 NOT)
选择人口大于 1500 万 且 GDP 大于 25000 亿的城市
high_pop_gdp = df[(df[‘人口’] > 1500) & (df[‘GDP’] > 25000)]
print(“\nCities with pop > 1500 AND GDP > 25000:”)
print(high_pop_gdp)
选择面积小于 5000 平方公里 或 GDP 小于 20000 亿的城市
small_area_low_gdp = df[(df[‘面积’] < 5000) | (df[‘GDP’] < 20000)]
print(“\nCities with area < 5000 OR GDP < 20000:”)
print(small_area_low_gdp)
使用 isin() 方法选择在特定列表中的值
cities_to_select = [‘北京’, ‘深圳’]
selected_cities_isin = df[df[‘城市’].isin(cities_to_select)]
print(“\nCities in [‘北京’, ‘深圳’] using isin():”)
print(selected_cities_isin)
“`
布尔索引返回的 DataFrame 包含满足条件的原始行的所有列。
5. 数据修改与添加
修改 DataFrame 中的数据或添加新列也很容易。
5.1 修改元素或块
可以使用 loc
或 iloc
来修改特定的元素、行或列。
“`python
print(“\nOriginal df:”)
print(df)
修改单个元素 (例如,将北京的人口修改为 2200 万)
需要使用 df.loc[] 来确保修改的是原始 DataFrame
df.loc[df[‘城市’] == ‘北京’, ‘人口’] = 2200
print(“\nModified df (北京人口 updated):”)
print(df)
修改整列 (例如,将所有城市的 GDP 乘以 10000 转换为元)
df[‘GDP_元’] = df[‘GDP’] * 10000 # 也可以直接添加新列
修改一个范围的值 (例如,将索引 1 到 3 的城市的面积修改为 0)
注意 iloc 的右边界是不包含的
df.iloc[1:4, 2] = 0
print(“\nModified df (iloc slicing for area):”)
print(df)
将 DataFrame 恢复到接近原始状态以便后续演示
df = pd.DataFrame(data)
“`
5.2 添加新列
添加新列非常简单,只需像访问字典一样给一个不存在的列名赋值即可。
“`python
添加一个新列 ‘密度’ (人口/面积)
df[‘密度’] = df[‘人口’] / df[‘面积’]
print(“\nDataFrame after adding ‘密度’ column:”)
print(df)
添加一个基于现有列的条件列
df[‘规模’] = np.where(df[‘人口’] > 2000, ‘大型城市’, ‘中小型城市’)
print(“\nDataFrame after adding ‘规模’ column (using np.where):”)
print(df)
添加一个常量列
df[‘国家’] = ‘中国’
print(“\nDataFrame after adding ‘国家’ column (constant value):”)
print(df)
``
np.where(condition, value_if_true, value_if_false)` 函数在创建条件列时非常有用。
NumPy 的
5.3 删除列或行
使用 drop()
方法可以删除指定的列或行。需要注意的是,drop()
默认返回一个新的 DataFrame,原始 DataFrame 不会被修改。如果要修改原始 DataFrame,需要设置 inplace=True
参数。
“`python
删除列 (例如,删除 ‘国家’ 列)
axis=1 表示操作沿着列进行
df_dropped_country = df.drop(‘国家’, axis=1)
print(“\nDataFrame after dropping ‘国家’ column (new DataFrame):”)
print(df_dropped_country)
print(“\nOriginal df remains unchanged:”)
print(df)
如果想直接修改原始 DataFrame,使用 inplace=True
df.drop(‘国家’, axis=1, inplace=True)
print(“\nOriginal df after inplace drop of ‘国家’:”)
print(df)
删除多列
df.drop([‘密度’, ‘规模’], axis=1, inplace=True)
print(“\nOriginal df after inplace drop of multiple columns:”)
print(df)
删除行 (例如,删除索引为 0 和 2 的行,即北京和广州)
axis=0 表示操作沿着行进行 (这是默认值,可以省略)
df_dropped_rows = df.drop([0, 2], axis=0)
或者 df_dropped_rows = df.drop([0, 2])
print(“\nDataFrame after dropping rows 0 and 2 (new DataFrame):”)
print(df_dropped_rows)
如果行索引是标签,也可以使用标签删除
df_indexed = df.set_index(‘城市’)
df_dropped_labels = df_indexed.drop([‘上海’, ‘深圳’])
print(“\nDataFrame with city index after dropping rows ‘上海’ and ‘深圳’:”)
print(df_dropped_labels)
“`
6. 处理缺失数据 (Missing Data)
现实世界的数据很少是完美的,经常包含缺失值。Pandas 使用 NaN
(Not a Number) 来表示数值类型的缺失值,对于对象类型(如字符串),有时也使用 None
或 NaN
。
6.1 检测缺失值
isnull()
: 返回一个布尔型 DataFrame,其中缺失值为True
。notnull()
: 返回一个布尔型 DataFrame,其中非缺失值为True
。
“`python
创建一个包含缺失值的 DataFrame
data_with_nan = {
‘A’: [1, 2, np.nan, 4],
‘B’: [5, np.nan, np.nan, 8],
‘C’: [9, 10, 11, 12],
‘D’: [np.nan, 14, 15, 16]
}
df_nan = pd.DataFrame(data_with_nan)
print(“\nDataFrame with NaN values:”)
print(df_nan)
print(“\ndf_nan.isnull():”)
print(df_nan.isnull())
print(“\ndf_nan.notnull():”)
print(df_nan.notnull())
检查每列的缺失值数量
print(“\nNumber of missing values per column:”)
print(df_nan.isnull().sum())
检查总共的缺失值数量
print(“\nTotal number of missing values:”)
print(df_nan.isnull().sum().sum())
“`
6.2 删除缺失值
dropna()
方法可以删除包含缺失值的行或列。
“`python
删除包含任何缺失值的行 (默认 axis=0, how=’any’)
df_dropped_rows_nan = df_nan.dropna()
print(“\nDataFrame after dropping rows with any NaN:”)
print(df_dropped_rows_nan)
删除所有值都是缺失值的行
df_dropped_all_nan_rows = df_nan.dropna(how=’all’)
print(“\nDataFrame after dropping rows with all NaN:”)
print(df_dropped_all_nan_rows) # 在当前例子中,结果和 df_nan 相同,因为没有全 NaN 的行
删除包含任何缺失值的列 (axis=1)
df_dropped_cols_nan = df_nan.dropna(axis=1)
print(“\nDataFrame after dropping columns with any NaN:”)
print(df_dropped_cols_nan) # C 列被保留
删除所有值都是缺失值的列
df_dropped_all_nan_cols = df_nan.dropna(axis=1, how=’all’)
print(“\nDataFrame after dropping columns with all NaN:”)
print(df_dropped_all_nan_cols) # 在当前例子中,结果和 df_nan 相同,因为没有全 NaN 的列
使用 thresh 参数指定至少需要有多少非缺失值才保留
保留至少有 3 个非缺失值的行
df_thresh = df_nan.dropna(thresh=3)
print(“\nDataFrame after dropping rows with less than 3 non-NaN values:”)
print(df_thresh) # 第1行(索引0)和第4行(索引3)被保留
“`
6.3 填充缺失值
fillna()
方法可以用指定的值或方法填充缺失值。
“`python
用常量值填充所有 NaN
df_filled_zero = df_nan.fillna(0)
print(“\nDataFrame after filling NaN with 0:”)
print(df_filled_zero)
用列的均值填充 NaN
mean_A = df_nan[‘A’].mean()
df_filled_mean = df_nan.fillna({‘A’: mean_A, ‘B’: df_nan[‘B’].mean()}) # 可以为不同列指定不同值
print(“\nDataFrame after filling NaN with column mean:”)
print(df_filled_mean)
向前填充 (用前一个有效值填充 NaN)
df_filled_ffill = df_nan.fillna(method=’ffill’)
print(“\nDataFrame after forward filling NaN:”)
print(df_filled_ffill)
向后填充 (用后一个有效值填充 NaN)
df_filled_bfill = df_nan.fillna(method=’bfill’)
print(“\nDataFrame after backward filling NaN:”)
print(df_filled_bfill)
使用 inplace=True 直接修改原始 DataFrame
df_nan.fillna(value=df_nan.mean(), inplace=True) # 用每列的均值填充
print(“\nOriginal df_nan after inplace filling with mean:”)
print(df_nan)
“`
7. 数据加载与保存
Pandas 提供了方便的函数来从各种文件格式读取数据,以及将 DataFrame 写入文件。
7.1 读取数据
常用的读取函数包括:
pd.read_csv()
: 读取 CSV 文件。pd.read_excel()
: 读取 Excel 文件。pd.read_sql()
: 从数据库读取数据。pd.read_json()
: 读取 JSON 文件。
读取 CSV 文件:
假设你有一个名为 data.csv
的文件,内容如下:
csv
Name,Age,City
Alice,25,New York
Bob,30,London
Charlie,35,Paris
David,,Tokyo
注意 David 的年龄是缺失的。
“`python
创建一个模拟的 data.csv 文件
csv_content = “””Name,Age,City
Alice,25,New York
Bob,30,London
Charlie,35,Paris
David,,Tokyo
“””
with open(‘data.csv’, ‘w’) as f:
f.write(csv_content)
读取 CSV 文件
try:
df_csv = pd.read_csv(‘data.csv’)
print(“\nDataFrame read from data.csv:”)
print(df_csv)
print(“\ndf_csv info:”)
df_csv.info() # Notice Age column might be float due to NaN
except FileNotFoundError:
print(“Error: data.csv not found.”)
常用的 read_csv 参数:
sep: 分隔符 (默认为 ‘,’)
header: 指定哪一行作为列头 (默认为 0, 即第一行)
index_col: 指定哪一列作为行索引
names: 指定列名 (如果文件没有列头)
skiprows: 跳过文件开头的行
na_values: 指定哪些值应该被视为 NaN
``
read_csv
在上面的例子中,因为 David 的年龄是空的,会将其识别为缺失值,并且由于缺失值的存在,'Age' 列的数据类型会被推断为浮点型 (
float64`)。
读取 Excel 文件:
假设你有一个名为 data.xlsx
的文件,包含一个名为 Sheet1
的工作表。
“`python
创建一个模拟的 data.xlsx 文件
try:
df_to_excel = pd.DataFrame({
‘Product’: [‘A’, ‘B’, ‘C’],
‘Price’: [10, 20, 30],
‘Stock’: [100, 50, np.nan] # 含缺失值
})
df_to_excel.to_excel(‘data.xlsx’, sheet_name=’Sheet1′, index=False) # index=False 表示不写入行索引
print(“\nCreated dummy data.xlsx”)
# 读取 Excel 文件
df_excel = pd.read_excel('data.xlsx', sheet_name='Sheet1')
print("\nDataFrame read from data.xlsx:")
print(df_excel)
print("\ndf_excel info:")
df_excel.info() # Notice Stock column might be float due to NaN
except FileNotFoundError:
print(“Error: data.xlsx not found.”)
except ImportError:
print(“Please install openpyxl to read/write Excel files: pip install openpyxl”)
常用的 read_excel 参数:
sheet_name: 指定要读取的工作表名称或索引 (默认为第一个工作表)
header: 指定哪一行作为列头
index_col: 指定哪一列作为行索引
usecols: 指定要读取的列
na_values: 指定哪些值应该被视为 NaN
“`
7.2 保存数据
常用的保存函数包括:
df.to_csv()
: 将 DataFrame 写入 CSV 文件。df.to_excel()
: 将 DataFrame 写入 Excel 文件。df.to_sql()
: 将 DataFrame 写入数据库。df.to_json()
: 将 DataFrame 写入 JSON 文件。
保存到 CSV 文件:
“`python
将 DataFrame 保存到 CSV 文件
df.to_csv(‘output.csv’, index=False) # index=False 表示不将行索引写入文件
print(“\nDataFrame saved to output.csv (without index).”)
将 DataFrame 保存到 CSV 文件,包含索引
df.to_csv(‘output_with_index.csv’, index=True)
print(“DataFrame saved to output_with_index.csv (with index).”)
常用的 to_csv 参数:
path_or_buf: 文件路径或缓冲区
sep: 分隔符 (默认为 ‘,’)
header: 是否写入列头 (默认为 True)
index: 是否写入行索引 (默认为 True)
na_rep: 用什么字符串表示缺失值 (默认为空字符串)
“`
保存到 Excel 文件:
“`python
try:
# 将 DataFrame 保存到 Excel 文件
df.to_excel(‘output.xlsx’, sheet_name=’CityData’, index=False)
print(“\nDataFrame saved to output.xlsx (without index).”)
# 将 DataFrame 保存到 Excel 文件,包含索引
df.to_excel('output_with_index.xlsx', sheet_name='CityData', index=True)
print("DataFrame saved to output_with_index.xlsx (with index).")
# 保存多个 DataFrame 到同一个 Excel 文件的不同工作表
with pd.ExcelWriter('multi_sheet_output.xlsx') as writer:
df.to_excel(writer, sheet_name='CityData', index=False)
df_nan.to_excel(writer, sheet_name='NanData', index=False)
print("Multiple DataFrames saved to multi_sheet_output.xlsx.")
except ImportError:
print(“Please install openpyxl to read/write Excel files: pip install openpyxl”)
常用的 to_excel 参数:
excel_writer: ExcelWriter 对象或文件路径
sheet_name: 工作表名称 (默认为 ‘Sheet1’)
header: 是否写入列头 (默认为 True)
index: 是否写入行索引 (默认为 True)
na_rep: 用什么字符串表示缺失值 (默认为空字符串)
“`
8. 基本统计与分组聚合
Pandas 提供了丰富的统计函数和强大的分组(groupby)功能,用于数据汇总和分析。
8.1 基本统计函数
DataFrame 和 Series 都有很多内置的统计方法,例如:
count()
: 非空值的数量。mean()
: 均值。median()
: 中位数。sum()
: 总和。std()
: 标准差。min()
: 最小值。max()
: 最大值。value_counts()
: 计算 Series 中唯一值的数量(常用于分类数据)。unique()
: 获取 Series 中的唯一值数组。
“`python
print(“\nBasic Statistics on df:”)
print(df) # 原始 df 只有 城市, 人口, 面积, GDP 列
print(“\nPopulation mean:”, df[‘人口’].mean())
print(“GDP sum:”, df[‘GDP’].sum())
print(“Area standard deviation:”, df[‘面积’].std())
print(“Min GDP:”, df[‘GDP’].min())
print(“Max GDP:”, df[‘GDP’].max())
添加 ‘规模’ 列以便演示 value_counts
df[‘规模’] = np.where(df[‘人口’] > 2000, ‘大型城市’, ‘中小型城市’)
print(“\nValue counts for ‘规模’ column:”)
print(df[‘规模’].value_counts())
print(“\nUnique values in ‘规模’ column:”)
print(df[‘规模’].unique())
“`
8.2 分组聚合 (groupby)
groupby()
方法是 Pandas 中进行“分而治之”操作的关键。它将 DataFrame 按照某个(或多个)列的值分成不同的组,然后可以对每个组独立地应用一个聚合函数(如求和、求均值、计数等)。
典型的 groupby 操作流程是:
- Split (拆分): 根据某个键(列)将数据拆分成组。
- Apply (应用): 对每个组独立地应用一个函数(通常是聚合函数、转换函数或过滤函数)。
- Combine (合并): 将各个组的结果合并成一个新的 DataFrame 或 Series。
“`python
创建一个包含分类数据的 DataFrame
data_sales = {
‘Category’: [‘水果’, ‘水果’, ‘蔬菜’, ‘蔬菜’, ‘水果’, ‘蔬菜’],
‘Item’: [‘苹果’, ‘香蕉’, ‘胡萝卜’, ‘菠菜’, ‘橙子’, ‘土豆’],
‘Price’: [5, 3, 2, 4, 6, 3],
‘Quantity’: [10, 15, 20, 10, 12, 18]
}
df_sales = pd.DataFrame(data_sales)
print(“\nSales DataFrame:”)
print(df_sales)
按 ‘Category’ 分组,计算每种类别的总销售额 (Price * Quantity)
df_sales[‘Sales’] = df_sales[‘Price’] * df_sales[‘Quantity’]
print(“\nSales DataFrame with Sales column:”)
print(df_sales)
按类别分组,计算每个类别的总销售额
sales_by_category = df_sales.groupby(‘Category’)[‘Sales’].sum()
print(“\nTotal Sales by Category:”)
print(sales_by_category) # 返回 Series
按类别分组,计算每个类别的总数量和平均价格
summary_by_category = df_sales.groupby(‘Category’).agg({
‘Quantity’: ‘sum’,
‘Price’: ‘mean’
})
print(“\nSummary (Total Quantity, Average Price) by Category:”)
print(summary_by_category) # 返回 DataFrame
按多个列分组
data_multi = {
‘Year’: [2020, 2020, 2021, 2021, 2020, 2021],
‘Region’: [‘North’, ‘South’, ‘North’, ‘South’, ‘North’, ‘South’],
‘Sales’: [100, 150, 120, 180, 110, 170]
}
df_multi = pd.DataFrame(data_multi)
print(“\nMulti-level Grouping DataFrame:”)
print(df_multi)
按 Year 和 Region 分组,计算总销售额
sales_by_year_region = df_multi.groupby([‘Year’, ‘Region’])[‘Sales’].sum()
print(“\nTotal Sales by Year and Region:”)
print(sales_by_year_region) # 返回 MultiIndex Series
如果想让分组的键作为列,可以使用 as_index=False
sales_by_year_region_df = df_multi.groupby([‘Year’, ‘Region’], as_index=False)[‘Sales’].sum()
print(“\nTotal Sales by Year and Region (as DataFrame):”)
print(sales_by_year_region_df)
``
groupby()` 是 Pandas 中进行数据分析和报表生成的强大工具,值得深入学习。
9. 综合示例:简单的数据分析流程
让我们把上面学到的一些基础知识串联起来,完成一个简单的数据分析流程:加载数据 -> 数据清洗 -> 数据探索 -> 基本分析。
假设我们有一个 CSV 文件 sales_data.csv
:
csv
OrderID,Product,Category,Price,Quantity,OrderDate,CustomerSegment,Discount,Region,DeliveryDate
1001,Laptop,Electronics,1200,1,2023-01-05,Corporate,0.1,North,2023-01-10
1002,Mouse,Electronics,25,2,2023-01-05,Consumer,0,South,2023-01-08
1003,Keyboard,Electronics,75,1,2023-01-06,Corporate,0.1,North,2023-01-11
1004,Desk,Furniture,300,1,2023-01-06,Home Office,0.15,South,2023-01-15
1005,Chair,Furniture,150,2,2023-01-07,Consumer,0,North,2023-01-12
1006,Monitor,Electronics,250,1,2023-01-07,Home Office,0.1,South,2023-01-12
1007,Bookcase,Furniture,450,1,2023-01-08,Corporate,0.15,North,2023-01-18
1008,Headphones,Electronics,100,3,2023-01-08,Consumer,0.05,South,2023-01-10
1009,Desk Lamp,Furniture,50,2,2023-01-09,Home Office,0,North,2023-01-14
1010,Webcam,Electronics,80,1,2023-01-09,Corporate,0.05,South,2023-01-11
1011,Table,Furniture,200,1,2023-01-10,Consumer,0.1,North,2023-01-15
1012,Notebook,Office Supplies,3,10,2023-01-10,Corporate,0,South,2023-01-12
1013,Pen,Office Supplies,1,50,2023-01-11,Consumer,0,North,2023-01-13
1014,Stapler,Office Supplies,5,5,2023-01-11,Home Office,0.05,South,2023-01-14
1015,Eraser,Office Supplies,0.5,20,2023-01-12,Corporate,0,North,2023-01-13
“`python
创建模拟的 sales_data.csv 文件
csv_sales_content = “””OrderID,Product,Category,Price,Quantity,OrderDate,CustomerSegment,Discount,Region,DeliveryDate
1001,Laptop,Electronics,1200,1,2023-01-05,Corporate,0.1,North,2023-01-10
1002,Mouse,Electronics,25,2,2023-01-05,Consumer,0,South,2023-01-08
1003,Keyboard,Electronics,75,1,2023-01-06,Corporate,0.1,North,2023-01-11
1004,Desk,Furniture,300,1,2023-01-06,Home Office,0.15,South,2023-01-15
1005,Chair,Furniture,150,2,2023-01-07,Consumer,0,North,2023-01-12
1006,Monitor,Electronics,250,1,2023-01-07,Home Office,0.1,South,2023-01-12
1007,Bookcase,Furniture,450,1,2023-01-08,Corporate,0.15,North,2023-01-18
1008,Headphones,Electronics,100,3,2023-01-08,Consumer,0.05,South,2023-01-10
1009,Desk Lamp,Furniture,50,2,2023-01-09,Home Office,0,North,2023-01-14
1010,Webcam,Electronics,80,1,2023-01-09,Corporate,0.05,South,2023-01-11
1011,Table,Furniture,200,1,2023-01-10,Consumer,0.1,North,2023-01-15
1012,Notebook,Office Supplies,3,10,2023-01-10,Corporate,0,South,2023-01-12
1013,Pen,Office Supplies,1,50,2023-01-11,Consumer,0,North,2023-01-13
1014,Stapler,Office Supplies,5,5,2023-01-11,Home Office,0.05,South,2023-01-14
1015,Eraser,Office Supplies,0.5,20,2023-01-12,Corporate,0,North,2023-01-13
“””
with open(‘sales_data.csv’, ‘w’) as f:
f.write(csv_sales_content)
1. 加载数据
try:
df_sales_analysis = pd.read_csv(‘sales_data.csv’)
print(“Original Sales Data:”)
print(df_sales_analysis.head())
print(“\nData Info:”)
df_sales_analysis.info() # Check data types and non-null counts
except FileNotFoundError:
print(“Error: sales_data.csv not found.”)
exit() # Exit if data file not found
2. 数据清洗/预处理
检查缺失值 (本例数据没有缺失值,但这是一个重要步骤)
print(“\nMissing values per column:”)
print(df_sales_analysis.isnull().sum())
确保日期列是日期时间类型 (通常 read_csv 会自动识别,但最好显式转换)
df_sales_analysis[‘OrderDate’] = pd.to_datetime(df_sales_analysis[‘OrderDate’])
df_sales_analysis[‘DeliveryDate’] = pd.to_datetime(df_sales_analysis[‘DeliveryDate’])
print(“\nData Info after converting dates:”)
df_sales_analysis.info()
计算实际销售额 = 价格 * 数量 * (1 – 折扣)
df_sales_analysis[‘ActualSales’] = df_sales_analysis[‘Price’] * df_sales_analysis[‘Quantity’] * (1 – df_sales_analysis[‘Discount’])
print(“\nDataFrame with ActualSales column:”)
print(df_sales_analysis.head())
计算送货天数
df_sales_analysis[‘DeliveryDays’] = (df_sales_analysis[‘DeliveryDate’] – df_sales_analysis[‘OrderDate’]).dt.days
print(“\nDataFrame with DeliveryDays column:”)
print(df_sales_analysis.head())
3. 数据探索与分析
按产品类别统计总销售额
sales_by_category = df_sales_analysis.groupby(‘Category’)[‘ActualSales’].sum().sort_values(ascending=False)
print(“\nTotal Actual Sales by Category:”)
print(sales_by_category)
按客户细分统计平均送货天数
avg_delivery_by_segment = df_sales_analysis.groupby(‘CustomerSegment’)[‘DeliveryDays’].mean()
print(“\nAverage Delivery Days by Customer Segment:”)
print(avg_delivery_by_segment)
找出销售额最高的产品
top_selling_product = df_sales_analysis.loc[df_sales_analysis[‘ActualSales’].idxmax()]
print(“\nTop Selling Product:”)
print(top_selling_product)
按区域和类别统计总销售额,并将结果重塑为交叉表 (可选,但常用)
sales_pivot = df_sales_analysis.pivot_table(index=’Region’, columns=’Category’, values=’ActualSales’, aggfunc=’sum’)
print(“\nTotal Actual Sales Pivot Table (Region vs Category):”)
print(sales_pivot)
过滤出特定日期范围的数据 (例如,1月8号之后的数据)
after_jan_8 = df_sales_analysis[df_sales_analysis[‘OrderDate’] >= ‘2023-01-08’]
print(“\nOrders placed on or after 2023-01-08:”)
print(after_jan_8)
按区域过滤,并计算该区域的总销售额
north_sales = df_sales_analysis[df_sales_analysis[‘Region’] == ‘North’][‘ActualSales’].sum()
print(f”\nTotal Actual Sales in North Region: {north_sales:.2f}”)
4. 保存结果
将按类别统计的销售额保存到 CSV 文件
sales_by_category.to_csv(‘sales_by_category.csv’, header=[‘TotalSales’])
print(“\nSales by category saved to sales_by_category.csv.”)
将分析后的 DataFrame 保存到 Excel 文件
df_sales_analysis.to_excel(‘analyzed_sales_data.xlsx’, index=False)
print(“Analyzed sales data saved to analyzed_sales_data.xlsx.”)
“`
这个例子展示了如何使用 Pandas 读取数据、进行简单的日期处理、创建新列、执行分组聚合以及过滤数据,最后将结果保存。这构成了一个非常基础但完整的数据分析工作流。
10. 总结与下一步
恭喜你!你已经学习了 Python Pandas 的基础知识,包括:
- Pandas 的用途和安装。
- 核心数据结构
Series
和DataFrame
的创建和属性。 - 使用标签 (
loc
) 和整数位置 (iloc
) 选择数据。 - 布尔索引进行数据过滤。
- 添加、修改和删除列与行。
- 检测和处理缺失数据。
- 读取和保存 CSV、Excel 等文件。
- 执行基本的统计计算和分组聚合。
这只是 Pandas 功能的冰山一角。Pandas 还有许多更高级和强大的特性,例如:
- 数据合并 (merge, join, concat)。
- 数据重塑 (pivot, melt, stack, unstack)。
- 时间序列分析功能。
- 窗口函数 (rolling, expanding)。
- 分类数据 (Categorical Data) 处理。
- 与其他库的集成(如 Matplotlib 进行可视化)。
掌握这些基础知识后,下一步可以尝试:
- 找一些实际数据集(如 Kaggle 上的数据集)进行练习。
- 深入学习
merge
和concat
用于组合数据。 - 学习时间序列相关的操作,如果你的数据包含时间信息。
- 学习如何使用 Pandas 结合 Matplotlib 或 Seaborn 进行数据可视化。
持续的实践是掌握 Pandas 最好的方法。希望这篇详细的基础教程能为你打开 Pandas 世界的大门,助你在数据分析的道路上更加高效和得心应手!