1
votes

Garder certains éléments du tableau fixes dans R

Je suis sûr que c'est une question assez simple, mais je ne sais pas comment procéder. J'ai donné un exemple de tableau avec des dimensions (4,4,5) comme suit:

     [,1] [,2] [,3] [,4]
[1,]    1    0    5    0
[2,]    0   NA    0    6
[3,]    0    0    0    0
[4,]    0    0    0    0

     [,1] [,2] [,3] [,4]
[1,]    1    0    10   0
[2,]    0   NA    0    12
[3,]    0    0    0    0
[4,]    0    0    0    0

     [,1] [,2] [,3] [,4]
[1,]    1    0    15   0
[2,]    0   NA    0    18
[3,]    0    0    0    0
[4,]    0    0    0    0

     [,1] [,2] [,3] [,4]
[1,]    1    0    20   0
[2,]    0   NA    0    24
[3,]    0    0    0    0
[4,]    0    0    0    0

     [,1] [,2] [,3] [,4]
[1,]    1    0    25   0
[2,]    0   NA    0    30
[3,]    0    0    0    0
[4,]    0    0    0    0

En gros, pour cet exemple de tableau, j'aimerais les éléments en [1,3] et [2 , 4] pour changer le long de la 3ème dimension mais je ne sais pas comment écrire ce code dans R. J'ai essayé d'utiliser des variantes du tableau de code (c (1,0,0,0,0, NA , 0,0,5,0,0,0,0,6,0,0), dim = c (4,4,3)) et j'ai essayé de vérifier en ligne mais je n'arrive pas à trouver tout ce qui peut aider à résoudre ce problème, donc toute aide que je pourrais obtenir serait grandement appréciée, merci d'avance.


2 commentaires

Pouvez-vous partager des données en utilisant dput et afficher également la sortie attendue?


