Reputation: 31452
Suppose I have a vector (x
) whose values are in such an order that if they were in an nx
by ny
matrix, the matrix would contain values that increase by row starting from the bottom left upwards. Any remaining unfilled values in the matrix will then be NA
. Let me illustrate with an example:
nr=3
nc=3
mx = matrix(c(7,NA,NA,4:6,1:3), nr, nc, byrow = T)
# [,1] [,2] [,3]
# [1,] 7 NA NA
# [2,] 4 5 6
# [3,] 1 2 3
x = c(mx)
# [1] 7 4 1 NA 5 2 NA 6 3
Now, I would like to re-order x into a new vector (y
) such that if the values of y
were in a matrix the NA
s would remain in the same location, but the other values would be ordered increasing row-wise from the top left. I.e. y
should look like this
my = matrix(c(1,NA,NA,2:4,5:7), nr, nc, byrow = T)
# [,1] [,2] [,3]
# [1,] 1 NA NA
# [2,] 2 3 4
# [3,] 5 6 7
y = c(y)
# [1] 1 2 5 NA 3 6 NA 4 7
I want to find a vector of indices that maps x to y. In this case it would be
indices = c(3, 6, 5, 4, 9, 8, 7, 2, 1)
identical(x[indices], y)
#TRUE
But, I'm struggling to find a simple algorithm that can generate indices
for any values of nr
, nc
, and any number of NA values in x
. Any suggestions? NB, we can assume that there will never be enough NA
s to fill an entire matrix row
Upvotes: 1
Views: 57
Reputation: 32548
my = t(replace(t(mx), which(!is.na(t(mx))), sort(mx)))
my
# [,1] [,2] [,3]
#[1,] 1 NA NA
#[2,] 2 3 4
#[3,] 5 6 7
match(my, mx)
#[1] 3 6 5 4 9 8 4 2 1
Or, if the indices vector must have a one-to-one correspondence between the two vectors, such that it does not point to the same NA twice:
match(replace(my, is.na(my), paste0("NA", seq(sum(is.na(my))))),
replace(mx, is.na(mx), paste0("NA", seq(sum(is.na(mx))))))
#[1] 3 6 5 4 9 8 7 2 1
Upvotes: 3
Reputation: 263332
If you use apply
rowwise, and then transpose, you get back the original matrix arrangement. The function given to apoply replaces non-NA values with ordered non-NA values and then returns the full row..
t( apply(mx, 1, function(x){ x[!is.na(x)] <- x[!is.na(x)][order(x[!is.na(x)])]; x}) )
[,1] [,2] [,3]
[1,] 7 NA NA
[2,] 4 5 6
[3,] 1 2 3
If you wanted the values without the matrix arrangement just unclass or wrap it in c
. If you want the order vector then you could first set a y value inside the function that is 1:length(x) and then assign the !is.na ordering.
Upvotes: 0