5.5 数据框

数据框用于存储类似电子表格的数据。它们既可被看作是每列可存储不同数据类型的矩阵,或是非嵌套的列表,其中每个元素具有相同的长度。

5.5.1 创建数据框

我们用data.frame函数创建数据框:

  1. (a_data_frame <- data.frame(
  2. x = letters[1:5],
  3. y = rnorm(5),
  4. z = runif(5) > 0.5
  5. ))
  6. ## x y z
  7. ##1 a 2.3772326 FALSE
  8. ##2 b -1.2188022 TRUE
  9. ##3 c 1.3034011 FALSE
  10. ##4 d -0.5001624 TRUE
  11. ##5 e -1.5923465 FALSE
  12. class(a_data_frame)
  13. ## [1] "data.frame"

请注意,每列的类型可与其他列不同,但在同一列中的元素类型必须相同。还要注意的是,对象的类名是data.frame,中间有一个点而非空字符。

在此例中,行自动从一到五编号。如果输入的任何向量有名称,那么行名称就取自第一个向量名称。例如,如果y列有命名,那么数据框中每行的名字将以y列的向量名命名:

  1. y <- rnorm(5)
  2. names(y) <- month.name[1:5]
  3. data.frame(
  4. x = letters[1:5],
  5. y = y,
  6. z = runif(5) > 0.5
  7. )
  8. ## x y z
  9. ##January a -0.9373 FALSE
  10. ##February b 0.7314 TRUE
  11. ##March c -0.3030 TRUE
  12. ##April d -1.3307 FALSE
  13. ##May e -0.6857 FALSE

这种命名规则可通过给data.frame函数传入参数row.names = NULL覆盖掉:

  1. data.frame(
  2. x = letters[1:5],
  3. y = y,
  4. z = runif(5) > 0.5,
  5. row.names = NULL
  6. )
  7. ## x y z
  8. ## a -0.9373 FALSE
  9. ## b 0.7314 TRUE
  10. ## c -0.3030 TRUE
  11. ## d -1.3307 FALSE
  12. ## e -0.6857 FALSE

另外,还可以通过给row.names传入一个向量来为每行命名。这个向量将被转换为字符character,如果它不是此类型的话:

  1. data.frame(
  2. x = letters[1:5],
  3. y = y,
  4. z = runif(5) > 0.5,
  5. row.names = c("Jackie", "Tito", "Jermaine", "Marlon", "Michael")
  6. )
  7. ## x y z
  8. ##Jackie a -0.9373 FALSE
  9. ##Tito b 0.7314 TRUE
  10. ##Jermaine c -0.3030 TRUE
  11. ##Marlon d -1.3307 FALSE
  12. ##Michael e -0.6857 FALSE

像矩阵一样,行的名称可通过使用rownames(或row.names)在后期进行检索或更改。同样地,通过使用colnamesdimnames可分别获取或设置列和维度的名称。事实上,几乎所有可用于矩阵的函数亦可用在数据框上。例如,nrowncoldim函数的使用与它们在矩阵中的用法一样:

  1. rownames(a_data_frame)
  2. ## [1] "1" "2" "3" "4" "5"
  3. colnames(a_data_frame)
  4. ## [1] "x" "y" "z"
  5. dimnames(a_data_frame)
  6. ## [[1]]
  7. ## [1] "1" "2" "3" "4" "5"
  8. ##
  9. ## [[2]]
  10. ## [1] "x" "y" "z"
  11. nrow(a_data_frame)
  12. ## [1] 5
  13. ncol(a_data_frame)
  14. ## [1] 3
  15. dim(a_data_frame)
  16. ## [1] 5 3

需注意两个奇怪之处。首先,length将返回与ncol相同的值,而不是数据框元素的总数。同样地,names将返回与colnames相同的值。为了使代码易于理解,我们建议你避开这两个函数而使用ncolcolnames

  1. length(a_data_frame)
  2. ## [1] 3
  3. names(a_data_frame)
  4. ## [1] "x" "y" "z"

