Pandas: Series 入门
最后更新:2026-08-26
Series 是 Pandas 最基础的数据结构——你可以把它想象成"带标签的一维数组"。一个 Series 由两部分组成:数据(NumPy ndarray)和标签(Index)。正是这个 Index 让 Series 超越了普通数组,让数据拥有了"名字"。本节从 Series 的本质出发,带你掌握创建、运算和常用方法。
⚠️ 注意: 以下代码需在本地 Python 环境中运行。
1. 你将学到
- ❶ Series 的本质(ndarray + Index)
- ❷ Index 标签系统与定位方式
- ❸ 创建 Series 的多种方式
- ❹ Series 的向量化运算
- ❺ 常用方法(head / tail / describe / value_counts)
2. Alice 的咖啡店销售日记
(1) 痛点:数字没有名字
Alice 经营一家咖啡店,她用 NumPy 记录了一周的每日销售额:
PYTHON
import numpy as np
sales = np.array([320, 280, 350, 410, 390, 520, 480])
# What day is 520? Position 5... but which day?
print(sales[5]) # 520 — but what does this number mean?
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
数字 520 是周五还是周六?sales[5] 没有告诉你。你必须额外维护一份"位置→星期"的对照表。
(2) 解法:Series 让数据自描述
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:Series 带标签的销售额(难度⭐)
PYTHON
import pandas as pd
sales = pd.Series(
[320, 280, 350, 410, 390, 520, 480],
index=['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
name='daily_sales'
)
print(sales)
# Mon 320
# Tue 280
# Wed 350
# Thu 410
# Fri 390
# Sat 520
# Sun 480
# Name: daily_sales, dtype: int64
print(sales['Sat']) # 520 — label-based access
print(sales.iloc[5]) # 520 — position-based access
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
Series 把数据和标签绑定在一起——sales['Sat'] 比 sales[5] 自解释得多。
(3) 收益:Index 就是数据的名片
有了 Index 标签,数据变得自描述:
- 按标签访问:
s['Mon']比s[0]更清晰 - 对齐运算:两个 Series 运算时自动按标签对齐
- 语义丰富:Index 可以是日期、字符串、甚至多层标签
3. Series 的本质结构
(1) 两大组件:values + index
graph LR
A[Series] --> B[values: NumPy ndarray]
A --> C[index: Index object]
B --> D[320, 280, 350, ...]
C --> E["Mon, Tue, Wed, ..."]
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
| 属性 | 类型 | 说明 |
|---|---|---|
s.values |
np.ndarray | 底层数据数组 |
s.index |
pd.Index | 行标签 |
s.name |
str | Series 名称 |
s.dtype |
np.dtype | 元素数据类型 |
s.shape |
tuple | 形状(一维,如 (7,)) |
s.size |
int | 元素个数 |
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:Series 属性检查(难度⭐)
PYTHON
import pandas as pd
import numpy as np
s = pd.Series([320, 280, 350], index=['Mon', 'Tue', 'Wed'])
print(f"Type of values: {type(s.values)}") # <class 'numpy.ndarray'>
print(f"Type of index: {type(s.index)}") # <class 'pandas.core.indexes.base.Index'>
print(f"dtype: {s.dtype}") # int64
print(f"shape: {s.shape}") # (3,)
print(f"size: {s.size}") # 3
print(f"name: {s.name}") # None
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
(2) Series vs ndarray 对比
| 特性 | NumPy ndarray | Pandas Series |
|---|---|---|
| 标签 | 无(仅位置) | 有(Index) |
| 混合类型 | 不支持 | 不推荐但可(object) |
| 缺失值 | np.nan(仅浮点) | NaN / pd.NA(全覆盖) |
| 运算对齐 | 按位置 | 按标签 |
| 名称 | 无 | name 属性 |
| 常用统计 | np.mean 等 | .mean() 等方法链 |
| 底层 | ndarray | ndarray + Index |
📌 重点: Series 本质上就是"ndarray + Index + name"。当你只使用
.values 时,它退化为普通 ndarray。
4. 创建 Series 的多种方式
(1) 从列表创建
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:从列表创建 Series(难度⭐)
PYTHON
import pandas as pd
# Default index: 0, 1, 2, ...
s1 = pd.Series([10, 20, 30, 40])
print(s1)
# 0 10
# 1 20
# 2 30
# 3 40
# Custom index
s2 = pd.Series([10, 20, 30, 40], index=['a', 'b', 'c', 'd'])
print(s2['b']) # 20
# With name
s3 = pd.Series([10, 20, 30], name='scores')
print(s3.name) # scores
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
(2) 从字典创建
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:从字典创建 Series(难度⭐)
PYTHON
import pandas as pd
# Keys become index, values become data
population = pd.Series({
'Tokyo': 13960000,
'New York': 8336000,
'London': 8982000,
'Paris': 2161000
})
print(population)
# Tokyo 13960000
# New York 8336000
# London 8982000
# Paris 2161000
print(population['London']) # 8982000
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
字典创建的优势:键自动成为 Index,无需手动指定。
(3) 从 NumPy ndarray 创建
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:从 ndarray 创建 Series(难度⭐)
PYTHON
import numpy as np
import pandas as pd
arr = np.array([3.14, 2.72, 1.41, 1.73])
s = pd.Series(arr, index=['pi', 'e', 'sqrt2', 'sqrt3'])
print(s)
# pi 3.14
# e 2.72
# sqrt2 1.41
# sqrt3 1.73
print(f"Underlying type: {type(s.values)}") # numpy.ndarray
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
(4) 从标量创建(广播)
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:标量广播创建 Series(难度⭐)
PYTHON
import pandas as pd
# Broadcast scalar to fill all index positions
s = pd.Series(0, index=['A', 'B', 'C', 'D'])
print(s)
# A 0
# B 0
# C 0
# D 0
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
(5) 创建方式对比
| 方式 | 语法 | Index 来源 | 适用场景 |
|---|---|---|---|
| 列表 | pd.Series([1,2,3]) |
默认 RangeIndex | 快速测试 |
| 列表+index | pd.Series([1,2,3], index=...) |
手动指定 | 需要自定义标签 |
| 字典 | pd.Series({'a':1, 'b':2}) |
字典键 | 标签-值对应 |
| ndarray | pd.Series(np.array(...)) |
默认或指定 | NumPy 数据转换 |
| 标量 | pd.Series(0, index=...) |
手动指定 | 初始化填充 |
5. Series 的访问方式
(1) 标签访问 vs 位置访问
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:两种访问方式(难度⭐⭐)
PYTHON
import pandas as pd
s = pd.Series([320, 280, 350, 410],
index=['Mon', 'Tue', 'Wed', 'Thu'])
# Label-based access (preferred)
print(s['Tue']) # 280
print(s.loc['Tue']) # 280 — explicit label access
# Position-based access
print(s.iloc[1]) # 280 — explicit position access
# Slice by label (INCLUDES endpoint)
print(s['Mon':'Wed'])
# Mon 320
# Tue 280
# Wed 350
# Slice by position (EXCLUDES endpoint, like Python)
print(s.iloc[0:3])
# Mon 320
# Tue 280
# Wed 350
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
🔥 易错: 标签切片包含端点(
'Mon':'Wed' 包含 Wed),位置切片不包含端点(iloc[0:3] 不包含位置 3)。这是 Pandas 初学者最常见的混淆点。
(2) 访问方式对比
| 方式 | 语法 | 端点 | 推荐场景 |
|---|---|---|---|
s[label] |
简写 | — | 单个标签 |
s.loc[label] |
显式标签 | 含端点 | 明确意图 |
s.iloc[pos] |
显式位置 | 不含端点 | 按位置索引 |
s[[l1,l2]] |
列表选取 | — | 多个标签 |
6. Series 的运算
(1) 向量化运算
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:Series 向量化运算(难度⭐)
PYTHON
import pandas as pd
sales = pd.Series([320, 280, 350, 410],
index=['Mon', 'Tue', 'Wed', 'Thu'])
# Arithmetic operations
print(sales * 1.1) # 10% price increase
# Mon 352.0
# Tue 308.0
# Wed 385.0
# Thu 451.0
# Comparison operations
print(sales > 300)
# Mon True
# Tue False
# Wed True
# Thu True
# Filter with boolean
print(sales[sales > 300])
# Mon 320
# Wed 350
# Thu 410
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
(2) Index 对齐运算
这是 Series 最重要的特性之一:运算时按 Index 对齐,而非按位置。
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:Index 对齐运算(难度⭐⭐)
PYTHON
import pandas as pd
week1 = pd.Series({'Mon': 320, 'Tue': 280, 'Wed': 350})
week2 = pd.Series({'Tue': 310, 'Wed': 370, 'Thu': 400})
# Auto-align by index, non-matching → NaN
total = week1 + week2
print(total)
# Mon NaN # only in week1
# Tue 590.0 # 280 + 310
# Wed 720.0 # 350 + 370
# Thu NaN # only in week2
# Fill missing with 0 instead of NaN
total_filled = week1.add(week2, fill_value=0)
print(total_filled)
# Mon 320.0
# Tue 590.0
# Wed 720.0
# Thu 400.0
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
💡 提示: 当两个 Series 的 Index 不完全相同时,Pandas 自动对齐——匹配的标签做运算,不匹配的填 NaN。这避免了"位置错位"的隐蔽 bug。使用
.add(fill_value=0) 等方法可替代 + 运算符,指定缺失值填充策略。
7. Series 常用方法
(1) 统计与描述
| 方法 | 说明 | 等价 NumPy |
|---|---|---|
s.mean() |
均值 | np.mean(arr) |
s.median() |
中位数 | np.median(arr) |
s.std() |
标准差 | np.std(arr) |
s.min() / s.max() |
最小/最大 | np.min() / np.max() |
s.sum() |
求和 | np.sum(arr) |
s.describe() |
统计摘要 | 无 |
s.value_counts() |
频率统计 | 无 |
s.unique() |
去重 | np.unique(arr) |
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:统计方法(难度⭐)
PYTHON
import pandas as pd
scores = pd.Series([85, 92, 78, 95, 88, 92, 78, 85, 90, 92],
name='exam_score')
print(scores.describe())
# count 10.000000
# mean 87.500000
# std 5.976143
# min 78.000000
# 25% 85.000000
# 50% 89.000000
# 75% 92.000000
# max 95.000000
print(scores.value_counts())
# 92 3
# 85 2
# 78 2
# 95 1
# 90 1
# 88 1
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
(2) 查看与筛选
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:head / tail / isin(难度⭐)
PYTHON
import pandas as pd
s = pd.Series(range(1, 101)) # 1 to 100
print(s.head()) # first 5
print(s.tail(3)) # last 3
# isin: membership test
fruits = pd.Series(['apple', 'banana', 'cherry', 'apple', 'date'])
print(fruits.isin(['apple', 'cherry']))
# 0 True
# 1 False
# 2 True
# 3 True
# 4 False
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
(3) NaN 处理
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:NaN 处理方法(难度⭐)
PYTHON
import pandas as pd
import numpy as np
s = pd.Series([10, np.nan, 30, np.nan, 50])
print(s.isna()) # detect NaN
# 0 False
# 1 True
# 2 False
# 3 True
# 4 False
print(s.notna()) # detect non-NaN
print(s.dropna()) # remove NaN: [10.0, 30.0, 50.0]
print(s.fillna(0)) # fill NaN with 0
print(s.fillna(s.mean())) # fill with mean value
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
8. 完整示例:咖啡店一周销售分析
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:咖啡店销售全流程分析(难度⭐⭐⭐)
PYTHON
import pandas as pd
import numpy as np
# ============================================
# Comprehensive example: Coffee shop weekly
# sales analysis using Series
# ============================================
# 1. Create sales data with day labels
sales = pd.Series({
'Mon': 320, 'Tue': 280, 'Wed': 350,
'Thu': 410, 'Fri': 390, 'Sat': 520, 'Sun': 480
}, name='daily_sales_USD')
# 2. Basic statistics
print("=== Weekly Sales Report ===")
print(f"Total: ${sales.sum():,}")
print(f"Daily avg: ${sales.mean():.0f}")
print(f"Best day: {sales.idxmax()} (${sales.max():,})")
print(f"Worst day: {sales.idxmin()} (${sales.min():,})")
print(f"Std dev: ${sales.std():.0f}")
# 3. Weekend vs weekday analysis
weekday_avg = sales[['Mon','Tue','Wed','Thu','Fri']].mean()
weekend_avg = sales[['Sat','Sun']].mean()
print(f"\nWeekday avg: ${weekday_avg:.0f}")
print(f"Weekend avg: ${weekend_avg:.0f}")
print(f"Weekend boost: {(weekend_avg/weekday_avg - 1)*100:.1f}%")
# 4. Daily change
change = sales.pct_change() * 100
print(f"\nBiggest daily drop: {change.min():.1f}% on {change.idxmin()}")
print(f"Biggest daily gain: {change.max():.1f}% on {change.idxmax()}")
# 5. Cumulative sales
cum_sales = sales.cumsum()
print(f"\nSales target $2,000 reached on: {cum_sales[cum_sales >= 2000].index[0]}")
# 6. Ranking
print(f"\nSales ranking:\n{sales.rank(ascending=False).astype(int)}")
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
预期输出(节选):
TEXT
📖 仅展示
=== Weekly Sales Report ===
Total: $2,750
Daily avg: $393
Best day: Sat ($520)
Worst day: Tue ($280)
Std dev: $82
Weekday avg: $350
Weekend avg: $500
Weekend boost: 42.9%
Biggest daily drop: -12.5% on Tue
Biggest daily gain: 33.3% on Sat
❓ 常见问题
Q Series 和 ndarray 区别是什么?
A Series = ndarray + Index + name。Series 的底层 values 就是 ndarray,但多了标签索引和名称。运算时 Series 按 Index 对齐,ndarray 按位置对齐。当你不需要标签时,用
.values 提取 ndarray 即可。Q Index 可以重复吗?
A 可以。Pandas 允许重复 Index,但会降低性能且访问重复标签时返回多个值(一个 Series 而非标量)。建议尽量保持 Index 唯一,用
s.index.is_unique 检查。Q 运算时 NaN 怎么处理?
A 默认 NaN 传播——任何涉及 NaN 的运算结果都是 NaN。使用
s.mean(skipna=True) 等方法自动跳过 NaN(默认行为)。s.dropna() 删除缺失值,s.fillna() 填充缺失值。Q value_counts 和 unique 区别?
A
unique() 返回去重后的数组(不计数),value_counts() 返回每个值出现的频率(降序排列)。统计频率用 value_counts,只需去重列表用 unique。Q Series 能存不同类型吗?
A 技术上可以(dtype=object),但不推荐。混合类型的 Series 失去向量化运算能力,性能大幅下降。需要混合类型时应该用 DataFrame——每列独立类型。
Q loc 和 iloc 用哪个好?
A 需要按标签访问用 loc,按位置访问用 iloc。推荐用 loc/iloc 显式声明意图,避免
s[...] 的歧义(可能是标签也可能是位置)。Q s[label] 和 s.loc[label] 区别?
A 对 Series 来说效果相同。但
s[...] 有歧义——可能是标签也可能是位置(当 Index 是整数时)。s.loc[label] 明确表示按标签,s.iloc[pos] 明确表示按位置,推荐显式写法。📖 小节
- Series 本质是 ndarray + Index + name,底层 values 就是 NumPy 数组
- Index 让数据自描述,支持按标签访问(loc)和按位置访问(iloc)
- 创建方式:列表 / 字典 / ndarray / 标量广播,字典创建最自然
- Series 运算自动按 Index 对齐,不匹配的标签产生 NaN
- 标签切片包含端点(
'Mon':'Wed'含 Wed),位置切片不含端点(iloc[0:3]不含3) - 常用方法:describe() / value_counts() / head() / tail() / isna() / fillna() / dropna()
- Series 适合一维数据,二维表格数据用 DataFrame
📝 作业
- 基础题(难度⭐):用 3 种方式创建 Series(列表+index / 字典 / ndarray),分别打印 index 和 values 属性。
- 进阶题(难度⭐⭐):创建两个 Index 不完全相同的 Series(如月度销售额),用 add(fill_value=0) 做加法,对比直接用 + 运算的结果差异。
- 挑战题(难度⭐⭐⭐):用 Series 模拟一个产品的月度销售数据(12 个月),完成:计算累计销售额 → 找到累计突破 50,000 的月份 → 计算环比增长率 → 找出增长率最高和最低的月份。