transpond matrices columns change r matrix transpose

matrices - Transposición de objetos idénticos



transpond r (2)

identical es simplemente demasiado laxo por defecto, pero puede cambiar eso:

> identical(x, y, attrib.as.set = FALSE) [1] FALSE

La razón se puede encontrar inspeccionando los objetos con más detalle:

> dput(x) structure(list(x = 1:3, y = 11:13), .Names = c("x", "y"), row.names = c(NA, -3L), class = "data.frame") > dput(y) structure(list(x = 1:3, y = 11:13), .Names = c("x", "y"), row.names = c(NA, 3L), class = "data.frame")

Tenga en cuenta los distintos atributos row.names :

> .row_names_info(x) [1] -3 > .row_names_info(y) [1] 3

De la documentación podemos deducir que un número negativo implica nombres de fila automáticos (para x ), mientras que los nombres de fila de y no son automáticos. Y as.matrix trata de manera diferente.

Tengo un resultado extraño hoy.

Para replicarlo, considere los siguientes marcos de datos:

x <- data.frame(x=1:3, y=11:13) y <- x[1:3, 1:2]

Se supone que son y en realidad son idénticos:

identical(x,y) # [1] TRUE

La aplicación de t() a objetos idénticos debería producir el mismo resultado, pero:

identical(t(x),t(y)) # [1] FALSE

La diferencia está en los nombres de las columnas:

colnames(t(x)) # NULL colnames(t(y)) # [1] "1" "2" "3"

Dado esto, si quieres apilar y por columnas, obtienes lo que esperas:

stack(as.data.frame(t(y))) # values ind # 1 1 1 # 2 11 1 # 3 2 2 # 4 12 2 # 5 3 3 # 6 13 3

mientras:

stack(as.data.frame(t(x))) # values ind # 1 1 V1 # 2 11 V1 # 3 2 V2 # 4 12 V2 # 5 3 V3 # 6 13 V3

En este último caso, as.data.frame() no encuentra los nombres de columna originales y los genera automáticamente.

El culpable está en as.matrix() , llamado por t() :

rownames(as.matrix(x)) # NULL rownames(as.matrix(y)) # [1] "1" "2" "3"

Una solución alternativa es establecer rownames.force :

rownames(as.matrix(x, rownames.force=TRUE)) # [1] "1" "2" "3" rownames(as.matrix(y, rownames.force=TRUE)) # [1] "1" "2" "3" identical(t(as.matrix(x, rownames.force=TRUE)), t(as.matrix(y, rownames.force=TRUE))) # [1] TRUE

(y reescribe stack(...) llama en consecuencia.)

Mis preguntas son:

  1. Por qué as.matrix() trata diferentemente x y y y

  2. ¿Cómo puedes diferenciarlos?

Tenga en cuenta que otras funciones de información no revelan diferencias entre x, y :

identical(attributes(x), attributes(y)) # [1] TRUE identical(str(x), str(y)) # ... #[1] TRUE

Comentarios a soluciones

Konrad Rudolph brinda una explicación concisa pero efectiva del comportamiento descrito anteriormente (ver también mt1022 para más detalles).

En resumen, Konrad muestra que:

a) y son internamente diferentes;
b) " identical también es simplemente demasiado flojo por defecto" para captar esta diferencia interna.

Ahora, si tomas un subconjunto T del conjunto S , que tiene todos los elementos de S , entonces S y T son exactamente los mismos objetos. Por lo tanto, si toma un marco de datos y , que tiene todas las filas y columnas de x , entonces y deben ser exactamente los mismos objetos. Desafortunadamente x /neq y !
Este comportamiento no solo es contra intuitivo sino también ofuscado, es decir, la diferencia no es evidente por sí misma, pero solo la función interna e incluso la función identical predeterminada no puede verla.

Otro principio natural es que la transposición de dos objetos idénticos (tipo matrix) produce objetos idénticos. De nuevo, esto se rompe por el hecho de que, antes de transponer, identical es "demasiado laxo"; después de la transposición, el valor predeterminado identical es suficiente para ver la diferencia.

En mi humilde opinión, este comportamiento (incluso si no es un error) es una mala conducta para un lenguaje científico como R.
Con suerte, este post atraerá algo de atención y el equipo de R considerará revisarlo.


Como en el comentario, x no son estrictamente lo mismo. Cuando llamamos t a data.frame , se ejecutará t.data.frame :

function (x) { x <- as.matrix(x) NextMethod("t") }

Como podemos ver, llama as.matrix , es decir, as.matrix.data.frame :

function (x, rownames.force = NA, ...) { dm <- dim(x) rn <- if (rownames.force %in% FALSE) NULL else if (rownames.force %in% TRUE) row.names(x) else if (.row_names_info(x) <= 0L) NULL else row.names(x) ...

Como comentó @oropendola, el retorno de .row_names_info de y son diferentes y la función anterior es donde la diferencia tiene efecto.

Entonces, ¿por qué y tiene diferentes rownames ? Veamos [.data.frame , he agregado un comentario en las líneas clave:

{ ... # many lines of code xx <- x #!! this is where xx is defined cols <- names(xx) x <- vector("list", length(x)) x <- .Internal(copyDFattr(xx, x)) # This is where I am not sure about oldClass(x) <- attr(x, "row.names") <- NULL if (has.j) { nm <- names(x) if (is.null(nm)) nm <- character() if (!is.character(j) && anyNA(nm)) names(nm) <- names(x) <- seq_along(x) x <- x[j] cols <- names(x) if (drop && length(x) == 1L) { if (is.character(i)) { rows <- attr(xx, "row.names") i <- pmatch(i, rows, duplicates.ok = TRUE) } xj <- .subset2(.subset(xx, j), 1L) return(if (length(dim(xj)) != 2L) xj[i] else xj[i, , drop = FALSE]) } if (anyNA(cols)) stop("undefined columns selected") if (!is.null(names(nm))) cols <- names(x) <- nm[cols] nxx <- structure(seq_along(xx), names = names(xx)) sxx <- match(nxx[j], seq_along(xx)) } else sxx <- seq_along(x) rows <- NULL ## this is where rows is defined, as we give numeric i, the following ## if block will not be executed if (is.character(i)) { rows <- attr(xx, "row.names") i <- pmatch(i, rows, duplicates.ok = TRUE) } for (j in seq_along(x)) { xj <- xx[[sxx[j]]] x[[j]] <- if (length(dim(xj)) != 2L) xj[i] else xj[i, , drop = FALSE] } if (drop) { n <- length(x) if (n == 1L) return(x[[1L]]) if (n > 1L) { xj <- x[[1L]] nrow <- if (length(dim(xj)) == 2L) dim(xj)[1L] else length(xj) drop <- !mdrop && nrow == 1L } else drop <- FALSE } if (!drop) { ## drop is False for our case if (is.null(rows)) rows <- attr(xx, "row.names") ## rows changed from NULL to 1,2,3 here rows <- rows[i] if ((ina <- anyNA(rows)) | (dup <- anyDuplicated(rows))) { if (!dup && is.character(rows)) dup <- "NA" %in% rows if (ina) rows[is.na(rows)] <- "NA" if (dup) rows <- make.unique(as.character(rows)) } if (has.j && anyDuplicated(nm <- names(x))) names(x) <- make.unique(nm) if (is.null(rows)) rows <- attr(xx, "row.names")[i] attr(x, "row.names") <- rows ## this is where the rownames of x changed oldClass(x) <- oldClass(xx) } x }

podemos ver que recibe sus nombres por algo como attr(x, ''row.names'') :

> attr(x, ''row.names'') [1] 1 2 3

Entonces, cuando creamos y con [.data.frame , recibe los atributos row.names que son diferentes de x , de los cuales row.names son automáticos e indicados con signo negativo en los resultados de dput .

editar

En realidad, esto se ha indicado en el manual de row.names :

Nota

row.names es similar a rownames para arrays, y tiene un método que llama rownames para un argumento de matriz.

Los nombres de fila del formulario 1: n para n> 2 se almacenan internamente en una forma compacta, que se puede ver desde el código C o por deparsing pero nunca a través de row.names o attr (x, "row.names"). Además, algunos nombres de este tipo están marcados como ''automáticos'' y manejados de manera diferente por as.matrix y data.matrix (y potencialmente otras funciones).

Entonces attr no discrimina entre los row.names automáticos (como el de x ) y los row.names interger explícitos (como el de y ), mientras que esto es discriminado por as.matrix través de la representación interna .row_names_info .