根据另一个数据框中的列,统计一个数据框中行数。

3

好的,我有一个第一数据框df1:

|timestamp                |ip         |
|2022-01-06 11:58:53+00:00|1.1.1.5.   |
|2022-01-08 03:56:35+00:00|10.10.10.24|
|2022-01-09 22:29:30+00:00|3.3.3.89.  |
|2022-03-08 22:37:52+00:00|8.8.8.88.  |

另一个数据框df2:

|timestamp                |other|
|2022-01-07 22:08:59+00:00|other|
|2022-01-07 23:08:59+00:00|other|
|2022-01-09 17:04:09+00:00|other|
|2022-03-05 17:04:09+00:00|other|

我希望能够根据df1中连续出现的2个时间戳,统计df2中有多少行数据。

|timestamp                |ip         |count|
|2022-01-06 11:58:53+00:00|1.1.1.5    |NaN  |
|2022-01-08 03:56:35+00:00|10.10.10.24|2    |
|2022-01-09 22:29:30+00:00|3.3.3.89   |1    |
|2022-03-08 22:37:52+00:00|8.8.8.88   |1    |

我尝试的是首先在df1中创建另一列,该列包含先前的时间戳,使用以下方法:
df1 = df1.assign(timestamp_b4=df1.timestamp.shift(1)).fillna({'timestamp_b4': df1.timestamp})

这给了我:

|timestamp                |ip         |timestamp_b4             |
|2022-01-06 11:58:53+00:00|1.1.1.5    |2022-03-08 22:37:52+00:00|
|2022-01-08 03:56:35+00:00|10.10.10.24|2022-01-06 11:58:53+00:00|
|2022-01-09 22:29:30+00:00|3.3.3.89   |2022-01-08 03:56:35+00:00|
|2022-03-08 22:37:52+00:00|8.8.8.88   |2022-01-09 22:29:30+00:00|

然后做某种排序

s = (df2[df2['timestamp'].between(df1['timestamp'], df1['timestamp_b4'])].size())

但不幸的是,它不能正常工作,因为pandas要求比较标签相同的对象。

有没有一个好的pandas/python方法可以做到这一点?

谢谢。


我认为使用“df.loc”并查看您要查找多少条记录,然后相应地将其插入到行中会很好,所有这些都使用apply函数。 - Rafael MR
好的,有时我们需要某些东西来进行比较。你能解释一下为什么“10.10.10.24”应返回2个计数,但“1.1.1.5”却返回NaN而不是1吗? - Drakax
1
好的,所以不是在df2中两个相邻的时间戳之间,而是在df2中两个时间戳之间连续的时间戳(在df1中)。 - Drakax
抱歉,是英语错误 :/ - Laure D
1
没问题,别往心里去,只是为了确保而已 ;) - Drakax
显示剩余3条评论
4个回答

1

这里有一种方法。请注意,df1的列将保留在最终输出df中:

从具有额外列的df1开始:

                   timestamp           ip another_col
0  2022-01-06 11:58:53+00:00     1.1.1.5.       val_1
1  2022-01-08 03:56:35+00:00  10.10.10.24       val_2
2  2022-01-09 22:29:30+00:00    3.3.3.89.       val_3
3  2022-03-08 22:37:52+00:00    8.8.8.88.       val_4 

df1.merge(df2, on='timestamp', how='outer').sort_values('timestamp') \
    .assign(c1=df1.loc[~df1['ip'].isna()]['ip'], c2=lambda x: x['c1'].bfill() ) \
    .assign(count=lambda x: x.groupby('c2').apply('count').reset_index(drop=True)['timestamp']-1) \
    .drop(['other','c1','c2'], axis=1).dropna().astype({'count': 'int32'})

                   timestamp           ip another_col  count
0  2022-01-06 11:58:53+00:00     1.1.1.5.       val_1      0
1  2022-01-08 03:56:35+00:00  10.10.10.24       val_2      2
2  2022-01-09 22:29:30+00:00    3.3.3.89.       val_3      1
3  2022-03-08 22:37:52+00:00    8.8.8.88.       val_4      1

请注意,输出结果中保留了another_col
这种方法合并然后按时间戳排序,接着创建另一列-c2-用于复制df1时间戳,然后在df2时间戳上进行回填。从那里开始,实例按df1时间戳(反映在c2列中)分组并计数。换句话说,df1时间戳的回填允许将其用作计算前面df2时间戳的分组键。之后,将df修剪回以匹配输出要求。
还要注意,使用此方法需要像示例中当前索引的0-n一样对数据框进行索引。

