2012-10-22 74 views
1

返回不同長度矢量我有三個來源:高效的功能,從查找表

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] 
    } 
    } 
} 

我一直試圖以某種方式矢量化這一點,但我越來越掛了的‘地方’的可變長度和‘this.obs’

在實踐中當然,lookup.counts表是相當大一點的(500 x 40),我有一些長度爲1到長度爲39的位置列表的900K觀察值。

+0

一個好第一步是將所有這些數據放在一個對象中。我認爲你可以使用數據。爲每個觀察記錄一個條目。但從描述中很難說清楚。你也可以使用一個嵌套的命名列表,但你會去'lapply',而不是「矢量化」。 – Justin

+0

@Justin,'data.frame'將不接受列表類型的列,也不接受列數不同的行。 – MvG

+0

@MvG是的,但我會建議爲列表製作多行(例如,長對寬數據) – Justin

回答

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) 
+0

使我的功能從數小時到數秒。我知道有一種方法。不會自己在這裏,不知道爲什麼它比MvG快得多,但它是。謝謝。 – csfowler

+0

如果你有興趣學習如何自己做這個,我建議首先嚐試將你的for循環變成一個函數。嘗試使用原始函數,並刪除'for'行並用'test <-function(i)'替換它。從那裏開始,看看你是否可以'開始'你的功能。然後努力改進功能。 – nograpes

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]]]) 
    }) 
] 
+0

這兩個工作。謝謝,並感謝他們爲什麼工作的說明。很有幫助。 nograpes的回答速度要快得多,所以我會繼續這樣做,但你的努力是值得讚賞的。 – csfowler

+0

@csfowler,您可以按下我的問題旁邊那個美麗的指向上方的箭頭表示您的感謝。 :-)雖然你只能接受一個單一的答案,你可能會贊成多個正確和/或有用的答案。 – MvG