难以随机化,基于频率排名

时间:2011-06-13 05:20:17

标签: r random

我有一个这样的数据框:

x = data.frame(A = c(“D1”,“D1”,“D1”,“D1”,“D1”,“D2”,“D3”,“D3”,“D4”,“ D4“,”D4“,”D5“,”D5“),B = c(”A1“,”A3“,”A4“,”A5“,”A6“,”A5“,”A5“,”A6 ”, “A6”, “A1”, “A2”, “A5”, “A6”))

A        B  
D1  A1  
D1  A3  
D1  A4  
D1  A5  
D1  A6  
D2  A5  
D3  A5  
D3  A6  
D4  A6  
D4  A1  
D4  A2  
D5  A5  
D5  A6 

要按B列排序,B列中的实体具有不同的频率。

A   B   freq(B)  
D1  A1  2  
D4  A1  2  
D4  A2  1  
D1  A3  1  
D1  A4  1  
D1  A5  4  
D2  A5  4  
D3  A5  4  
D5  A5  4  
D1  A6  4  
D3  A6  4  
D4  A6  4  
D5  A6  4  

我想在数据帧x的B列上生成随机数据帧,但是随机化只能在条目的频率相同或相似(+/-一个等级)的地方进行。 Let'said。现在,A2,A3,A4的频率为1,因此A2,A3和A4可以自由地相互替换,但不是A5和A6,也不是A1。类似地,由于A5和A6具有频率= 4,所以它们可以在它们之间随机化。对于A1,这是唯一具有频率= 2的条目(基于频率(B)排名第2),因为不能进行替换,所以给出了A1的特殊条件。 A1可以被A2,A3,A4(其中一个等级(1,排名第一,基于频率(B))低于A1)或A5 / A6(等级为一等级(4,排名第2,排名第3)的基础上随机替换在频率(B)上高于A1)。

是否可以通过R轻松完成?

3 个答案:

答案 0 :(得分:3)

第一部分可以通过我的permute包中的功能轻松处理(目前仅限于R-forge

require(permute) ## install from R-forge if not available
x <- data.frame(A = c("D1","D1","D1","D1","D1","D2","D3","D3",
                      "D4","D4","D4","D5","D5"),
                B = c("A1","A3","A4","A5","A6","A5","A5","A6",
                      "A6","A1","A2","A5","A6"))
x <- x[order(x$B), ]
x <- transform(x, freq = rep((lens <- sapply(with(x, split(B, B)), 
                             length)), lens))
set.seed(529)
ind <- permuted.index(NROW(x), control = permControl(strata = factor(x$freq)))

给出了:

R> x[ind, ]
    A  B freq
10 D4 A1    2
1  D1 A1    2
11 D4 A2    1
2  D1 A3    1
3  D1 A4    1
12 D5 A5    4
4  D1 A5    4
9  D4 A6    4
13 D5 A6    4
5  D1 A6    4
6  D2 A5    4
8  D3 A6    4
7  D3 A5    4
R> ind
 [1]  2  1  3  4  5  9  6 12 13 10  7 11  8

我们可以包装这是一个生成 n 排列

的语句
ctrl <- permControl(strata = factor(x$freq))
n <- 10
set.seed(83)
IND <- replicate(n, permuted.index(NROW(x), control = ctrl))

给出了:

> IND
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    2    2    1    2    1    2    1    2    1     1
 [2,]    1    1    2    1    2    1    2    1    2     2
 [3,]    3    5    4    3    5    5    4    5    5     5
 [4,]    5    3    5    5    3    4    5    4    4     4
 [5,]    4    4    3    4    4    3    3    3    3     3
 [6,]    9   12   11   12    6   10   13   10    8    13
 [7,]   10   11    6   11   13    7    7   12    7     9
 [8,]    8    9    9   10    8    6   11   13   12    10
 [9,]   12   10    8    6    9   13    9    6    9    11
[10,]   13    6   12    9    7    9    8    8   13     8
[11,]    6    7   10   13   12   11    6   11   10     7
[12,]   11    8   13    7   11    8   10    7    6    12
[13,]    7   13    7    8   10   12   12    9   11     6

现在你还需要做一些特殊的采样。如果我理解正确,你想要的是确定哪个频率级别只包含一个B级别。然后可能随机地用相邻频率类别中B的随机选择的B替换该频率级别的B。如果是这样,那么要更换正确的行会有点复杂,但我认为下面的函数可以做到:

randSampleSpecial <- function(x, replace = TRUE) {
    ## have we got access to permute?
    stopifnot(require(permute))
    ## generate a random permutation within the levels of freq
    ind <- permuted.index(NROW(x), 
                          control = permControl(strata = factor(x$freq)))
    ## split freq into freq classes
    ranks <- with(x, split(freq, freq))
    ## rank the freq classes
    Ranked <- rank(as.numeric(names(ranks)))
    ## split the Bs on basis of freq classes
    Bs <- with(x, split(B, freq))
    ## number of unique Bs in freq class
    uniq <- sapply(Bs, function(x) length(unique(x)))
    ## which contain only a single type of B?
    repl <- which(uniq == 1)
    ## if there are no freq classes with only one level of B, return
    if(!(length(repl) > 0))
        return(ind) 
    ## if not, continue
    ## which of the freq classes are adjacent to unique class?
    other <- which(Ranked %in% (repl + c(1,-1)))
    ## generate uniform random numbers to decide if we replace
    Rand <- runif(length(ranks[[repl]]))
    ## Which are the rows in `x` that we want to change?
    candidates <- with(x, which(freq == as.numeric(names(uniq[repl]))))
    ## which are the adjacent values we can replace with
    replacements <- with(x, which(freq %in% as.numeric(names(uniq[other]))))
    ## which candidates to replace? Decision is random
    change <- sample(candidates, sum(Rand > 0.5))
    ## if we are changing a candidate, sample from the replacements and
    ## assign
    if(length(change) > 0)
        ind[candidates][change] <- sample(ind[replacements], length(change), 
                                          replace = replace)
    ## return
    ind
}

要使用它,我们会这样做:

R> set.seed(35)
R> randSampleSpecial(x)
 [1]  2  1  5  3  4  6  9 12 10 11  7  8 13

我们可以在replicate()调用中包装它以产生许多此类替换:

R> IND <- replicate(10, randSampleSpecial(x))
R> IND
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]   11    3    6    4    2    1    1    2   10     3
 [2,]    1   11    1   12   11   11    2    1    1    13
 [3,]    4    5    4    3    4    3    4    5    5     4
 [4,]    5    4    5    5    5    4    5    3    3     3
 [5,]    3    3    3    4    3    5    3    4    4     5
 [6,]   11    7   11   12    9    6    7    8    9     9
 [7,]   13   12   12    7   11    7    9   10    8    10
 [8,]   10    8    9    8   12   12    8    6   13     8
 [9,]    7    9   13   10    8   10   13    9   12    11
