如何基于多个条件更快地合并 2 个 pandas 数据帧

Posted

技术标签:

【中文标题】如何基于多个条件更快地合并 2 个 pandas 数据帧【英文标题】:how to merge 2 pandas daataframes base on multiple conditions faster 【发布时间】:2021-09-29 05:23:12 【问题描述】:

我有 2 个数据框:

df1:

    RB  BeginDate   EndDate    Valindex0
0   00  19000100    19811231    45
1   00  19820100    19841299    47
2   00  19850100    20010699    50
3   00  20010700    99999999    39

df2:

    RB  IssueDate   gs
0   L3  19990201    8
1   00  19820101    G
2   48  19820101    G
3   50  19820101    G
4   50  19820101    G

如何在以下条件下合并这两个数据框:

if df1['BeginDate'] <= df2['IssueDate'] <= df1['EndDate'] and df1['RB']==df2['RB']:
    merge the value of df1['Valindex0'] to df2

输出应该是:

df2:

    RB  IssueDate   gs  Valindex0
0   L3  19990201    8   None
1   00  19820101    G   47    # df2['RB']==df1['RB'] and df2['IssueDate'] between df1['BeginDate'] and df1['EndDate'] of this row
2   48  19820101    G   None
3   50  19820101    G   None
4   50  19820101    G   None

我知道一种方法可以做到这一点,但是很慢:

conditions = []

for index, row in df1.iterrows():
    conditions.append((df2['IssueDate']>= df1['BeginDate']) &
                      (df2['IssueDate']<= df1['BeginDate'])&
                      (df2['RB']==df1['RB']))

    df2['Valindex0'] = np.select(conditions, df1['Valindex0'], default=None)

有更快的解决方案吗?

【问题讨论】:

【参考方案1】:

您可以尝试使用 sql,因为在 pandas 中它更复杂:

import pandas as pd
import sqlite3

conn = sqlite3.connect(':memory:')

df_1.to_sql('A', conn, index=False)
df_2.to_sql('B', conn, index=False)

qry = '''
    select  
        B.RB, B.IssueDate, B.gs, A.Valindex0
    from
        B left join A on
        (B.IssueDate between A.BeginDate and A.EndDate and B.RB = A.RB)
    '''
df = pd.read_sql_query(qry, conn)

#    RB  IssueDate gs  Valindex0
# 0  L3   19990201  8        NaN
# 1  00   19820101  G       47.0
# 2  48   19820101  G        NaN
# 3  50   19820101  G        NaN
# 4  50   19820101  G        NaN

【讨论】:

【参考方案2】:

解决方案

用途:与pd.Series.between比较+与pd.DataFrame.pipe的方法链接

你可以试试这个。

注意:我使用了一个稍微通用的数据集(df1、df2)来查看它适用于所有 RB 值。

此解决方案能为您提供什么?

合并(内连接)数据帧df1df2 使用pandasDataFrame.pipe的便捷函数update_column: 这将评估条件BeginDate &lt;= IssueDate &lt;= EndDate 并将None 值分配给条件评估为False 的任何行。 如果此时检查输出数据帧,您将能够验证逻辑是否正确实现,因为 BeginDateEndDate 列仍然可用。 删除不必要的列(BeginDateEndDate)以获得最终结果。

代码

import pandas as pd

def update_column(df: pd.DataFrame, target_column: str="Valindex0"):
    cond = df["IssueDate"].between(df["BeginDate"], df["EndDate"])
    df.loc[~cond, target_column] = None
    return df

# evalute result
result = (df2
    .merge(df1, how='inner', on="RB")                ## merge dataframes on column "RB"
    .pipe(update_column, target_column="Valindex0")  ## using piping for custom logic
    .drop(columns=["BeginDate", "EndDate"])          ## drop unnecessary columns
)

## Output: result
#    RB  IssueDate gs  Valindex0
# 0  L3   19990201  8       51.0
# 1  L3   19990201  8       50.0
# 2  00   19820101  G        NaN
# 3  00   19820101  G        NaN
# 4  00   19820101  G        NaN
# 5  00   19820101  G        NaN
# 6  48   19820101  G       58.0
# 7  50   19870101  G       52.0
# 8  50   19820121  G        NaN

输出

这是结果数据帧的输出,在删除列 BeginDateEndDate 之前。

虚拟数据

加载数据框df1

import pandas as pd
from io import StringIO

df1s = """
RB  BeginDate   EndDate    Valindex0
00  19000120    19801231    45
00  19820110    19841229    47
00  19850101    20010629    50
00  20010701    99991230    39
L3  19850101    20450630    51
L3  19850111    20010609    50
50  19850121    20010619    52
48  19810204    20010699    58
"""

df1 = pd.read_csv(StringIO(df1s.strip()), sep='\s+', 
                  dtype="RB": str, "BeginDate": int, "EndDate": int)

加载数据框df2

import pandas as pd
from io import StringIO

df2s = """
RB  IssueDate   gs
L3  19990201    8
00  19820101    G
48  19820101    G
50  19870101    G
50  19820121    G
"""

df2 = pd.read_csv(StringIO(df2s.strip()), sep='\s+', 
                  dtype="RB": str, "IssueDate": int)

【讨论】:

@William 给你。 非常感谢您的回复,但是输出应该和df2一样长,现在和df1一样长。 那是因为你所说的逻辑。如果df1 中有多个行对于df2 中的给定RB 值,那么如何映射它们?您使用哪个 BeginDateEndDate 值?正如我所看到的,您在此处的逻辑并未涵盖该场景。因此,我会要求您获取我使用的演示数据(因为它比您共享的更通用),并解释如何为df2 中的每一行获取一行以及总行数如何result 中的内容与 df2 中的内容相同。一旦你完成了这个(使用简单的普通笔和纸),那么你可以再次向我们解释你的逻辑。 您好朋友,非常感谢您的帮助,我在这里更新了我的问题***.com/questions/68806043/…【参考方案3】:

试试这些:

df2 = df2.merge(df1, left_on='RB', right_on='RB', how='inner')
df2 = df2[(df2['BeginDate'] <= df2['IssueDate']) & (df2['IssueDate'] <= df2['EndDate']]

【讨论】:

df2 = df2[(df1['BeginDate'] 它们已经合并到 df2 中,所以只要 df2 就可以了

以上是关于如何基于多个条件更快地合并 2 个 pandas 数据帧的主要内容,如果未能解决你的问题,请参考以下文章

使用 python、pandas 合并 2 个基于 3 个条件的单独 excel 文件

Pandas:如何更快地应用数据框?

Pandas:如何更快地应用数据框?

在某些条件下更快地复制 pandas 数据

如何使用 Python Pandas 合并多个 CSV 文件

有效地合并熊猫中的多个数据框[重复]