似乎不可能在R中得到因子矩阵.这是真的吗?如果是,为什么?如果没有,我该怎么办?
f <- factor(sample(letters[1:5], 20, rep=TRUE), letters[1:5])
m <- matrix(f,4,5)
is.factor(m) # fail.
m <- factor(m,letters[1:5])
is.factor(m) # oh, yes?
is.matrix(m) # nope. fail.
dim(f) <- c(4,5) # aha?
is.factor(f) # yes..
is.matrix(f) # yes!
# but then I get a strange behavior
cbind(f,f) # is not a factor anymore
head(f,2) # doesn't give the first 2 rows but the first 2 elements of f
# should I worry about it?
Reinstate Mo..
20
在这种情况下,它可能像鸭子一样走路,甚至像鸭子一样嘎嘎叫,但f
来自:
f <- factor(sample(letters[1:5], 20, rep=TRUE), letters[1:5])
dim(f) <- c(4,5)
真的不是一个矩阵,即使is.matrix()
声称它严格是一个矩阵.就所is.matrix()
涉及的矩阵而言,f
只需要是一个向量并具有一个dim
属性.通过添加属性给f
您通过测试.然而,正如您所看到的,一旦您开始使用f
矩阵,它很快就会失去使其成为一个因素的功能(您最终会使用级别或维度丢失).
实际上只有原子向量类型的矩阵和数组:
合乎逻辑的,
整数,
真实,
复杂,
字符串(或字符),和
生的
另外,正如@hadley提醒我的那样,你也可以有列表矩阵和数组(通过dim
在列表对象上设置属性.例如,参见Hadley的书,Advanced R的Matrices&Arrays部分.)
任何超出这些类型的东西都会被强制转换为某种较低类型的东西as.vector()
.这发生在matrix(f, nrow = 3)
不是因为f
是原子根据 is.atomic()
(它返回TRUE
对f
,因为它在内部存储为一个整数和typeof(f)
返回"integer"
),但因为它具有class
属性.这将设置OBJECT
内部表示的位,f
并且任何具有类的内容都应该通过以下方式强制转换为其中一种原子类型as.vector()
:
matrix <- function(data = NA, nrow = 1, ncol = 1, byrow = FALSE,
dimnames = NULL) {
if (is.object(data) || !is.atomic(data))
data <- as.vector(data)
....
通过添加维度dim<-()
是一种快速创建数组而无需复制对象的方法,但这会绕过R f
通过其他方法强制转换为矩阵时所做的一些检查和平衡
matrix(f, nrow = 3) # or
as.matrix(f)
当您尝试使用在矩阵上工作的基本函数或使用方法分派时,会发现这一点.请注意,在分配尺寸后f
,f
仍然是类"factor"
:
> class(f)
[1] "factor"
这解释了head()
行为; 你没有得到这个head.matrix
行为,因为f
它不是一个矩阵,至少就S3机制而言:
> debug(head.matrix)
> head(f) # we don't enter the debugger
[1] d c a d b d
Levels: a b c d e
> undebug(head.matrix)
并且head.default
方法调用[
有一个factor
方法,因此观察到的行为:
> debugonce(`[.factor`)
> head(f)
debugging in: `[.factor`(x, seq_len(n))
debug: {
y <- NextMethod("[")
attr(y, "contrasts") <- attr(x, "contrasts")
attr(y, "levels") <- attr(x, "levels")
class(y) <- oldClass(x)
lev <- levels(x)
if (drop)
factor(y, exclude = if (anyNA(levels(x)))
NULL
else NA)
else y
}
....
该cbind()
行为可以从(从记录的行为进行解释?cbind
,重点煤矿):
功能cbind
和rbind
是S3通用,...
....
在默认方法中,所有向量/矩阵必须是原子(参见vector
)或列表.表达是不允许的.语言对象(例如公式和调用)和pairlists将被强制转换为列表:其他对象(如名称和外部指针)将作为元素包含在列表结果中. 输入可能具有的任何类都被丢弃(特别是,因子被其内部代码替换).
同样,这其实f
是阶级的"factor"
是打败你,因为默认的cbind
方法被调用,它将剥离水平的信息,当你观察到的返回内部整数代码.
在许多方面,你必须忽略或至少不完全信任is.foo
函数告诉你的内容,因为它们只是使用简单的测试来说明某些东西是否是某个foo
对象.is.matrix()
和is.atomic()
是显然是错误的,当涉及到f
(用尺寸)从一个特定点.它们在实施方面也是正确的,或者至少可以从实施中理解它们的行为; 我认为is.atomic(f)
是不正确的,但如果用"如果是一个原子类型的" [R核心意思是"型"是由返回的东西typeof(f)
,然后is.atomic()
是正确的.更严格的测试is.vector()
,它f
失败:
> is.vector(f)
[1] FALSE
因为它具有超出names
属性的属性:
> attributes(f)
$levels
[1] "a" "b" "c" "d" "e"
$class
[1] "factor"
$dim
[1] 4 5
至于你应该如何获得因子矩阵,至少如果你想要它保留因子信息(水平的标签),你就不能.一种解决方案是使用字符矩阵,它将保留标签:
> fl <- levels(f)
> fm <- matrix(f, ncol = 5)
> fm
[,1] [,2] [,3] [,4] [,5]
[1,] "c" "a" "a" "c" "b"
[2,] "d" "b" "d" "b" "a"
[3,] "e" "e" "e" "c" "e"
[4,] "a" "b" "b" "a" "e"
并且我们存储f
未来使用的级别,因为我们在此过程中会丢失一些矩阵元素.
或者使用内部整数表示:
> (fm2 <- matrix(unclass(f), ncol = 5))
[,1] [,2] [,3] [,4] [,5]
[1,] 3 1 1 3 2
[2,] 4 2 4 2 1
[3,] 5 5 5 3 5
[4,] 1 2 2 1 5
并且您可以通过以下方式再次返回级别/标签:
> fm2[] <- fl[fm2]
> fm2
[,1] [,2] [,3] [,4] [,5]
[1,] "c" "a" "a" "c" "b"
[2,] "d" "b" "d" "b" "a"
[3,] "e" "e" "e" "c" "e"
[4,] "a" "b" "b" "a" "e"
使用数据框似乎并不理想,因为数据框的每个组件都将被视为一个单独的因素,而您似乎希望将数组视为具有一组级别的单个因子.
如果你真的想做你想要的,有一个因子矩阵,你很可能需要创建自己的S3类来完成这个,加上所有的方法.例如,您可以将因子矩阵存储为字符矩阵但"factorMatrix"
存储类,其中您将级别存储在因子矩阵旁边作为额外属性.然后你需要编写[.factorMatrix
,这将获取级别,然后使用[
矩阵上的默认方法,然后再次添加级别属性.你也可以写cbind
和head
方法.然而,所需方法的列表会快速增长,但是一个简单的实现可能适合,如果你让你的对象有类c("factorMatrix", "matrix")
(即继承自"matrix"
类),你将获取该类的所有属性/方法"matrix"
(这将删除级别和其他属性)因此您至少可以使用这些对象,并查看您需要添加新方法以填写类的行为.
1> Reinstate Mo..:
在这种情况下,它可能像鸭子一样走路,甚至像鸭子一样嘎嘎叫,但f
来自:
f <- factor(sample(letters[1:5], 20, rep=TRUE), letters[1:5])
dim(f) <- c(4,5)
真的不是一个矩阵,即使is.matrix()
声称它严格是一个矩阵.就所is.matrix()
涉及的矩阵而言,f
只需要是一个向量并具有一个dim
属性.通过添加属性给f
您通过测试.然而,正如您所看到的,一旦您开始使用f
矩阵,它很快就会失去使其成为一个因素的功能(您最终会使用级别或维度丢失).
实际上只有原子向量类型的矩阵和数组:
合乎逻辑的,
整数,
真实,
复杂,
字符串(或字符),和
生的
另外,正如@hadley提醒我的那样,你也可以有列表矩阵和数组(通过dim
在列表对象上设置属性.例如,参见Hadley的书,Advanced R的Matrices&Arrays部分.)
任何超出这些类型的东西都会被强制转换为某种较低类型的东西as.vector()
.这发生在matrix(f, nrow = 3)
不是因为f
是原子根据 is.atomic()
(它返回TRUE
对f
,因为它在内部存储为一个整数和typeof(f)
返回"integer"
),但因为它具有class
属性.这将设置OBJECT
内部表示的位,f
并且任何具有类的内容都应该通过以下方式强制转换为其中一种原子类型as.vector()
:
matrix <- function(data = NA, nrow = 1, ncol = 1, byrow = FALSE,
dimnames = NULL) {
if (is.object(data) || !is.atomic(data))
data <- as.vector(data)
....
通过添加维度dim<-()
是一种快速创建数组而无需复制对象的方法,但这会绕过R f
通过其他方法强制转换为矩阵时所做的一些检查和平衡
matrix(f, nrow = 3) # or
as.matrix(f)
当您尝试使用在矩阵上工作的基本函数或使用方法分派时,会发现这一点.请注意,在分配尺寸后f
,f
仍然是类"factor"
:
> class(f)
[1] "factor"
这解释了head()
行为; 你没有得到这个head.matrix
行为,因为f
它不是一个矩阵,至少就S3机制而言:
> debug(head.matrix)
> head(f) # we don't enter the debugger
[1] d c a d b d
Levels: a b c d e
> undebug(head.matrix)
并且head.default
方法调用[
有一个factor
方法,因此观察到的行为:
> debugonce(`[.factor`)
> head(f)
debugging in: `[.factor`(x, seq_len(n))
debug: {
y <- NextMethod("[")
attr(y, "contrasts") <- attr(x, "contrasts")
attr(y, "levels") <- attr(x, "levels")
class(y) <- oldClass(x)
lev <- levels(x)
if (drop)
factor(y, exclude = if (anyNA(levels(x)))
NULL
else NA)
else y
}
....
该cbind()
行为可以从(从记录的行为进行解释?cbind
,重点煤矿):
功能cbind
和rbind
是S3通用,...
....
在默认方法中,所有向量/矩阵必须是原子(参见vector
)或列表.表达是不允许的.语言对象(例如公式和调用)和pairlists将被强制转换为列表:其他对象(如名称和外部指针)将作为元素包含在列表结果中. 输入可能具有的任何类都被丢弃(特别是,因子被其内部代码替换).
同样,这其实f
是阶级的"factor"
是打败你,因为默认的cbind
方法被调用,它将剥离水平的信息,当你观察到的返回内部整数代码.
在许多方面,你必须忽略或至少不完全信任is.foo
函数告诉你的内容,因为它们只是使用简单的测试来说明某些东西是否是某个foo
对象.is.matrix()
和is.atomic()
是显然是错误的,当涉及到f
(用尺寸)从一个特定点.它们在实施方面也是正确的,或者至少可以从实施中理解它们的行为; 我认为is.atomic(f)
是不正确的,但如果用"如果是一个原子类型的" [R核心意思是"型"是由返回的东西typeof(f)
,然后is.atomic()
是正确的.更严格的测试is.vector()
,它f
失败:
> is.vector(f)
[1] FALSE
因为它具有超出names
属性的属性:
> attributes(f)
$levels
[1] "a" "b" "c" "d" "e"
$class
[1] "factor"
$dim
[1] 4 5
至于你应该如何获得因子矩阵,至少如果你想要它保留因子信息(水平的标签),你就不能.一种解决方案是使用字符矩阵,它将保留标签:
> fl <- levels(f)
> fm <- matrix(f, ncol = 5)
> fm
[,1] [,2] [,3] [,4] [,5]
[1,] "c" "a" "a" "c" "b"
[2,] "d" "b" "d" "b" "a"
[3,] "e" "e" "e" "c" "e"
[4,] "a" "b" "b" "a" "e"
并且我们存储f
未来使用的级别,因为我们在此过程中会丢失一些矩阵元素.
或者使用内部整数表示:
> (fm2 <- matrix(unclass(f), ncol = 5))
[,1] [,2] [,3] [,4] [,5]
[1,] 3 1 1 3 2
[2,] 4 2 4 2 1
[3,] 5 5 5 3 5
[4,] 1 2 2 1 5
并且您可以通过以下方式再次返回级别/标签:
> fm2[] <- fl[fm2]
> fm2
[,1] [,2] [,3] [,4] [,5]
[1,] "c" "a" "a" "c" "b"
[2,] "d" "b" "d" "b" "a"
[3,] "e" "e" "e" "c" "e"
[4,] "a" "b" "b" "a" "e"
使用数据框似乎并不理想,因为数据框的每个组件都将被视为一个单独的因素,而您似乎希望将数组视为具有一组级别的单个因子.
如果你真的想做你想要的,有一个因子矩阵,你很可能需要创建自己的S3类来完成这个,加上所有的方法.例如,您可以将因子矩阵存储为字符矩阵但"factorMatrix"
存储类,其中您将级别存储在因子矩阵旁边作为额外属性.然后你需要编写[.factorMatrix
,这将获取级别,然后使用[
矩阵上的默认方法,然后再次添加级别属性.你也可以写cbind
和head
方法.然而,所需方法的列表会快速增长,但是一个简单的实现可能适合,如果你让你的对象有类c("factorMatrix", "matrix")
(即继承自"matrix"
类),你将获取该类的所有属性/方法"matrix"
(这将删除级别和其他属性)因此您至少可以使用这些对象,并查看您需要添加新方法以填写类的行为.
2> BrodieG..:
不幸的是,因子支持在R中并不完全通用,因此许多R函数默认将因子视为其内部存储类型,即integer
:
> typeof(factor(letters[1:3]))
[1] "integer
这是发生的事情matrix
,cbind
.他们不知道如何处理因子,但他们确实知道如何处理整数,因此他们将你的因子视为整数. head
实际上恰恰相反.它确实知道如何处理一个因子,但它从来不会检查你的因子也是一个矩阵,所以只需将它视为一个普通的无量纲因子向量.
你最好的操作就好像你的矩阵有因素一样,就是强迫它去角色.完成操作后,可以将其恢复为因子形式.您也可以使用整数形式执行此操作,但是您可能会遇到奇怪的事情(例如,您可以对整数矩阵进行矩阵乘法,但这对因子没有意义).
请注意,如果您将"矩阵"类添加到您的因子中,则某些(但不是全部)事情开始起作用:
f <- factor(letters[1:9])
dim(f) <- c(3, 3)
class(f) <- c("factor", "matrix")
head(f, 2)
生产:
[,1] [,2] [,3]
[1,] a d g
[2,] b e h
Levels: a b c d e f g h i
这不能解决rbind
等问题.