Pandas检查哪个子网IP地址属于

问题描述 投票:4回答:1

我有一个用户及其IP地址的pandas数据帧:

users_df = pd.DataFrame({'id': [1,2,3],
                         'ip': ['96.255.18.236','105.49.228.135','104.236.210.234']})

   id               ip
0   1    96.255.18.236
1   2   105.49.228.135
2   3  104.236.210.234

以及包含网络范围和相应地理标识ID的单独数据框:

geonames_df = pd.DataFrame({'network': ['96.255.18.0/24','105.49.224.0/19','104.236.128.0/17'],
                            'geoname': ['4360369.0','192950.0','5391959.0']})

     geoname           network
0  4360369.0    96.255.18.0/24
1   192950.0   105.49.224.0/19
2  5391959.0  104.236.128.0/17

对于每个用户,我需要检查他们的ip对所有网络,并拉出相应的geoname并将其添加到users_df。我希望这个输出:

   id               ip   geonames
0   1    96.255.18.236  4360369.0
1   2   105.49.228.135   192950.0
2   3  104.236.210.234  5391959.0

在这个例子中它很容易,因为它们是正确排序的,只有3个例子。实际上,users_df有4000行,而geonames_df有超过300万行

我目前正在使用这个:

import ipaddress

networks = []
for n in geonames_df['network']:
    networks.append(ipaddress.ip_network(n))

geonames = []

for idx, row in users_df.iterrows():
    ip_address = ipaddress.IPv4Address(row['ip'])

    for block in networks:
        if ip_address in block:
            geonames.append(str(geonames_df.loc[geonames_df['network'] == str(block), 'geoname'].item()))
            break

users_df['geonames'] = geonames

由于数据框/列表上的嵌套循环,这非常慢。是否有更快的方式来利用numpy / pandas?或者至少某种方式比上述方法更快?

这是一个类似的问题(How can I check if an ip is in a network in python 2.x?),但1)它不涉及pandas / numpy,2)我想检查多个IP对多个网络,3)最高投票的答案不能避免嵌套循环,这是我的慢表现源于

python python-3.x pandas ip
1个回答
0
投票

我不认为可以避免嵌套循环,但我已将评论中提到的先前解决方案与pandas结合起来。你可以检查它是否更快。

import socket,struct

def makeMask(n):
    "return a mask of n bits as a long integer"
    return (2<<n-1) - 1

def dottedQuadToNum(ip):
    "convert decimal dotted quad string to long integer"
    return struct.unpack('L',socket.inet_aton(ip))[0]

def networkMask(network):
    "Convert a network address to a long integer" 
    return dottedQuadToNum(network.split('/')[0]) & makeMask(int(network.split('/')[1]))

def whichNetwork(ip):
    "return the network to which the ip belongs"
    numIp = dottedQuadToNum(ip)
    for index,aRow in geonames_df.iterrows():
        if (numIp & aRow["Net"] == aRow["Net"]):
            return aRow["geoname"]
    return "Not Found"

geonames_df["Net"] = geonames_df["network"].map(networkMask)
users_df["geonames"] = users_df["ip"].map(whichNetwork)
© www.soinside.com 2019 - 2024. All rights reserved.