你好,谢谢!有一个问题:如果df1中除了“ip”还有其他列,我该怎么办?是这样吗 c1=df1.drop(['timestamp'], axis=1) - Laure D
1
@LaureD 这种方法保留了df1中的其他列。我更新了我的答案以反映这一点。 - jch
我已经成功地完成了我想要的事情,但我也想检查一下你的答案 ;) 更加优雅。 - Laure D

1
def time_compare(df1,df2):
  return [np.sum((df1['timestamp'].values[i-1] < df2['timestamp'].values) & (df1['timestamp'].values[i] > df2['timestamp'].values)) for i in range(len(df1.timestamp))]

df2.join(pd.Series(time_compare(df1,df2), name='Count'))

奇怪,我不能像往常一样发布数据框输出:

索引 时间戳 其他 计数
0 2022-01-07 22:08:5900:00 其他 0
1 2022-01-07 23:08:5900:00 其他 2
2 2022-01-09 17:04:0900:00 其他 1
3 2022-03-05 17:04:0900:00 其他 1

1
@Laure D,我不确定返回“0”作为“Count”的值是否可以,我会等待您的评论 :) - Drakax
奇怪,它不能处理我的数据(没有给我期望的结果 :/)。 - Laure D
有任何错误吗?返回了哪些结果?我刚刚在我的Google Colab上使用您提供的数据进行了检查,它可以正常工作。您能再次检查一下df名称吗? - Drakax
也许是因为我给你的示例数据与我的实际数据相比要简单得多?我不知道。 - Laure D
它没有报错,但计数数字不正确。 - Laure D
1
好的,可能吧。以防万一,请再确认一下:https://ibb.co/Bt5dZB6。我已经在你提供的代码上工作了... ^^ 如果数据不是太不同的话,你仍然可以更新你的问题 ;) - Drakax

0

好的,最终我做了什么呢?我使用了@Drakax的答案。

我创建了一个带有先前时间戳的列。

df1 = df1.assign(previous_deconnection=df1.timestamp.shift(1)).fillna({'previous_deconnection': df1.timestamp})

然后我设置第一行的值,

df1['previous_deconnection'].iloc[0]=pd.to_datetime('2022-01-01 00:00:00+00:00')

然后我将这个函数应用到df1的每一行
def time_compare(a,b):  
  return len(b[((b['timestamp'] >= a['previous_deconnection']) & (b['timestamp'] <= a['timestamp']))])

df1['Count'] = df1.apply(lambda row: time_compare(row, df2), axis=1)

0
尝试这个,这是一个示例,可以帮助您找到解决方案。
import pandas as pd
table1 = {
    'timestamp':['2022-01-06 11:58:53+00:00','2022-01-08 03:56:35+00:00',
                 '2022-01-09 22:29:30+00:00','2022-03-08 22:37:52+00:00'],
    'other':['other','other','other','other']
              }
df1 = pd.DataFrame(table1)

table2 = {
    'timestamp':['2022-01-07 23:08:59+00:00','2022-01-07 22:08:59+00:00',
                 '2022-03-05 17:04:09+00:00','2022-01-09 17:04:09+00:00'],
    'ip':['1.1.1.5.','10.10.10.24','3.3.3.89.','8.8.8.88.']
    
              }

df2 = pd.DataFrame(table2)

print(f'\n\n-------------df1-----------\n\n')
print(df2)
print(f'\n\n-------------df2-----------\n\n')
print(df1)

listdf1 = df1['timestamp'].values.tolist()
def func(line):
    cont = df1.loc[df1['timestamp'].str.contains(line[0][:7], case = False)]
    temp = line.name - 1
    if temp == -1:
        temp = 0

    try :
        cont = [cont['timestamp'].iloc[temp],line[0]]
    except:
        cont = [line[0],line[0]]

    cont2 = df2['timestamp'].loc[df2['timestamp'].str.contains(line[0][:7], case = False)]
    
    repetitions = 0
    for x in cont2:

        if int(x[8:10]) >= int(cont[0][8:10]) and int(x[8:10]) <= int(cont[1][8:10]) and int(x[8:10]) <= int(line[0][8:10]):
            repetitions += 1
    return repetitions
    

print(f'\n\n-------------BREAK-----------\n\n')

df1['count'] = df1.apply(func, axis = 1)

print(df1)

我不确定你的代码是否适用于我的问题,为什么我们要将其与df2的line[0]进行比较? - Laure D
我已经修改了代码,使其类似于你的问题。 - Rafael MR
你想找什么相似之处吗?我在你的例子中没有发现任何相似之处。 - Rafael MR
1
好的,谢谢。我会尝试一下,但是它不太符合Pythonic/Pandas友好的风格 x) - Laure D
1
我有点新手,所以我还不知道如何做到非常良好的结构化。 - Rafael MR
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接