ejemplos - forma musical ternaria
¿Cómo verificar eficientemente si una matriz está en forma binaria(por ejemplo, todos los 1 o 0)? (5)
Tengo una función que toma una matriz binaria de tamaño mxn (potencialmente) como entrada, y me gustaría devolver un manejo de error si la matriz contiene un número que no es 0 o 1, o es NA. ¿Cómo puedo verificar esto de manera eficiente?
Por ejemplo, al generar algunos datos para un 10 x 10:
> n=10;m=10
> mat = round(matrix(runif(m*n), m, n))
> mat
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 0 1 0 1 1 0 1 0 1 0
[2,] 0 0 0 0 0 0 0 0 0 1
[3,] 1 1 0 1 1 0 0 1 1 0
[4,] 1 1 1 1 0 1 0 0 1 1
[5,] 1 1 1 0 0 1 1 1 0 1
[6,] 1 0 1 0 0 0 0 1 0 0
[7,] 0 0 0 1 0 1 1 1 1 0
[8,] 0 0 0 1 0 1 1 1 1 1
[9,] 0 0 1 1 0 1 1 1 1 1
[10,] 1 0 1 1 0 0 0 0 1 1
siempre debe devolver que la matriz es binaria, pero cambiarla de una de las siguientes maneras:
> mat[1,1]=NA
> mat[1,1]=2
Debería devolver que la matriz no es binaria.
Actualmente, he estado usando en mi función:
for(i in 1:nrow(mat))
{
for(j in 1:ncol(mat))
{
if(is.na(mat[i,j])|(!(mat[i,j] == 1 | mat[i,j] == 0)))
{
stop("Data must be only 0s, 1s")
}
}
}
pero parece terriblemente lento e ineficiente verificar individualmente cada valor para matrices grandes. ¿Hay alguna forma inteligente y fácil de hacer esto que me falta?
Gracias
Estos son los tiempos para algunas opciones (incluidas las opciones sugeridas en otras respuestas):
n=5000;m=5000
mat = round(matrix(runif(m*n), m, n))
> system.time(stopifnot(sum(mat==0) + sum(mat==1) == length(mat)))
user system elapsed
0.30 0.02 0.31
> system.time(stopifnot(all(mat %in% c(0,1))))
user system elapsed
0.58 0.06 0.63
> system.time(stopifnot(all(mat==0 | mat==1)))
user system elapsed
0.77 0.03 0.80
¡Todos son bastante rápidos, considerando que es una matriz de 5000 por 5000! El más rápido de los tres parece ser:
stopifnot(sum(mat==0) + sum(mat==1) == length(mat))
Inmediatamente pensé en identical(mat,matrix(as.numeric(as.logical(mat),nr=nrow(mat)) ) )
Esto deja NA
como NA
por lo que si desea identificar la existencia de tal, solo necesitará una any(is.na(mat))
rápida de any(is.na(mat))
o similar.
EDITAR: contrarreloj
fun2 <- function(x) {
all(x %in% 0:1)
}
fun1 <-function(x) {identical(as.vector(x),as.numeric(as.logical(x)))}
mfoo<-matrix(sample(0:10,1e6,rep=TRUE),1e3)
microbenchmark(fun1(mfoo),fun2(mfoo),is.binary.sum2(mfoo),times=10)
Unit: milliseconds
expr min lq median uq
fun1(mfoo) 2.286941 2.809926 2.835584 2.865518
fun2(mfoo) 20.369075 20.894627 21.100528 21.226464
is.binary.sum2(mfoo) 11.394503 12.418238 12.431922 12.458436
max neval
2.920253 10
21.407777 10
28.316492 10
Y en contra de lo not...
tuve que try
para evitar romper la prueba.
notfun <- function(mat) try(stopifnot(sum(mat==0) + sum(mat==1) == length(mat)))
microbenchmark(fun1(mfoo),notfun(mfoo),is.binary.sum2(mfoo),times=10)
Error : sum(mat == 0) + sum(mat == 1) == length(mat) is not TRUE
##error repeated 10x for the 10 trials
Unit: milliseconds
expr min lq median uq
fun1(mfoo) 4.870653 4.978414 5.057524 5.268344
notfun(mfoo) 18.149273 18.685942 18.942518 19.241856
is.binary.sum2(mfoo) 11.428713 12.145842 12.516165 12.605111
max neval
5.438111 10
34.826230 10
13.090465 10
¡Yo gano! :-)
Me gusta agregar una versión ligeramente modificada de la comparación basada en sum
que es más rápida que la versión de @ JamesTrimble. Espero que todas mis suposiciones sean correctas:
is.binary.sum2 <- function(x) {
identical(sum(abs(x)) - sum(x == 1), 0)
}
Aquí el punto de referencia:
library(rbenchmark)
n=5000
m=5000
mat = round(matrix(runif(m*n), m, n))
is.binary.sum <- function(x) {
sum(x == 0) + sum(x == 1) == length(x)
}
is.binary.sum2 <- function(x) {
identical(sum(abs(x)) - sum(x == 1), 0)
}
is.binary.all <- function(x) {
all(x == 0 | x == 1)
}
is.binary.in <- function(x) {
all(x %in% c(0, 1))
}
benchmark(is.binary.sum(mat), is.binary.sum2(mat),
is.binary.all(mat), is.binary.in(mat),
order="relative", replications=10)
# test replications elapsed relative user.self sys.self user.child sys.child
#2 is.binary.sum2(mat) 10 4.635 1.000 3.872 0.744 0 0
#1 is.binary.sum(mat) 10 7.097 1.531 6.565 0.512 0 0
#4 is.binary.in(mat) 10 10.359 2.235 9.216 1.108 0 0
#3 is.binary.all(mat) 10 12.565 2.711 11.753 0.772 0 0
Una forma bastante eficiente (y legible ) podría ser
all(mat %in% c(0,1))
Sin embargo, como se señaló, puede no ser la más rápida, si se compara con otras soluciones.
Pero, para agregar algunos, si la eficiencia es imprescindible (por ejemplo, se realiza esta prueba muchas veces) se obtiene una gran cantidad de ganancia trabajando con integer
matriz de integer
(las double
tienen más bytes) y se integer
valores integer
. Esta ganancia también podría aplicarse a otras soluciones también. Algunas pruebas con %in%
siguen:
library(microbenchmark)
set.seed(1)
my.dim <- 1e04
n <- my.dim
m <- my.dim
mat <- round(matrix(runif(m*n), m, n))
int.mat <- as.integer(mat)
fun1 <- function(x) {
all(x %in% c(0,1))
}
fun2 <- function(x) {
all(x %in% 0:1)
}
## why?
storage.mode(0:1)
## [1] "integer"
storage.mode(c(0,1))
## [1] "double"
object.size(0:1)
## 48 bytes
object.size(c(0,1))
## 56 bytes
## and considering mat and int.mat
object.size(mat)
## 800000200 bytes
object.size(int.mat)
## 400000040 bytes
(res <- microbenchmark(fun1(mat), fun2(int.mat), times = 10, unit = "s"))
## Unit: seconds
## expr min lq median uq max neval
## fun1(mat) 3.68843 3.69325 3.70433 3.72627 3.73041 10
## fun2(int.mat) 1.28956 1.29157 1.32934 1.34370 1.35718 10
De 3.70 a 1.32 no es tan malo :)
Tenga en cuenta que he cambiado algunas cosas para que se ejecute en octave
, pero debería ser bastante similar a matlab
.
Genera la matriz:
n=5000;m=5000
mat=randi([0,1],n,m);
Ahora solo hacemos algo simple, sabemos que 1*2-1
haría que el 1
igual a 1
, mientras que hace que 0
sea igual a -1
. Entonces, abs
hace todo igual. Para cualquier otro valor, digamos -1
, -1*2-1=-3
este no es el caso. Luego restamos 1
y deberíamos quedarnos con una matriz con solo ceros. Esto se puede verificar fácilmente en matlab / octava con any
:
any(any(abs(mat*2-1)-1));
Comprobando su velocidad:
mat=randi([0,1],n,m);
[t0 u0 s0]=cputime(); any(any(abs(mat+mat-1)-1)); [t1 u1 s1]=cputime(); [t1-t0 u1-u0 s1-s0]
ans =
0.176772 0.127546 0.049226
En el orden total
, user
y hora del system
.
Bastante decente en 0.18
segundos con la mayor parte en modo de usuario. Con 10.000 * 10.000
entradas todavía está por debajo de un segundo, registrando en 0.86
segundos en mi sistema.
Oh, diablos, solo ahora veo que en realidad se pide R
, no matlab
. Espero que a alguien le guste la comparación sin embargo.
Manejar los valores de NaN
es fácil en octave
/ matlab
con isnan(mat)
, eventualmente en forma de any(any(isnan(mat)))
si lo desea. Esto incluye los valores de NA
. Manejar solo los valores de NA
es a través de isna(mat)
.