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:
Por qué
as.matrix()
trata diferentementex
yy
y¿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
.