深入解析 Pandas Series 数据结构与核心操作
一、Series 核心概念与初始化
1. 核心概念
在 Pandas 中,Series 是一种一维的带标签数组结构。它能够容纳任意数据类型(如整数、浮点数、字符串或 Python 对象)。其轴标签通常被称为索引(Index)。与 NumPy 的一维数组(ndarray)相比,Series 的最大特点是自带索引;与 Python 字典相比,它更像是一个具有严格顺序的字典。
import numpy as np
import pandas as pd
# 生成包含5个随机数的 Series
random_series = pd.Series(np.random.rand(5))
print(random_series)
# 检查数据类型与底层结构
print(f"对象类型: {type(random_series)}")
print(f"索引类型: {type(random_series.index)}") # 通常为 RangeIndex
print(f"值类型: {type(random_series.values)}") # 底层为 NumPy ndarray
2. 初始化方法
通过字典创建
当使用字典初始化 Series 时,字典的键(Key)会自动转换为索引,字典的值(Value)则成为数据。如果值的数据类型不统一,Pandas 会自动进行类型推断或将其转换为 object 类型。
data_dict = {'x': 10, 'y': 20, 'z': 30, 'w': 'text'}
dict_series = pd.Series(data_dict)
print(dict_series)
通过一维数组创建
使用 NumPy 数组或 Python 列表创建时,可以通过 index 参数自定义索引,并通过 dtype 强制指定数据类型。
array_data = np.random.randn(4)
# 自定义索引并指定数据类型为 object
array_series = pd.Series(array_data, index=['A', 'B', 'C', 'D'], dtype=object)
print(array_series)
通过标量创建
如果传入的是一个标量值,则必须提供 index 参数。Pandas 会将该标量值广播(重复)以匹配索引的长度。
scalar_series = pd.Series(99.9, index=range(3))
print(scalar_series)
3. 名称属性管理
Series 对象可以拥有一个名称(name),这在将多个 Series 组合成 DataFrame 时非常有用。
unnamed_series = pd.Series([1, 2, 3])
named_series = pd.Series([4, 5, 6], name='metrics')
print(f"未命名: {unnamed_series.name}, 已命名: {named_series.name}")
# 使用 rename 方法修改名称,返回新对象,原对象不变
renamed_series = named_series.rename('new_metrics')
print(f"重命名后: {renamed_series.name}, 原对象: {named_series.name}")
二、Series 索引与数据选择
1. 位置索引
类似于 Python 列表,可以通过从 0 开始的整数位置来访问元素。需要注意的是,Pandas 的标签索引机制可能导致负数位置索引(如 s[-1])引发 KeyError,具体取决于索引的数据类型。
pos_series = pd.Series(np.random.rand(4))
first_element = pos_series[0]
print(f"首个元素: {first_element}, 类型: {type(first_element)}")
# 转换为原生 Python float
print(f"原生浮点数: {float(first_element)}")
2. 标签索引
通过自定义的标签(Index)来访问数据。如果需要提取多个不连续的标签,可以传入一个标签列表。
label_series = pd.Series([10, 20, 30, 40], index=['a', 'b', 'c', 'd'])
print(f"单个标签: {label_series['b']}")
# 多标签选择,返回一个新的 Series
multi_select = label_series[['a', 'c', 'd']]
print(multi_select)
3. 切片操作
Series 支持切片操作。需要特别注意的是:基于位置的切片是左闭右开区间,而基于标签的切片是左闭右闭区间(包含末端)。
slice_series = pd.Series([1, 2, 3, 4, 5], index=['v', 'w', 'x', 'y', 'z'])
# 位置切片 (不包含索引3)
print("位置切片:\n", slice_series[1:3])
# 标签切片 (包含标签 'x')
print("标签切片:\n", slice_series['w':'x'])
4. 布尔索引与条件过滤
通过传入布尔数组或条件表达式,可以过滤出满足特定条件的数据。结合 isnull() 和 notnull() 可以轻松处理缺失值。
filter_series = pd.Series([15, 85, np.nan, 45, 90])
# 条件过滤
high_values = filter_series[filter_series > 50]
print("大于50的值:\n", high_values)
# 缺失值处理
valid_data = filter_series[filter_series.notnull()]
print("非空数据:\n", valid_data)
三、Series 进阶操作技巧
1. 数据预览
在处理大型数据集时,使用 head() 和 tail() 可以快速查看数据的头部和尾部,默认显示 5 行,也可传入整数指定行数。
large_series = pd.Series(np.random.rand(100))
print("前3行:\n", large_series.head(3))
print("后2行:\n", large_series.tail(2))
2. 重新索引 (Reindexing)
reindex() 方法用于创建一个符合新索引的新 Series。如果新索引在原数据中不存在,则会填充缺失值(NaN),可以通过 fill_value 参数指定默认填充值。
orig_series = pd.Series([1.1, 2.2, 3.3], index=['x', 'y', 'z'])
# 引入新索引 'w',并设置缺失值填充为 0.0
reindexed = orig_series.reindex(['z', 'y', 'x', 'w'], fill_value=0.0)
print(reindexed)
3. 自动数据对齐
Series 在进行算术运算时,会自动根据索引标签进行对齐,而无需关心数据在内存中的物理顺序。如果两个 Series 存在不重合的索引,计算结果中这些位置将显示为 NaN。
series_alpha = pd.Series([1, 2, 3], index=['A', 'B', 'C'])
series_beta = pd.Series([10, 20, 30], index=['B', 'C', 'D'])
# 自动对齐索引,A和D的位置将产生 NaN
aligned_sum = series_alpha + series_beta
print(aligned_sum)
4. 数据的增删改
删除数据:使用 drop() 方法移除指定标签的数据。默认情况下返回新对象,设置 inplace=True 可直接修改原对象。
新增与修改:可以直接通过标签赋值来修改现有数据或添加新数据。对于批量追加,推荐使用 pd.concat()(注:旧版本中的 append 方法在 Pandas 2.0+ 中已被彻底弃用)。
modify_series = pd.Series([10, 20, 30], index=['p', 'q', 'r'])
# 修改现有值
modify_series['p'] = 99
# 添加新值
modify_series['s'] = 40
print("修改与新增后:\n", modify_series)
# 删除指定标签
dropped_series = modify_series.drop(['q', 'r'])
print("删除后:\n", dropped_series)
# 使用 concat 追加新 Series (替代已废弃的 append)
new_data = pd.Series([50, 60], index=['t', 'u'])
combined_series = pd.concat([modify_series, new_data])
print("追加后:\n", combined_series)