如何用Pandas/Polars纯DataFrame操作查找前序更大元素的索引?
问题描述
给定数据:
data = { 'value': [1,9,6,7,3, 2,4,5,1,9] }
需要为每个元素找到最近的前序比当前元素大的元素的行号,预期输出:
[None, 0, 1, 2, 1, 1, 3, 4, 1, 0]
规则说明:
- 第一个元素无前序元素,结果为
None - 第二个元素9大于所有前序元素,取最前面的行号0
- 第三个元素6的最近前序更大元素是9(行号1),结果为1
限制条件:仅使用Pandas或Polars的DataFrame操作实现,禁止使用apply、map_elements、map_rows、iter_rows及任何逐行遍历的Python循环。
Pandas 实现
这个问题属于经典的单调栈场景,我们可以通过Numpy广播结合Pandas的向量化操作实现,避免Python循环:
import pandas as pd import numpy as np df = pd.DataFrame(data) n = len(df) # 构造前序元素与当前元素的比较矩阵,仅保留j < i的位置(下三角区域) value_arr = df['value'].values # 生成矩阵:行i对应当前元素,列j对应前序元素,标记value[j] > value[i] compare_mask = value_arr[None, :] > value_arr[:, None] # 仅保留j < i的有效区域(上三角转置后得到下三角) compare_mask = np.triu(compare_mask, k=1).T # 对每一行找到最大的j(最近的前序符合条件的行号),无符合条件则设为NaN result = np.where(compare_mask.any(axis=1), compare_mask.argmax(axis=1), np.nan) # 将NaN转换为None,匹配预期输出格式 result = [int(idx) if not np.isnan(idx) else None for idx in result] print(result) # 输出:[None, 0, 1, 2, 1, 1, 3, 4, 1, 0]
思路说明
- 利用Numpy广播生成所有前序元素与当前元素的比较矩阵,筛选出
value[j] > value[i]的位置; - 通过
argmax找到每一行最右侧(最大j)的符合条件的位置,即为最近的前序更大元素的行号; - 最后将无符合条件的位置转换为
None。
Polars 实现
Polars可以通过累积计算结合列表操作实现单调栈逻辑,全程使用向量化表达式:
import polars as pl df = pl.DataFrame(data).with_row_index("row_idx") # 通过累积表达式维护单调递减栈逻辑 result_expr = pl.col("value").cumulative_eval( lambda lst: pl.when(lst.len() == 1) # 第一个元素无前置元素,返回None .then(pl.lit(None)) # 后续元素:筛选所有前序中比当前元素大的,取最后一个的行号 .then( pl.struct( val=lst.slice(0, lst.len() - 1), idx=pl.int_range(0, lst.len() - 1) ) .filter(pl.col("val") > lst[-1]) .last() .struct.field("idx") .fill_null(None) ) ) result = df.select(result_expr).to_series().to_list() print(result) # 输出:[None, 0, 1, 2, 1, 1, 3, 4, 1, 0]
思路说明
- 用
cumulative_eval对每一步的累积元素列表进行计算; - 对每个新加入的元素,筛选累积列表中所有比它大的元素对应的行号,取最后一个(最近的);
- 第一个元素直接返回
None,无符合条件的元素时也返回None。
内容的提问来源于stack exchange,提问作者ignoring_gravity
相关产品推荐
相关产品推荐

