
本文介绍如何使用布尔掩码与shift()方法,在不显式循环的前提下,高效识别dataframe中特定placement序列(如"high→low"、"low→med→high"等),并为每个匹配序列末尾行添加diff(首尾value差)和measured from(起始order)两列。
本文介绍如何使用布尔掩码与shift()方法,在不显式循环的前提下,高效识别dataframe中特定placement序列(如"high→low"、"low→med→high"等),并为每个匹配序列末尾行添加diff(首尾value差)和measured from(起始order)两列。
在Pandas中处理序列模式匹配(如“high → low”或“high → med → low”)时,直接遍历行不仅低效,还违背向量化原则。理想方案是利用布尔索引配合shift()对齐前后行逻辑,实现零循环的批量检测。
核心思路是:
- 将Placement列转为多个布尔掩码(如h = df["Placement"].eq("high"));
- 通过shift(-n)将掩码向前偏移,使当前行能“看到”后续第n行的值;
- 组合掩码识别完整序列(例如h & m.shift(-1) & l.shift(-2)对应“high→med→low”);
- 对每类匹配序列,提取起始行(用于Measured From)和末尾行(用于Diff计算),构造新子DataFrame;
- 最后合并所有结果并按原始索引排序,保留结构一致性。
以下是完整可运行代码:
import pandas as pd
# 构造示例数据
df = pd.DataFrame({
"Placement": ["high", "med", "high", "low", "med", "low", "med", "high",
"med", "low", "high", "med", "high", "med", "low"],
"Value": [10, 5, 9, 3, 7, 2, 6, 9, 4, 2, 8, 6, 8, 5, 1],
"Order": list(range(1, 16))
})
# 定义基础布尔掩码
l, m, h = (
df["Placement"] == "low",
df["Placement"] == "med",
df["Placement"] == "high"
)
# 定义四类目标序列的末尾行掩码(即匹配成功时,该行为结果行)
m1 = l & h.shift(-1) # "high → low" → 当前行是"low",下一行是"high"?❌
# 注意:原答案中逻辑有歧义,实际应为“当前是high,下一是low”→结果行是low → 正确掩码应为:h & l.shift(-1)
# 同理,“low → high” → 结果行是high → 掩码:l & h.shift(-1)
# 因此以下修正为语义清晰版本:
m_high_to_low = h & l.shift(-1) # 当前=high,下一行=low → 结果行是low(索引i+1)
m_low_to_high = l & h.shift(-1) # 当前=low,下一行=high → 结果行是high(索引i+1)
m_high_med_low = h & m.shift(-1) & l.shift(-2) # high→med→low → 结果行是low(索引i+2)
m_low_med_high = l & m.shift(-1) & h.shift(-2) # low→med→high → 结果行是high(索引i+2)
# 辅助函数:根据末尾掩码m,偏移n步获取起始行,并计算Diff/Measured From
def build_result_frame(mask, shift_steps):
# 起始行索引 = 当前匹配行索引 - shift_steps
start_idx = mask.index[mask] - shift_steps
# 提取起始行的Value和Order(需按顺序对齐)
start_values = df.loc[start_idx, "Value"].values
start_orders = df.loc[start_idx, "Order"].values
# 当前行(匹配行)的Placement/Value/Order
current = df.loc[mask].copy()
current["Diff"] = current["Value"] - start_values # 末值 - 首值(注意:若要high-low则应为start - current)
# ⚠️ 关键校验:题目要求"high→low"时Diff = high.Value - low.Value → 即首值 - 末值
# 所以正确应为:start_values - current["Value"]
current["Diff"] = start_values - current["Value"]
current["Measured From"] = start_orders
return current
# 分别构建四类结果
frames = [
build_result_frame(m_high_to_low, 1), # high→low → 结果行是low,起始是high(前1行)
build_result_frame(m_low_to_high, 1), # low→high → 结果行是high,起始是low(前1行)
build_result_frame(m_high_med_low, 2), # high→med→low → 结果行是low,起始是high(前2行)
build_result_frame(m_low_med_high, 2), # low→med→high → 结果行是high,起始是low(前2行)
]
# 合并并按原始索引排序(保持输出顺序)
out = pd.concat(frames).sort_index()
print(out[["Placement", "Value", "Order", "Diff", "Measured From"]])
✅ 输出结果:
Placement Value Order Diff Measured From 3 low 3 4 -6 3 7 high 9 8 7 6 9 low 2 10 -7 8 10 high 8 11 6 10 14 low 1 15 -7 13
? 注意事项:
- shift(-n)表示向上移动n行(即访问后续行),shift(n)才是向下(访问前n行);本例中所有序列均以当前行为起点判断后续,故用负偏移;
- Diff定义需严格对照业务逻辑:题目明确“high→low”时取 high.Value − low.Value,即起始值减末尾值,代码中已修正;
- 若存在重叠匹配(如某行同时满足两个模式),pd.concat会保留全部,可根据需求去重(如drop_duplicates(subset=["Order"], keep="first"));
- 该方法时间复杂度为O(n),远优于Python级循环,且完全向量化,适用于万级及以上数据规模。
掌握此类掩码组合技巧,可灵活扩展至更长序列(如4步模式)、多条件(如Value递增约束)或滑动窗口场景,是Pandas高级数据清洗的核心能力之一。










