什么时候应该使用 a data.frame
,什么时候使用 a 更好matrix
?
两者都以矩形格式保存数据,因此有时不清楚。
对于何时使用哪种数据类型,是否有任何一般的经验法则?
部分答案已包含在您的问题中:如果可以预期列(变量)具有不同类型(数字/字符/逻辑等),则使用数据框。矩阵用于相同类型的数据。
因此,只有当您拥有相同类型的数据时,选择 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
如果您打算进行任何线性代数类型的运算,矩阵是必不可少的。
如果您经常按名称(通过紧凑的 $ 运算符)引用数据框,则数据框会更方便。
恕我直言,数据框也更适合报告(打印)表格信息,因为您可以将格式分别应用于每一列。
@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
我几乎总是在我的数据分析任务中使用数据框,因为我通常有的不仅仅是数字变量。当我为包编写函数时,我几乎总是强制转换为矩阵,然后将结果格式化为数据框。这是因为数据框很方便。
@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.frame 是一个列表。区别在于向量与列表。为了计算效率,坚持使用矩阵。如果需要,请使用 data.frame。
我不能更强调两者之间的效率差异!虽然 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 被初始化为具有最终大小,因此在函数的代码中,只有新值被记录到已经分配的空间中,并且没有将新行添加到东风。这使比较更加公平,也使我的工作更简单,因为我不需要在函数中进一步重写任何内容。从所需大小的 data.frame 的初始分配到相同大小的矩阵,只需一行更改。为了使新版本的函数适应 ggplot(),我将现在返回的矩阵转换为数据。
After I rerun the code I could not believe the result. The code run in a fraction of a second! Instead of about 12 seconds. And again, the function during the 10,000 iterations only read and wrote values to already allocated spaces in a DF (and now in a matrix). And this difference is also for the reasonable (or rather small) size 10000x3.
So, if your only reason to use a DF is to make it compatible with a library function such as ggplot(), you can always convert it to a DF at the last moment -- work with matrices as far as you feel convenient. If on the other hand there is a more substantial reason to use a DF, such as you use some data analysis package that would require otherwise constant transforming from matrices to DFs and back, or you do not do any intensive calculations yourself and only use standard packages (many of them actually internally transform a DF to a matrix, do their job, and then transform the result back -- so they do all efficiency work for you), or do a one-time job so you do not care and feel more comfortable with DFs, then you should not worry about efficiency.
Or a different more practical rule: if you have a question such as in the OP, use matrices, so you would use DFs only when you do not have such a question (because you already know you have to use DFs, or because you do not really care as the code is one-time etc.).
But in general keep this efficiency point always in mind as a priority.
矩阵和数据帧是矩形二维数组,可以 按行和列异构。它们共享一些方法和属性,但不是全部。
例子:
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