Pandas DataFrame 基础知识详解:数据分析的基石
在现代数据科学和数据分析领域,Python 语言凭借其强大的生态系统和易用性,成为了主流的选择。而在 Python 的数据科学生态中,Pandas 库无疑扮演着核心角色。Pandas 提供了高性能、易于使用的数据结构和数据分析工具,其中最核心、最常用的数据结构就是 DataFrame。理解和掌握 DataFrame 是进行高效数据处理、清洗、转换和分析的基础。本文将详细介绍 Pandas DataFrame 的基础知识,帮助您打下坚实的数据分析基础。
一、 什么是 Pandas 和 DataFrame?
-
Pandas 简介: Pandas 是一个开源的 Python 库,名字来源于 “Panel Data”(面板数据)和 “Python Data Analysis”。它构建在 NumPy 库之上,旨在使数据分析工作变得更快、更简单、更具表现力。Pandas 提供了两种主要的数据结构:
- Series: 一维带标签的数组,可以存储任何数据类型(整数、字符串、浮点数、Python 对象等)。它类似于一个带索引的列表或字典。
- DataFrame: 二维带标签的数据结构,可以看作是 Excel 电子表格、SQL 表或由多个 Series 对象组成的字典。它是 Pandas 中最常用的数据结构。
-
DataFrame 概念: DataFrame 是一个表格型的数据结构,它包含有一组有序的列,每列可以是不同的值类型(数值、字符串、布尔值等)。DataFrame 既有行索引(index),也有列索引(columns)。可以将其想象成一个电子表格或数据库表,其中行代表观测记录,列代表变量或特征。
二、 为什么要使用 DataFrame?
DataFrame 之所以如此受欢迎,是因为它提供了许多强大的功能和优势:
- 数据对齐: 在进行运算时,DataFrame 会自动根据行索引和列索引进行数据对齐,这极大地简化了处理缺失数据或不同来源数据的操作。
- 丰富的 I/O 功能: Pandas 可以轻松地从各种文件格式(如 CSV、Excel、JSON、SQL 数据库等)读取数据到 DataFrame,也可以方便地将 DataFrame 写入这些格式。
- 强大的数据选择和切片: 提供了灵活多样的方式来选择、索引和切片数据子集(基于标签、位置或条件)。
- 数据清洗和处理: 内置了处理缺失数据(NaN)、数据类型转换、数据排序、重复值处理等常用功能。
- 高效的数据操作: 支持列的插入、删除、重命名,以及数据的合并(Merge)、连接(Join)、分组(Group By)、重塑(Reshape)等操作。
- 性能优化: 底层代码(部分由 Cython 或 C 实现)经过优化,对于大型数据集也能保持较好的性能。
- 与生态系统集成: 与 NumPy、Matplotlib、Scikit-learn 等其他 Python 数据科学生态库无缝集成。
三、 创建 DataFrame
创建 DataFrame 的方式多种多样,以下是一些最常用的方法:
-
从字典创建:
- 字典的值是列表或 NumPy 数组: 这是最常见的方式。字典的键将成为列名,字典的值(列表或数组)将成为列数据。所有列表/数组的长度必须相同。
“`python
import pandas as pd
import numpy as npdata_dict = {
‘col_A’: [1, 2, 3, 4, 5],
‘col_B’: [‘apple’, ‘banana’, ‘orange’, ‘grape’, ‘melon’],
‘col_C’: np.random.rand(5),
‘col_D’: [True, False, True, False, True]
}
df_from_dict = pd.DataFrame(data_dict)
print(df_from_dict)col_A col_B col_C col_D
0 1 apple 0.123456 True
1 2 banana 0.789012 False
2 3 orange 0.345678 True
3 4 grape 0.901234 False
4 5 melon 0.567890 True
可以指定行索引
df_with_index = pd.DataFrame(data_dict, index=[‘row1’, ‘row2’, ‘row3’, ‘row4’, ‘row5’])
print(df_with_index)
“`- 字典的值是 Series: DataFrame 会将每个 Series 的索引联合起来形成最终的行索引。
“`python
s1 = pd.Series([1, 2, 3], index=[‘a’, ‘b’, ‘c’])
s2 = pd.Series([10, 20, 30], index=[‘a’, ‘b’, ‘c’])
s3 = pd.Series([100, 200, 300], index=[‘b’, ‘c’, ‘d’]) # 注意索引不同
df_from_series = pd.DataFrame({‘X’: s1, ‘Y’: s2, ‘Z’: s3})
print(df_from_series)X Y Z
a 1.0 10.0 NaN # Z 在 ‘a’ 处无值,填充为 NaN
b 2.0 20.0 100.0
c 3.0 30.0 200.0
d NaN NaN 300.0 # X, Y 在 ‘d’ 处无值,填充为 NaN
“`
-
从列表创建:
- 列表嵌套列表: 每个内部列表代表一行数据。
“`python
data_list = [
[1, ‘apple’, 0.5],
[2, ‘banana’, 0.8],
[3, ‘orange’, 0.3]
]需要手动指定列名
df_from_list = pd.DataFrame(data_list, columns=[‘ID’, ‘Fruit’, ‘Value’], index=[‘r1’, ‘r2’, ‘r3’])
print(df_from_list)ID Fruit Value
r1 1 apple 0.5
r2 2 banana 0.8
r3 3 orange 0.3
“`
- 列表嵌套字典: 每个字典代表一行数据,字典的键是列名。这种方式更灵活,不需要保证字典的键顺序一致,Pandas 会自动对齐。
“`python
data_list_dict = [
{‘Name’: ‘Alice’, ‘Age’: 30, ‘City’: ‘New York’},
{‘Name’: ‘Bob’, ‘Age’: 25, ‘City’: ‘Paris’},
{‘Name’: ‘Charlie’, ‘Age’: 35, ‘City’: ‘London’, ‘Salary’: 50000} # 可以有不同的键
]
df_from_list_dict = pd.DataFrame(data_list_dict)
print(df_from_list_dict)Name Age City Salary
0 Alice 30 New York NaN # Alice 没有 Salary,填充为 NaN
1 Bob 25 Paris NaN
2 Charlie 35 London 50000.0
“`
-
从 NumPy ndarray 创建:
“`python
data_np = np.random.randint(0, 10, size=(4, 3)) # 4行3列的随机整数数组
df_from_np = pd.DataFrame(data_np, columns=[‘Feat1’, ‘Feat2’, ‘Feat3’])
print(df_from_np)Feat1 Feat2 Feat3
0 5 8 9
1 2 1 4
2 7 0 6
3 3 5 2
“`
-
从文件读取: 这是实际工作中最常用的方式。
- 读取 CSV 文件:
“`python
假设有一个名为 data.csv 的文件
pd.read_csv(‘data.csv’) # 基本读取
常用参数:
sep: 指定分隔符,默认为 ‘,’
header: 指定哪一行作为列名,默认为 0 (第一行)
index_col: 指定哪一列作为行索引
usecols: 指定要读取的列
parse_dates: 指定需要解析为日期类型的列
df_from_csv = pd.read_csv(‘path/to/your/data.csv’, sep=’,’, header=0, index_col=’ID’)
“`
- 读取 Excel 文件:
“`python
假设有一个名为 data.xlsx 的文件
pd.read_excel(‘data.xlsx’) # 默认读取第一个 sheet
常用参数:
sheet_name: 指定要读取的 sheet 名称或索引 (0-based)
header: 同 read_csv
index_col: 同 read_csv
df_from_excel = pd.read_excel(‘path/to/your/data.xlsx’, sheet_name=’Sheet1′)
“`
四、 查看 DataFrame 的基本信息
加载或创建 DataFrame 后,通常需要先了解其基本情况:
-
查看维度 (形状):
.shape
属性返回一个元组,表示 (行数, 列数)。python
print(df_from_dict.shape) # 输出: (5, 4) -
查看数据类型:
.dtypes
属性返回一个 Series,显示每列的数据类型。“`python
print(df_from_dict.dtypes)col_A int64
col_B object # 字符串通常显示为 object
col_C float64
col_D bool
dtype: object
“`
-
查看行索引:
.index
属性python
print(df_from_dict.index) # 输出: RangeIndex(start=0, stop=5, step=1)
print(df_with_index.index) # 输出: Index(['row1', 'row2', 'row3', 'row4', 'row5'], dtype='object') -
查看列索引 (列名):
.columns
属性python
print(df_from_dict.columns) # 输出: Index(['col_A', 'col_B', 'col_C', 'col_D'], dtype='object') -
查看整体信息:
.info()
方法提供了 DataFrame 的简要摘要,包括索引类型、列信息(非空值数量、数据类型)和内存使用情况。非常有用!“`python
df_from_list_dict.info()RangeIndex: 3 entries, 0 to 2
Data columns (total 4 columns):
# Column Non-Null Count Dtype
— —— ————– —–
0 Name 3 non-null object
1 Age 3 non-null int64
2 City 3 non-null object
3 Salary 1 non-null float64 # 注意这里显示只有一个非空值
dtypes: float64(1), int64(1), object(2)
memory usage: 160.0+ bytes
“`
-
查看描述性统计:
.describe()
方法计算数值列的常用描述性统计量(计数、均值、标准差、最小值、25%/50%/75% 分位数、最大值)。对于非数值列,会提供不同的统计信息(计数、唯一值数量、最常见值、最常见值频率)。“`python
print(df_from_dict.describe()) # 只对数值和布尔列(视为0/1)col_A col_C
count 5.000000 5.000000
mean 3.000000 0.545454 # 示例值,实际随机数不同
std 1.581139 0.312345
min 1.000000 0.123456
25% 2.000000 0.345678
50% 3.000000 0.567890
75% 4.000000 0.789012
max 5.000000 0.901234
print(df_from_dict.describe(include=’object’)) # 只看 object 类型列
col_B
count 5
unique 5 # 有5个唯一值
top apple # 第一个值作为 top
freq 1 # 每个值频率都是1
“`
-
预览数据:
.head(n)
: 查看前 n 行数据,默认为 5。.tail(n)
: 查看后 n 行数据,默认为 5。
“`python
print(df_from_dict.head(2))col_A col_B col_C col_D
0 1 apple 0.123456 True
1 2 banana 0.789012 False
print(df_from_dict.tail(3))
col_A col_B col_C col_D
2 3 orange 0.345678 True
3 4 grape 0.901234 False
4 5 melon 0.567890 True
“`
五、 数据选择和索引 (Indexing & Selection)
这是 Pandas DataFrame 操作的核心和难点,掌握它至关重要。
-
选择列:
- 使用
[]
:- 选择单列(返回 Series):
df['col_name']
- 选择多列(返回 DataFrame):
df[['col_name1', 'col_name2']]
(注意里面是列表)
- 选择单列(返回 Series):
- 使用点
.
:df.col_name
(仅当列名是有效的 Python 标识符且不与 DataFrame 的方法名冲突时可用,不推荐用于生产代码,易混淆)
“`python
选择 col_B (Series)
col_b_series = df_from_dict[‘col_B’]
print(type(col_b_series)) #
print(col_b_series)选择 col_A 和 col_C (DataFrame)
cols_ac_df = df_from_dict[[‘col_A’, ‘col_C’]]
print(type(cols_ac_df)) #
print(cols_ac_df)使用点表示法 (不推荐)
col_a_series_dot = df_from_dict.col_A
print(col_a_series_dot)
“`
- 使用
-
选择行:
-
使用
.loc[]
(基于标签 Label-based): 主要用于通过行索引标签和列索引标签来选择数据。- 选择单行(返回 Series):
df.loc['row_label']
- 选择多行(返回 DataFrame):
df.loc[['row_label1', 'row_label2']]
- 选择行切片(包含结束标签):
df.loc['start_label':'end_label']
- 同时选择行和列:
df.loc['row_label', 'col_label']
或df.loc[['row1', 'row2'], ['colA', 'colB']]
或df.loc['start_row':'end_row', 'start_col':'end_col']
- 选择单行(返回 Series):
-
使用
.iloc[]
(基于整数位置 Integer-location based): 主要用于通过行和列的整数位置(从 0 开始)来选择数据。- 选择单行(返回 Series):
df.iloc[row_position]
- 选择多行(返回 DataFrame):
df.iloc[[row_pos1, row_pos2]]
- 选择行切片(不包含结束位置,类似 Python 列表切片):
df.iloc[start_pos:end_pos]
- 同时选择行和列:
df.iloc[row_pos, col_pos]
或df.iloc[[row1, row2], [col1, col2]]
或df.iloc[start_row:end_row, start_col:end_col]
- 选择单行(返回 Series):
.loc
vs.iloc
的关键区别:
*.loc
使用的是 DataFrame 的 索引标签。
*.iloc
使用的是 整数位置(0-based index)。
*.loc
的切片包含结束标签,.iloc
的切片不包含结束位置。“`python
使用 .loc (基于 df_with_index)
print(“— .loc Examples —“)
print(“Single row (Series):\n”, df_with_index.loc[‘row2’])
print(“\nMultiple rows (DataFrame):\n”, df_with_index.loc[[‘row1’, ‘row4’]])
print(“\nRow slice (inclusive):\n”, df_with_index.loc[‘row2′:’row4’])
print(“\nSingle value:\n”, df_with_index.loc[‘row3’, ‘col_B’])
print(“\nRow and Column subset:\n”, df_with_index.loc[[‘row1’, ‘row5’], [‘col_A’, ‘col_D’]])
print(“\nRow slice and Column slice:\n”, df_with_index.loc[‘row2′:’row4’, ‘col_B’:’col_D’])使用 .iloc (基于 df_with_index 或 df_from_dict,因为整数位置相同)
print(“\n— .iloc Examples —“)
print(“Single row (Series) at position 1:\n”, df_with_index.iloc[1]) # row2
print(“\nMultiple rows (DataFrame) at positions 0 and 3:\n”, df_with_index.iloc[[0, 3]]) # row1, row4
print(“\nRow slice (exclusive) from position 1 to 4 (i.e., 1, 2, 3):\n”, df_with_index.iloc[1:4]) # row2, row3, row4
print(“\nSingle value at row 2, col 1:\n”, df_with_index.iloc[2, 1]) # row3, col_B -> ‘orange’
print(“\nRow and Column subset by position:\n”, df_with_index.iloc[[0, 4], [0, 3]]) # row1, row5 & col_A, col_D
print(“\nRow slice and Column slice by position:\n”, df_with_index.iloc[1:4, 1:3]) # row2,3,4 & col_B, col_C
“` -
-
布尔索引 (Boolean Indexing): 使用布尔向量(True/False 序列)来过滤数据,非常强大。
- 基于单列条件:
df[df['col_name'] > value]
- 基于多列条件(使用
&
(与),|
(或),~
(非),注意用括号包裹每个条件):df[(df['col_A'] > 2) & (df['col_D'] == True)]
“`python
print(“— Boolean Indexing Examples —“)选择 col_A 大于 3 的行
print(“col_A > 3:\n”, df_from_dict[df_from_dict[‘col_A’] > 3])
选择 col_D 为 True 的行
print(“\ncol_D is True:\n”, df_from_dict[df_from_dict[‘col_D’] == True]) # 或者 df_from_dict[df_from_dict[‘col_D’]]
选择 col_A 大于 2 且 col_B 是 ‘banana’ 或 ‘melon’ 的行
condition = (df_from_dict[‘col_A’] > 2) & (df_from_dict[‘col_B’].isin([‘banana’, ‘melon’]))
print(“\nComplex condition:\n”, df_from_dict[condition])
“` - 基于单列条件:
六、 数据修改与操作
-
修改值: 可以通过索引选择数据后直接赋值。
“`python
df_copy = df_from_dict.copy() # 推荐修改副本修改单个值
df_copy.loc[0, ‘col_B’] = ‘pear’
修改整列
df_copy[‘col_A’] = df_copy[‘col_A’] * 10
修改满足条件的多个值
df_copy.loc[df_copy[‘col_D’] == False, ‘col_C’] = -1.0
print(“Modified DataFrame:\n”, df_copy)
“` -
添加列:
- 直接赋值:
df['new_col'] = values
(values 可以是 Series、列表、数组,长度需匹配,或标量) - 使用
.assign()
: 创建一个包含新列的 新 DataFrame,原 DataFrame 不变。支持链式调用。
“`python
添加标量值列
df_copy[‘source’] = ‘manual’
添加基于现有列计算的新列
df_copy[‘col_A_plus_100’] = df_copy[‘col_A’] + 100
添加 Series (需要索引对齐)
new_series = pd.Series([100, 200, 300, 400, 500], index=df_copy.index)
df_copy[‘new_s’] = new_seriesprint(“\nDataFrame with new columns:\n”, df_copy)
使用 .assign()
df_assigned = df_from_dict.assign(
col_E = df_from_dict[‘col_A’] ** 2,
col_F = lambda x: x[‘col_C’] * 100 # 可以使用 lambda 函数
)
print(“\nDataFrame created with .assign():\n”, df_assigned)
print(“\nOriginal DataFrame is unchanged:\n”, df_from_dict)
“` - 直接赋值:
-
删除列/行: 使用
.drop()
方法。axis=0
删除行 (默认)。axis=1
删除列。labels
: 要删除的索引标签或列名(单个或列表)。inplace=True
: 直接修改原 DataFrame,而不是返回副本(谨慎使用)。
“`python
删除行 (标签 0 和 2)
df_dropped_rows = df_from_dict.drop([0, 2], axis=0) # axis=0 可省略
print(“DataFrame after dropping rows 0 and 2:\n”, df_dropped_rows)删除列 (‘col_B’ 和 ‘col_D’)
df_dropped_cols = df_from_dict.drop([‘col_B’, ‘col_D’], axis=1)
print(“\nDataFrame after dropping columns B and D:\n”, df_dropped_cols)原地删除列 ‘col_C’ (不推荐,除非明确需要)
df_copy_for_inplace = df_from_dict.copy()
df_copy_for_inplace.drop(‘col_C’, axis=1, inplace=True)
print(“\nDataFrame after inplace drop:\n”, df_copy_for_inplace)
“`
-
处理缺失值 (NaN):
.isnull()
/.isna()
: 检测缺失值,返回布尔型 DataFrame。.notnull()
/.notna()
: 检测非缺失值。.dropna()
: 删除包含缺失值的行或列。axis=0
: 删除包含 NaN 的行 (默认)。axis=1
: 删除包含 NaN 的列。how='any'
: 只要有一个 NaN 就删除 (默认)。how='all'
: 所有值都是 NaN 才删除。subset
: 指定检查 NaN 的列/行标签。
.fillna(value)
: 用指定值填充 NaN。value
: 可以是标量、字典 (按列填充)、Series、DataFrame。method='ffill'
: 用前一个有效值填充 (forward fill)。method='bfill'
: 用后一个有效值填充 (backward fill)。
“`python
df_nan = df_from_series.copy() # 使用之前创建的含 NaN 的 DataFrame
print(“Original DataFrame with NaNs:\n”, df_nan)print(“\nCheck for NaNs:\n”, df_nan.isnull())
print(“\nSum of NaNs per column:\n”, df_nan.isnull().sum())删除包含任何 NaN 的行
print(“\nDrop rows with any NaN:\n”, df_nan.dropna(axis=0, how=’any’))
删除包含任何 NaN 的列
print(“\nDrop columns with any NaN:\n”, df_nan.dropna(axis=1, how=’any’))
用 0 填充所有 NaN
print(“\nFill all NaNs with 0:\n”, df_nan.fillna(0))
按列指定填充值
fill_values = {‘X’: -1, ‘Y’: -2, ‘Z’: -3}
print(“\nFill NaNs with column-specific values:\n”, df_nan.fillna(value=fill_values))使用前向填充
print(“\nForward fill NaNs:\n”, df_nan.fillna(method=’ffill’))
“`
七、 其他常用操作
-
排序:
.sort_values()
和.sort_index()
.sort_values(by='col_name', ascending=True)
: 按指定列的值排序。by
可以是列名列表。ascending
控制升序/降序。.sort_index(axis=0, ascending=True)
: 按索引排序。axis=0
按行索引,axis=1
按列索引。
“`python
按 col_C 降序排序
df_sorted_values = df_from_dict.sort_values(by=’col_C’, ascending=False)
print(“Sorted by col_C descending:\n”, df_sorted_values)按行索引降序排序 (df_with_index)
df_sorted_index = df_with_index.sort_index(axis=0, ascending=False)
print(“\nSorted by index descending:\n”, df_sorted_index)
“` -
应用函数:
.apply(func, axis=0)
: 将函数func
应用于每列 (axis=0, 默认) 或每行 (axis=1)。函数接收 Series 作为输入。.applymap(func)
: 将函数func
应用于 DataFrame 的每个元素。.map(func)
: (用于 Series) 将函数或字典映射应用于 Series 的每个元素。
“`python
对每列求最大值 (axis=0)
print(“Max value per column:\n”, df_from_dict[[‘col_A’, ‘col_C’]].apply(np.max))
对每行求和 (axis=1)
print(“\nSum per row (only numeric cols):\n”, df_from_dict[[‘col_A’, ‘col_C’]].apply(np.sum, axis=1))
对所有数值元素应用自定义函数 (保留结构)
numeric_df = df_from_dict[[‘col_A’, ‘col_C’]]
print(“\nApply lambda function element-wise:\n”, numeric_df.applymap(lambda x: f'{x:.2f}’)) # 格式化为两位小数对 col_B 应用 map (Series 操作)
mapping = {‘apple’: ‘fruit’, ‘banana’: ‘fruit’, ‘orange’: ‘fruit’, ‘grape’:’fruit’, ‘melon’:’fruit’}
print(“\nMap categories to col_B:\n”, df_from_dict[‘col_B’].map(mapping.get)) # Use .get to handle potential misses gracefully
“` -
分组聚合 (Group By): 类似于 SQL 的 GROUP BY 操作,遵循 “Split-Apply-Combine” 模式。
df.groupby('key_col')
: 按 ‘key_col’ 的值对 DataFrame 进行分组,返回一个 GroupBy 对象。grouped.agg(func)
/grouped.aggregate(func)
: 对分组后的数据应用聚合函数 (如sum
,mean
,count
,size
,min
,max
, 自定义函数)。
“`python
创建一个带分组键的示例 DataFrame
data_group = {‘Category’: [‘A’, ‘B’, ‘A’, ‘B’, ‘A’],
‘Value1’: [10, 20, 15, 25, 12],
‘Value2’: [100, 200, 150, 250, 120]}
df_group = pd.DataFrame(data_group)按 Category 分组
grouped = df_group.groupby(‘Category’)
计算每个分组的均值
print(“Mean per category:\n”, grouped.mean())
计算每个分组的大小 (行数)
print(“\nSize per category:\n”, grouped.size())
对不同列应用不同的聚合函数
agg_funcs = {
‘Value1’: [‘sum’, ‘mean’, ‘count’],
‘Value2’: [‘min’, ‘max’]
}
print(“\nMultiple aggregations per category:\n”, grouped.agg(agg_funcs))
“` -
合并与连接 (Merge & Join): 用于合并多个 DataFrame。
pd.merge(left_df, right_df, on='key_col', how='inner')
: 类似于 SQL 的 JOIN。on
指定连接键,how
指定连接方式 (inner
,outer
,left
,right
)。df1.join(df2, on='key_col', how='left')
: 主要基于索引进行连接,但也可以通过on
指定df1
中的列来与df2
的索引连接。
(详细用法较为复杂,此处仅作概念介绍)
八、 数据输入与输出 (I/O)
Pandas 提供了强大的 I/O 工具,方便读写各种格式。
- 写入 CSV:
df.to_csv('output.csv', index=False)
(常用index=False
避免将索引写入文件) - 写入 Excel:
df.to_excel('output.xlsx', sheet_name='Data', index=False)
- 其他格式:
to_json()
,to_sql()
,to_parquet()
,to_pickle()
等。
九、 总结
Pandas DataFrame 是 Python 数据分析不可或缺的工具。它提供了丰富的功能来创建、检查、选择、清洗、转换、聚合和可视化数据。本文详细介绍了 DataFrame 的核心基础知识,包括:
- DataFrame 的概念和优势。
- 多种创建 DataFrame 的方法。
- 检查 DataFrame 基本信息的常用属性和方法。
- 强大的数据选择和索引技术 (
[]
,.loc
,.iloc
, 布尔索引)。 - 数据修改、添加/删除列、处理缺失值。
- 排序、函数应用、分组聚合等常用操作。
- 基本的数据读写。
掌握这些基础知识是进行更高级数据分析和机器学习任务的前提。虽然 Pandas 功能远不止于此(例如时间序列处理、多级索引、数据透视表等),但理解并熟练运用这些基本概念,将使您能够高效地处理绝大多数结构化数据任务。建议通过实际操作和练习来巩固这些知识,逐步探索 Pandas 的更多强大功能。