Home > Blockchain >  use r base function to find the relatively min value of each row in matrix but each min value at dif
use r base function to find the relatively min value of each row in matrix but each min value at dif

Time:12-08

I have a matrix like this

d <- matrix(c(10, -20, -30, 20, 30,
              10, -15, -30, 20, 30,
              10, 40, -30, 20, 30,
              10, -20, -30, 20, 40), byrow = TRUE, nrow = 4)

# > d
#      [,1] [,2] [,3] [,4] [,5]
# [1,]   10  -20  -30   20   30
# [2,]   10  -15  -30   20   30
# [3,]   10   40  -30   20   30
# [4,]   10  -20  -30   20   40

I want to find the index of each row min value whose abs value is less equal 25, but each row min value should at different column. I use for loop to do it like this, which is not good enough.

rng <- 25
ok <- abs(d) < rng
res <- rep(NA, 4)
for (i in seq_len(nrow(d))) {
    if (any(ok[i, ])) {
        dd <- d[i, ]
        idx <- which(ok[i, ])
        idx.min <- idx[which.min(dd[idx])]
        res[i] <- idx.min
        ok[, idx.min] <- FALSE
    }
}
res

the result is this

# > res
# [1]  2  1  4 NA

which means that the index of the first row is 2, whose value is -20. and since the second column is used, although the min value of the second row is still in the second column, it should be the first column value 10, and the index is 1. If it can not find the min value, the index is NA.

Is there vectorizing function that can do this? thanks guys. If there is any English expression problem, please also tell me.

CodePudding user response:

If you want to get the column index with the minimal absolute value for each row, you can do:

library(tidyverse)

d <- matrix(c(
  10, -20, -30, 20, 30,
  10, -15, -30, 20, 30,
  10, 40, -30, 20, 30,
  10, -20, -30, 20, 40
), byrow = TRUE, nrow = 4)

d %>%
  as_tibble(rownames = "row") %>%
  pivot_longer(-row, names_to = "col") %>%
  mutate(col = col %>% str_extract("[0-9] ") %>% as.numeric()) %>%
  group_by(row) %>%
  # only consider values with small abs volaue
  filter(abs(value) < 25) %>%
  # get the smallest value
  arrange(abs(value)) %>%
  slice(1) %>%
  # get column indicies
  pull(col)
#> Warning: The `x` argument of `as_tibble.matrix()` must have unique column names if `.name_repair` is omitted as of tibble 2.0.0.
#> Using compatibility `.name_repair`.
#> [1] 1 1 1 1

Created on 2021-12-07 by the reprex package (v2.0.1)

Is this your expected result?

CodePudding user response:

You can try for loop like below

res <- c()
for (i in 1:nrow(d)) {
  v <- d[i, ]
  idx <- abs(v) <= 25 & !(1:ncol(d) %in% res)
  if (all(!idx)) {
    res <- c(res, NA)
  } else {
    res <- c(res, which(v == min(v[idx])))
  }
}

and you will see

> res
[1]  2  1  4 NA

CodePudding user response:

Here is a base R way. It sets each value in the columns already in ok and each value not less than rng to Inf. And uses which.min to find the minima per row.

rng <- 25
ok <- rep(NA_integer_, nrow(d))
for(i in seq_len(nrow(d))) {
  x <- abs(d[i,])
  x[ok] <- Inf
  x[x >= rng] <- Inf
  if(any(is.finite(x))) ok[i] <- which.min(x)
}

ok
#[1]  1  2  4 NA

CodePudding user response:

Here's an option that removes the chosen column at each iteration.

d <- matrix(c(10, -20, -30, 20, 30,
              10, -15, -30, 20, 30,
              10, 40, -30, 20, 30,
              10, -20, -30, 20, 40), byrow = TRUE, nrow = 4)

d[abs(d) > 25] <- NA
res <- integer(nrow(d))
# rbind the column index
d <- rbind(1:ncol(d), d)

for (i in 2:nrow(d)) {
  idx <- which.min(d[i,])
  
  if (length(idx)) {
    res[i - 1L] <- d[1, idx]
    d <- d[, -idx]
  } else {
    res[i - 1L] <- NA
  }
}

res
#> [1]  2  1  4 NA
  •  Tags:  
  • r
  • Related