当行名等于列名时,更改矩阵中的值

3

我试图更改矩阵的值,以便对于每个元素,其中行名称等于列名称,结果矩阵将具有值为1。

> z<-matrix(0, nrow=10, ncol=8)
> colnames(z)<-letters[1:8]
> rownames(z)<-c("f", "c", "a", "f", "a", "b", "f", "b", "h", "c")
> z
  a b c d e f g h
f 0 0 0 0 0 0 0 0
c 0 0 0 0 0 0 0 0
a 0 0 0 0 0 0 0 0
f 0 0 0 0 0 0 0 0
a 0 0 0 0 0 0 0 0
b 0 0 0 0 0 0 0 0
f 0 0 0 0 0 0 0 0
b 0 0 0 0 0 0 0 0
h 0 0 0 0 0 0 0 0
c 0 0 0 0 0 0 0 0

z应该是:

  a b c d e f g h
f 0 0 0 0 0 1 0 0
c 0 0 1 0 0 0 0 0
a 1 0 0 0 0 0 0 0
f 0 0 0 0 0 1 0 0
a 1 0 0 0 0 0 0 0
b 0 1 0 0 0 0 0 0
f 0 0 0 0 0 1 0 0
b 0 1 0 0 0 0 0 0
h 0 0 0 0 0 0 0 1
c 0 0 1 0 0 0 0 0

我尝试过:
> z[unique(rownames(z)), unique(rownames(z))]<-1
> z
  a b c d e f g h
f 1 1 1 0 0 1 0 1
c 1 1 1 0 0 1 0 1
a 1 1 1 0 0 1 0 1
f 0 0 0 0 0 0 0 0
a 0 0 0 0 0 0 0 0
b 1 1 1 0 0 1 0 1
f 0 0 0 0 0 0 0 0
b 0 0 0 0 0 0 0 0
h 1 1 1 0 0 1 0 1
c 0 0 0 0 0 0 0 0

并且:

> z["a", "a"]<-1
> z
  a b c d e f g h
f 0 0 0 0 0 0 0 0
c 0 0 0 0 0 0 0 0
a 1 0 0 0 0 0 0 0
f 0 0 0 0 0 0 0 0
a 0 0 0 0 0 0 0 0
b 0 0 0 0 0 0 0 0
f 0 0 0 0 0 0 0 0
b 0 0 0 0 0 0 0 0
h 0 0 0 0 0 0 0 0
c 0 0 0 0 0 0 0 0

但这只改变了“a”列中第一个“a”的位置。
3个回答

9

您也可以使用基本的R语言和outer函数来实现这个功能。

z[outer(rownames(z), colnames(z), "==")] <- 1
z
  a b c d e f g h
f 0 0 0 0 0 1 0 0
c 0 0 1 0 0 0 0 0
a 1 0 0 0 0 0 0 0
f 0 0 0 0 0 1 0 0
a 1 0 0 0 0 0 0 0
b 0 1 0 0 0 0 0 0
f 0 0 0 0 0 1 0 0
b 0 1 0 0 0 0 0 0
h 0 0 0 0 0 0 0 1
c 0 0 1 0 0 0 0 0

3
我们可以使用行/列索引将元素更改为1。
z[cbind(1:nrow(z), match( rownames(z), colnames(z)))] <- 1
z
#  a b c d e f g h
#f 0 0 0 0 0 1 0 0
#c 0 0 1 0 0 0 0 0
#a 1 0 0 0 0 0 0 0
#f 0 0 0 0 0 1 0 0
#a 1 0 0 0 0 0 0 0
#b 0 1 0 0 0 0 0 0
#f 0 0 0 0 0 1 0 0
#b 0 1 0 0 0 0 0 0
#h 0 0 0 0 0 0 0 1
#c 0 0 1 0 0 0 0 0

或者另一种选择是(对于大型数据集应该会更慢)
`dimnames<-`(+(sapply(colnames(z), `==`, rownames(z))), dimnames(z))
#  a b c d e f g h
#f 0 0 0 0 0 1 0 0
#c 0 0 1 0 0 0 0 0
#a 1 0 0 0 0 0 0 0
#f 0 0 0 0 0 1 0 0
#a 1 0 0 0 0 0 0 0
#b 0 1 0 0 0 0 0 0
#f 0 0 0 0 0 1 0 0
#b 0 1 0 0 0 0 0 0
#h 0 0 0 0 0 0 0 1
#c 0 0 1 0 0 0 0 0

注意:顺便提一下,这两种解决方案都是纯粹使用base R而不是来自外部包。

基准测试

z1 <- matrix(0, 5000, 5000)
colnames(z1) <- 1:5000
set.seed(24)
row.names(z1) <- sample(1:5000, 5000, replace=TRUE)
z2 <- z1
z3 <- z1
z4 <- z1
system.time(z1[cbind(1:nrow(z1), match( rownames(z1), colnames(z1)))] <- 1)
#    user  system elapsed 
#   0.03    0.08    0.11 
system.time(z2[outer(rownames(z2), colnames(z2), "==")] <- 1)
#   user  system elapsed 
#   0.67    0.16    0.83 
identical(z1, z2)
#[1] TRUE

system.time( `dimnames<-`(+(sapply(colnames(z3), `==`, rownames(z3))), dimnames(z3)))
#   user  system elapsed 
#  31.70    0.39   32.28 

system.time(z3[vapply(colnames(z3), function(x) x== rownames(z3), 
         logical(nrow(z3)))] <- 1)
#  user  system elapsed 
#   0.22    0.00    0.21 

使用@Procrastinatus Maximus的修改进行测试

system.time(z4[sapply(colnames(z4), `==`, rownames(z4))] <- 1)
#   user  system elapsed 
#  28.42    0.36   28.85 

在一个10000×10000的矩阵上进行测试,时间如下:

system.time(z1[cbind(1:nrow(z1), match( rownames(z1), colnames(z1)))] <- 1)
#   user  system elapsed 
#    0.12    0.32    0.44 
system.time(z2[outer(rownames(z2), colnames(z2), "==")] <- 1)
#   user  system elapsed 
#   2.72    0.86    3.58 

并在一个20000 X 20000的矩阵上执行

system.time(z1[cbind(1:nrow(z1), match( rownames(z1), colnames(z1)))] <- 1)
#   user  system elapsed 
#   0.95    1.00    1.95 
system.time(z2[outer(rownames(z2), colnames(z2), "==")] <- 1)
#    user  system elapsed 
#   15.47    5.87   21.39 

nomatch 应该是什么? - MichaelChirico
1
@MichaelChirico 这可以是0。 - akrun
你得到了我的点赞,而且我仍然会给你点赞。我添加评论是为了完整性/自我提高。 - MichaelChirico

3
另一个选项是(这是@akrun第二个选项的修改版):
z[sapply(colnames(z), `==`, rownames(z))] <- 1

这也提供了正确的答案:

> z
  a b c d e f g h
f 0 0 0 0 0 1 0 0
c 0 0 1 0 0 0 0 0
a 1 0 0 0 0 0 0 0
f 0 0 0 0 0 1 0 0
a 1 0 0 0 0 0 0 0
b 0 1 0 0 0 0 0 0
f 0 0 0 0 0 1 0 0
b 0 1 0 0 0 0 0 0
h 0 0 0 0 0 0 0 1
c 0 0 1 0 0 0 0 0

与@akrun的“dimnames”解决方案不同的是,在上述方法中,仅将必要的位置转换为1,这在原始矩阵不仅包含零时非常有优势。这也可以通过@lmo的“outer”选项和@akrun的“cbind”选项实现。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接