【问题标题】:Replacing nested loop in R替换R中的嵌套循环
【发布时间】:2013-05-20 12:47:50
【问题描述】:

我对 R 非常陌生,并在论坛上搜索过这个问题,但找不到足够接近的解决方案。我正在尝试在 IP 地址和相应地理位置之间进行映射。我有 2 个数据集。

Set-a (1,60,000 rows):
ip(int) | ID(int)

Set-b (16,00,000 rows):
Ip1(int) | Ip2(int) | Code(str) | Country(str) | Area1(str) | Area2(str)

我正在尝试执行以下操作: 如果 ip 位于 Ip1 和 Ip2 之间,则将 Country & Region 添加到 Set-a。

我正在做以下事情(显然不是一个很好的方法):

ip1<-as.numeric(b$Ip1)
ip2<-as.numeric(b$Ip2)
country<-b$Country
area1<-b$Area1
area2<-b$Area2

for(i in 1:160000){
  for(j in 1:1674303){
    if(a[i]>ip1[j] & a[i]<ip2[j]) {
                                   a$country[i]<-country[j]
                                   a$area1[i]<-area1[j]
                                   a$area2[i]<-area2[j]}
   }
}

谁能告诉我一个有效的方法来做到这一点。这需要很多时间。 (对于 i=1 到 100 需要大约 10 分钟才能运行)。

样本数据集-b为:

Ip1, Ip2, Code, Country, Area1, Area2
"0","16777215","-","-","-","-"
"16777216","16777471","AU","AUSTRALIA","QUEENSLAND","SOUTH BRISBANE"
"16777472","16778239","CN","CHINA","FUJIAN","FUZHOU"
"16778240","16778495","AU","AUSTRALIA","VICTORIA","MELBOURNE"
"16778496","16778751","AU","AUSTRALIA","NEW SOUTH WALES","SYDNEY"

它是连续递增的。

dput(head(a)) & dput(head(b)) 分别是: (参考上面的示例数据)

structure(IP_Addr = c("38825563", "38921619", "42470287", "42471923","42473368","42473428"), 
 Desc_value = c("0", "1.2", "4.97", "1", "5.9", "22.06")), .Names = c("IP_Addr", "Desc_value"), row.names = c(NA, 6L), class = "data.frame")

structure(list(Ip1 = c("0", "16777216", "16777472", "16778240", 
"16778496", "16778752"), Ip2 = c("16777215", "16777471", "16778239", 
"16778495", "16778751", "16779263"), Code = c("-", "AU", "CN", 
"AU", "AU", "AU"), Country = c("-", "AUSTRALIA", "CHINA", "AUSTRALIA", 
"AUSTRALIA", "AUSTRALIA"), Area1 = c("-", "QUEENSLAND", "FUJIAN", 
"VICTORIA", "NEW SOUTH WALES", "-"), Area2 = c("-", "SOUTH BRISBANE", 
"FUZHOU", "MELBOURNE", "SYDNEY", "-")), .Names = c("Ip1", "Ip2", 
"Code", "Country", "Area1", "Area2"), row.names = c(NA, 6L), class = "data.frame")

【问题讨论】:

  • 也许你可以提供一个小的示例数据集?
  • 如果Set-b 中每个国家/地区的Ip1Ip2 范围都不同,这将更容易,那么您能告诉我们是否是这种情况吗?即使不是,我敢打赌,如果您首先对您的集合进行排序,以便 IP 值按顺序排列,它会导致更简单的“筛选”算法。
  • @Frank, Carl :是的,范围不同并且连续递增顺序。样本数据集为 Ip1, Ip2, Code, Country, Area1, Area2 "16777216","16777471","AU","AUSTRALIA","QUEENSLAND","SOUTH BRISBANE" "16777472","16778239","CN" ,"中国","福建","福州""16778240","16778495","澳大利亚","澳大利亚","维多利亚","墨尔本"
  • 请提供一个样本数据集我们可以读入。 :) 另外,最好编辑您的问题以添加信息。我忘了提:你可以使用dput(a)dput(b)...
  • 欢迎来到 Stack Overflow!如您所见,我们对您的数据状态感到非常困惑。请创建一个reproducible example。正如弗兰克所说,粘贴dput(head(a))dput(head(b)) 的输出会非常有用。

标签: r nested-loops apply


【解决方案1】:

这是data.table 解决方案:

# Let's take Blue Magister's example set:
set.seed(10)
a <- data.frame(ip=sample(16777216:16778751,10,replace=TRUE))
b <- read.table(sep=",",header=TRUE,text='Ip1, Ip2, Code, Country, Area1, Area2
"0","16777215","-","-","-","-"
"16777216","16777471","AU","AUSTRALIA","QUEENSLAND","SOUTH BRISBANE"
"16777472","16778239","CN","CHINA","FUJIAN","FUZHOU"
"16778240","16778495","AU","AUSTRALIA","VICTORIA","MELBOURNE"
"16778496","16778751","AU","AUSTRALIA","NEW SOUTH WALES","SYDNEY"')

b$Ip1 <-as.numeric(b$Ip1)

# include library, convert to data.table
library(data.table)

a = data.table(a)
b = data.table(b, key = "Ip1")

# and now the actual computation
a = b[a, roll = Inf][, Ip2 := NULL] # yep, amazingly, it's *that* simple in data.table
setnames(a, "Ip1", "ip")            # you can also include, exclude whatever columns you want
a
#          ip Code   Country      Area1          Area2
# 1: 16777995   CN     CHINA     FUJIAN         FUZHOU
# 2: 16777687   CN     CHINA     FUJIAN         FUZHOU
# 3: 16777871   CN     CHINA     FUJIAN         FUZHOU
# 4: 16778280   AU AUSTRALIA   VICTORIA      MELBOURNE
# 5: 16777346   AU AUSTRALIA QUEENSLAND SOUTH BRISBANE
# 6: 16777562   CN     CHINA     FUJIAN         FUZHOU
# 7: 16777637   CN     CHINA     FUJIAN         FUZHOU
# 8: 16777634   CN     CHINA     FUJIAN         FUZHOU
# 9: 16778161   CN     CHINA     FUJIAN         FUZHOU
#10: 16777875   CN     CHINA     FUJIAN         FUZHOU

如果Ip1ip 可以匹配的数字的详尽列表,那么上面将只是一个合并(b 中的Ip1a 的第一列,即ip),但是data.table 还提供了当没有完全匹配时该怎么做的选项。你可以告诉它,例如将之前的观察结果向前滚动(这就是我在上面所做的),或者将其回滚或滚动到最近的观察结果 - 请参阅 ?data.table 了解更多信息。

【讨论】:

  • 哇...非常感谢。这甚至不需要 2 秒 :) 。我试过搜索,但仍然不太清楚这是如何工作的。
  • @GauravNemade 我添加了一个简短的解释,希望对您有所帮助
【解决方案2】:

您不能使用删除第二个循环吗,

j = intersect(which(ip1 < x[i]), which(ip2 > x[i]))
if  (length(j)==1){
         a$country[i]<-country[j]
         a$area1[i]<-area1[j]
         a$area2[i]<-area2[j]
}else{
         cat("Multiple matches found!\n")  
}

【讨论】:

  • 感谢您的帮助。不知道 intersect & which。这使过程更快,但仍然需要很长时间(大约 120 次迭代/分钟 - 整个过程需要 22 小时)。还有比这更快的方法吗?
  • @GauravNemade - data.tablefindInterval 更快吗? “接受”最适合你的答案。
【解决方案3】:

我会试试findInterval:

#create example
set.seed(10)
a <- data.frame(ip=sample(16777216:16778751,10,replace=TRUE))
b <- read.table(sep=",",header=TRUE,text='Ip1, Ip2, Code, Country, Area1, Area2
"0","16777215","-","-","-","-"
"16777216","16777471","AU","AUSTRALIA","QUEENSLAND","SOUTH BRISBANE"
"16777472","16778239","CN","CHINA","FUJIAN","FUZHOU"
"16778240","16778495","AU","AUSTRALIA","VICTORIA","MELBOURNE"
"16778496","16778751","AU","AUSTRALIA","NEW SOUTH WALES","SYDNEY"')

b$Ip1 <-as.numeric(b$Ip1)
indices <- findInterval(a$ip,b$Ip1,rightmost.closed=FALSE,all.inside=FALSE)
a <- data.frame(a,b[indices,c("Country","Area1","Area2")])

【讨论】:

    猜你喜欢
    • 2021-08-09
    • 2019-01-24
    • 1970-01-01
    • 2021-09-05
    • 2021-08-08
    • 1970-01-01
    • 2020-06-05
    • 1970-01-01
    • 2016-07-14
    相关资源
    最近更新 更多