[10,]    6   11   10   11   10   13   12   13   10    13
[11,]   12   10    6    6    6    9   11   12    7    12
[12,]    9    6    7    9    7    8   10    7    6     7
[13,]    8   13    8   13   13   11    6   11   11     6

对于这个数据集,我们知道它是排序x中的第1行和第2行,我们可能希望用其他freq类中的值替换它们。如果我们没有进行任何替换,则IND的前两行仅包含值12(请参阅前面的IND)。在新的IND中,前两行中的值 a 12,我们已将其替换为来自其中一个的B相邻频率等级。

我的功能假设你想要:

  1. 仅使用相邻类随机之一替换齐次频率类中的元素!如果您想要始终更换,那么我们会更改功能以适应。
  2. 如果我们正在进行替换,那么替换可以是任何替换,如果我们需要超过1次替换,则可以多次选择相同的替换。如果这是您想要的,请在调用中设置replace = FALSE以进行无需替换的采样。
  3. 该函数假设您只有单个单一特定频率类。如果应该使用两个或多个单特定类的循环来轻松修改,但这确实使函数复杂化,并且由于您对问题的描述不太清楚,我保持简单。

答案 1 :(得分:2)

@Gavin为您提供了一个很好的方法,并询问是否有人可以提出更简单的方法。基于仅基本函数,下一个函数也是如此。它使用count来处理频率,并考虑到最小的最大频率,只有一个相邻的等级。在这种情况下,Gavin的功能会出错。

Permdf <- function(x,v){
  # some code to allow Permdf(df,var)
  mc <- match.call()
  v <- as.quoted(mc$v)
  y <- unlist(eval.quoted(v,x))
  # make bins with values in v per frequency
  freqs <- count(x,v)
  bins <- split(freqs[[1]],freqs[[2]])
  nbins <- length(bins)
  # define the output
  dfid <- 1:nrow(x)

  for (i in 1:nbins){
    # which id's to change
    id <- which(y %in% bins[[i]])

    if(length(bins[[i]]) > 1){
      # in case there's more than one value for that frequency
      dfid[id] <- sample(dfid[id])
    } else {
      bid <- c(i-1,i,i+1)
      # control wether id in range
      bid <- bid[bid > 0 & bid <=nbins]
      # id values to choose from
      vid <- which(y %in% unlist(bins[bid]))
      # random selection
      dfid[id] <- sample(vid,length(id),replace=TRUE)
    }
  }
  #return
  dfid
}

这可以用作

Permdf(x,B)

答案 2 :(得分:1)

关于随机化的问题的下半部分有点不清楚,但这是一个开始。当您更新问题时 - 我会相应地更新答案。下面的代码添加了列B的计数信息,然后根据我们添加的频率列的值对行进行采样。我认为从这里需要的是修改哪些列可用于采样的可用性,但请确认您想要的。

require(plyr)
x <- merge(x,count(x, "B"))
ddply(x, "freq", function(x) sample(x))