我应该使用data.frame还是矩阵?


152

什么时候应该使用a data.frame,什么时候使用更好matrix

两者都以矩形格式保存数据,因此有时还不清楚。

对于何时使用哪种数据类型,是否有一般的经验法则?


通常,矩阵可以更好地适合于特定类型的数据,但是如果您要用于分析所述矩阵的数据包期望一个数据帧,则始终将不必要地转换它。我认为没有办法避免重新考虑哪个软件包使用哪个软件包。
xApple

Answers:


176

答案的一部分已经包含在您的问题中:如果希望列(变量)具有不同的类型(数字/字符/逻辑等),则使用数据框架。矩阵用于相同类型的数据。

因此,仅当您具有相同类型的数据时,选择matrix / data.frame才有问题。

答案取决于您将如何处理data.frame / matrix中的数据。如果要传递给其他函数,则这些函数的参数的预期类型将决定选择。

也:

矩阵的存储效率更高:

m = matrix(1:4, 2, 2)
d = as.data.frame(m)
object.size(m)
# 216 bytes
object.size(d)
# 792 bytes

如果您计划进行任何线性代数类型的运算,则矩阵是必不可少的。

如果经常通过名称(通过紧凑的$运算符)引用其列,则数据框更方便。

数据框还可以更好地报告(打印)表格信息,因为您可以将格式分别应用于每一列。


5
我要添加到此答案中的一件事是,如果您计划使用ggplot2软件包制作图形,则ggplot2仅适用于data.frames而不适用于矩阵。只是要注意的事情!
巴伊奇(Bajcz)

77

@Michal没有提到的一点是,不仅矩阵比等效数据帧还小,使用矩阵可以使您的代码比使用数据帧更有效,通常情况下如此。这就是为什么内部许多R函数将强制转换为数据帧中的矩阵数据的原因之一。

数据帧通常要方便得多;人们并不总是只拥有原子的数据块。

注意,您可以拥有一个字符矩阵。您不仅需要数字数据来在R中建立矩阵。

在将数据帧转换为矩阵时,请注意有一个data.matrix()函数,该函数通过基于内部级别将因子转换为数值来适当地处理因子。as.matrix()如果任何因子标签都是非数字的,则强制过孔将导致一个字符矩阵。比较:

> head(as.matrix(data.frame(a = factor(letters), B = factor(LETTERS))))
     a   B  
[1,] "a" "A"
[2,] "b" "B"
[3,] "c" "C"
[4,] "d" "D"
[5,] "e" "E"
[6,] "f" "F"
> head(data.matrix(data.frame(a = factor(letters), B = factor(LETTERS))))
     a B
[1,] 1 1
[2,] 2 2
[3,] 3 3
[4,] 4 4
[5,] 5 5
[6,] 6 6

我几乎总是将数据框用于数据分析任务,因为我不仅拥有数字变量,还拥有更多的信息。当我为程序包编写函数代码时,几乎总是强制转换为矩阵,然后将结果格式化为数据框。这是因为数据帧很方便。


我一直想知道data.matrix()和as.matrix()之间的区别。感谢您澄清它们以及您的编程技巧。
微生物

感谢您分享@Gavin Simpson!您能否再介绍一下如何从1-6恢复到af?
YJZ

1
@YZhang您需要存储每个因子的标签和一个逻辑向量,以指示矩阵的哪些列是因子。然后,将那些因素列仅转换回具有正确标签的因素将是相对琐碎的。注释不是编写代码的好地方,因此请查看是否已问过Q和是否回答过Q,以及是否不问新问题。
加文·辛普森

47

@Michal:矩阵并没有真正提高内存效率:

m <- matrix(1:400000, 200000, 2)
d <- data.frame(m)
object.size(m)
# 1600200 bytes
object.size(d)
# 1600776 bytes

...除非您有大量的列:

m <- matrix(1:400000, 2, 200000)
d <- data.frame(m)
object.size(m)
# 1600200 bytes
object.size(d)
# 22400568 bytes

内存效率的争论实际上是关于data.frames为列类型提供更多的灵活性。由于类型为强制data.frame(a = rnorm(1e6), b = sample(letters, 1e6, TRUE)),因此内存中的内存将比该matrix版本小得多(据我的快速计算为6倍)。
MichaelChirico

