在另一个带通配符的Pandas Dataframe 中查找多列组合

ybzsozfc  于 2023-03-11  发布在  其他
关注(0)|答案(2)|浏览(145)

我正在尝试执行日常协调,它将检查pandas Dataframe 中的主数据集,以检查另一个数据集(协调键)中是否存在各种组合,其中包含通配符。
以下是该问题的说明性描述(未使用我的rec中的真实的信息):

主数据集

| 名字|姓氏|职业|性别问题|
| - ------|- ------|- ------|- ------|
| 安杰拉|诺里斯|消防员|女性|
| 安杰拉|汤普森|教师|女性|
| 本|Json|警务人员|男性|
| 本|彼得森|律师|男性|
| 查理|戴维斯|护士|男性|
| 黛比|史密斯|律师|女性|

对帐键

| 名字|姓氏|职业|性别问题|
| - ------|- ------|- ------|- ------|
| 安杰拉|土耳其 *|土耳其 *|土耳其 *|
| 本|Json|土耳其 *|男性|
| 黛比|土耳其 *|土耳其 *|女性|
我现在要做的是在主数据集中找到:

  • 名字= Angela的任何记录(姓氏、职业、性别有三个通配符)
  • 名字= Ben、姓氏=Json且性别=男性的任何记录(在职业上使用一个通配符)
  • 名字= Debbie且性别=女性的任何记录(姓氏和职业使用两个通配符)

我不知道如何完成这项任务...我通常会创建一个唯一的关键字与组合的名字,姓氏,职业和性别,然后合并两个数据集,但与通配符,这是不适合我。
我还尝试了一个笛卡尔积(我可以稍后过滤),但我正在处理的数据集的大小导致了内存问题,所以它也不起作用。
理想情况下,检查的输出将给予此表(即,显示主数据集中满足协调关键字中的标准的行):
| 名字|姓氏|职业|性别问题|
| - ------|- ------|- ------|- ------|
| 安杰拉|诺里斯|消防员|女性|
| 安杰拉|汤普森|教师|女性|
| 本|Json|警务人员|男性|
| 黛比|史密斯|律师|女性|

s3fp2yjn

s3fp2yjn1#

听起来,也许这种特殊类型的任务更适合“数据库”。
duckdb为例,因为它也可以从 Dataframe 读取:

# probably better to write out the full query
query = "\nand\n".join(
    f"(df2.{column} = '*' or df1.{column} = df2.{column})" 
    for column in df2.columns
)

duckdb.sql(f"""
from df1, df2
select df1.*
where
   {query}
""")

(尽管在提到大量数据时直接使用它可能有意义)

┌───────────┬──────────┬────────────────┬─────────┐
│ FirstName │ LastName │   Occupation   │ Gender  │
│  varchar  │ varchar  │    varchar     │ varchar │
├───────────┼──────────┼────────────────┼─────────┤
│ Angela    │ Norris   │ Firefighter    │ Female  │
│ Angela    │ Thompson │ Teacher        │ Female  │
│ Ben       │ Johnson  │ Police Officer │ Male    │
│ Debbie    │ Smith    │ Lawyer         │ Female  │
└───────────┴──────────┴────────────────┴─────────┘

使用的代码示例:

import duckdb
import pandas as pd

df1 = pd.read_csv(io.BytesIO(b"""
FirstName,LastName,Occupation,Gender
Angela,Norris,Firefighter,Female
Angela,Thompson,Teacher,Female
Ben,Johnson,Police Officer,Male
Ben,Peterson,Solicitor,Male
Charlie,Davies,Nurse,Male
Debbie,Smith,Lawyer,Female"""))

df2 = pd.read_csv(io.BytesIO(b"""
FirstName,LastName,Occupation,Gender
Angela,*,*,*
Ben,Johnson,*,Male
Debbie,*,*,Female"""))

query = "\nand\n".join(
    f"(df2.{column} = '*' or df1.{column} = df2.{column})" 
    for column in df2.columns
)

duckdb.sql(f"""
from df1, df2
select df1.*
where
   {query}
""")
moiiocjp

moiiocjp2#

假设协调密钥中始终存在FirstName,则这应该可以实现:
设置:

import numpy as np
import pandas as pd

master_df = pd.DataFrame({
    'FirstName': ['Angela', 'Angela', 'Ben', 'Ben', 'Charlie', 'Debbie'],
    'LastName': ['N', 'T', 'J', 'P', 'D', 'S'],
    'Occupation': ['Firefighter', 'Teacher', 'Police', 'Solicitor', 'Nurse', 'Lawyer'],
    'Gender': ['Female', 'Female', 'Male', 'Male', 'Male', 'Female'],
})

recon_df = pd.DataFrame({
    'FirstName': ['Angela', 'Ben', 'Debbie'],
    'LastName': ['*', 'J', '*'],
    'Occupation': ['*', '*', '*'],
    'Gender': ['*', 'Male', 'Female'],
})

解决方案:

temp_df = master_df.merge(
    recon_df,
    how='left',
    on='FirstName',
)

update_cols = [col for col in temp_df.columns if col[-2:] == '_x']
for col in update_cols:
    y_col = col[:-2] + '_y'
    temp_df[col] = np.where(
        temp_df[y_col] == '*',
        temp_df[y_col],
        temp_df[col],
    )
temp_df = temp_df.rename(columns={col: col[:-2] for col in update_cols})
temp_df = temp_df.drop(columns=[col for col in temp_df.columns if col[-2:] == '_y'])

keys = list(recon_df.columns.values)
index1 = temp_df.set_index(keys).index
index2 = recon_df.set_index(keys).index

output_df = master_df.loc[index1.isin(index2)]

输出:

FirstName LastName   Occupation  Gender
0    Angela        N  Firefighter  Female
1    Angela        T      Teacher  Female
2       Ben        J       Police    Male
5    Debbie        S       Lawyer  Female

(基于solution here构建)

相关问题