【问题标题】:Python / pandas: create a data frame's column and set it's value based on finding a column value in range of another dataframePython / pandas:创建数据框的列并根据在另一个数据框范围内查找列值来设置它的值
【发布时间】:2021-05-03 02:38:59
【问题描述】:

我有两个熊猫数据框, 第一帧 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]

非常感谢您抽出宝贵时间帮助我解决此问题

【问题讨论】:

  • @a-hadidi,我真的需要知道在哪里可以找到ip2CountryDF 源文件。我很确定我们可以优化以提高搜索速度。
  • @Corralien 我从 ip2location 得到了那个,更具体地说是 ip2location-lite 可免费下载的数据集:lite.ip2location.com/database/ip-country

标签: python python-3.x pandas dataframe


【解决方案1】:

你离得更近了。您只是缺少对“地图”功能的调用。

加载 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)

【讨论】:

  • 看起来你的 dfs 是 200K 和 10K 而 OP 是 2M 和 60K。我的笔记本电脑仍在与更大的 dfs(第 10 代 6 核 i7 H 系列,32GB RAM)作斗争。
  • 如何获取你的 ip2country 数据库?
  • 我使用了 OP 的 3x3 样本 ip2CountryDF 并用 ip2CountryDF = pd.concat([ip2CountryDF] * 700000) 连接成 2100000x3
  • 这肯定行得通,我不得不等待更少的时间(在代码完成之前在房子周围做更少的事情)。要么我的数据集太大,要么我的机器没有足够的资源来更快地提供这个......
  • @Corralien 地图功能是我肯定缺少的。我只是在创建自己的代码时学习熊猫。
猜你喜欢
  • 2023-01-04
  • 2020-05-30
  • 1970-01-01
  • 1970-01-01
  • 2018-03-14
  • 2020-11-26
  • 2019-03-06
  • 2022-10-04
  • 2020-07-18
相关资源
最近更新 更多