9

矩阵实际上是带有其他方法的向量。而data.frame是一个列表。区别在于vector vs list。为了提高计算效率,请坚持使用矩阵。如果需要,请使用data.frame。


3
嗯,矩阵是一个带有维的向量,我不知道方法在哪里?
加文·辛普森

0

矩阵和数据帧是矩形2D数组,行和列可以是 异构的。它们共享一些方法和属性,但不是全部。

例子:

M <- list(3.14,TRUE,5L,c(2,3,5),"dog",1i)  # a list
dim(M) <- c(2,3)                           # set dimensions
print(M)                                   # print result

#      [,1]  [,2]      [,3]
# [1,] 3.14  5         "dog"
# [2,] TRUE  Numeric,3 0+1i

DF <- data.frame(M)                   # a data frame
print(DF)                             # print result

#      X1      X2   X3
#  1 3.14       5  dog
#  2 TRUE 2, 3, 5 0+1i

M <- matrix(c(1,1,1,1,2,3,1,3,6),3)   # a numeric matrix
DF <- data.frame(M)                   # a all numeric data frame

solve(M)                              # obtains inverse matrix
solve(DF)                             # obtains inverse matrix
det(M)                                # obtains determinant
det(DF)                               # error

0

我不能再强调两者之间的效率差异!尽管在某些特定的数据分析情况下DF确实是更方便的,但它们也允许异构数据,并且某些库仅接受它们,除非您为特定任务编写一次性代码,否则所有这些实际上都是次要的。

让我举一个例子。有一个函数可以计算MCMC方法的2D路径。基本上,这意味着我们采用一个初始点(x,y),并在每个步骤中迭代某种算法以找到一个新点(x,y),以此方式构建整个路径。该算法涉及计算一个非常复杂的函数,并在每次迭代时生成一些随机变量,因此当它运行12秒时,考虑到每个步骤要执行多少工作,我认为这很好。就是说,该函数在3列data.frame中收集了构造路径中的所有点以及目标函数的值。因此,3列不是那么大,步数也超过了合理的10,000(在这种问题中,长度为1,000,000的路径是典型的,所以10,000算不上什么)。所以我以为DF 10 000x3绝对不是问题。使用DF的原因很简单。调用函数后,调用ggplot()绘制生成的(x,y)路径。ggplot()不接受矩阵。

然后,出于好奇,我决定更改函数以矩阵形式收集路径。很高兴DF和矩阵的语法相似,我所做的就是将指定df作为data.frame的行更改为将其初始化为矩阵的行。在这里,我还需要提及的是,在初始代码中,DF已初始化为具有最终大小,因此,在该函数的代码中,稍后仅将新值记录到已分配的空间中,并且没有向新的行添加新行的开销。 DF。这使比较更加公平,并且也使我的工作更加简单,因为我不需要在函数中进一步重写任何内容。从所需大小的data.frame的初始分配到相同大小的矩阵只有一行更改。为了使该函数的新版本适应ggplot(),我将现在返回的矩阵转换为数据。

重新运行代码后,我不敢相信结果。代码只需一秒钟!而不是大约12秒。再一次,该函数在10,000次迭代中仅将值读取和写入到DF中已分配的空间(现在在矩阵中)。而且这种差异也适用于合理(或较小)的10000x3尺寸。

因此,如果您使用DF的唯一原因是使其与ggplot()之类的库函数兼容,则始终可以在最后一刻将其转换为DF -在您认为方便的范围内使用矩阵。另一方面,如果有更多实质性的原因使用DF,例如您使用某些数据分析包,而这需要从矩阵到DF的不断转换,或者您自己不做任何密集的计算,而仅使用标准软件包(许多软件包实际上是在内部将DF转换为矩阵,执行其工作,然后将结果转换回-这样它们就可以为您完成所有效率工作),或者只做一次工作,这样您就不会在乎和感到更适合使用DF,那么您不必担心效率。

或另一个更实际的规则:如果您在OP中遇到问题,请使用矩阵,因此仅在没有此类问题时才使用DF(因为您已经知道必须使用DF,或者因为不太在乎,因为代码是一次性的,等等。)

但总的来说,始终牢记这一效率点是优先事项。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.