在数据分析的日常工作中,我们经常需要处理各种形态的数据 —— 从一维的用户评分序列到二维的交易明细表,选择合适的数据结构往往是高效处理的第一步。pandas 的 Series 和 DataFrame 作为核心数据结构,以其标签化特性和高效的底层实现,成为数据科学家的 “瑞士军刀”。本文将结合完整的数据示例与输出展示,深入解析两者的底层逻辑,让你直观理解每个操作的效果。
一、Series 基础:带标签的动态一维数组
1. 多形态创建:灵活适配数据输入
Series 可以看作是 NumPy 数组的 “增强版”,最大的特点是引入了索引标签,让数据与标签一一绑定。以下是带输出的完整示例:
▍从列表 / 数组创建(自动生成 RangeIndex)
python
import pandas as pd
import numpy as np
# 创建带缺失值的Series
s_list = pd.Series([1, 3, 5, np.nan, 6, 8])
print("列表创建Series:")
print(s_list)
输出结果:
plaintext
0 1.0
1 3.0
2 5.0
3 NaN
4 6.0
5 8.0
dtype: float64
关键点:索引为 0-based 整数,缺失值np.nan
会被自动识别,后续计算默认排除缺失值。
▍从字典创建(键为索引,值为数据)
python
d = {"a": 0.0, "b": 1.0, "c": 2.0}
s_dict = pd.Series(d)
print("\n字典创建Series:")
print(s_dict)
输出结果:
plaintext
a 0.0
b 1.0
c 2.0
dtype: float64
注意:若索引与字典键不匹配(如指定index=["a", "b", "d"]
),缺失值会自动填充为NaN
。
▍从标量值创建(指定索引重复填充)
python
s_scalar = pd.Series(5.0, index=["a", "b", "c"])
print("\n标量值创建Series:")
print(s_scalar)
输出结果:
plaintext
a 5.0
b 5.0
c 5.0
dtype: float64
2. 类数组与类字典特性:无缝切换操作习惯
▍类 ndarray 特性:向量化操作与切片
python
# 向量化运算:对Series应用指数函数
s_vectorized = np.exp(s_list)
print("\n向量化运算结果:")
print(s_vectorized)
输出结果:
plaintext
0 2.718282
1 20.085537
2 148.413159
3 NaN
4 403.428793
5 2980.957987
dtype: float64
▍类 dict 特性:标签访问与安全获取
python
# 标签访问
print("\ns_dict['b']的值:", s_dict["b"]) # 输出:1.0
# 安全获取缺失标签
print("\n安全获取f的值:", s_dict.get("f", np.nan)) # 输出:nan(默认返回NaN)
3. 数据类型与底层存储
▍分类数据类型(节省内存)
python
s_category = pd.Series(["test", "train", "test"], dtype="category")
print("\n分类类型Series:")
print(s_category)
print("数据类型:", s_category.dtype)
输出结果:
plaintext
0 test
1 train
2 test
dtype: category
Categories (2, object): ['test', 'train']
▍底层数组访问
python
# 获取ExtensionArray
print("\n底层数组:", s_category.array)
# 转换为NumPy数组
print("转换为NumPy数组:", s_category.to_numpy())
输出结果:
plaintext
底层数组: <CategoricalArray with categories ['test', 'train']>
转换为NumPy数组: ['test' 'train' 'test']
二、DataFrame 核心:二维结构化数据的终极方案
1. 多元化构建:适配复杂数据场景
▍字典嵌套 Series(自动对齐索引)
python
d = {
"A": pd.Series([1, 2, 3], index=["a", "b", "c"]),
"B": pd.Series([4, 5], index=["b", "c"]) # 索引对齐,a行B列为NaN
}
df_dict_series = pd.DataFrame(d)
print("\n字典嵌套Series创建DataFrame:")
print(df_dict_series)
输出结果:
plaintext
A B
a 1 NaN
b 2 4.0
c 3 5.0
▍结构化数组(字段映射为列)
python
data = np.zeros(2, dtype=[("A", int), ("B", float)])
data[:] = [(1, 2.0), (2, 3.0)]
df_struct = pd.DataFrame(data)
print("\n结构化数组创建DataFrame:")
print(df_struct)
输出结果:
plaintext
A B
0 1 2.0
1 2 3.0
2. 行列操作:索引对齐的魔法
▍列访问与索引对齐
python
# 列访问示例
print("\n访问'A'列:")
print(df_dict_series["A"])
输出结果:
plaintext
a 1.0
b 2.0
c 3.0
Name: A, dtype: float64
▍索引对齐运算
python
df1 = pd.DataFrame({"A": [1, 2], "B": [3, 4]}, index=["x", "y"])
df2 = pd.DataFrame({"A": [5, 6], "B": [7, 8]}, index=["y", "z"])
result = df1 + df2 # 自动对齐索引x/y/z
print("\n索引对齐运算结果:")
print(result)
输出结果:
plaintext
A B
x NaN NaN
y 7.0 11.0
z NaN NaN
3. 动态修改:高效调整数据结构
▍列添加与类型转换
python
# 新增列
df_dict_series["C"] = df_dict_series["A"] * 2
print("\n新增列后:")
print(df_dict_series)
# 转换类型
df_dict_series["A"] = df_dict_series["A"].astype("int32")
print("\n转换A列为int32后:")
print(df_dict_series.dtypes)
输出结果:
plaintext
新增列后:
A B C
a 1 NaN 2
b 2 4.0 4
c 3 5.0 6
转换A列为int32后:
A int32
B float64
C int64
dtype: object
三、索引与数据访问:精准操作的核心逻辑
1. 标签索引.loc[]
:基于语义的精准定位
python
# 创建带日期索引的DataFrame
dates = pd.date_range("2023-01-01", periods=3, names="日期")
df_date = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]}, index=dates)
print("\n标签索引示例:")
print(df_date.loc["2023-01-01":"2023-01-02", ["A", "B"]])
输出结果:
plaintext
A B
日期
2023-01-01 1 4
2023-01-02 2 5
2. 位置索引.iloc[]
:基于整数的机械定位
python
print("\n位置索引示例:")
print(df_date.iloc[0:2, 0]) # 前2行,第1列
输出结果:
plaintext
2023-01-01 1
2023-01-02 2
Name: A, dtype: int64
3. 布尔索引:动态过滤数据
python
# 筛选A列大于1的行
print("\n布尔索引示例:")
print(df_date[df_date["A"] > 1])
输出结果:
plaintext
A B
日期
2023-01-02 2 5
2023-01-03 3 6
四、数据操作与生态集成
1. 向量化运算:性能提升的核心
python
# 标量广播运算
df_scalar = df_date * 2
print("\n向量化运算(乘以2):")
print(df_scalar)
输出结果:
plaintext
A B
日期
2023-01-01 2 8
2023-01-02 4 10
2023-01-03 6 12
2. 缺失值处理:数据清洗的必修课
python
df_missing = df_date.copy()
df_missing.loc["2023-01-01", "B"] = np.nan # 人为添加缺失值
print("\n含缺失值的DataFrame:")
print(df_missing)
# 填充缺失值
df_filled = df_missing.fillna(0)
print("\n填充后:")
print(df_filled)
输出结果:
plaintext
含缺失值的DataFrame:
A B
日期
2023-01-01 1 NaN
2023-01-02 2 5.0
2023-01-03 3 6.0
填充后:
A B
日期
2023-01-01 1 0.0
2023-01-02 2 5.0
2023-01-03 3 6.0
五、高级特性与最佳实践
1. 分组与聚合:复杂统计的利器
python
# 示例数据
df_group = pd.DataFrame({
"类别": ["A", "A", "B", "B"],
"数值": [1, 2, 3, 4]
})
print("\n分组聚合示例:")
print(df_group.groupby("类别")["数值"].sum())
输出结果:
plaintext
类别
A 3
B 7
Name: 数值, dtype: int64
2. 数据合并与重塑:多表联动处理
▍纵向合并(concat)
python
df1 = pd.DataFrame({"A": [1, 2], "B": [3, 4]}, index=[1, 2])
df2 = pd.DataFrame({"A": [5, 6], "B": [7, 8]}, index=[3, 4])
result_concat = pd.concat([df1, df2])
print("\n纵向合并结果:")
print(result_concat)
输出结果:
plaintext
A B
1 1 3
2 2 4
3 5 7
4 6 8
▍透视表(pivot_table)
python
df_pivot = pd.DataFrame({
"日期": ["2023-01-01", "2023-01-01", "2023-01-02", "2023-01-02"],
"产品": ["苹果", "香蕉", "苹果", "香蕉"],
"销量": [10, 20, 15, 25]
})
print("\n透视表结果:")
print(pd.pivot_table(df_pivot, values="销量", index="日期", columns="产品"))
输出结果:
plaintext
产品 香蕉 苹果
日期
2023-01-01 20.0 10.0
2023-01-02 25.0 15.0
总结
通过以上带输出的完整示例,我们深入解析了 Series 和 DataFrame 的核心操作。关键点包括:
- 标签化数据模型:索引对齐是 pandas 的灵魂,确保不同数据源可直接运算;
- 向量化优先:任何情况下优先使用向量化操作(如
df * 2
),避免循环; - 数据类型优化:合理使用分类类型(
category
)、数值类型(int32
)减少内存占用; - 索引精准访问:
.loc[]
和.iloc[]
需严格区分,避免混淆导致的错误。
建议在实际项目中,每次创建数据结构后,用print()
或head()
查看数据形态,确保索引和数据类型符合预期。后续将分享更多关于时间序列分析、性能调优的实战技巧,欢迎关注!
如果觉得本文对你有帮助,欢迎点赞、收藏,你的支持是我持续输出技术干货的动力!