首页 > 解决方案 > Python / pandas:创建数据框的列并根据在另一个数据框范围内找到列值来设置它的值

问题描述

我有两个熊猫数据框,第一帧 ip2CountryDF 有 2M+ 记录:

startIP, endIP, countryISO
16777216,16777471,US
16777472,16778239,CN
16778240,16779263,AU

出于效率和匹配目的,此数据帧中的 IP 地址表示为整数

第二帧 inputDF 有 60K+ 记录:

sourceIP, eventTime, integerIP
114.119.157.43,01/Mar/2021,1920441643
193.205.128.7,01/Mar/2021,3251470343
193.205.128.7,01/Mar/2021,3251470343
193.205.128.7,01/Mar/2021,3251470343

我拥有的数据都来自公开可用的数据集

我要做的是根据 ip2CountryDF 中的值识别 inputDF 中每一行的源国家/地区。

理想情况下,我将选择 inputDF['integerIP'] 并获取 ip2CountryDF['countryISO'] 其中 inputDF 的 integerIP 在 ip2CountryDF['startIP'] 和 ip2CountryDF['endIP'] 之间的范围内

到目前为止,我使用 for 循环完成了数据,它适用于测试集(在 inputDF 中搜索 5 个条目的数据),但是当我遇到更大的数据集时,我的机器粉丝会拿起,几分钟后我没有得到任何结果并且我取消了工作(这告诉我我的代码效率如何),这是我使用的代码(效率低但有效):

countryList = []
for index, row in inputDF.iterrows():
    integerIP   = row['integerIP']
    countryISO  = ip2CountryDF.loc[(integerIP >= ip2CountryDF['startIP']) & (integerIP <= ip2CountryDF['endIP']),'countryISO'].iloc[0]
    countryList.append(countryISO)
inputDF['countryISO']   = countryList

我需要什么帮助,能否以更有效和更(类似熊猫)的方式更好地处理,我试图使用类似的东西:

inputDF['countryISO'] = ip2CountryDF.loc[(inputDF['integerIP'] >= ip2CountryDF['startIP']) & (inputDF['integerIP'] <= ip2CountryDF['endIP']),'countryISO'].iloc[0]

非常感谢您花时间帮助我解决这个问题

标签: pythonpython-3.xpandasdataframe

解决方案


你是如此接近。您只是缺少对“地图”功能的调用。

加载 IpToCountry.csv(用于文档目的):

IP2COUNTRY = "https://github.com/urbanadventurer/WhatWeb/raw/master/plugins/IpToCountry.csv"
db = pd.read_csv(IP2COUNTRY, header=None, usecols=[0, 1, 4],
                 names=["startIP", "endIP", "countryISO"], comment="#")
>>> db
           startIP       endIP countryISO
0                0    16777215         ZZ
1         16777216    16777471         AU
2         16777472    16777727         CN
3         16777728    16778239         CN
4         16778240    16779263         AU
...            ...         ...        ...
211757  4211081216  4227858431         ZZ
211758  4227858432  4244635647         ZZ
211759  4244635648  4261412863         ZZ
211760  4261412864  4278190079         ZZ
211761  4278190080  4294967295         ZZ

[211762 rows x 3 columns]

创建一个函数ip2country,为十进制 ip 返回相应的 iso 国家代码:

def ip2country(ip: int):
    return db.loc[(db["startIP"] <= ip) & (ip <= db["endIP"]), "countryISO"].squeeze()


df["countryISO"] = df["integerIP"].map(ip2country)
>>> df
         sourceIP   eventTime   integerIP countryISO
0  114.119.157.43  2021-03-01  1920441643         SG
1   193.205.128.7  2021-03-01  3251470343         IT
2   193.205.128.7  2021-03-01  3251470343         IT
3   193.205.128.7  2021-03-01  3251470343         IT

表现

对于 10k ip 地址,在2.5 GHz 四核 Intel Core i7上平均在 11.7 秒内返回结果。

df1 = pd.DataFrame({"integerIP": np.random.randint(db["startIP"].min(), 
                                                   db["endIP"].max()+1,
                                                   size=10000)})

%timeit df1["integerIP"].map(ip2country)
11.7 s ± 489 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

推荐阅读