如何使用 Pandas 将查询与单个外部变量结合起来
Posted
技术标签:
【中文标题】如何使用 Pandas 将查询与单个外部变量结合起来【英文标题】:How to combine queries with a single external variable using Pandas 【发布时间】:2021-07-26 07:20:38 【问题描述】:我正在尝试通过 html 表单 (@search) 接受由逗号分隔的许多搜索词的可变输入,并查询数据框的 2 列。
每个列查询都独立工作,但我无法让它们以和/或方式一起工作。
第一列查询:
filtered = df.query ('`Drug Name` in @search')
第二列查询:
filtered = df.query ('BP.str.contains(@search, na=False)', engine='python')
编辑 像这样组合:
filtered = df.query ("('`Drug Name` in @search') and ('BP.str.contains(@search, na=False)', engine='python')")
给出以下错误,在引擎参数中突出显示 python 标识符
SyntaxError: Python 关键字在 numexpr 查询中无效标识符
编辑 2
数据框是从 excel 文件中读取的,其中包含以下列: 药物名称(包含单个药物名称)、BP、U&E(带有长描述性文本条目)
搜索词将通过html表单输入:
search = request.values.get('searchinput').replace(" ","").split(',')
作为患者有时可能使用的药物列表,并添加与药物使用相关的特定条件。示例用户输入:
卡托普利、扑热息痛、肾脏疾病、慢性
我希望对照特定药物名称检查列表,并检查其他列(例如 BP 和 U&E)是否提及搜索词。
编辑 3
抱歉,但尝试实施给出的答案会给我带来一堆错误。我下面的内容是我所追求的 90%,让我搜索两个列,包括“BP”的全部内容。但是我只能通过终端搜索单个术语,如果我 #out 并交换收集使用输入的行(从与终端相对的 html 表单中获取)我得到:
TypeError: unhashable type: 'list'
@app.route('/', methods=("POST", "GET"))
def html_table():
searchterms = []
#searchterms = request.values.get('searchinput').replace(" ","").split(',')
searchterms = input("Enter drug...")
filtered = df.query('`Drug Name` in @searchterms | BP.str.contains(@searchterms, na=False)', engine='python')
return render_template('drugsafety.html', tables=[filtered.to_html(classes='data')], titles=['na', 'Drug List'])
<form action="" method="post">
<p><label for="search">Search</label>
<input type="text" name="searchinput"></p>
<p><input type="submit"></p>
</form>
样本数据
BP 列的内容可能很长、描述性和可变性,但一个例子是:
每 12 个月 - CKD 患者每 3 至 6 个月一次。
Drug Name BP U&E
Perindopril Every 12 months Not needed
Alendronic Acid Not needed Every 12 months
Allopurinol Whilst titrating - 3 months Not needed
用这一行:
searchterms = request.values.get('searchinput')
在 html 表单输出中输入“月”:
1 Perindopril Every 12 months Not needed
14 Allopurinol Whilst titrating – 3 months Not needed
一切都好。
在 html 表单输出中输入“阿仑膦酸”:
13 Alendronic Acid Not needed Every 12 months
也不错,但输入“培哚普利,别嘌呤醇”没有返回任何结果。
如果我将行更改为:
searchterms = request.values.get('searchinput').replace(" ","").split(',')
当页面重新加载时,我得到 TypeError: unhashable type: 'list'。
但是 - 如果我再改变:
filtered = df.query('`Drug Name` in @searchterms | BP.str.contains(@searchterms, na=False)', engine='python')
到:
filtered = df.query('`Drug Name` in @searchterms')
然后出现不可哈希的类型错误并输入“培哚普利,别嘌醇” 返回:
1 Perindopril Every 12 months Not needed
14 Allopurinol Whilst titrating – Every 3 months Not needed
但我现在不再在 BP 列中搜索搜索词。
只是想可能是因为 searchterms 是一个列表 '[]' 将其更改为 o 元组 '()' 没有改变任何东西。
非常感谢任何帮助。
【问题讨论】:
这可能只是一个发布错误,但加入的过滤在查询中包含engine
关键字...(即您的 "
没有在正确的位置结束)。
是否可以获取一些示例输入数据?
.query
功能太强大,通常是执行此任务最慢的函数。如果我是你,我会使用 sets intersection
找出 X 中的值在 B 中。请参阅下面的答案。
@Dave 您能否提供一个示例输入,例如像这样:df = pd.DataFrame(data)
或者只是在此处复制数据框的前 10 行,以便我们可以复制粘贴它,如果您可以提供示例用户输入以及预期输出是什么,这也会很有帮助。否则,它更像是猜测而不是帮助。
@Dave 您要搜索所有列(药物名称、BP 和 U&E),还是只搜索药物名称和 BP?如果全部三个,那么搜索“months”将返回所有 3 行(因为“Alendronic Acid”在 U&E 中具有“每 12 个月”),对吗?
【参考方案1】:
我假设您要查询 2 列,并希望在任何查询匹配时返回该行。
在这一行中,问题在于 engine=python 在查询中。
filtered = df.query ("('`Drug Name` in @search') and ('BP.str.contains(@search, na=False)', engine='python')")
应该是
df.query("BP.str.contains(@search, na=False)", engine='python')
如果您执行searchterms = request.values.get('searchinput').replace(" ","").split(',')
,它会将您的字符串转换为单词列表,这将导致Unhashable type list
错误,因为 str.contains 需要 str 作为输入。
你可以做的是使用正则表达式在列表中搜索搜索词,它看起来像这样:
df.query("BP.str.contains('|'.join(@search), na=False, regex=True)", engine='python')
它的作用是使用正则表达式搜索所有单个单词。 ('|'.join(@search)
将是 "searchterm_1|search_term2|..." 并且 "|" 用于表示或在正则表达式中,因此它会在 BP 列值中查找 searchterm_1 或 searchterm_2)
要合并两个查询的输出,您可以分别运行它们并连接结果
pd.concat([df.query("`Drug Name` in @search", engine='python'),df.query("BP.str.contains('|'.join(@search), na=False, regex=True)", engine='python')])
此外,任何基于字符串的匹配都需要您的字符串完美匹配,包括大小写。因此您可以将数据框和查询中的所有内容小写。同样对于空格分隔的单词,这将删除空格。
如果您对Every 12 months
执行searchterms = request.values.get('searchinput').replace(" ","").split(',')
,它将转换为“每12 个月”。所以你也许可以删除 .replace() 部分并使用searchterms = request.values.get('searchinput').split(',')
【讨论】:
太棒了,非常感谢!也感谢对 unhashable 类型错误的解释。【参考方案2】:如果要在所有列中搜索文本,可以先join
所有列,然后使用str.contains
和至少匹配其中一个词的正则表达式模式检查每一行中的搜索词( term1|term2|...|termN
)。我还添加了flags=re.IGNORECASE
以使搜索不区分大小写:
# search function
def search(searchterms):
return df.loc[df.apply(' '.join, axis=1) # join text in all columns
.str.contains( # check if it contains
'|'.join([ # regex pattern
x.strip() # strip spaces
for x in searchterms.split(',') # split by ','
]), flags=re.IGNORECASE)] # case-insensitive
# test search terms
for s in ['Alendronic Acid', 'months', 'Perindopril, Allopurinol']:
print(f'Search terms: "s"')
print(search(s))
print('-'*70)
输出:
Search terms: "Alendronic Acid"
Drug Name BP U&E
1 Alendronic Acid Not needed Every 12 months
----------------------------------------------------------------------
Search terms: "months"
Drug Name BP U&E
0 Perindopril Every 12 months Not needed
1 Alendronic Acid Not needed Every 12 months
2 Allopurinol Whilst titrating - 3 months Not needed
----------------------------------------------------------------------
Search terms: "Perindopril, Allopurinol"
Drug Name BP U&E
0 Perindopril Every 12 months Not needed
2 Allopurinol Whilst titrating - 3 months Not needed
----------------------------------------------------------------------
附:如果您想将搜索限制在特定列,这里有一个版本可以做到这一点(为方便起见,默认搜索所有列):
# search function
def search(searchterms, cols=None):
# search columns (if None, searches in all columns)
if cols is None:
cols = df.columns
return df.loc[df[cols].apply(' '.join, axis=1) # join text in cols
.str.contains( # check if it contains
'|'.join([ # regex pattern
x.strip() # remove spaces
for x in searchterms.split(',') # split by ','
]), flags=re.IGNORECASE)] # make search case-insensitive
现在如果我只在Drug Name
和BP
中搜索months
,它将不会返回Alendronic Acid
,其中months
仅在U&E
中找到:
search('months', ['Drug Name', 'BP'])
输出:
Drug Name BP U&E
0 Perindopril Every 12 months Not needed
2 Allopurinol Whilst titrating - 3 months Not needed
【讨论】:
【参考方案3】:使用sets
。您可以将文本列更改为集合并检查与输入的交集。其余的是纯pandas
。我从不使用.query
,因为它很慢。
# change your search from list to set
search = set(request.values.get('searchinput').replace(" ","").split(','))
filtered = df.loc[(df['Drug Name'].str.split().map(lambda x: set(x).intersection(search)))
& (df['BP'].str.split().map(lambda x: set(x).intersection(search)))]
print(filtered)
演示:
import pandas as pd
search = set(["apple", "banana", "orange"])
df = pd.DataFrame(
"Drug Name": ["I am eating an apple", "We are banana", "nothing is here"],
"BP": ["apple is good", "nothing is here", "nothing is there"],
"Just": [1, 2, 3]
)
filtered = df.loc[(df['Drug Name'].str.split().map(lambda x: set(x).intersection(search)))
& (df['BP'].str.split().map(lambda x: set(x).intersection(search)))]
print(filtered)
# Drug Name BP Just
# 0 I am eating an apple apple is good 1
更新:
我希望结果也显示我们是香蕉,这里什么都没有,2
这需要 or
是 Pandas 的 |
而不是 and
是 Pandas 的 $
filtered = df.loc[(df['Drug Name'].str.split().map(lambda x: set(x).intersection(search)))
| (df['BP'].str.split().map(lambda x: set(x).intersection(search)))]
print(filtered)
# Drug Name BP Just
# 0 I am eating an apple apple is good 1
# 1 We are banana nothing is here 2
【讨论】:
感谢您的回答,但如果用户输入了苹果、香蕉、橙子。我希望结果也显示我们是香蕉,这里什么都没有,2。 那么条件不是&
(和)而是|
(或)。改变它,你也会有第二行。
我已经更新了答案以显示两个差异【参考方案4】:
在没有样本输入数据的情况下,我使用随机生成的数据集作为展示:
import pandas as pd
import numpy as np
df = pd.DataFrame('Drug_Name':['Drug1','Drug2','Drug3','Drug2','Drug5','Drug3']*4,
'Inv_Type': ['X', 'Y']*12,
'Quant': np.random.randint(2,20, size=24))
# Search 1
search = "Drug3"
df.query('Drug_Name==@search')
# Search 2
search2 = "Y"
df.query ('Inv_Type.str.contains(@search2, na=False)', engine='python')
# Combined (use booleans, such as & or | instead of and or or
df.query ('Drug_Name==@search & Inv_Type.str.contains(@search2, na=False)')
请注意engine='python'
应避免如文档中所述:
同样,您可以传递
engine='python'
以使用 Python 本身作为后端。不建议这样做,因为它是 与使用 numexpr 作为引擎相比效率低。
也就是说,如果你一心想要使用它,你可以这样做:
mask = df["Inv_Type"].str.contains(search2, na=False)
df.query('Drug_Name==@search & @mask')
或者,您可以完全不使用 .query() 来实现同样的目标:
df[(df['Drug_Name']==search) & df['Inv_Type'].str.contains(search2, na=False)]
【讨论】:
以上是关于如何使用 Pandas 将查询与单个外部变量结合起来的主要内容,如果未能解决你的问题,请参考以下文章
珠联璧合太香了,Pandas 与 lambda 合起来用才真棒
如何在 Python 中创建具有两列作为元组或 Pandas 数据框的单个变量?
Pandas:如何将 MultiIndex DataFrame 与单个索引 DataFrame 连接,以及自定义排序