我正在尝试恢复R中矩阵的索引。以下示例说明了我的问题:
#sample data:
set.seed(21)
m <- matrix(sample(100,size = 100),10,10)
# sorting:
t(apply(m,1,order))
# new exemplary order after sorting:
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 3 7 10 6 5 9 2 4 1 8
[2,] 1 6 4 7 3 9 5 8 2 10
[3,] 2 5 8 10 4 7 9 1 3 6
[4,] 8 1 9 2 7 3 4 6 10 5
[5,] 6 9 5 2 7 3 10 4 8 1
[6,] 2 7 4 8 6 9 3 10 1 5
[7,] 1 6 4 10 3 2 7 8 9 5
[8,] 1 2 6 9 3 10 5 7 4 8
[9,] 9 4 5 7 10 2 8 3 1 6
[10,] 6 8 4 3 2 1 5 10 7 9
# we can create m2 with the above sorting. We also add 1000 to all values
m2 <- t(apply(m,1,function(x){
x[order(x)]
})) + 1000
# the next step would be to obtain the original arrangement of columns again, as described below.
在对数据进行排序后,我们得到以下情况:在第1行中,第3列(矩阵m2)映射到原始第一列(矩阵m),第7列映射到原始第二列,原始第3列的第10列,依此类推。
我的问题如下:我可以以某种方式在R中恢复此映射吗?我的意思是对于第1行,将第1列(m2)移动到第3列(m)的位置,然后将第2列移动到第7列的位置,将第3列移动到第3列的位置十号,等等。
最后我尝试实现的是对数据进行排序,但以某种方式保存列的现有排列,以后,这意味着在对数据进行一些转换后,我可以将它们重新排列为原始排序。当我在R中使用通常的排序algortihms时,我正在失去我的列的旧位置。当然,大多数时候你不再需要那些,但我总是需要它们。
答案 0 :(得分:4)
我认为这将有助于检查order()
和rank()
函数对简单向量的影响。考虑:
x <- c('c','b','d','b','a');
seq_along(x);
## [1] 1 2 3 4 5
order(x);
## [1] 5 2 4 1 3
rank(x); ## default is ties.method='average'
## [1] 4.0 2.5 5.0 2.5 1.0
rank(x,ties.method='first');
## [1] 4 2 5 3 1
rank(x,ties.method='last'); ## available from 3.3.0
## [1] 4 3 5 2 1
rank(x,ties.method='random'); ## we can ignore this one, obviously
## [1] 4 2 5 3 1
rank(x,ties.method='max');
## [1] 4 3 5 3 1
rank(x,ties.method='min');
## [1] 4 2 5 2 1
(我使用字符值来证明这些原则和算法可以应用于任何(可比较的)数据类型,而不仅仅是数字类型。但显然这包括数字类型。)
order()
函数返回一个与输入向量长度相同的向量。订单值表示输入索引的重新排序(上面显示为seq_along()
),这样当输入向量使用顺序向量编制索引时,它将被排序(根据所选择的排序方法,如果没有被method
参数明确覆盖),则radixsort表示整数,逻辑和因子,shellsort否则,并考虑到不使用radixsort时字符值的当前区域设置的排序顺序)。换句话说,对于结果向量的元素,其值给出了输入向量中元素的输入索引,该元素应该移动到该位置以对其进行排序。
为了更加明确地说,顺序向量的一个元素基本上表示&#34;将带有此索引的输入向量元素放在我的位置&#34;中。或者,以稍微更通用的方式(将与rank()
的并行描述相吻合):
order元素:带有此索引的输入向量元素将排序到我的位置。
从某种意义上说,rank()
与order()
所做的相反。它的元素通过索引对应于输入向量的元素,其值表示相应输入元素的排序顺序(具有取决于ties.method
参数的tiebreaking行为;这与order()
形成对比,始终保留传入的关联顺序,相当于ties.method='first'
的{{1}}。
使用我刚才用于rank()
的语言结构,这是我能想到的最简洁的表达方式:
排名元素:我位置的输入向量元素排序到此索引中。
当然,此描述仅对order()
完全准确。对于其他人,关系的目标索引实际上与传入订单的反向(对于ties.method='first'
),重复集的最低索引(对于'last'
),最高(对于{{1} }},平均值(对于'min'
,实际上是默认值)或随机值(对于'max'
)。但就我们的目的而言,由于我们需要根据'average'
(以及'random'
(内部使用order()
)镜像正确的排序顺序,因此请忽略其他案例这一点。
我已经想到了阐明sort()
和order()
函数行为的最后一种方式:order()
定义了如何拉元素将输入向量转换为排序顺序,而rank()
定义如何将输入向量的推送元素转换为排序顺序。
这就是为什么使用order()
的结果索引输入向量是对其进行排序的正确方法。索引向量本质上是拉操作。每个相应的索引向量元素有效地拉出将由该索引向量元素给出的索引处存储的输入向量元素拉入索引向量中该索引向量元素占据的位置。
当然,&#34;推送矢量&#34; rank()
生成的{+ 1}}不能以与&#34;拉矢量&#34;相同的方式使用由order()
生成以直接对输入向量进行排序,因为索引是一个拉操作。但我们可以问,是否可以使用推送向量对输入向量进行排序?是的,我已经想过如何做到这一点。解决方案是索引分配,这本质上是一种推送操作。具体来说,我们可以将推送向量作为(左值)LHS索引输入向量,并将输入向量本身指定为RHS。
因此,以下是可用于对矢量进行排序的三种方法:
rank()
order()
函数与x[order(x)];
[1] "a" "b" "b" "c" "d"
sort(x); ## uses order() internally
[1] "a" "b" "b" "c" "d"
y <- x; y[rank(y,ties.method='first')] <- y; y; ## (copied to protect x, but not necessary)
[1] "a" "b" "b" "c" "d"
的一个有趣属性是idempotent。这是因为,一旦您生成了等级向量,再次对其进行排名将不会改变结果。想一想:说第一个元素排名第四。然后第一个调用将在该位置产生4。再次运行rank()
将再次发现它排名第4。您甚至不需要为随后的排名调用指定ties.method='first'
,因为这些值在第一次通话(潜在)抢七时会变得明显。
rank()
另一方面,ties.method
不幂等。反复调用rank(x,ties.method='first');
## [1] 4 2 5 3 1
rank(rank(x,ties.method='first'));
## [1] 4 2 5 3 1
rank(rank(rank(x,ties.method='first')));
## [1] 4 2 5 3 1
y <- rank(x,ties.method='first'); for (i in seq_len(1e3L)) y <- rank(y); y;
## [1] 4 2 5 3 1
具有在推拉向量之间交替的有趣效果。
order()
考虑一下:如果最后一个元素排序第一,那么第一次调用order()
会将其索引(所有索引中最大的一个)放入第一个位置,将其拉到第一个位置。对order(x);
## [1] 5 2 4 1 3
order(order(x));
## [1] 4 2 5 3 1
order(order(order(x)));
## [1] 5 2 4 1 3
的第二次调用将识别第一个位置中的元素在整个向量中最大,因此将索引1拉入最后一个位置,这相当于对排名为1的最后一个元素进行排名。
基于以上所有内容,如果您愿意,我们可以为您的#des; desorting&#34;设计3个解决方案。
对于输入,我们假设我们有(1)输入向量order()
,(2)它的排序顺序order()
,以及(3)已排序和可能转换的向量{ {1}}。对于输出,我们需要生成相同的向量x
,但根据o
进行解除。
常见输入:
xs
xs
由于顺序和秩向量是彼此有效的反转(即拉和向量),一种解决方案是除了顺序向量o
之外还计算秩向量,并使用它来解除{{ 1}}。
x <- c('c','b','d','b','a'); ## input vector
o <- order(x); ## order vector
xs <- x[o]; ## sorted vector
xs <- paste0(xs,seq_along(xs)); ## somewhat arbitrary transformation
x;
## [1] "c" "b" "d" "b" "a"
o;
## [1] 5 2 4 1 3
xs;
## [1] "a1" "b2" "b3" "c4" "d5"
rank()
或者,我们可以在o
上使用重复的xs
调用来生成相同的推送向量,而不是计算xs[rank(x,ties.method='first')];
## [1] "c4" "b2" "d5" "b3" "a1"
,而不是计算order()
。
rank()
order()
我在想,既然我们已经有了订单向量o
,我们真的不应该去计算另一个订单或排名向量的麻烦。最终我意识到最好的解决方案是使用拉向量xs[order(o)];
## [1] "c4" "b2" "d5" "b3" "a1"
作为推送向量。这样就可以用最少的工作来完成摧毁目标。
order()
o
o
xs[o] <- xs;
xs;
## [1] "c4" "b2" "d5" "b3" "a1"
因此,显然索引分配解决方案是最好的。
下面演示了如何将此解决方案用于您的样本输入。
老实说,在这种情况下,对行进行简单的for循环比library(microbenchmark);
desort.rank <- function(x,o,xs) xs[rank(x,ties.method='first')];
desort.2order <- function(x,o,xs) xs[order(o)];
desort.assign <- function(x,o,xs) { xs[o] <- xs; xs; };
调用更可取,因为您可以就地修改矩阵。如果需要保留已排序的中间矩阵,可以在应用此排序操作之前复制它。
## simple test case
x <- c('c','b','d','b','a');
o <- order(x);
xs <- x[o];
xs <- paste0(xs,seq_along(xs));
ex <- desort.rank(x,o,xs);
identical(ex,desort.2order(x,o,xs));
## [1] TRUE
identical(ex,desort.assign(x,o,xs));
## [1] TRUE
microbenchmark(desort.rank(x,o,xs),desort.2order(x,o,xs),desort.assign(x,o,xs));
## Unit: microseconds
## expr min lq mean median uq max neval
## desort.rank(x, o, xs) 106.487 122.523 132.15393 129.366 139.843 253.171 100
## desort.2order(x, o, xs) 9.837 12.403 15.66990 13.686 16.251 76.122 100
## desort.assign(x, o, xs) 1.711 2.567 3.99916 3.421 4.277 17.535 100
答案 1 :(得分:2)
rank
是order()
的补充。您需要保存原始rank()
,然后在使用order()
重新排列后,可以使用它来恢复原始排序。
我认为你的例子过于复杂(远非最小化!)通过将事物放入矩阵并做额外的事情。因为您在行级应用函数,所以您只需要为向量求解它。一个例子:
set.seed(47)
x = rnorm(10)
xo = order(x)
xr = rank(x)
x[xo][xr] == x
# [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
在您的情况下,您可以在有序向量x[xo]
上执行所需的任何转换,然后按[xr]
索引结果以返回原始排序。
sorted_result = x[xo] + c(1, diff(x[xo])) # some order-dependent transformation
final_result = sorted_result[xr] # back to original ordering
如果存在联系的可能性,您需要在ties.method = 'first'
来电中使用rank()
。
回到matrix
示例:
m3 = t(apply(m, 1, function(x) {
xo = order(x)
xr = rank(x, ties.method = 'first')
(x[xo] + 1000)[xr] # add 1000 to sorted matrix and then "unsort"
}))
# check that it worked
all(m3 == (m + 1000))
# [1] TRUE