我有一个IP地址的数据集(dat),其格式如下:
Person IP_Address
267 555.66.44.222
299 111.222.55.22
513 222.111.8.777
524 888.88.333.222
我还有一个IP地址数据库(db),其格式如下:
First_IP_Address_In_Netblock Last_IP_Address_In_Netblock Latitude Longitude
16777216 16777471 -27.48333 153.01667
16777472 16778239 26.06139 119.30611
16778240 16779263 -37.814 144.96332
16779264 16781311 23.11667 113.25
16781312 16785407 35.689506 139.6917
16785408 16793599 23.11667 113.25
16793600 16797695 34.38528 132.45528
16797696 16798719 35.689506 139.6917
16798720 16799743 34.38528 132.45528
16799744 16799999 35.689506 139.6917
我的问题有两个: 1)如何转换IP地址(来自数据集或数据库),以便它们采用相同的格式? 2)我怎样才能将每个人的纬度和经度相匹配?第二个问题让我感到困惑,因为每个坐标都与一系列IP地址相关联(从网络块中的第一个IP地址到网络块中的最后一个IP地址),而不是一个地址。
答案 0 :(得分:5)
在我的iptools
包(https://gitlab.dds.ec/bob.rudis/iptools)中,我有一个ip2long
函数,可以将IPv4字符串转换为长整数。它是基于Rcpp的,需要Boost标头。以下是纯R实现:
ip2long <- function(ip) {
# convert string into vector of characters
parts <- unlist(strsplit(ip, '.', fixed=TRUE))
# set up a function to bit-shift, then "OR" the octets
octets <- function(x,y) bitOr(bitShiftL(x, 8), y)
# Reduce applys a funcution cumulatively left to right
Reduce(octets, as.integer(parts))
}
注意:这需要bitops
包。
然后,您可以将IP地址转换为长整数,并检查它是否在First&amp;最后的IP网络地址。例如:
library(data.table)
library(bitops)
ip2long <- function(ip) {
# convert string into vector of characters
parts <- unlist(strsplit(ip, '.', fixed=TRUE))
# set up a function to bit-shift, then "OR" the octets
octets <- function(x,y) bitOr(bitShiftL(x, 8), y)
# Reduce applys a funcution cumulatively left to right
Reduce(octets, as.integer(parts))
}
# i added the 1.0.0.2 entry to show you the result
dat <- read.table(text="Person IP_Address
267 555.66.44.222
299 111.222.55.22
513 222.111.8.777
555 1.0.0.2
524 888.88.333.222", stringsAs=FALSE, header=TRUE)
lookup <- read.table(text="First_IP_Address_In_Netblock Last_IP_Address_In_Netblock Latitude Longitude
16777216 16777471 -27.48333 153.01667
16777472 16778239 26.06139 119.30611
16778240 16779263 -37.814 144.96332
16779264 16781311 23.11667 113.25
16781312 16785407 35.689506 139.6917
16785408 16793599 23.11667 113.25
16793600 16797695 34.38528 132.45528
16797696 16798719 35.689506 139.6917
16798720 16799743 34.38528 132.45528
16799744 16799999 35.689506 139.6917", stringsAs=FALSE, header=TRUE)
rbindlist(lapply(dat$IP_Address, function(ip) {
ip_long <- ip2long(ip)
res <- lookup[ip_long>=lookup$First_IP_Address_In_Netblock &
ip_long<=lookup$Last_IP_Address_In_Netblock, c(3,4)]
if (nrow(res) > 0) {
return(data.table(ip=ip, res))
} else {
return(data.table(ip=ip))
}
}), fill=TRUE)
## ip Latitude Longitude
## 1: 555.66.44.222 NA NA
## 2: 111.222.55.22 NA NA
## 3: 222.111.8.777 NA NA
## 4: 1.0.0.2 -27.48333 153.0167
## 5: 888.88.333.222 NA NA
我正在使用data.table
的{{1}},因为它有一个很好的rbindlist
选项,可以填充缺少的列。
我有一个较旧的MaxMind纯R包:https://github.com/hrbrmstr/Rmaxmind:这可以提供帮助,我上面提到的基于Rcpp的fill
包也可以进行IPv4地址的地理定位。
如果您尝试使用此答案中的代码,我强烈建议您将查找表存储为iptools
,因为它会显着提高效率(并且还有加快查询速度的方法)。您还可以将查找表存储在SQL数据库中,并使用智能索引和查询优化来执行查找。