悠悠楠杉
从用户输入筛选PandasDataFrame的实用指南
首先,明确一点:用户输入本质上是不可信的。无论来自表单、URL 参数还是 API 请求,都必须经过清洗和验证。假设我们有一个包含“商品名”、“类别”、“售价”和“上架日期”的 DataFrame:
python
import pandas as pd
from datetime import datetime
data = {
'product': ['笔记本电脑', '无线鼠标', '机械键盘', '平板电脑'],
'category': ['电子', '外设', '外设', '电子'],
'price': [5999, 129, 499, 3299],
'dateadded': pd.todatetime(['2023-01-15', '2023-02-20', '2023-03-10', '2023-04-05'])
}
df = pd.DataFrame(data)
现在,用户希望查找“类别为‘外设’且价格低于500”的商品。我们可以构建一个条件字典来接收用户输入:
python
filters = {
'category': '外设',
'max_price': 500,
'keyword': '鼠'
}
接下来的关键是如何逐步应用这些条件。一个清晰的做法是初始化一个掩码(mask),然后逐个叠加过滤逻辑:
python
mask = pd.Series([True] * len(df)) # 初始为全真
if filters.get('category'):
mask &= df['category'] == filters['category']
if filters.get('maxprice'): mask &= df['price'] <= filters['maxprice']
if filters.get('keyword'):
mask &= df['product'].str.contains(filters['keyword'], case=False)
这种方式的优势在于逻辑清晰、易于调试,并且避免了字符串拼接带来的安全隐患。更重要的是,它支持部分条件为空时的优雅跳过——用户没填某项,就不参与筛选。
对于日期范围这类复杂条件,也可以如法炮制。例如用户输入起止时间:
python
startdate = '2023-02-01'
enddate = '2023-03-31'
if startdate:
mask &= df['dateadded'] >= pd.todatetime(startdate)
if enddate:
mask &= df['dateadded'] <= pd.todatetime(enddate)
值得注意的是,str.contains 默认使用正则表达式,若用户输入包含特殊字符(如点号、括号),可能引发解析错误。因此,在实际应用中应设置 regex=False,除非明确需要正则功能。
此外,对于数值型字段,务必进行类型校验。用户输入的“价格”可能是字符串,甚至恶意代码。建议在进入筛选逻辑前统一做类型转换与异常捕获:
python
try:
max_price = float(filters['max_price'])
except (TypeError, ValueError):
max_price = None
最后,返回结果时也应考虑空集情况。若筛选后无匹配数据,应友好提示而非抛出异常:
python
result = df[mask]
if result.empty:
print("未找到符合条件的数据")
else:
print(result)
整个流程下来,我们没有依赖 .query() 这类字符串执行方法,而是通过结构化条件组合实现了安全可控的动态筛选。这种方法不仅适用于 Web 后端,也能用于 Jupyter 中的交互式分析,让数据探索更加贴近真实业务需求。
