基于条件在Python中加入两个表

时间:2018-04-13 23:30:26

标签: python python-3.x pandas join merge

我在熊猫中有两张桌子:

df1:包含150K用户的用户ID和IP地址。

|---------------|---------------|  
|    User_ID    |   IP_Address  |
|---------------|---------------|  
|      U1       |   732758368.8 |
|      U2       |   350311387.9 |
|      U3       |   2621473820  |
|---------------|---------------|

df2:包含其所属的IP地址范围和国家/地区,139K记录

|---------------|-----------------|------------------|  
|    Country    | Lower_Bound_IP  |  Upper_Bound_IP  |
|---------------|-----------------|------------------|  
|   Australia   |   1023787008    |    1023791103    |
|   USA         |   3638734848    |    3638738943    |
|   Australia   |   3224798976    |    3224799231    |
|   Poland      |   1539721728    |    1539721983    |
|---------------|-----------------|------------------|

我的目标是在df1中创建一个国家/地区列,使得df1的IP_Address位于df2中该国家/地区的Lower_Bound_IP和Upper_Bound_IP范围之间。

|---------------|---------------|---------------|   
|    User_ID    |   IP_Address  |    Country    |
|---------------|---------------|---------------|   
|      U1       |   732758368.8 |   Indonesia   |
|      U2       |   350311387.9 |   Australia   |
|      U3       |   2621473820  |   Albania     |
|---------------|---------------|---------------|

我的第一种方法是对两个表进行交叉连接(笛卡尔积),然后过滤到相关记录。但是,使用pandas.merge()的交叉连接是不可行的,因为它将创建210亿条记录。代码每次都崩溃了。你能否提出一个可行的替代解决方案?

1 个答案:

答案 0 :(得分:0)

我不确定如何使用pandas.where来处理这个问题,但是使用numpy.where你可以做到

idx = numpy.where((df1.Ip_Address[:,None] >= df2.Lower_Bound_IP[None,:]) 
    & (df1.IP_Address[:,None] <= df2.Upper_Bound_IP[None,:]))[1]
df1["Country"] = df2.Country[idx]

numpy.where给出给定条件为True的索引。 '&安培;'对应于'和',整个[:,None]位添加一个虚拟轴,其中'None'位于其中。这可以确保您为每个User_ID找到df2中的索引,其中IP_Address在该范围内。 '[1]'给出条件为真的df2中的索引。 如果df2中的范围重叠,这将会中断。

这可能仍会导致内存问题,但您可以添加一个循环,以便批量执行此比较。 E.g。

batch_size = 1000
n_batches = df1.shape[0] // batch_size
# Integer division rounds down, so if the number
# of User_ID's is not divisable by the batch_size,
# we need to add 1 to n_batches
if n_batches * batch_size < df1.shape[0]:
    n_batches += 1
indices = []
for i in range(n_batches):
    idx = numpy.where((df1.Ip_Address[i*batch_size:(i+1)*batch_size,None]
            >= df2.Lower_Bound_IP[None,:]) & 
            (df1.IP_Address[i*batch_size:(i+1)*batch_size,None] 
            <= df2.Upper_Bound_IP[None,:]))[1]
    indices.extend(idx.tolist())

df1["Country"] = df2.Country[np.asarray(indices)]