快速,简洁的方法来生成唯一矩阵行的有序频率计数

时间:2014-10-02 11:45:12

标签: r

我有一个带有一些非唯一行的矩阵,例如:

x <- read.csv(textConnection(
'0,1,1,0
0,1,1,0
1,0,1,0
0,1,0,1
1,0,0,1'),
header = FALSE)

挑战一种快速的方式(或许称为umat_count的函数)来获得该矩阵的唯一行的计数,按照它们在x中出现的顺序。理想情况下,结果如下:

y <- umat_count(x) 
y
##    2    1    1    1 

为了测试这个结果是我想要的,我们可以创建x的唯一版本,然后对其行进行y次采样,然后我们回到x:

ux <- unique(x)
ux[rep(1:nrow(ux), y),]

## V1 V2 V3 V4
## 1    0  1  1  0
## 1.1  0  1  1  0
## 3    1  0  1  0
## 4    0  1  0  1
## 5    1  0  0  1

所以问题是什么是写umat_count的快捷方式? ATM这是我的笨重代码,但我确信有更好的方法,也许是一个班轮:

umat_count <- function(x) {
  xp <- apply(x, 1, paste0, collapse = "") # "pasted" version of constraints
  freq <- table(xp) # frequency of occurence of each individual
  xu <- unique(x) # save only unique individuals
  rns <- as.integer(row.names(xu)) # save the row names of unique values of ind
  xpu <- xp[rns]
  # xpu <- apply(xu, 1, paste0, collapse = "") # old way of generating ind_pu
  o <- order(xpu, decreasing = TRUE) # the order of the output (to rectify table)
  y <- freq[o] # frequency with which each individual appears (more efficient way?)
  y
}

y <- umat_count(x)

对于上下文,我正在数据准备阶段使用它来努力优化R脚本以进行“空间微观模拟”,如本课程所述:https://www.dropbox.com/s/ffnrl2ofv18rm3n/book-cambridge.pdf?dl=0

非常感谢。

3 个答案:

答案 0 :(得分:4)

更新回答:

y <- apply(x, 1, paste, collapse = " ")
y <- rle(sort(as.numeric(factor(y, unique(y), ordered = T))))$lengths

do.call方式:

y <- do.call(paste, as.data.frame(x))
y <- rle(sort(as.numeric(factor(y, unique(y), ordered = T))))$lengths

尝试

y <- rle(apply(x, 1, paste, collapse = " "))
# y$lengths is the vector containing the number of times each row appears
# y$values are the rows in the order that y$lengths reports frequency

归功于@JonathanChang,请参阅this page此页面了解他的解决方案。如果行无序,则应在使用rle之前对其进行排序。

y <- rle(sort(apply(x, 1, paste, collapse = " ")))

答案 1 :(得分:3)

你可以考虑使用“data.table”包,你可以这样使用:

library(data.table)
as.data.table(x, keep.rownames = TRUE)[, list(n = .N, rn = rn[1]), by = names(x)]
#    V1 V2 V3 V4 n rn
# 1:  0  1  1  0 2  1
# 2:  1  0  1  0 1  3
# 3:  0  1  0  1 1  4
# 4:  1  0  0  1 1  5

我添加了“rn”列,以确保我们可以在必要时保留行顺序。


如果行被混淆,并且您的目标之一是尝试重新创建原始数据集,我建议创建一个list行位置,如下所示:

X <- as.data.table(x)  ## your new "x" in your answer
X[, rn := sequence(nrow(X))][, list(
    .N, rn = list(rn)), by = eval(paste0("V", 1:4))]
#    V1 V2 V3 V4 N    rn
# 1:  0  1  1  0 3 1,2,6
# 2:  1  0  1  0 1     3
# 3:  0  1  0  1 1     4
# 4:  1  0  0  1 1     5
str(.Last.value)
# Classes ‘data.table’ and 'data.frame':  4 obs. of  6 variables:
#  $ V1: int  0 1 0 1
#  $ V2: int  1 0 1 0
#  $ V3: int  1 1 0 0
#  $ V4: int  0 0 1 1
#  $ N : int  3 1 1 1
#  $ rn:List of 4
#   ..$ : int  1 2 6
#   ..$ : int 3
#   ..$ : int 4
#   ..$ : int 5
#  - attr(*, ".internal.selfref")=<externalptr>

由于list中有rn,您可以稍后使用unlistorder恢复原始格式。

答案 2 :(得分:0)

对于记录(抱歉回答我自己的问题),这是另一个使用dplyr的解决方案。无论重复行发生在哪里,都要按正确顺序简洁并获取行。从x开始作为data.frame:

library(dplyr)


rns <-as.integer(row.names(unique(x)))
x$p <- apply(x, 1, paste0, collapse = "")
up <- p[rns]
y <- count(x, p)
o <- order(up, decreasing = TRUE)
y$n[o]

测试rle解决方案失败的数据集,(感谢Anananda):

x <- read.csv(textConnection(
  '0,1,1,0
  0,1,1,0
  1,0,1,0
  0,1,0,1
  1,0,0,1
  0,1,0,1'),
  header = FALSE)

umat_count_dplyr <- function(x){
rns <-as.integer(row.names(unique(x)))
x$p <- apply(x, 1, paste0, collapse = "")
up <- p[rns]
y <- count(x, p)
o <- order(up, decreasing = TRUE)
y$n[o]} # correct order of output
umat_count(x)

  V1 V2 V3 V4    p ind_num rns
1  0  1  1  0 0110       2   1
3  1  0  1  0 1010       1   3
4  0  1  0  1 0101       2   4
5  1  0  0  1 1001       1   5

问题:这个解决方案与我原来的umat_count函数一样长而且慢 - 也许dplyr解决方案对于更大的数据集会相对更快。希望有更好的方法来编写这个整体...

microbenchmark(umat_count(x), umat_count_dplyr(x))
Unit: microseconds
                expr      min       lq     mean   median        uq      max neval
       umat_count(x)  698.606  714.710  770.613  742.501  770.3165 3061.149   100
 umat_count_dplyr(x) 1142.678 1168.565 1249.977 1187.452 1214.5750 3579.286   100