首页 > 解决方案 > 在python数据帧的时间范围内检查常见的交互器

问题描述

我有以下数据集,可以使用以下代码重新创建:

   import pandas as pd
    
    data3 =  [[20210308, 'A','B',100], 
              [20210307, 'B','A',95],
            [20210307, 'B','A',65],
            [20210305, 'A','C',50],
            [20210304, 'D','E',25],
            [20210303, 'E','D',200],
            [20210201,'E','B',10 ], 
            [20210101, 'X','X',50]] 
    
    df3 = pd.DataFrame(data3, columns = ['Date_1', 'Interactor_A','Interactor_B','Value']) 
    
    df3['Date_2'] = pd.to_datetime(df3['Date_1'], format='%Y%m%d')

最终的数据集如下所示,我在最后一列设置日期时间,以防解决方案需要一些日期时间函数:

     Date_1 Interactor_A Interactor_B  Value     Date_2
0  20210308            A            B    100 2021-03-08
1  20210307            B            A     95 2021-03-07
2  20210307            B            A     65 2021-03-07
3  20210305            A            C     50 2021-03-05
4  20210304            D            E     25 2021-03-04
5  20210303            E            D    200 2021-03-03
6  20210201            E            B     10 2021-02-01
7  20210101            X            X     50 2021-01-01

我想要做的是检测两个人是否在 3 天内相互交互,并且他们的交互值在 90% 到 110% 的范围内。也就是说,A 是否作为 Interactor_A 与 B 交互,B 是否作为 Interactor_A 与 A 交互,并且它们的交互发生在 3 天的时间窗口内,它们的交互值是否在 90% 到 110% 的范围内?

按照这个逻辑,只有前两行会被选中,因为 A 与 B 交互,然后 B 与 A 交互,它们交互的值在 90% 到 110% 范围内,因为 95/100 或 100/95 在90% 到 110% 频带。同样,不会选择 DE 和 ED 交互,因为 25/200 和 200/25 都在 90% 到 110% 的范围之外。索引 2 中的 BA 交互也不会被选中,因为它不在 90% 到 110% 范围内,即使它在 3 天窗口内。

我正在通过 SQL 尝试一堆解决方案:

  1. 我尝试在表上对其自身进行内部连接,但这只是给了我类似于 X 与 X 交互的最后一行的结果
  2. 我尝试了一个在 3 天窗口内聚合的自联接,但它只聚合了 A 在 3 天内总共的值,没有捕获任何交互。

Python中有没有办法只能返回前两行(索引0和1中的AB和BA),因为它们满足在3天时间窗口内相互交互的条件并且它们的交互值在90以内% 到 110% 波段?

标签: pythonpandas

解决方案


['Interactor_A', 'Interactor_B']您可以使用左键和右键将 DataFrame 与其自身合并['Interactor_B', 'Interactor_A'],这为我们提供了一个 DataFrame,其中每行中都有一对交互,例如A-BB-A,我们可以在其上计算此类交互之间的天数(days_diff列),以及值的比率(value_ratio列)。然后我们可以根据 和 的值进行days_diff过滤value_ratio

作为最后一步,我们只取合并的“左侧”,并且drop_duplicates(因为merge会生成所有可能的组合,并且它们的一些“左侧”可能相同):

# create a copy of df3
z = df3.copy()

# keep interactions within 3 days, value ratio between 0.9 and 1.1,
# and Interactor_A != Interactor_B
z = z.merge(z,
            left_on=['Interactor_A', 'Interactor_B'],
            right_on=['Interactor_B', 'Interactor_A'],
            suffixes=['', '_'])
z['days_diff'] = (z['Date_2'] - z['Date_2_']).abs().dt.days
z['value_ratio'] = z['Value'] / z['Value_']
z = z.loc[z['days_diff'].le(3) &
          z['value_ratio'].between(0.9, 1.1) &
          z['Interactor_A'].ne(z['Interactor_B'])]

# use columns from df3 and drop duplicates
z = z[df3.columns].drop_duplicates()

z

输出:

     Date_1 Interactor_A Interactor_B  Value     Date_2
0  20210308            A            B    100 2021-03-08
1  20210307            B            A     95 2021-03-07

推荐阅读