@RonakShah Je n'ai pas vraiment de sortie dput parce que je n'ai aucune idée de comment le coder même :( La sortie attendue est ce que j'ai donné dans le post ci-dessus


3 Réponses :


2
votes

Je ne suis pas tout à fait sûr de la sortie attendue, mais peut-être quelque chose comme ça en utilisant une boucle for ?

arr[1, 3, ] <- 100
arr[2, 4, ] <- 100

Comme indiqué par @ Cole, dans ce cas (simple), il n'y a pas besoin d'une boucle for

arr <- array(c(1,0,0,0,0,NA,0,0,5,0,0,0,0,6,0,0), dim=c(4,4,3))

for (i in seq_len(dim(arr)[3])) {
    arr[1, 3, i] <- 100;       # Change entry (1, 3) of every 2d matrix 
    arr[2, 4, i] <- 100;       # Change entry (2, 4) of every 2d matrix
}
arr
#, , 1
#
#     [,1] [,2] [,3] [,4]
#[1,]    1    0  100    0
#[2,]    0   NA    0  100
#[3,]    0    0    0    0
#[4,]    0    0    0    0
#
#, , 2
#
#     [,1] [,2] [,3] [,4]
#[1,]    1    0  100    0
#[2,]    0   NA    0  100
#[3,]    0    0    0    0
#[4,]    0    0    0    0
#
#, , 3
#
#     [,1] [,2] [,3] [,4]
#[1,]    1    0  100    0
#[2,]    0   NA    0  100
#[3,]    0    0    0    0
#[4,]    0    0    0    0

est beaucoup plus rapide que.


6 commentaires

Cela semble faire ce que je veux, mais y a-t-il un moyen plus rapide de le faire en plus d'une boucle for ? Les données avec lesquelles je travaille sont malheureusement assez volumineuses: /


@ThePlowKing C'est une idée fausse que les boucles for sont intrinsèquement lentes dans R. Dans ce cas, nous écrasons les valeurs, ce qui est rapide. C'est lorsque vous abusez des boucles for pour développer dynamiquement des objets R lorsque les choses tournent mal. Si cela est vraiment trop lent, vous devrez peut-être examiner les options R non basiques, par exemple en utilisant Rcpp .


Il ne devrait pas y avoir besoin de la boucle. arr [1, 3,] <- 100 fournira le même résultat que pour (i in ...) {arr [1,3, i] <- 100} - la suppression de la boucle rend cette réponse 1000 fois plus rapide et environ 3 fois plus rapide que @thelatemail


@Cole - c'est certainement un bon point, mais vous devrez tout de même faire des affectations pour chacun des arr [1,3,] et arr [2,4,] etc. Si vous avez un petit nombre de combos ligne / colonne à écraser, votre suggestion sera très rapide. Je ne sais pas comment généraliser cela à de nombreux index.


Je ne suis pas sûr que arr [cbind (c (1,2), c (3,4), rep (...) économise beaucoup de saisie non plus. Mais comme votre réponse le suggère, si OP avait une règle des éléments à mettre à jour, votre réponse accepterait une règle beaucoup plus rapide que arr [1,3,] <- 100 . Il est facile d'imaginer cbind (1: 3, 4: 6, rep (...) . J'ajoute une troisième réponse car pourquoi pas arr [arr> 4] <- 100 .


@Cole a été absent pendant une heure lors d'une réunion et a manqué le suivi; merci à tous pour les mises à jour et suggestions intéressantes!



2
votes

Vous pouvez également le faire avec une affectation en utilisant l'indexation matricielle:

cbind(c(1,2),c(3,4),rep(seq_len(dim(arr)[[3]]), each=2))
#     row  col  strata
#     [,1] [,2] [,3]
#[1,]    1    3    1
#[2,]    2    4    1
#[3,]    1    3    2
#[4,]    2    4    2
#[5,]    1    3    3
#[6,]    2    4    3

La partie à l'intérieur de [] donne les index row / col / strata pour chaque valeur à remplacer:

arr[cbind(c(1,2),c(3,4),rep(seq_len(dim(arr)[[3]]), each=2))] <- c(80,100)
arr
#, , 1
# 
#     [,1] [,2] [,3] [,4]
#[1,]    1    0   80    0
#[2,]    0   NA    0  100
#[3,]    0    0    0    0
#[4,]    0    0    0    0
# 
#, , 2
#
#    [,1] [,2] [,3] [,4]
#[1,]    1    0   80    0
#[2,]    0   NA    0  100
#[3,]    0    0    0    0
#[4,]    0    0    0    0
# 
#, , 3
#
#     [,1] [,2] [,3] [,4]
#[1,]    1    0   80    0
#[2,]    0   NA    0  100
#[3,]    0    0    0    0
#[4,]    0    0    0    0


0 commentaires

1
votes

Si vous effectuez une mise à jour en fonction de valeurs, vous appliquez une condition:

arr <- array(c(1,0,0,0,0,NA,0,0,5,0,0,0,0,6,0,0), dim=c(4,4,3))

library(microbenchmark)

x = microbenchmark(
  maur_improved = {
    arr[1,3, ] <- 100
    arr[2, 4, ] <- 100
  },
  latemail_all_at_once = {
    arr[cbind(c(1,2),c(3,4),rep(seq_len(dim(arr)[[3]]), each=2))] <- c(80,100)
  },
  maur_for_loop = {
    for (i in seq_len(dim(arr)[3])) {
      arr[1, 3, i] <- 100;       # Change entry (1, 3) of every 2d matrix 
      arr[2, 4, i] <- 100;       # Change entry (2, 4) of every 2d matrix
    }
  },
  cole_subset_mat = {
    arr[arr > 4] <- 100
  }
  , cole_which = {
    which_cond <- which(arr>4, arr.ind = T) 
    arr[which_cond] <- arr[which_cond] * which_cond[, 3]
  }
)
print(x, signif = 3)

Ce qui se passe, c'est que l'intérieur arr> 4 génère un tableau:

#4x4x3 array
Unit: microseconds
                 expr    min      lq    mean  median      uq    max neval
        maur_improved    2.4    3.55    5.42    4.90    5.95   24.4   100
 latemail_all_at_once    6.4    8.70   14.00   15.20   18.40   25.3   100
        maur_for_loop 3280.0 3510.00 3810.00 3630.00 3770.00 6430.0   100
      cole_subset_mat    2.0    3.05    4.71    4.05    6.50   10.2   100
           cole_which   27.9   34.50   47.70   45.40   54.80  228.0   100

#4x4x3E6 array
Unit: milliseconds
                 expr   min    lq  mean median    uq  max neval
        maur_improved  82.9  84.8  89.7   85.8  87.4  165   100
 latemail_all_at_once 347.0 361.0 391.0  378.0 417.0  564   100
        maur_for_loop 422.0 432.0 462.0  451.0 486.0  721   100
      cole_subset_mat 304.0 330.0 369.0  354.0 395.0  527   100
           cole_which 783.0 842.0 899.0  878.0 928.0 1370   100

Et puis nous disons simplement d'assigner une valeur aux conditions qui sont vraies. On peut aussi utiliser which (arr> 4, arr.ind = T) pour renvoyer une matrice similaire à la solution de @ thelatemail sans le typage. Cela nous permet d'accéder à la réponse d'origine de votre message:

which_cond <- which(arr>4, arr.ind = T) 
arr[which_cond] <- arr[which_cond] * which_cond[, 3]
arr
, , 1

     [,1] [,2] [,3] [,4]
[1,]    1    0    5    0
[2,]    0   NA    0    6
[3,]    0    0    0    0
[4,]    0    0    0    0

, , 2

     [,1] [,2] [,3] [,4]
[1,]    1    0   10    0
[2,]    0   NA    0   12
[3,]    0    0    0    0
[4,]    0    0    0    0

, , 3

     [,1] [,2] [,3] [,4]
[1,]    1    0   15    0
[2,]    0   NA    0   18
[3,]    0    0    0    0
[4,]    0    0    0    0

which_cond
     dim1 dim2 dim3
[1,]    1    3    1
[2,]    2    4    1
[3,]    1    3    2
[4,]    2    4    2
[5,]    1    3    3
[6,]    2    4    3

Performances:

, , 1

      [,1]  [,2]  [,3]  [,4]
[1,] FALSE FALSE  TRUE FALSE
[2,] FALSE    NA FALSE  TRUE
[3,] FALSE FALSE FALSE FALSE
[4,] FALSE FALSE FALSE FALSE

, , 2

      [,1]  [,2]  [,3]  [,4]
[1,] FALSE FALSE  TRUE FALSE
[2,] FALSE    NA FALSE  TRUE
[3,] FALSE FALSE FALSE FALSE
[4,] FALSE FALSE FALSE FALSE

, , 3

      [,1]  [,2]  [,3]  [,4]
[1,] FALSE FALSE  TRUE FALSE
[2,] FALSE    NA FALSE  TRUE
[3,] FALSE FALSE FALSE FALSE
[4,] FALSE FALSE FALSE FALSE

Et code:

arr[arr > 4 ] <- 100


2 commentaires

Il convient de noter dans ces benchmarks qu'une microseconde est absolument minuscule. Même avec 2 millions de groupes de baies, la différence entre le plus rapide et le plus lent n'est que de 0,2 seconde d'après mes tests.


La surcharge de la boucle for semble être un coup unique. Bon appel pour soulever cela - mais je suis toujours étonné que R puisse mettre à jour 3 millions de baies en moins de 90 ms. Je ferai plus attention aux horaires sur un si petit ensemble de données car les microsecondes sont presque dénuées de sens.