我们可以使用不同长度的向量来创建数据框,只要长度较短的向量能刚好循环至总长度。从技术上讲,所有向量长度的最小公倍数必须与最长向量的长度相等:

  1. data.frame( # 长度1、2和 4是OK的
  2. x = 1, # 循环4次
  3. y = 2:3, # 循环2次
  4. z = 4:7 # 最长的输入,不需要循环
  5. )
  6. ##  x y z
  7. ## 1 1 2 4
  8. ## 2 1 3 5
  9. ## 3 1 2 6
  10. ## 4 1 3 7

如果长度不兼容,将引发错误:

  1. data.frame( # 长度1、2和3将导致错误
  2. x = 1, # 最小公倍数是6, 比3大
  3. y = 2:3,
  4. z = 4:6
  5. )

创建数据框时的另一个要点为:默认情况下,列名必须是唯一且有效的变量名称。此功能可通过给data.frame传入check.names = FALSE关闭:

  1. data.frame(
  2. "A column" = letters[1:5],
  3. "!@#$%^&*()" = rnorm(5),
  4. "..." = runif(5) > 0.5,
  5. check.names = FALSE
  6. )
  7. ## A column !@#$%^&*() ...
  8. ## 1 a -1.0050744 FALSE
  9. ## 2 b -1.3887884 FALSE
  10. ## 3 c 1.0953114 TRUE
  11. ## 4 d -1.3222524 FALSE
  12. ## 5 e -0.1395639 FALSE

一般情况下,使用非标准的列名并不好。复制列名则更糟糕,因为一旦你使用子集,它会导致难以发现的错误。另外,如果关闭列名检查,你将陷于险境。

5.5.2 索引数据框

有很多种不同的方式可用于索引数据框。首先,与矩阵索引的方式一样,可使用四种不同的向量索引(正整数、负整数、逻辑值和字符)。以下的命令将选择数据框中前两列的第二个和第三个元素:

  1. a_data_frame[2:3, -3]
  2. ## x y
  3. ## 2 b 0.06894
  4. ## 3 c 0.74217
  5. a_data_frame[c(FALSE, TRUE, TRUE, FALSE, FALSE), c("x", "y")]
  6. ## x y
  7. ## 2 b 0.06894
  8. ## 3 c 0.74217

因为选择了一个以上的列,所以得到的子集也是一个数据框。如果只选择一个列,其结果将被简化为一个向量:

  1. class(a_data_frame[2:3, -3])
  2. ## [1] "data.frame"
  3. class(a_data_frame[2:3, 1])
  4. ## [1] "factor"

如果我们只需选择一个列,那么也可以使用列表样式的索引(带有正整数或名称的双方括号,或者带有名称的美元符号运算符)。以下命令将选择第一列中的第二个和第三个元素:

  1. a_data_frame$x[2:3]
  2. ## [1] b c
  3. ## Levels: a b c d e
  4. a_data_frame[[1]][2:3]
  5. ## [1] b c
  6. ## Levels: a b c d e
  7. a_data_frame[["x"]][2:3]
  8. ## [1] b c
  9. ## Levels: a b c d e

如果我们需要给列加上条件来得到一个数据框子集,使用的语法会有点冗长,而subset函数能做同样的事情且更简洁。subset需要三个参数:一个数据框,一个行的条件逻辑向量,以及一个需要保留的名字向量(如果最后这个参数被省略了,那么将保留所有列)。subset的过人之处在于它使用了特殊的估算技巧以避免多余的操作:你无需输入a_data_frame$y以访问a_data_frame的第y列,因为它已经知道要看哪个数据框,所以你只需键入y。同样地,选择列时,你无需附上引号中列的名称,而是可以直接键入名称。在下例中,记得|是逻辑or的操作符:

  1. a_data_frame[a_data_frame$y > 0 | a_data_frame$z, "x"]
  2. ## [1] a b c d e
  3. ## Levels: a b c d e
  4. subset(a_data_frame, y > 0 | z, x)
  5. ## x
  6. ## 1 a
  7. ## 2 b
  8. ## 3 c
  9. ## 4 d
  10. ## 5 e

