У меня есть пандас с данными о пользователях и их IP-адресах:
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
И отдельный фрейм данных, содержащий диапазоны сети и соответствующие идентификаторы геонамов:
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 для всех сетей, вытащить соответствующее географическое имя и добавить его к 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
имеет более 3 миллионов
Я сейчас использую это:
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? Или, по крайней мере, каким-то образом, который быстрее, чем вышеописанный метод?
Есть похожий вопрос по этому поводу ( Как я могу проверить, есть ли ip в сети в python 2.x? ), но 1) он не включает панд / numpy, 2) я хочу сравнить несколько IP-адресов с несколькими сетями и 3) ответ с наибольшим количеством голосов не может избежать вложенного цикла, из-за которого моя низкая производительность исходит из