我写了一个将IP转换为Country的脚本。
我是怎么做的。
我们采用IP并将其转换为IP号码
假设IP地址是A.B.C.D.
IP号= A×(256×256×256)+ B×(256×256)+ C×256 + D
例如:
ip<-"5.102.240.155"
ip_Number<-5*(256*256*256)+102*(256*256)+240*(256)+1
我有另一个将IP号码转换为国家/地区的数据(可以从https://db-ip.com/db下载)。格式如下:
下一件事是检查IP_FROM&lt; = IP-Number&lt; = IP_TO
的位置
如果16779008&lt; = IP-Number&lt; = 16779263,那么该地点是Austarlia Melbourne。
这是将大量IP转换为位置的代码:
ipNumberFun<-function(ip){
strIP<-as.integer(strsplit(x = ip,split = "\\.")[[1]])
strIP[1]*(256*256*256)+strIP[2]*(256*256)+strIP[3]*(256)+strIP[4]
}
geoIp <- function(ip)
{
if(!is.na(ip)){
ip<-ipNumberFun(ip)
IpData<-Data[which(Data$IP_FROM<=ip & Data$IP_TO>=ip),]
out<-data.frame(COUNTRY_CODE=IpData$COUNTRY_CODE,
COUNTRY_NAME=IpData$COUNTRY_NAME,
REGION=IpData$REGION,
CITY=IpData$CITY)
} else{
out<-NULL
}
return(out)
}
outcomes <- lapply(X = BigArrayOfIP$ip,FUN = geoIp)
问题是此脚本确实无法运行。 我认为问题在于:
IpData<-Data[which(Data$IP_FROM<=ip & Data$IP_TO>=ip),]
数据是非常大的文件。它包含9364224行
我该如何优化脚本。
谢谢!
答案 0 :(得分:2)
使用data.table
假设您拥有data.table
个IP地址,结构如下:
library(data.table)
dt_ips <- data.table(ip_string = c("100.100.100.100", "...", "etc"))
## This will likely be your 'BigArrayOfIP'
然后,您可以使用ipNumberFun
## This update by reference is not working
## dt_ips[, ip := ipNumberFun(ip_string)]
## so instead, we can use
dt_ips <- dt_ips[, .(ip = ipNumberFun(ip_string)), by=ip_string]
现在我们可以使用这些新形成的整数,找到适合Data
查找表的位置
例如,假设我的上一次操作的结果给出:
dt_ips <- data.table(ip = c(16778245, 16777213, 16778497))
> dt_ips
# ip
#1: 16778245
#2: 16777213
#3: 16778497
我们将Data
设置为data.table
setDT(Data)
Data
# IP_FROM IP_TO COUNTRY_CODE CITY
# 1: 0 16777215 . .
# 2: 16777216 16777471 Australia Brisbane
# 3: 16777472 16778239 China Fuzhou
# 4: 16778240 16778495 Australia Melbourne
# 5: 16778496 16779007 Australia Sydney
# 6: 16779008 1677923 Australia Melbourne
然后我们有几种方法可以使用ip
包找到data.table
整数适合此表的位置。最简单的方法是将它们全部加在一起并过滤掉不正确的结果:
定义一个公共密钥列并加入两个表togeter,然后过滤掉不正确的结果。
Data[ , key_col := 1][ dt_ips[, key_col := 1], on="key_col", allow.cartesian = T][IP_FROM <= ip & ip < IP_TO]
# CITY COUNTRY_CODE IP_FROM IP_TO key_col ip
#1: Melbourne Australia 16778240 16778495 1 16778245
#2: . . 0 16777215 1 16777213
#3: Sydney Australia 16778496 16779007 1 16778497
编辑 - 替代解决方案
您看到的错误表明'加入所有内容'方法的结果将无效,因为结果中的行太多。
正如错误消息所示,另一种方法是在I
J
上执行联接
dt_ips[, key_col := 1]
DATA[, key_col := 1]
dt <- DATA[dt_ips,
{
idx = IP_FROM <= ip & ip < IP_TO
.(ip_string = i.ip,
IP_FROM = IP_FROM[idx],
IP_TO = IP_TO[idx],
COUNTRY_CODE = COUNTRY_CODE[idx],
CITY = CITY[idx])
},
on=c("key_col"),
by=.EACHI]
编辑 - foverlap解决方案
或者,我们可以尝试foverlap
解决方案
DATA <- DATA[, .(COUNTRY_CODE, CITY, IP_FROM, IP_TO)]
dt_ips[, `:=`(IP_FROM = ip, IP_TO = ip)]
setkey(DATA, IP_FROM, IP_TO)
setkey(dt_ips, IP_FROM, IP_TO)
foverlaps(dt_ips,
DATA)
修改 - 数据
OP正在使用此数据:
dput(head(dt_ips))
structure(list(ip_string = c("71.190.193.124", "70.130.142.86",
"66.32.18.22", "87.155.51.131", "217.195.236.114", "162.195.53.38"
), ip = c(1203683708, 1203683708, 1203683708, 1203683708, 1203683708,
1203683708)), .Names = c("ip_string", "ip"), class = c("data.table",
"data.frame"), row.names = c(NA, -6L), .internal.selfref = <pointer: 0x00000000013f0788>)