5.5.3 基本数据框操作

像矩阵一样,数据框可使用t函数进行转置。但在此过程中,所有的列(它们即将变成行)将被转换为相同的类型,然后将变成一个矩阵:

  1. t(a_data_frame)
  2. ## [,1] [,2] [,3] [,4] [,5]
  3. ## x "a" "b" "c" "d" "e"
  4. ## y " 2.3772326" "-1.2188022" " 1.3034011" "-0.5001624" "-1.5923465"
  5. ## z "FALSE" " TRUE" "FALSE" " TRUE" "FALSE"

如果两个数据框的大小一致,也可使用cbindrbind把它们连接(join)起来。rbind能智能地对列重新排序以匹配。然而,因为cbind不会对列名作重复性检查,使用时要格外小心:

  1. another_data_frame <- data.frame( # 与a_data_frame有相同的cols,不过次序不同
  2. z = rlnorm(5), # 对数分布的数
  3. y = sample(5), # 1到5随机排列的数
  4. x = letters[3:7]
  5. )
  6. rbind(a_data_frame, another_data_frame)
  7. ## x y z
  8. ## 1 a 0.17581 1.0000
  9. ## 2 b 0.06894 1.0000
  10. ## 3 c 0.74217 1.0000
  11. ## 4 d 0.72816 1.0000
  12. ## 5 e -0.28940 1.0000
  13. ## 6 c 1.00000 0.8714
  14. ## 7 d 3.00000 0.2432
  15. ## 8 e 5.00000 2.3498
  16. ## 9 f 4.00000 2.0263
  17. ## 10 g 2.00000 1.7145
  18. cbind(a_data_frame, another_data_frame)
  19. ## x y z z y x
  20. ## 1 a 0.17581 TRUE 0.8714 1 c
  21. ## 2 b 0.06894 TRUE 0.2432 3 d
  22. ## 3 c 0.74217 TRUE 2.3498 4 e
  23. ## 4 d 0.72816 TRUE 2.0263 5 f
  24. ## 5 e -0.28940 TRUE 1.7145 2 g

当两个数据框有相同的列时,可使用merge函数合并。merge为数据库风格的连接提供了多种选择。当要连接两个数据框时,你需要指定包含键值的列以作匹配。默认情况下,merge函数会使用两个数据框中所有共同的列,但通常你会想用一个共享ID列。在下例中,我们将通过by参数指定x为我们所要包含的ID:

  1. merge(a_data_frame, another_data_frame, by = "x")
  2. ## x y.x z.x z.y y.y
  3. ## 1 c 0.7422 TRUE 0.8714 1
  4. ## 2 d 0.7282 TRUE 0.2432 3
  5. ## 3 e -0.2894 TRUE 2.3498 5
  6. merge(a_data_frame, another_data_frame, by = "x", all = TRUE)
  7. ## x y.x z.x z.y y.y
  8. ## 1 a 0.17581 TRUE NA NA
  9. ## 2 b 0.06894 TRUE NA NA
  10. ## 3 c 0.74217 TRUE 0.8714 1
  11. ## 4 d 0.72816 TRUE 0.2432 3
  12. ## 5 e -0.28940 TRUE 2.3498 5
  13. ## 6 f NA NA 2.0263 4
  14. ## 7 g NA NA 1.7145 2

如果数据框中只包含数值,那么colSumscolMeans函数可分别用于计算每列的总和和平均值。同样地,rowSumsrowMeans将计算每一行的总和及平均值:

  1. colSums(a_data_frame[, 2:3])
  2. ## y z
  3. ## 1.426 5.000
  4. colMeans(a_data_frame[, 2:3])
  5. ## y z
  6. ## 0.2851 1.0000

操作数据框是一个很大的话题,我们将在第13章深入探讨。