我有两个桌子。数据表和过滤器表。我想将筛选器表应用于数据表以仅选择某些记录。当过滤器表的列中有#时,将忽略过滤器。另外,可以使用|来应用多个选择。分隔符。
我使用了带&和|的for循环来实现这一点。条件。但是,鉴于我的过滤器表很大,我想知道是否有更有效的方法来实现这一目标。我的过滤器表如下:
import pandas as pd
import numpy as np
f = {'business':['FX','FX','IR','IR','CR'],
'A/L':['A','L','A','L','#'],
'Company':['207|401','#','#','207','#']}
filter = pd.DataFrame(data=f)
filter
,数据表如下:
d = {'business': ['FX','a','CR'],
'A/L': ['A','A','L'],
'Company': ['207','1','2']}
data = pd.DataFrame(data=d)
data
最终,过滤器如下所示:
for counter in range (0, len(filter)):
businessV = str(filter.iat[counter,0])
ALV = str(filter.iat[counter,1])
CompanyV = str(filter.iat[counter,2])
businessV1 = businessV.split("|", 100)
ALV1 = ALV.split("|", 100)
CompanyV1 = CompanyV.split("|", 100)
businessV2 = ('#' in businessV1)| (data['business'].isin(businessV1))
ALV2 = ('#' in ALV1)|(data['A/L'].isin(ALV1))
CompanyV2 = ('#' in CompanyV1)| (data['Company'].isin(CompanyV1))
final_filter = businessV2 & ALV2 & CompanyV2
print(final_filter)
我正在尝试找到一种更有效的方法,以使用过滤器表中的过滤器选择数据表中的第一行和最后一行。
具体地说,我想知道如何:
答案 0 :(得分:1)
这是一个相当复杂的问题。我将通过复制包含'|'
的行来对过滤器表进行预处理,使其每个字段只有一个值。为了限制无用的行数,我首先将包含'#'
和其他值的所有内容替换为一个'#'
。
完成此操作后,可以合并到不包含任何锋利字符的列中,并使用merge
从业务表中选择行。
代码可能是:
# store the original column names
cols = filter.columns
# remove any alternate value if a # is already present:
tosimp = pd.DataFrame({col: filter[col].str.contains('#')&
filter[col].str.contains('\|')
for col in cols})
# add a column to store in a (hashable) tuple the columns with no '#'
filter['wild'] = filter.apply(lambda x: tuple(col for col in cols
if x[col] != '#'), axis=1)
# now explode the fields containing a '|'
tosimp = pd.DataFrame({col: filter[col].str.contains('\|')
for col in filter.columns})
# again, store in a new column the columns containing a '|'
tosimp['wild'] = filter.apply(lambda x: tuple(col for col in cols
if '|' in filter.loc[x.name, col]),
axis=1)
# compute a new filter table with one single value per field (or #)
# by grouping on tosimp['wild']
dfl = [filter[tosimp['wild'].astype(str)=='()']]
for k, df in filter[tosimp['wild'].astype(str)!='()'].groupby(tosimp['wild']):
for ix, row in df.iterrows():
tmp = pd.MultiIndex.from_product([df.loc[ix, col].split('|')
for col in k], names=k).to_frame(None)
l = len(tmp)
dfl.append(pd.DataFrame({col: tmp[col]
if col in k else [row[col]] * l
for col in filter.columns}))
filter2 = pd.concat(dfl)
# Ok, we can now use that new filter table to filter the business table
result = pd.concat([data.merge(df, on=k, suffixes=('', '_y'),
right_index=True)[cols]
for k, df in filter2.groupby('wild')]).sort_index()
限制:
iterrows
调用:在大型过滤器表上可能要花一些时间'#'
的行。如果是可能的用例,则必须在进行任何其他处理之前对其进行搜索。无论如何,业务表中的任何行都将保留。 pd.concat(...
行的说明:
[... for k, df in filter2.groupby('wild')]
:将过滤器数据框划分为子数据框,每个子数据框具有不同的wild
值,即不同的一组非#字段data.merge(df, on=k, suffixes=('', '_y'), right_index=True)
:将每个子过滤器数据框与非#字段上的数据数据框合并,即从数据数据框中选择与这些过滤器行之一匹配的行。保留数据数据帧的原始索引...[cols]
仅保留相关字段pd.concat(...)
合并所有这些部分数据帧... .sort_index()
根据其索引对连接的数据帧进行排序,这是通过构造原始数据数据帧的索引来实现的。答案 1 :(得分:0)
我对您的问题的理解是,您希望business,A/L
的所有第一个匹配项都在相应的过滤器中指定(如果使用Company
,则指定#
)。
我假设您的预期结果是一个仅带有data
第一行的数据框。当过滤器变大时,可以通过对过滤器使用联接操作来加快处理速度,并只保留第一个结果。
# Split on | so that every option is represented in a single row
filter0 = filter.set_index(['business','A/L']).Company.str.split('|',expand=True).stack().reset_index().drop('level_2',axis=1).rename(columns={0:'Company'})
# The set of *all* rows in data which are caught by filters with a Company specification
r1 = data.merge(filter0[filter0.Company != '#'])
# The set of *all* rows in data which are caught by filters allowing for *any* Company
r2 = data.merge(filter0[filter0.Company == '#'].drop('Company', axis=1))
# r1 and r2 are not necessarily disjoint, and each one may have multiple rows that pass one filter
# Take the union, sort on the index to preserve the original ordering,
# then finally drop duplicates of business+A/L, keeping only the first entry
pd.concat([r1,r2]).drop_duplicates(subset=['business','A/L'], keep='first')
关于您在过滤器上处理多列的情况:过滤器中的单行本质上说的是,
“我要field1=foo
和field2=bar
和field3=baz1 OR field3=baz2
和field4=qux1 OR field4=qux2
。”
主要思想是将其扩展为仅由AND条件组成的多行,因此在这种情况下,它将变为四行。
field1=foo
和field2=bar
和field3=baz1
和field4=qux1
field1=foo
和field2=bar
和field3=baz1
和field4=qux2
field1=foo
和field2=bar
和field3=baz2
和field4=qux1
field1=foo
和field2=bar
和field3=baz2
和field4=qux2
换句话说,多次使用.split
和.stack
,对于具有OR条件的每一列一次。这可能效率不高(在某个地方使用itertools.product
可能会提高速度和代码可读性),但是您的瓶颈通常在联接操作中,因此就速度而言,这不必太担心。