簡體   English   中英

R中矩陣的反向索引

[英]Reverse indexing of a matrix in R

我試圖還原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行中,(矩陣m2的)第3列映射到原始(矩陣m的)第一列,第7列映射到原始的第二列,第10列列到原始的第3列,依此類推。

我的問題如下:我可以以某種方式還原R中的此映射嗎? 我的意思是再次針對第1行,將(m2)的第一列移至(m)的第三列,然后將第二列移至第七列,將第三列移至第十,依此類推。

最后,我試圖實現的是對數據進行排序,但以某種方式保存列的現有排列,因此,這意味着在對數據進行一些轉換之后,我可以將它們重新排列為原始順序。 當我在R中使用常規的排序算法時,我失去了列的舊位置。 當然,大多數時候您將不再需要這些,但是atm我確實需要它們。

背景

我認為這將有助於檢查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時考慮字符值的當前語言環境的排序順序。 換句話說,對於結果向量的元素,其值給出了輸入向量中該元素的輸入索引,應將其移動到該位置以對其進行排序。

為了更明確地說,順序向量的元素基本上說“將具有此索引的輸入向量元素放在我的位置”。 或者,以一種更為通用的方式(將與rank()的並行描述相吻合):

order元素:具有此索引的輸入矢量元素將歸類到我的位置。

從某種意義上說, rank()order()作用相反。 它的元素按索引與輸入向量的元素相對應,其值表示相應輸入元素的排序順序(根據ties.method參數的不同,打破ties.method行為;這與order()始終保持一致)關系的輸入順序,等效於rank() ties.method='first' )。

使用與我剛才用於order()語言結構相同的語言結構,這是我能想到的最簡單的表達方式:

等級元素:我位置中的輸入向量元素將歸入此索引。

