【问题标题】:Converting IP Addresses in R [duplicate]在R中转换IP地址[重复]
【发布时间】:2014-12-18 04:41:08
【问题描述】:

我有一个 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 地址)相关联,而不是单个地址。

【问题讨论】:

  • 请参阅this question 将 IP 地址转换为整数。然后您只需要按范围查看,因为 IP 地址通常以块的形式分配,因此它们共享一个位置。
  • 另外,您的示例 IP 地址不是有效的 IP 地址也无济于事。每个数字应介于 0-255 之间。

标签: r ip ip-address


【解决方案1】:

在我的 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 地址转换为长整数并检查它是否在第一个和最后一个 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.tablerbindlist,因为它有一个很好的fill 选项来填充缺失的列。

我有一个较旧的 MaxMind 纯 R 包:https://github.com/hrbrmstr/Rmaxmind:可以在这里提供帮助,我上面提到的基于 Rcpp 的 iptools 包也可以进行 IPv4 地址的地理定位。

如果您尝试使用此答案中的代码,我强烈建议您将查找表存储为 data.table,因为它会显着提高效率(并且还有一些方法可以加快查询速度)。您还可以将查找表存储在 SQL 数据库中,并使用智能索引和查询优化来进行查找。

【讨论】:

  • 感谢您的详细回复!!当我按原样运行代码时,我收到以下错误消息:Error in rbindlist(lapply(dat$IP_address, function(ip) { : unused argument (fill = TRUE) 我删除了 fill=TRUE 并收到以下错误消息:Error in strsplit(ip, ".", fixed = TRUE) : non-character argument 关于从这里去哪里有什么建议吗?
  • 从 CRAN 或 github 加载最新的 data.table。 “fill”参数是最新的。
  • 我相信我已经安装了最新版本 (1.9.2)...
  • 不。 1.9.4 是最新的 CRAN 和 github
猜你喜欢
  • 1970-01-01
  • 2013-04-11
  • 2019-05-13
  • 1970-01-01
  • 2014-03-03
  • 2011-06-05
  • 1970-01-01
  • 2011-05-31
  • 2013-05-09
相关资源
最近更新 更多