繁体   English   中英

基于多个范围合并dataframe的方法

[英]Way to merge dataframe based on several ranges

这个问题与这个问题非常相似: Fastest way to merge pandas dataframe on ranges

但在我的加入过程中,我有几个需要考虑的范围。

我有一个 dataframe A:

   ip_address  server_port
0   13         2
1   5          2
2   20         4
3   11         4
.. ........

和一个 dataframe B:

    lowerbound_ip_address   upperbound_ip_address   server_low  server_high          country
0    0                       10                     2          3                    Australia
1    11                      20                     2          3                   China
2    11                      20                     4          7                   Belgium

我如何合并它以遵守规则:

   ip_address  server_port   country
0   13         2             China
1   5          2             Australia
2   20         4             Belgium
3   11         4             Belgium

我最初的想法是为它写一个循环,但是有没有向量化的解决方案呢?

使用numpy广播:

ip_low = df_b['lowerbound_ip_address'].values
ip_high = df_b['upperbound_ip_address'].values
port_low = df_b['server_low'].values
port_high = df_b['server_high'].values

ip = df_a['ip_address'].values[:, None]
port = df_a['server_port'].values[:, None]

mask = (ip_low <= ip) & (ip <= ip_high) & (port_low <= port) & (port <= port_high)
id_b = np.argmax(mask, axis=1)
df_a.assign(id_b=id_b).join(df_b, on='id_b')

结果:

   ip_address  server_port  id_b  lowerbound_ip_address  upperbound_ip_address  server_low  server_high    country
0          13            2     1                     11                     20           2            3      China
1           5            2     0                      0                     10           2            3  Australia
2          20            4     2                     11                     20           4            7    Belgium
3          11            4     2                     11                     20           4            7    Belgium

这个怎么运作

它使用数组广播寻找到每行df_a在匹配df_b 结果存储在mask ,如下所示:

array([[False,  True, False],   # line 0 in df_a matches to line 1 in df_b
       [ True, False, False],   #      1                         0
       [False, False,  True],   #      2                         2
       [False, False,  True]])  #      3                         2

numpy将True/False视为1/0因此您可以对其进行比较。 对于每一行,我们想找到第一个True值的索引。 由于True == 1 > False == 0 ,我们可以使用argmax来做到这一点:

id_b = np.argmax(mask, axis=1)

array([1, 0, 2, 2], dtype=int64)

最后一行只是分配新列并将两个框架连接在一起。

受到在range上合并熊猫数据帧最快方法的启发,使用pd.IntervalIndex可以创建多个间隔(在这种情况下,两个间隔;一个用于ip_address ,一个用于server_port ):

ip_intv = pd.IntervalIndex.from_arrays(df_b.lowerbound_ip_address.unique(), 
                             df_b.upperbound_ip_address.unique(), 
                             'both')

server_intv = pd.IntervalIndex.from_arrays(df_b.server_low.unique(), 
                             df_b.server_high.unique(), 
                             'both')

然后使用pd.cut您将在两个数据帧中找到相应的间隔:

df_a['ip_intv'] = pd.cut(df_a.ip_address, ip_intv)
df_a['server_intv'] = pd.cut(df_a.server_port, server_intv)

df_b['ip_intv'] = pd.cut(df_b.lowerbound_ip_address, ip_intv)
df_b['server_intv'] = pd.cut(df_b.server_low, server_intv)

df_a.set_index(['ip_intv', 'server_intv'], inplace=True)
df_b.set_index(['ip_intv', 'server_intv'], inplace=True)

最后,您将join

df_a.join(df_b.country)

Out:

                      ip_address  server_port    country
ip_intv  server_intv                                    
[0, 10]  [2, 3]                5            2  Australia
[11, 20] [2, 3]               13            2      China
         [4, 7]               20            4    Belgium
         [4, 7]               11            4    Belgium

一种选择是使用pyjanitor中的conditional_join

# pip install pyjanitor
import pandas as pd
import janitor

(A
.conditional_join(
    B, 
    ('ip_address', 'lowerbound_ip_address', '>='), 
    ('ip_address', 'upperbound_ip_address', '<='), 
    ('server_port', 'server_low', '>='), 
    ('server_port', 'server_high', '<='))
.loc[:, ['ip_address', 'server_port', 'country']]
)

   ip_address  server_port    country
0          13            2      China
1           5            2  Australia
2          20            4    Belgium
3          11            4    Belgium

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM