从查找表返回变长度向量的高效函数

时间:2012-10-22 15:39:05

标签: r vectorization lookup-tables

我有三个数据源:

types<-c(1,3,3)
places<-list(c(1,2,3),1,c(2,3))
lookup.counts<-as.data.frame(matrix(runif(9,min=0,max=10),nrow=3,ncol=3))
assigned.places<-rep.int(0,length(types))

“类型”向量中的数字告诉我给定观察的“类型”是什么。地方列表中的向量告诉我可以在哪些地方找到观察结果(一些观察结果只在一个地方找到,其他地方在所有地方找到)。根据定义,每种观察的类型和一个列表都有一个条目。 Lookup.counts告诉我每个位置有多少个观察点(从另一个数据源生成)。

我想根据lookup.counts生成的概率将每个观察值随机分配到一个地方。使用for循环它看起来像“

for (i in 1:length(types)){
  row<-types[i]
  columns<-places[[i]]
  this.obs<-lookup.counts[row,columns] #the counts of this type in each place
  total<-sum(this.obs)
  this.obs<-this.obs/total #the share of observations of this type in these places
  pick<-runif(1,min=0,max=1)

  #the following should really be a 'while' loop, but regardless it needs help
  for(j in 1:length(this.obs[])){
    if(this.obs[j] > pick){
      #pick is less than this county so assign
      pick<- 100 #just a way of making sure an observation doesn't get assigned twice
      assigned.places[i]<-colnames(lookup.counts)[j]
    }else{
      #pick is greater, move to the next category
      pick<- pick-this.obs[j]
    }
  }
}

我一直在尝试以某种方式对其进行矢量化,但是我在“place”和“this.obs”的可变长度上被挂起了

实际上,当然,lookup.counts表格要大得多(500 x 40),并且我有900K的观察值,其中有一些长度为1到39的地方列表。

2 个答案:

答案 0 :(得分:2)

要对内部循环进行矢量化,您可以使用samplesample.int从具有规定概率的多个替代项中进行选择。除非我错误地阅读了你的代码,否则你需要这样的东西:

assigned.places[i] <- sample(colnames(this.obs), 1, prob = this.obs)

我对您使用colnames(lookup.counts)感到有点惊讶。这不应该是columns的子集吗?似乎我错过了某些内容,或者代码中存在错误。

列表的不同长度是矢量化外循环的严重障碍。也许您可以使用Matrix包将该信息存储为稀疏矩阵。然后,您可以简单地将该概率乘以概率,以排除那些不在给定观察的位置列表中的列。但是,由于您可能仍然使用apply作为上述抽样代码,您可以保留列表并使用某种形式的apply来迭代它。

总体结果可能看起来像这样:

assigned.places <- colnames(lookup.counts)[
  apply(cbind(types, places), 1, function(x) {
    sample(x[[2]], 1, prob=lookup.counts[x[[1]],x[[2]]])
  })
]

使用cbindapply并不是特别漂亮,但似乎有效。每个x都是两个项目的列表,x[[1]]是类型,x[[2]]是相应的位置。我们就像你一样使用它们来索引lookup.counts。然后我们在选择下标中使用的列之一的索引时使用找到的计数作为相对概率。只有在apply将所有这些数字组合成单个向量后,才能将索引转换为基于colnames的名称。

如果你没有cbind组合在一起,你可以检查事情是否更快,而只是迭代索引:

assigned.places <- colnames(lookup.counts)[
  sapply(1:length(types), function(i) {
    sample(places[[i]], 1, prob=lookup.counts[types[i],places[[i]]])
  })
]

答案 1 :(得分:1)

这似乎也有效:

# More convenient if lookup.counts is a matrix.
lookup.counts<-matrix(runif(9,min=0,max=10),nrow=3,ncol=3)
colnames(lookup.counts)<-paste0('V',1:ncol(lookup.counts))

# A function that does what the for loop does for each i
test<-function(i) {
  this.places<-colnames(lookup.counts)[places[[i]]]
  this.obs<-lookup.counts[types[i],this.places]
  sample(this.places,size=1,prob=this.obs)
}

# Applies the function for all i
sapply(1:length(types),test)