當然,此描述僅適用於ties.method='first' 對於其他領帶,領帶的目的地索引實際上將是傳入順序(對於'last' )的反向,重復集的最低索引(對於'min' ),最高(對於'max' ),平均(對於'average' ,實際上是默認值)或random(對於'random' )。 但是出於我們的目的,由於我們需要根據order()鏡像正確的排序順序(因此需要在內部使用order() sort() ,因此從現在開始,我們將忽略其他情況。


我想過一種表達order()rank()函數行為的最終方法: order()定義如何將輸入向量的元素成排序后的順序,而rank()定義如何將輸入向量的元素推入輸入向量按排序順序排列。

這就是為什么用order()的結果索引輸入向量是對它進行排序的正確方法的原因。 索引向量本質上是拉動操作。 每個相應的索引向量元素有效地存儲在該索引向量元素給定的索引處的輸入向量元素拉到該索引向量元素在索引向量中占據的位置。

當然,由於索引是拉動操作,所以rank()產生的“推向量”不能與order()產生的“拉向量”以相同的方式直接對輸入向量進行排序。 但是我們可以問,是否可以使用推向量對輸入向量進行排序? 是的,我已經考慮過如何做到這一點。 解決方案是索引分配,這本質上是一種推送操作。 具體來說,我們可以將推入向量作為(左值)LHS索引輸入向量,並將輸入向量本身分配為RHS。

因此,這是可用於對向量進行排序的三種方法:

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"

具有ties.method='first'rank()函數的一個有趣特性是它是冪等的 這是因為,一旦生成了排名向量,再次對其進行排名將不會更改結果。 考慮一下:說第一個元素排在第四位。 然后,第一個通話將在該位置產生4。 再次運行rank()將再次發現它排名第四。 您甚至不需要為后續的排名調用指定ties.method ,因為在第一次調用(潛在) 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

想想看:如果最后一個元素排在第一位,那么對order()的第一次調用會將其索引(在所有索引中最大order()放在第一位置,從而將其拉到第一位置。 order()的第二次調用將識別出第一個位置的元素在整個向量中最大,因此會將索引1拉到最后一個位置,這等效於對最后一個元素的等級為1進行排名。


解決方案

根據上述所有情況,如果您願意,我們可以為您的“分類”問題設計3種解決方案。

對於輸入,假定我們具有(1)輸入向量x ,(2)其排序順序o ,以及(3)已排序並可能經過轉換的向量xs 對於輸出,我們需要產生相同的向量xs但根據o

常用輸入:

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"

方法1:拉rank()

由於順序向量和秩向量實際上是彼此相反的(即拉和推向量),因此一種解決方案是除順序向量o之外還計算秩向量,並使用它對xs進行排序。

xs[rank(x,ties.method='first')];
## [1] "c4" "b2" "d5" "b3" "a1"

方法2:拉重復的order()

另外,代替計算rank() ,我們可以簡單地對o使用重復的order()調用來生成相同的推矢量,並按上述方法使用它。

xs[order(o)];
## [1] "c4" "b2" "d5" "b3" "a1"

方法3:推送order()

我在想我自己,因為我們已經有了階向量o ,所以我們真的不必費心計算另一個階或階向量。 最終,我意識到最好的解決方案是使用拉向量o作為推向量。 這樣以最少的工作即可完成分類目標。

xs[o] <- xs;
xs;
## [1] "c4" "b2" "d5" "b3" "a1"

標桿

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

## scale test case
set.seed(1L);
NN <- 1e4; NE <- 1e5; x <- sample(seq_len(NN),NE,T);
o <- order(x);
xs <- x[o];
xs <- xs+seq(0L,NE-1L)/NE;

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: milliseconds
##                     expr       min        lq     mean    median        uq       max neval
##    desort.rank(x, o, xs) 36.488185 37.486967 39.89157 38.613191 39.145405 85.849143   100
##  desort.2order(x, o, xs) 16.764414 17.262630 18.10341 17.443527 19.014296 28.338835   100
##  desort.assign(x, o, xs)  1.457014  1.498495  1.82893  1.527363  1.592151  4.255573   100

因此,很明顯,索引分配解決方案是最好的。


演示

以下是如何將此解決方案用於樣本輸入的演示。

老實說,在這種情況下,對行進行簡單的for循環比apply()調用更可取,因為您可以就地修改矩陣。 如果需要保留排序的中間矩陣,則可以在應用此排序操作之前將其復制。

## generate input matrix
set.seed(21L); m <- matrix(sample(seq_len(100L)),10L); m;
##       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
##  [1,]   79   61    1   66   40   39    2   86   44    26
##  [2,]   25   84   49   35   67   32   36   70   50   100
##  [3,]   69    6   90   51   30   92   65   34   68    42
##  [4,]   18   54   72   73   85   75   55   15   27    77
##  [5,]   93   16   23   58    9    7   19   64    8    46
##  [6,]   88    4   60   13   98   47    5   29   56    80
##  [7,]   10   45   43   14   95   11   74   76   83    38
##  [8,]   17   24   57   82   63   28   71   87   53    59
##  [9,]   91   41   81   21   22   94   33   62   12    37
## [10,]   78   52   48   31   89    3   97   20   99    96

## sort each row, capturing sort order in rowwise order matrix
o <- matrix(NA_integer_,nrow(m),ncol(m)); ## preallocate
for (ri in seq_len(nrow(m))) m[ri,] <- m[ri,o[ri,] <- order(m[ri,],decreasing=T)];

## whole-matrix transformation
## embed row index as tenth digit, column index as hundredth (arbitrary)
m <- m+(row(m)-1L)/nrow(m)+(col(m)-1L)/ncol(m)/10;

## desort
for (ri in seq_len(nrow(m))) m[ri,o[ri,]] <- m[ri,]; m;
##        [,1]  [,2]  [,3]  [,4]  [,5]  [,6]  [,7]  [,8]  [,9]  [,10]
##  [1,] 79.01 61.03  1.09 66.02 40.05 39.06  2.08 86.00 44.04  26.07
##  [2,] 25.19 84.11 49.15 35.17 67.13 32.18 36.16 70.12 50.14 100.10
##  [3,] 69.22  6.29 90.21 51.25 30.28 92.20 65.24 34.27 68.23  42.26
##  [4,] 18.38 54.36 72.34 73.33 85.30 75.32 55.35 15.39 27.37  77.31
##  [5,] 93.40 16.46 23.44 58.42  9.47  7.49 19.45 64.41  8.48  46.43
##  [6,] 88.51  4.59 60.53 13.57 98.50 47.55  5.58 29.56 56.54  80.52
##  [7,] 10.69 45.64 43.65 14.67 95.60 11.68 74.63 76.62 83.61  38.66
##  [8,] 17.79 24.78 57.75 82.71 63.73 28.77 71.72 87.70 53.76  59.74
##  [9,] 91.81 41.84 81.82 21.88 22.87 94.80 33.86 62.83 12.89  37.85
## [10,] 78.94 52.95 48.96 31.97 89.93  3.99 97.91 20.98 99.90  96.92

rankorder()的補碼。 您需要保存原始的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 

如果存在聯系的可能性,則需要在rank()調用中使用ties.method = 'first'

回到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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM