转置相同的对象

时间:2017-04-04 14:17:41

标签: r matrix transpose

我今天得到了一个奇怪的结果。

要复制它,请考虑以下数据框:

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

它们应该是并且实际上是相同的:

identical(x,y)
# [1] TRUE

t()应用于indentical对象应该产生相同的结果,但是:

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

不同之处在于列名:

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

鉴于此,如果你想按列堆叠y,你会得到你所期望的:

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

,同时:

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

在后一种情况下,as.data.frame()找不到原始列名并自动生成它们。

罪魁祸首在as.matrix(),由t()调用:

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

解决方法是设置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

(并相应地重写stack(...)。)

我的问题是:

  1. 为什么as.matrix()区别对待xy以及

  2. 你怎么能区分它们呢?

  3. 请注意,其他信息功能不会显示x, y

    之间的差异
    identical(attributes(x), attributes(y))
    # [1] TRUE
    identical(str(x), str(y))
    # ...
    #[1] TRUE
    

    对解决方案的评论

    Konrad Rudolph为上述行为提供了简明但有效的解释(另请参阅mt1022  更多细节)。

    简而言之,康拉德表明:

    a)xy 内部不同;
    b)“identical在默认情况下过于宽松”是为了捕捉这种内部差异。

    现在,如果您使用集合T的子集S,其中 all 的元素为S,则ST是完全相同的对象。因此,如果您采用数据框y all x的行和列,则xy 应该完全相同的对象。不幸的是x \neq y
    这种行为不仅违反直觉,而且也是混淆的,也就是说,差异不是明显的,只有内部,甚至默认的identical函数都看不到它。

    另一个自然原则是,转置两个相同的(矩阵状)对象会产生相同的对象。同样,在转置之前,identical“太宽松”这一事实打破了这一点;在转置后,默认identical足以看出差异。

    恕我直言,这种行为(即使它不是一个错误)是对诸如R的科学语言的不良行为 希望这篇文章会引起一些关注,R团队会考虑修改它。

2 个答案:

答案 0 :(得分:5)

identical默认情况下过于宽松,但您可以更改:

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

通过更详细地检查对象可以找到原因:

> 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")

请注意不同的row.names属性:

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

从文档中我们可以看出负数表示自动rownames(对于x),而y的行名不是自动的。 as.matrix对待他们的方式不同。

答案 1 :(得分:3)

在评论中,db:reset db:migratex并不完全相同。当我们致电yt时,data.frame将被执行:

t.data.frame

我们可以看到,它调用了function (x) { x <- as.matrix(x) NextMethod("t") } ,即as.matrix

as.matrix.data.frame

正如@oropendola评论的那样,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) ... .row_names_info的{​​{1}}的回报是不同的,上述函数是差异生效的地方。

那么为什么x有不同的y?让我们看看y,我在关键行添加了评论:

rownames

我们可以看到[.data.frame的名称来自{ ... # 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 }

y

因此,当我们使用attr(x, 'row.names')创建> attr(x, 'row.names') [1] 1 2 3 时,它会收到与y不同的[.data.frame个属性,其中row.names是自动的并显示为x结果中出现负号。

修改

实际上,这已在row.names手册中说明:

  

请注意

     

row.names类似于数组的rownames,它有一个方法   调用数组参数的rownames。

     

表单1的行名称:n表示n> 2内部存储在紧凑型中   表单,可以从C代码中看到,也可以从deparsing中看到,但从不通过   row.names或attr(x,“row.names”)。另外,有些名字   sort被标记为“自动”,并由as.matrix以不同方式处理   和data.matrix(以及可能的其他功能)。

所以row.names不会区分自动dput(如attr)和显式整数row.names(如x的那个),而row.names通过内部代表y对此进行区分。