第 6 章 数据操作
6.1 排序
6.1.1 问题
你想将一个向量,矩阵或列表排序。
6.1.2 方案
6.1.2.1 向量
# 生成一个随机向量
<- sample(101:110)
v
# 排序
sort(v)
#> [1] 101 102 103 104 105 106 107 108 109 110
# 反向排序
sort(v, decreasing = TRUE)
#> [1] 110 109 108 107 106 105 104 103 102 101
6.1.2.2 列表
对列表的一个或多个列进行排序,可以使用 plyr 包里的 arrange()
函数或者使用 R 的内置函数。arrange()
函数更容易使用,但需要额外安装包。
# 生成一个列表
<- data.frame(id = 1:4, weight = c(20, 27, 24, 22), size = c("small",
df "large", "medium", "large"))
df#> id weight size
#> 1 1 20 small
#> 2 2 27 large
#> 3 3 24 medium
#> 4 4 22 large
library(plyr)
# 按照 'weight' 列排序,以下结果相同。
arrange(df, weight) # 使用 plyr 包里的 arrange 函数
#> id weight size
#> 1 1 20 small
#> 2 4 22 large
#> 3 3 24 medium
#> 4 2 27 large
order(df$weight), ] # 使用 R 内置函数
df[#> id weight size
#> 1 1 20 small
#> 4 4 22 large
#> 3 3 24 medium
#> 2 2 27 large
# 以 size 为第一关键词, weight 为第二关键词排序
arrange(df, size, weight) # 使用 plyr 包里的 arrange 函数
#> id weight size
#> 1 4 22 large
#> 2 2 27 large
#> 3 3 24 medium
#> 4 1 20 small
order(df$size, df$weight), ] # 使用 R 内置函数
df[#> id weight size
#> 4 4 22 large
#> 2 2 27 large
#> 3 3 24 medium
#> 1 1 20 small
# 所有列从左到右依次排序
do.call(order, as.list(df)), ]
df[#> id weight size
#> 1 1 20 small
#> 2 2 27 large
#> 3 3 24 medium
#> 4 4 22 large
# 在这个特殊的例子中,顺序将保持不变
请注意,size
列是一个因子且按照因子水平进行排序。在这种情况下将自动按字母顺序排列(创建数据表格),所以大是第一而小是最后。
6.1.2.2.1 反向排序
设定 decreasing=TRUE
可以获取反向排序结果。
反向排序某一列的方法依赖于数据类型:
- 数字:变量名前加一个
-
。例如:df[order(-df$weight), ]
- 因子:转换为整数,变量名前加一个
-
。例如:df[order(-xtfrm(df$size)), ]
- 字符:没有简单的方法能做到这一点。一种方法是先转换为一个因子,然后如上所述。
# 反向排序 weight 列,下面方法有相同结果:
arrange(df, -weight) # 使用 plyr 包里的 arrange 函数
#> id weight size
#> 1 2 27 large
#> 2 3 24 medium
#> 3 4 22 large
#> 4 1 20 small
order(df$weight, decreasing = TRUE), ] # 使用 R 内置函数
df[#> id weight size
#> 2 2 27 large
#> 3 3 24 medium
#> 4 4 22 large
#> 1 1 20 small
order(-df$weight), ] # 使用 R 内置函数
df[#> id weight size
#> 2 2 27 large
#> 3 3 24 medium
#> 4 4 22 large
#> 1 1 20 small
# 升序排列 size ,然后降序排列 weight
arrange(df, size, -weight) # 使用 plyr 包里的 arrange 函数
#> id weight size
#> 1 2 27 large
#> 2 4 22 large
#> 3 3 24 medium
#> 4 1 20 small
order(df$size, -df$weight), ] # 使用 R 内置函数
df[#> id weight size
#> 2 2 27 large
#> 4 4 22 large
#> 3 3 24 medium
#> 1 1 20 small
# 升序排列 size,然后降序排列 weight 因子需要 xtfrm()
arrange(df, -xtfrm(size), weight) # 使用 plyr 包里的 arrange 函数
#> id weight size
#> 1 1 20 small
#> 2 3 24 medium
#> 3 4 22 large
#> 4 2 27 large
order(-xtfrm(df$size), df$weight), ] # 使用 R 内置函数
df[#> id weight size
#> 1 1 20 small
#> 3 3 24 medium
#> 4 4 22 large
#> 2 2 27 large
6.2 随机排序
6.2.1 问题
你想使一个数据结构随机排序。
6.2.2 方案
# 创建一个向量
<- 11:20
v
# 随机化向量的顺序
<- sample(v)
v
# 创建一个列表
<- data.frame(label = letters[1:5], number = 11:15)
data
data#> label number
#> 1 a 11
#> 2 b 12
#> 3 c 13
#> 4 d 14
#> 5 e 15
# 随机化列表的顺序
<- data[sample(1:nrow(data)), ]
data
data#> label number
#> 5 e 15
#> 4 d 14
#> 3 c 13
#> 2 b 12
#> 1 a 11
6.2.2.1 注意
为了使随机化可重复,你应该设置随机数生成器。详见:生成随机数、生成可重复的随机数序列。
6.3 转换向量类型
6.3.1 问题
你想在数值向量、字符串向量和因子向量间做转换。
6.3.2 方案
假设你刚开始有一个数值型向量 n
:
<- 10:14
n
n#> [1] 10 11 12 13 14
将这个数值型向量转换为其他两种类型(将结果保存在 c
和 f
):
# 数值型转换字符串
<- as.character(n)
c
c#> [1] "10" "11" "12" "13" "14"
# 数值型转换因子型
<- factor(n)
f
f#> [1] 10 11 12 13 14
#> Levels: 10 11 12 13 14
将字符串向量转化为另外两种:
# 数值
as.numeric(c)
#> [1] 10 11 12 13 14
# 字符串转换为因子
factor(c)
#> [1] 10 11 12 13 14
#> Levels: 10 11 12 13 14
把一个因子转变为一个字符串型向量很简单:
# 因子转换为字符串
as.character(f)
#> [1] "10" "11" "12" "13" "14"
然而,将因子转化为数值型向量有点棘手。如果你使用as.numberic
,它将会给你因子编码的数值,恐怕不是你想要的。
as.numeric(f)
#> [1] 1 2 3 4 5
# 另一种方式得到数字的编码,如果这是你想要的:
unclass(f)
#> [1] 1 2 3 4 5
#> attr(,"levels")
#> [1] "10" "11" "12" "13" "14"
将因子转换为数值型的方法是先转化为字符串型,再转化为数值型。
# 因子转换为数值型
as.numeric(as.character(f))
#> [1] 10 11 12 13 14
6.4 查找并移除重复记录
6.4.1 问题
你想查找和(或)移除向量或列表里重复的条目。
6.4.2 方案
6.4.2.1 向量
# 生成一个向量
set.seed(158)
<- round(rnorm(20, 10, 5))
x
x#> [1] 14 11 8 4 12 5 10 10 3 3 11 6 0 16 8 10 8
#> [18] 5 6 6
# 对于每一个元素:它是否重复(第一个值不算)
duplicated(x)
#> [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE
#> [9] FALSE TRUE TRUE FALSE FALSE FALSE TRUE TRUE
#> [17] TRUE TRUE TRUE TRUE
# 重复条目的值 注意“6”出现了三次,所以它有两个
duplicated(x)]
x[#> [1] 10 3 11 8 10 8 5 6 6
# 重复的条目无需重复出现
unique(x[duplicated(x)])
#> [1] 10 3 11 8 5 6
# 移除重复数据,下面两种方法效果一样:
unique(x)
#> [1] 14 11 8 4 12 5 10 3 6 0 16
!duplicated(x)]
x[#> [1] 14 11 8 4 12 5 10 3 6 0 16
6.4.2.2 列表
# 一个样本列表:
<- read.table(header = TRUE, text = "
df label value
A 4
B 3
C 6
B 3
B 1
A 2
A 4
A 4
")
# 每一行是否有重复?
duplicated(df)
#> [1] FALSE FALSE FALSE TRUE FALSE FALSE TRUE TRUE
# 显示重复的条目
duplicated(df), ]
df[#> label value
#> 4 B 3
#> 7 A 4
#> 8 A 4
# 显示没有重复的条目 (行名可能不同,但值相同)
unique(df[duplicated(df), ])
#> label value
#> 4 B 3
#> 7 A 4
# 移除重复的数据,下面两种方法效果一样:
unique(df)
#> label value
#> 1 A 4
#> 2 B 3
#> 3 C 6
#> 5 B 1
#> 6 A 2
!duplicated(df), ]
df[#> label value
#> 1 A 4
#> 2 B 3
#> 3 C 6
#> 5 B 1
#> 6 A 2
6.5 NA 存在时进行向量或因子比较
6.5.1 问题
你想在 NA
存在的情况下比较比较两个向量或因子并返回 TRUE
或 FALSE
(而不是 NA
)。
6.5.2 方案
假设你有一个两列(包含布尔值)的列表:
<- data.frame(a = c(TRUE, TRUE, TRUE, FALSE, FALSE, FALSE,
df NA, NA, NA), b = c(TRUE, FALSE, NA, TRUE, FALSE, NA,
TRUE, FALSE, NA))
df#> a b
#> 1 TRUE TRUE
#> 2 TRUE FALSE
#> 3 TRUE NA
#> 4 FALSE TRUE
#> 5 FALSE FALSE
#> 6 FALSE NA
#> 7 NA TRUE
#> 8 NA FALSE
#> 9 NA NA
通常情况下,当你比较两个包含NA
值的向量或因子时,原始值是NA
,结果也将有NA
。根据你的目的,这或许是是你想要的结果。
$a == df$b
df#> [1] TRUE FALSE NA FALSE TRUE NA NA NA
#> [9] NA
# 同样的比较,但是可以生成列表的另一列:
data.frame(df, isSame = (df$a == df$b))
#> a b isSame
#> 1 TRUE TRUE TRUE
#> 2 TRUE FALSE FALSE
#> 3 TRUE NA NA
#> 4 FALSE TRUE FALSE
#> 5 FALSE FALSE TRUE
#> 6 FALSE NA NA
#> 7 NA TRUE NA
#> 8 NA FALSE NA
#> 9 NA NA NA
6.5.2.1 可以与 NA
相比的函数
这个比较函数会把NA
赋予另一个值。如果一个向量的两项都是 NA
,则返回 TRUE
;如果其中一个是 NA
,则返回 FALSE
;所有其他比较(无NA
之间)的方式是一样的。
# 这个函数将会返回 TRUE,当两个元素相同(包括两个
# NA),其他情况返回 FALSE
<- function(v1, v2) {
compareNA <- (v1 == v2) | (is.na(v1) & is.na(v2))
same is.na(same)] <- FALSE
same[return(same)
}
6.5.2.2 使用该函数的例子
比较两个布尔向量:
compareNA(df$a, df$b)
#> [1] TRUE FALSE FALSE FALSE TRUE FALSE FALSE FALSE
#> [9] TRUE
# 同样的比较,生成另一列
data.frame(df, isSame = compareNA(df$a, df$b))
#> a b isSame
#> 1 TRUE TRUE TRUE
#> 2 TRUE FALSE FALSE
#> 3 TRUE NA FALSE
#> 4 FALSE TRUE FALSE
#> 5 FALSE FALSE TRUE
#> 6 FALSE NA FALSE
#> 7 NA TRUE FALSE
#> 8 NA FALSE FALSE
#> 9 NA NA TRUE
它也能用于因子,即使因子的水平处于不同的次序:
# 创建一个含因子的列表
<- data.frame(a = factor(c("x", "x", "x", "y", "y",
df1 "y", NA, NA, NA)), b = factor(c("x", "y", NA, "x", "y",
NA, "x", "y", NA)))
# 比较
data.frame(df1, isSame = compareNA(df1$a, df1$b))
#> a b isSame
#> 1 x x TRUE
#> 2 x y FALSE
#> 3 x <NA> FALSE
#> 4 y x FALSE
#> 5 y y TRUE
#> 6 y <NA> FALSE
#> 7 <NA> x FALSE
#> 8 <NA> y FALSE
#> 9 <NA> <NA> TRUE
# 也能用于因子,即使因子的水平处于不同的次序
$b <- factor(df1$b, levels = c("y", "x"))
df1data.frame(df1, isSame = compareNA(df1$a, df1$b))
#> a b isSame
#> 1 x x TRUE
#> 2 x y FALSE
#> 3 x <NA> FALSE
#> 4 y x FALSE
#> 5 y y TRUE
#> 6 y <NA> FALSE
#> 7 <NA> x FALSE
#> 8 <NA> y FALSE
#> 9 <NA> <NA> TRUE
6.6 数据重新编码
6.6.1 问题
你想重新编码数据或从现有的数据中计算生产新的数据列。
6.6.2 方案
下面的例子将使用这些数据:
<- read.table(header = T, text = "
data subject sex control cond1 cond2
1 M 7.9 12.3 10.7
2 F 6.3 10.6 11.1
3 F 9.5 13.1 13.8
4 M 11.5 13.4 12.9
")
6.6.2.1 重新编码分类变量
最简单的方法是使用 plyr 包里的 revalue()
或 mapvalues()
。这可以令M
为 1
、F
为 2
,并把它放在一个新生产的列。注意,这两个函数均保存数据类型:如果输入一个因子,输出将会是一个因子;如果输入是一个字符串向量,输出将会是一个字符串向量。
library(plyr)
# 下面两行是等价的:
$scode <- revalue(data$sex, c(M = "1", F = "2"))
data$scode <- mapvalues(data$sex, from = c("M", "F"), to = c("1",
data"2"))
data#> subject sex control cond1 cond2 scode
#> 1 1 M 7.9 12.3 10.7 1
#> 2 2 F 6.3 10.6 11.1 2
#> 3 3 F 9.5 13.1 13.8 2
#> 4 4 M 11.5 13.4 12.9 1
# data$sex是一个因子,因此data$scode也是因子
详见映射向量的值。
如果你不想依赖 plyr包,你可以使用 R 内置函数:
$scode[data$sex == "M"] <- "1"
data$scode[data$sex == "F"] <- "2"
data
# 把列转换为因子
$scode <- factor(data$scode)
data
data#> subject sex control cond1 cond2 scode
#> 1 1 M 7.9 12.3 10.7 1
#> 2 2 F 6.3 10.6 11.1 2
#> 3 3 F 9.5 13.1 13.8 2
#> 4 4 M 11.5 13.4 12.9 1
另一种方法是使用 match()
函数:
<- c("M", "F")
oldvalues <- factor(c("g1", "g2")) # 转换为因子
newvalues
$scode <- newvalues[match(data$sex, oldvalues)]
data
data#> subject sex control cond1 cond2 scode
#> 1 1 M 7.9 12.3 10.7 g1
#> 2 2 F 6.3 10.6 11.1 g2
#> 3 3 F 9.5 13.1 13.8 g2
#> 4 4 M 11.5 13.4 12.9 g1
6.6.2.2 将一个连续变量重编码为分类变量
标记 control
值小于 7 为 low
,大于或等于 7 为 high
:
$category[data$control < 7] <- "low"
data$category[data$control >= 7] <- "high"
data
# 把列转换为因子
$category <- factor(data$category)
data
data#> subject sex control cond1 cond2 scode category
#> 1 1 M 7.9 12.3 10.7 g1 high
#> 2 2 F 6.3 10.6 11.1 g2 low
#> 3 3 F 9.5 13.1 13.8 g2 high
#> 4 4 M 11.5 13.4 12.9 g1 high
用 cut()
函数,可以指定边限和新值:
$category <- cut(data$control, breaks = c(-Inf, 7, 9,
dataInf), labels = c("low", "medium", "high"))
data#> subject sex control cond1 cond2 scode category
#> 1 1 M 7.9 12.3 10.7 g1 medium
#> 2 2 F 6.3 10.6 11.1 g2 low
#> 3 3 F 9.5 13.1 13.8 g2 high
#> 4 4 M 11.5 13.4 12.9 g1 high
默认情况下,范围是左边开放、右边关闭,如 (7,9]
。使用 right= FALSE
可以设置成左边关闭、右边开放,像 [7、9)
。
6.6.2.3 计算得到一个新的连续变量
假设您想添加一个新列,为三个量的和。
$total <- data$control + data$cond1 + data$cond2
data
data#> subject sex control cond1 cond2 scode category total
#> 1 1 M 7.9 12.3 10.7 g1 medium 30.9
#> 2 2 F 6.3 10.6 11.1 g2 low 28.0
#> 3 3 F 9.5 13.1 13.8 g2 high 36.4
#> 4 4 M 11.5 13.4 12.9 g1 high 37.8
6.7 映射向量值
6.7.1 问题
你想将向量中所有值为 x
的实例改为值 y
。
6.7.2 方案
# 创建一些样本数据
<- c("alpha", "beta", "gamma")
str <- c(1, 2, 3) num
最简单的方法是使用 plyr
包里的 revalue()
或 mapvalues()
。
library(plyr)
revalue(str, c(beta = "two", gamma = "three"))
#> [1] "alpha" "two" "three"
mapvalues(str, from = c("beta", "gamma"), to = c("two",
"three"))
#> [1] "alpha" "two" "three"
# 对于数值型向量,revalue()
# 没作用,由于它使用一个命名了的向量,向量名一般是字符串而不是数值,但
# mapvalues()仍然有作用
mapvalues(num, from = c(2, 3), to = c(5, 6))
#> [1] 1 5 6
如果你不想依赖 plyr包,你可以使用 R 内置函数。注意,这些方法将直接修改向量;也就是说,你不需要把结果保存回变量。
# 把'beta' 替换为 'two'
== "beta"] <- "two"
str[str
str#> [1] "alpha" "two" "gamma"
== 2] <- 5
num[num
num#> [1] 1 5 3
也可以使用R的字符串查找和替换函数来重新映射字符串向量的值。注意,alpha
前后的 ^
和 $
确保整个字符串匹配。没有它们,如果有一个值为alphabet
,它也会被匹配,替代 onebet
。
<- c("alpha", "beta", "gamma")
str
sub("^alpha$", "one", str)
#> [1] "one" "beta" "gamma"
# 把所有列的 'a' 替代为 'X'
gsub("a", "X", str)
#> [1] "XlphX" "betX" "gXmmX"
# gsub() 替代所有匹配的元素 sub()
# 只替代每一个元素首先匹配到的内容
6.8 重命名因子水平
6.8.1 问题
你想要重命名因子水平。
6.8.2 方案
# 处理一个因子的样例
<- factor(c("alpha", "beta", "gamma", "alpha", "beta"))
x
x#> [1] alpha beta gamma alpha beta
#> Levels: alpha beta gamma
levels(x)
#> [1] "alpha" "beta" "gamma"
最简单的办法是使用 plyr
包中的 revalue()
或者 mapvalues()
函数。
library(plyr)
revalue(x, c(beta = "two", gamma = "three"))
#> [1] alpha two three alpha two
#> Levels: alpha two three
mapvalues(x, from = c("beta", "gamma"), to = c("two", "three"))
#> [1] alpha two three alpha two
#> Levels: alpha two three
如果你不想要依赖 plyr 包,你可以使用 R 的内置函数进行以下处理。注意这些方法会直接修改变量x
;你不需要将结果重新赋值回给x
。
# 通过名字重命名: change 'beta' to 'two'
levels(x)[levels(x) == "beta"] <- "two"
# 你也可以通过位置重命名,但这种做法比较危险(会因数据变化造成不可控结果),不值得推荐
# 通过因子列表索引重命名: change third item, 'gamma',
# to 'three'.
levels(x)[3] <- "three"
x#> [1] alpha two three alpha two
#> Levels: alpha two three
# 重命名所有的因子水平
levels(x) <- c("one", "two", "three")
x#> [1] one two three one two
#> Levels: one two three
我们可以不使用 plyr 包而通过名字实现因子水平的重命名,但记住这只有在所有的水平都在列表中时才起作用,否则会返回 NA 以代替寻找不到的因子水平。
# 用名字重命名所有因子
<- factor(c("alpha", "beta", "gamma", "alpha", "beta"))
x levels(x) <- list(A = "alpha", B = "beta", C = "gamma")
x#> [1] A B C A B
#> Levels: A B C
我们也可以使用 R 的字符串搜索与替换函数去重命名因子水平。注意字符 alpha
周围的 ^
与 $
符号是用来确保整个字符串能够匹配(正则表达式)。如果没有它们,字符 alphabet
也能够被成功匹配并会被替换为 onbet
。
# 一个样例
<- factor(c("alpha", "beta", "gamma", "alpha", "beta"))
x
x#> [1] alpha beta gamma alpha beta
#> Levels: alpha beta gamma
levels(x) <- sub("^alpha$", "one", levels(x))
x#> [1] one beta gamma one beta
#> Levels: one beta gamma
# 将所有的 'a' 替换为 'X'
levels(x) <- gsub("a", "X", levels(x))
x#> [1] one betX gXmmX one betX
#> Levels: one betX gXmmX
# gsub() 替代所有匹配的元素 sub()
# 只替代每一个元素首先匹配到的内容
6.8.3 更多参考
匹配向量中值并将其替换为新的值操作类似,参见映射向量值获取更多信息。
6.9 重计算因子水平
6.9.1 问题
你想要重新计算一个因子的水平。这在因子水平实际并没有出现在数据中时非常有用。它可能发生在数据的导入或者当你移除一些行时。
6.9.2 方案
对于单个因子变量:
# 创建额外的因子水平 (gamma)
<- factor(c("alpha", "beta", "alpha"), levels = c("alpha",
x "beta", "gamma"))
x#> [1] alpha beta alpha
#> Levels: alpha beta gamma
# 移除额外的因子水平
<- factor(x)
x
x#> [1] alpha beta alpha
#> Levels: alpha beta
当导入数据之后,你可能有一个混合因子变量和其他向量的数据框,然后想要重新计算所有因子的水平。你可以使用 droplevels()
函数实现这一点。
# 创建一些因子的数据框 (有额外的因子水平)
<- data.frame(x = factor(c("alpha", "beta", "alpha"),
df levels = c("alpha", "beta", "gamma")), y = c(5, 8, 2),
z = factor(c("red", "green", "green"), levels = c("red",
"green", "blue")))
# 显示因子水平 (with extra levels)
$x
df#> [1] alpha beta alpha
#> Levels: alpha beta gamma
$z
df#> [1] red green green
#> Levels: red green blue
# 丢掉额外因子水平
<- droplevels(df)
df
# 再次显示因子,现在没有额外的因子水平了
$x
df#> [1] alpha beta alpha
#> Levels: alpha beta
$z
df#> [1] red green green
#> Levels: red green
6.9.3 另见
想要重计算一个数据框中所有的因子变量列的因子水平,参见重计算数据框所有因子列的水平。
6.10 改变因子水平次序
6.10.1 问题
你想要改变因子水平出现的次序。
6.10.2 方案
R 中有两种不同类型的因子变量:有序和无序。比如{小,中,大}和{钢笔,橡皮擦,铅笔}。对于绝大多数分析而言,一个因子变量是有序还是无序并不重要。如果因子是有序的,那么这个因子水平的特定次序是重要的(小 < 中 < 大)。如果因子是无序的,那么因子水平同样会以一定的顺序出现,但这仅仅为了方便而已(钢笔,橡皮擦,铅笔) - 但有时它是重要的,例如它会指导结果如何输出,图形元素如何展示。
一种改变因子次序的方式是对因子使用 factor()
函数并且直接指定它们的次序。下面这个例子中,ordered()
函数可以替换 factor()
函数。
下面是这个例子的数据:
# 创建一个错误次序的因子
<- factor(c("small", "large", "large", "small", "medium"))
sizes
sizes#> [1] small large large small medium
#> Levels: large medium small
因子水平被显式地指定:
<- factor(sizes, levels = c("small", "medium", "large"))
sizes
sizes#> [1] small large large small medium
#> Levels: small medium large
我们同样可以对有序因子这样操作:
<- ordered(c("small", "large", "large", "small", "medium"))
sizes <- ordered(sizes, levels = c("small", "medium", "large"))
sizes
sizes#> [1] small large large small medium
#> Levels: small < medium < large
另一种方式是使用 relevel()
函数在列表中制作一个特定水平(这对有序因子不起作用)。
# 创建错误次序的因子
<- factor(c("small", "large", "large", "small", "medium"))
sizes
sizes#> [1] small large large small medium
#> Levels: large medium small
# 使得 medium 排最前面
<- relevel(sizes, "medium")
sizes
sizes#> [1] small large large small medium
#> Levels: medium large small
# 使得 small 排最前面
<- relevel(sizes, "small")
sizes
sizes#> [1] small large large small medium
#> Levels: small medium large
当因子创建时,我们可以指定合适的顺序。
<- factor(c("small", "large", "large", "small", "medium"),
sizes levels = c("small", "medium", "large"))
sizes#> [1] small large large small medium
#> Levels: small medium large
反转因子水平次序。
# 创建错误次序的因子
<- factor(c("small", "large", "large", "small", "medium"))
sizes
sizes#> [1] small large large small medium
#> Levels: large medium small
<- factor(sizes, levels = rev(levels(sizes)))
sizes
sizes#> [1] small large large small medium
#> Levels: small medium large
6.11 重命名数据框的列
6.11.1 问题
你想要给数据框的列重新命名。
6.11.2 方案
让我们从有三列的一个数据框开始:
<- data.frame(alpha = 1:3, beta = 4:6, gamma = 7:9)
d
d#> alpha beta gamma
#> 1 1 4 7
#> 2 2 5 8
#> 3 3 6 9
names(d)
#> [1] "alpha" "beta" "gamma"
最简单的方式是使用 plyr 包的 rename()
函数:
library(plyr)
rename(d, c(beta = "two", gamma = "three"))
#> alpha two three
#> 1 1 4 7
#> 2 2 5 8
#> 3 3 6 9
如果你不想要依赖 plyr 包,可以使用下面的 R 内置函数。注意这些直接修改了d
,意味着你不需要把结果保存回d
。
# 通过 name 函数重命名列:把 'beta' 改为 'two'
names(d)[names(d) == "beta"] <- "two"
d#> alpha two gamma
#> 1 1 4 7
#> 2 2 5 8
#> 3 3 6 9
# 你也可以通过位置索引改变名字,但这种操作会有点危险,一旦你以后对数据做出改变,这些操作可能就不适用了。
# 通过名字向量的位置索引重命名:改变第三项,将 'gamma'
# 改成 'three'
names(d)[3] <- "three"
d#> alpha two three
#> 1 1 4 7
#> 2 2 5 8
#> 3 3 6 9
也可以使用 R 的字符串搜索和替换函数来重命名列。注意alpha
周围的^
和$
是确保全部字符串匹配。没有他们的话,如果存在一个叫 alphabet
的列也会匹配成功。
names(d) <- sub("^alpha$", "one", names(d))
d#> one two three
#> 1 1 4 7
#> 2 2 5 8
#> 3 3 6 9
# 所有的列中,替换列名中的't'为'X'
names(d) <- gsub("t", "X", names(d))
d#> one Xwo Xhree
#> 1 1 4 7
#> 2 2 5 8
#> 3 3 6 9
# gsub() 替换每个列名中所有的匹配项 sub()
# 只替换每个列名中匹配的第一项
6.12 添加和移除数据框的列
6.12.1 问题
你想要对一个数据框添加或者移除一些列(变量)。
6.12.2 方案
这里有许多解决办法:
<- read.table(header = TRUE, text = "
data id weight
1 20
2 27
3 24
")
# 添加一列的办法
$size <- c("small", "large", "medium")
data"size"]] <- c("small", "large", "medium")
data[["size"] <- c("small", "large", "medium")
data[, $size <- 0 # 添加的变量所有行都是一样的值 data
# 移除列的办法
$size <- NULL
data"size"]] <- NULL
data[["size"] <- NULL
data[,3]] <- NULL
data[[3] <- NULL
data[,<- subset(data, select=-size) data
6.13 对数据框的列重新排序
6.13.1 问题
你想要对一个数据框的列重新排序。
6.13.2 方案
# 一个样例数据框
<- read.table(header = TRUE, text = "
data id weight size
1 20 small
2 27 large
3 24 medium
")
# 根据列名数字排序
c(1, 3, 2)]
data[#> id size weight
#> 1 1 small 20
#> 2 2 large 27
#> 3 3 medium 24
# 如果你想要实际改变 `data`,需要把它重新赋值回 `data`
# 符号: data <- data[c(1,3,2)]
# 根据列名重新排序
c("size", "id", "weight")]
data[#> size id weight
#> 1 small 1 20
#> 2 large 2 27
#> 3 medium 3 24
上面例子中对数据框进行索引是将数据框作为一个列表(一个数据框实际上就是向量列表)。你也可以使用矩阵形式的索引方式:
c(1, 3, 2)]
data[, #> id size weight
#> 1 1 small 20
#> 2 2 large 27
#> 3 3 medium 24
矩阵形式索引的缺点在于当你只指定一列时结果会不同。下面例子中,返回的结果对象是一个向量而不是数据框。因为返回的数据类型并不能总是与矩阵索引保持一致,所以通常使用列表形式进行索引更为安全,或者在矩阵索引形式中指定 drop=FALSE
选项:
# 列表形式的索引
2]
data[#> weight
#> 1 20
#> 2 27
#> 3 24
# 矩阵形式的索引——降维会变为一个向量
2]
data[, #> [1] 20 27 24
# 矩阵形式的索引,指定 drop=FALSE
# ——保留维度以保存数据框形式
2, drop = FALSE]
data[, #> weight
#> 1 20
#> 2 27
#> 3 24
6.14 合并数据框
6.14.1 问题
你想要基于一个给定的列合并两个数据框(像 SQL 的 join
)。
6.14.2 方案
# 创建一个将 storyid 映射到 titles 上的数据框
<- read.table(header = TRUE, text = "
stories storyid title
1 lions
2 tigers
3 bears
")
# 创建另一个有数据和 storyid 的数据框(没有 titles)
<- read.table(header = TRUE, text = "
data subject storyid rating
1 1 6.7
1 2 4.5
1 3 3.7
2 2 3.3
2 3 4.1
2 1 5.2
")
# 合并两个数据框
merge(stories, data, "storyid")
#> storyid title subject rating
#> 1 1 lions 1 6.7
#> 2 1 lions 2 5.2
#> 3 2 tigers 1 4.5
#> 4 2 tigers 2 3.3
#> 5 3 bears 1 3.7
#> 6 3 bears 2 4.1
如果两个数据框里你想要匹配的列有不同的名字,可以通过选项指定:
# 下面使用的是 id 替换了 storyid
<- read.table(header = TRUE, text = "
stories2 id title
1 lions
2 tigers
3 bears
")
# 合并两个数据框
merge(x = stories2, y = data, by.x = "id", by.y = "storyid")
#> id title subject rating
#> 1 1 lions 1 6.7
#> 2 1 lions 2 5.2
#> 3 2 tigers 1 4.5
#> 4 2 tigers 2 3.3
#> 5 3 bears 1 3.7
#> 6 3 bears 2 4.1
# 注意结果的列名继承第一个数据框
我们也可以合并多个列:
# 制造更多的数据
<- read.table(header = T, text = "
animals size type name
small cat lynx
big cat tiger
small dog chihuahua
big dog \"great dane\"
")
<- read.table(header = T, text = "
observations number size type
1 big cat
2 small dog
3 small dog
4 big dog
")
merge(observations, animals, c("size", "type"))
#> size type number name
#> 1 big cat 1 tiger
#> 2 big dog 4 great dane
#> 3 small dog 2 chihuahua
#> 4 small dog 3 chihuahua
6.14.3 注意
合并之后,改变列名的顺序可能是有用的,参见对数据框的列重新排序 。
6.15 比较数据框
6.15.1 问题
你想要比较两个或多个数据框并找到在超过一个数据框中出现的行,或者仅在一个数据框中出现的行。
6.15.2 方案
6.15.2.1 一个例子
假设你有下面三个数据框,你想要知道那些至少在两个数据框中出现的行。
<- data.frame(Subject = c(1, 1, 2, 2), Response = c("X",
dfA "X", "X", "X"))
dfA#> Subject Response
#> 1 1 X
#> 2 1 X
#> 3 2 X
#> 4 2 X
<- data.frame(Subject = c(1, 2, 3), Response = c("X",
dfB "Y", "X"))
dfB#> Subject Response
#> 1 1 X
#> 2 2 Y
#> 3 3 X
<- data.frame(Subject = c(1, 2, 3), Response = c("Z",
dfC "Y", "Z"))
dfC#> Subject Response
#> 1 1 Z
#> 2 2 Y
#> 3 3 Z
在 dfA
中,包括 (1,X)
的行同样出现在了 dfB
,但是包含 (2,X)
的行没有出现在任何其他的数据框。相似地,dfB
包含的 (1,X)
出现在了 dfA
,(2,Y)
出现在了 dfC
,但是 (3,X)
没有出现在其他数据框。
你可能想要标记在其他数据框中出现了的行,或者在每个数据框中都是唯一的行。
6.15.2.2 连接数据框
进一步地,我们首先用一个可以识别每一行来自哪里的列来连接数据框。这里称为 Coder
变量因为它可能是由三个不同的人编码的数据。在这个例子中,你可能想要找到编码者相同之处(至少出现在两个数据框中的行),或者它们不同之处。
$Coder <- "A"
dfA$Coder <- "B"
dfB$Coder <- "C"
dfC
<- rbind(dfA, dfB, dfC) # 把它们粘在一起
df <- df[, c("Coder", "Subject", "Response")] # 重新排序
df
df#> Coder Subject Response
#> 1 A 1 X
#> 2 A 1 X
#> 3 A 2 X
#> 4 A 2 X
#> 5 B 1 X
#> 6 B 2 Y
#> 7 B 3 X
#> 8 C 1 Z
#> 9 C 2 Y
#> 10 C 3 Z
如果你的数据一开始就是这种格式,那就不要将它们连接到一起啦。
6.15.2.3 dupsBetweenGroups() 函数
该函数用来寻找不同组别的重复行:
<- function(df, idcol) {
dupsBetweenGroups # df: the data frame idcol: the column which
# identifies the group each row belongs to
# Get the data columns to use for finding matches
<- setdiff(names(df), idcol)
datacols
# Sort by idcol, then datacols. Save order so we can
# undo the sorting later.
<- do.call(order, df)
sortorder <- df[sortorder, ]
df
# Find duplicates within each id group (first copy
# not marked)
<- duplicated(df)
dupWithin
# With duplicates within each group filtered out,
# find duplicates between groups. Need to scan up
# and down with duplicated() because first copy is
# not marked.
= rep(NA, nrow(df))
dupBetween !dupWithin] <- duplicated(df[!dupWithin,
dupBetween[
datacols])!dupWithin] <- duplicated(df[!dupWithin,
dupBetween[fromLast = TRUE) | dupBetween[!dupWithin]
datacols],
# ============= Replace NA's with previous non-NA
# value ============== This is why we sorted earlier
# - it was necessary to do this part efficiently
# Get indexes of non-NA's
<- !is.na(dupBetween)
goodIdx
# These are the non-NA values from x only Add a
# leading NA for later use when we index into this
# vector
<- c(NA, dupBetween[goodIdx])
goodVals
# Fill the indices of the output vector with the
# indices pulled from these offsets of goodVals. Add
# 1 to avoid indexing to zero.
<- cumsum(goodIdx) + 1
fillIdx
# The original vector, now with gaps filled
<- goodVals[fillIdx]
dupBetween
# Undo the original sort
<- dupBetween
dupBetween[sortorder]
# Return the vector of which entries are duplicated
# across groups
return(dupBetween)
}
6.15.2.4 寻找重复行
使用在前文定义的函数 dupsBetweenGroups()
,我们可以找出在不同组别中重复的行。
# 找出在不同组别中重复的行
<- dupsBetweenGroups(df, "Coder")
dupRows
# 在数据框的旁边打印出来
cbind(df, dup = dupRows)
#> Coder Subject Response dup
#> 1 A 1 X TRUE
#> 2 A 1 X TRUE
#> 3 A 2 X FALSE
#> 4 A 2 X FALSE
#> 5 B 1 X TRUE
#> 6 B 2 Y TRUE
#> 7 B 3 X FALSE
#> 8 C 1 Z FALSE
#> 9 C 2 Y TRUE
#> 10 C 3 Z FALSE
注意这不会标记在同一组中的重复行,比如 Coder=A
时,有两行 Subject=2
以及 Response=X
,但没有标记出来。
6.15.2.5 寻找唯一行
同样可以找出在每一组中唯一出现的行。
cbind(df, unique = !dupRows)
#> Coder Subject Response unique
#> 1 A 1 X FALSE
#> 2 A 1 X FALSE
#> 3 A 2 X TRUE
#> 4 A 2 X TRUE
#> 5 B 1 X FALSE
#> 6 B 2 Y FALSE
#> 7 B 3 X TRUE
#> 8 C 1 Z TRUE
#> 9 C 2 Y FALSE
#> 10 C 3 Z TRUE
6.15.2.6 拆分数据框
如果你想要把连接的数据框拆分为三个原始的数据框:
# 保存df的结果
<- cbind(df, dup = dupRows)
dfDup
<- subset(dfDup, Coder == "A", select = -Coder)
dfA
dfA#> Subject Response dup
#> 1 1 X TRUE
#> 2 1 X TRUE
#> 3 2 X FALSE
#> 4 2 X FALSE
<- subset(dfDup, Coder == "B", select = -Coder)
dfB
dfB#> Subject Response dup
#> 5 1 X TRUE
#> 6 2 Y TRUE
#> 7 3 X FALSE
<- subset(dfDup, Coder == "C", select = -Coder)
dfC
dfC#> Subject Response dup
#> 8 1 Z FALSE
#> 9 2 Y TRUE
#> 10 3 Z FALSE
6.15.2.7 忽略列
有可能需要通过移除数据框的列来忽略一个或者多个列,结果又可以把原始完整的数据框连接起来。
# 忽略 Subject 列——仅使用 Response 列
<- subset(df, select = -Subject)
dfNoSub
dfNoSub#> Coder Response
#> 1 A X
#> 2 A X
#> 3 A X
#> 4 A X
#> 5 B X
#> 6 B Y
#> 7 B X
#> 8 C Z
#> 9 C Y
#> 10 C Z
# 检查重复行
<- dupsBetweenGroups(dfNoSub, "Coder")
dupRows
# 把结果连接起来
cbind(df, dup = dupRows)
#> Coder Subject Response dup
#> 1 A 1 X TRUE
#> 2 A 1 X TRUE
#> 3 A 2 X TRUE
#> 4 A 2 X TRUE
#> 5 B 1 X TRUE
#> 6 B 2 Y TRUE
#> 7 B 3 X TRUE
#> 8 C 1 Z FALSE
#> 9 C 2 Y TRUE
#> 10 C 3 Z FALSE
6.15.3 注意
想要寻找单个数据框中的重复行,参见查找并移除重复记录。
6.16 重计算数据框所有因子列的水平
6.16.1 问题
你想要重新计算一个数据框中所有因子列(变量)的因子水平。
6.16.2 方案
有时候在读入和清理数据之后,你会发现数据(数据框)结果中有的因子列有一些不存在的因子水平。
例如,下面的d
有一个空行。当它被读入时,因子列会出现水平 ""
,它不应该是数据的一部分。
<- read.csv(header = TRUE, text = "
d x,y,value
a,one,1
,,5
b,two,4
c,three,10
")
d#> x y value
#> 1 a one 1
#> 2 5
#> 3 b two 4
#> 4 c three 10
str(d)
#> 'data.frame': 4 obs. of 3 variables:
#> $ x : chr "a" "" "b" "c"
#> $ y : chr "one" "" "two" "three"
#> $ value: int 1 5 4 10
即便移除了空行,因子中仍有水平 ""
:
# 移除第二行
<- d[-2, ]
d
d#> x y value
#> 1 a one 1
#> 3 b two 4
#> 4 c three 10
str(d)
#> 'data.frame': 3 obs. of 3 variables:
#> $ x : chr "a" "b" "c"
#> $ y : chr "one" "two" "three"
#> $ value: int 1 4 10
6.16.2.1 使用 droplevels()
最简单的方式是使用 droplevels()
函数:
<- droplevels(d)
d1 str(d1)
#> 'data.frame': 3 obs. of 3 variables:
#> $ x : chr "a" "b" "c"
#> $ y : chr "one" "two" "three"
#> $ value: int 1 4 10
6.16.2.2 使用 vapply() 和 lapply()
为了重新计算所有因子列的水平,我们使用以 is.factor()
为参数的 vapply()
函数去找出哪些列是因子,然后再利用以 factor()
函数为参数的 lapply()
操作将那些列重新计算因子水平。
# 找出哪些列是因子
<- vapply(d, is.factor, logical(1))
factor_cols
# 把 factor() 函数应用到那些列,并把结果赋回 d
<- lapply(d[factor_cols], factor)
d[factor_cols] str(d)
#> 'data.frame': 3 obs. of 3 variables:
#> $ x : chr "a" "b" "c"
#> $ y : chr "one" "two" "three"
#> $ value: int 1 4 10
6.16.3 另见
关于重计算一个因子变量水平的信息,参见重计算因子水平。
6.17 长宽格式数据互换
6.17.1 问题
你想要把数据从宽格式转换为长格式。
R中许多函数希望输入的数据是长格式而不是宽格式。然而像 SPSS 软件经常使用宽格式数据。
6.17.2 方案
下面有两类方法:
- tidyr 包的
gather()
和spread()
函数。这 是reshape2 包的一个新接口。 - reshape2包的
melt()
与dcast()
函数。
这里不包含其他一些实现的方法,因为这些方法不是很好使用:
reshape()
函数比较让人迷惑,因为它是 R 基础包的一部分,而不是 reshape2 包的一部分。stack()
和unstack()
6.17.2.1 样例数据
这里使用的数据框包含同样数据的长、宽格式。它们接下来会被相互转换。
<- read.table(header = TRUE, text = "
olddata_wide subject sex control cond1 cond2
1 M 7.9 12.3 10.7
2 F 6.3 10.6 11.1
3 F 9.5 13.1 13.8
4 M 11.5 13.4 12.9
")
# 确保 subject 列是一个因子
$subject <- factor(olddata_wide$subject) olddata_wide
<- read.table(header = TRUE, text = "
olddata_long subject sex condition measurement
1 M control 7.9
1 M cond1 12.3
1 M cond2 10.7
2 F control 6.3
2 F cond1 10.6
2 F cond2 11.1
3 F control 9.5
3 F cond1 13.1
3 F cond2 13.8
4 M control 11.5
4 M cond1 13.4
4 M cond2 12.9
")
# 确保 subject 列是一个因子
$subject <- factor(olddata_long$subject) olddata_long
6.17.2.2 tidyr
6.17.2.2.1 从宽格式到长格式
使用 gather()
:
olddata_wide#> subject sex control cond1 cond2
#> 1 1 M 7.9 12.3 10.7
#> 2 2 F 6.3 10.6 11.1
#> 3 3 F 9.5 13.1 13.8
#> 4 4 M 11.5 13.4 12.9
library(tidyr)
# gather() 的主要参数: - data: 输入数据 - key:
# 分类key的列名 - value: 包含值的列名 - ...:
# 包换需要转换值的列名 - factor_key:
# 把新的合成列设置为因子
<- gather(olddata_wide, condition, measurement,
data_long :cond2, factor_key = TRUE)
control
data_long#> subject sex condition measurement
#> 1 1 M control 7.9
#> 2 2 F control 6.3
#> 3 3 F control 9.5
#> 4 4 M control 11.5
#> 5 1 M cond1 12.3
#> 6 2 F cond1 10.6
#> 7 3 F cond1 13.1
#> 8 4 M cond1 13.4
#> 9 1 M cond2 10.7
#> 10 2 F cond2 11.1
#> 11 3 F cond2 13.8
#> 12 4 M cond2 12.9
在这个例子中,来源列通过 control:cond2
指定聚集到一起。这里的意思是使用位置上在 control
和 conda2
之间(包括 control
与 conda2
)的所有列。另一种使用的方式是单独为每一列命名,如下:
gather(olddata_wide, condition, measurement, control, cond1,
cond2)#> subject sex condition measurement
#> 1 1 M control 7.9
#> 2 2 F control 6.3
#> 3 3 F control 9.5
#> 4 4 M control 11.5
#> 5 1 M cond1 12.3
#> 6 2 F cond1 10.6
#> 7 3 F cond1 13.1
#> 8 4 M cond1 13.4
#> 9 1 M cond2 10.7
#> 10 2 F cond2 11.1
#> 11 3 F cond2 13.8
#> 12 4 M cond2 12.9
如果你需要编程化使用gather()
函数,可能需要使用包含列名的变量。想要实现它的话,你需要使用 gather_()
函数,它会使用字符串而不是没加引号的列名。
<- "condition"
keycol <- "measurement"
valuecol <- c("control", "cond1", "cond2")
gathercols
gather_(olddata_wide, keycol, valuecol, gathercols)
#> subject sex condition measurement
#> 1 1 M control 7.9
#> 2 2 F control 6.3
#> 3 3 F control 9.5
#> 4 4 M control 11.5
#> 5 1 M cond1 12.3
#> 6 2 F cond1 10.6
#> 7 3 F cond1 13.1
#> 8 4 M cond1 13.4
#> 9 1 M cond2 10.7
#> 10 2 F cond2 11.1
#> 11 3 F cond2 13.8
#> 12 4 M cond2 12.9
可选内容:重命名变量列的因子水平并排序。
# 重命名因子水平
levels(data_long$condition)[levels(data_long$condition) ==
"cond1"] <- "first"
levels(data_long$condition)[levels(data_long$condition) ==
"cond2"] <- "second"
# 首先按照 subject 排序,然后按 condition
<- data_long[order(data_long$subject, data_long$condition),
data_long
]
data_long#> subject sex condition measurement
#> 1 1 M control 7.9
#> 5 1 M first 12.3
#> 9 1 M second 10.7
#> 2 2 F control 6.3
#> 6 2 F first 10.6
#> 10 2 F second 11.1
#> 3 3 F control 9.5
#> 7 3 F first 13.1
#> 11 3 F second 13.8
#> 4 4 M control 11.5
#> 8 4 M first 13.4
#> 12 4 M second 12.9
6.17.2.2.2 从长格式到宽格式
使用 spread()
:
olddata_long#> subject sex condition measurement
#> 1 1 M control 7.9
#> 2 1 M cond1 12.3
#> 3 1 M cond2 10.7
#> 4 2 F control 6.3
#> 5 2 F cond1 10.6
#> 6 2 F cond2 11.1
#> 7 3 F control 9.5
#> 8 3 F cond1 13.1
#> 9 3 F cond2 13.8
#> 10 4 M control 11.5
#> 11 4 M cond1 13.4
#> 12 4 M cond2 12.9
library(tidyr)
# spread() 主要参数: - data: 数据对象 - key:
# 包含新列名的列名 - value: 包含值得列名
<- spread(olddata_long, condition, measurement)
data_wide
data_wide#> subject sex cond1 cond2 control
#> 1 1 M 12.3 10.7 7.9
#> 2 2 F 10.6 11.1 6.3
#> 3 3 F 13.1 13.8 9.5
#> 4 4 M 13.4 12.9 11.5
可选项:一些可以使数据看起来更易读的操作。
# 重命名
names(data_wide)[names(data_wide) == "cond1"] <- "first"
names(data_wide)[names(data_wide) == "cond2"] <- "second"
# 排序
<- data_wide[, c(1, 2, 5, 3, 4)]
data_wide
data_wide#> subject sex control first second
#> 1 1 M 7.9 12.3 10.7
#> 2 2 F 6.3 10.6 11.1
#> 3 3 F 9.5 13.1 13.8
#> 4 4 M 11.5 13.4 12.9
因子水平的顺序决定了列的顺序。水平次序能够在重塑之前被改变,或者列也可以在之后重新排序。
6.17.2.3 reshape2
6.17.2.3.1 从宽格式到长格式
使用 melt()
:
olddata_wide#> subject sex control cond1 cond2
#> 1 1 M 7.9 12.3 10.7
#> 2 2 F 6.3 10.6 11.1
#> 3 3 F 9.5 13.1 13.8
#> 4 4 M 11.5 13.4 12.9
library(reshape2)
#>
#> 载入程辑包:'reshape2'
#> The following object is masked from 'package:tidyr':
#>
#> smiths
# 指定id.vars:需要保持的变量名
melt(olddata_wide, id.vars = c("subject", "sex"))
#> subject sex variable value
#> 1 1 M control 7.9
#> 2 2 F control 6.3
#> 3 3 F control 9.5
#> 4 4 M control 11.5
#> 5 1 M cond1 12.3
#> 6 2 F cond1 10.6
#> 7 3 F cond1 13.1
#> 8 4 M cond1 13.4
#> 9 1 M cond2 10.7
#> 10 2 F cond2 11.1
#> 11 3 F cond2 13.8
#> 12 4 M cond2 12.9
melt()
的一些选项可以使得输出更好处理:
<- melt(olddata_wide,
data_long # 变量ID,需要保持的变量名
id.vars=c("subject", "sex"),
# 来源列(被转换的)
measure.vars=c("control", "cond1", "cond2" ),
# 目的列的名字可以确定测量列数值的来自的原始列(变量)
# 这里 measurement 是数值,condition 指定了其来源
variable.name="condition",
value.name="measurement"
)
data_long#> subject sex condition measurement
#> 1 1 M control 7.9
#> 2 2 F control 6.3
#> 3 3 F control 9.5
#> 4 4 M control 11.5
#> 5 1 M cond1 12.3
#> 6 2 F cond1 10.6
#> 7 3 F cond1 13.1
#> 8 4 M cond1 13.4
#> 9 1 M cond2 10.7
#> 10 2 F cond2 11.1
#> 11 3 F cond2 13.8
#> 12 4 M cond2 12.9
如果你不设定 measure.vars
,melt()
函数会自动使用除 id.vars
的所有其他变量。反之亦然。
如果你不指定 variable.name
,它会把那列命名为"variable"
,如果你不使用 value.name
变量,它会将它命名为 "measurement"
。
可选项:重命名变量列的因子水平。
# 重命名因子
levels(data_long$condition)[levels(data_long$condition) ==
"cond1"] <- "first"
levels(data_long$condition)[levels(data_long$condition) ==
"cond2"] <- "second"
# 首先按 subject 排序,然后按 condition 排序
<- data_long[order(data_long$subject, data_long$condition),
data_long
]
data_long#> subject sex condition measurement
#> 1 1 M control 7.9
#> 5 1 M first 12.3
#> 9 1 M second 10.7
#> 2 2 F control 6.3
#> 6 2 F first 10.6
#> 10 2 F second 11.1
#> 3 3 F control 9.5
#> 7 3 F first 13.1
#> 11 3 F second 13.8
#> 4 4 M control 11.5
#> 8 4 M first 13.4
#> 12 4 M second 12.9
6.17.2.3.2 从长格式到宽格式
下面代码使用 dcast()
函数重塑数据。这个函数用于数据框,如果你处理数组或矩阵,替换使用 acast()
。
olddata_long#> subject sex condition measurement
#> 1 1 M control 7.9
#> 2 1 M cond1 12.3
#> 3 1 M cond2 10.7
#> 4 2 F control 6.3
#> 5 2 F cond1 10.6
#> 6 2 F cond2 11.1
#> 7 3 F control 9.5
#> 8 3 F cond1 13.1
#> 9 3 F cond2 13.8
#> 10 4 M control 11.5
#> 11 4 M cond1 13.4
#> 12 4 M cond2 12.9
# 信息: 'subject' 和 'sex' 是我们想要保留的列
# 'condition' 是我们想要放入新列名的列 'measurement'
# 包含数值
library(reshape2)
<- dcast(olddata_long, subject + sex ~ condition,
data_wide value.var = "measurement")
data_wide#> subject sex cond1 cond2 control
#> 1 1 M 12.3 10.7 7.9
#> 2 2 F 10.6 11.1 6.3
#> 3 3 F 13.1 13.8 9.5
#> 4 4 M 13.4 12.9 11.5
可选项:一些可以使数据看起来更易读的操作。
# 重命名
names(data_wide)[names(data_wide) == "cond1"] <- "first"
names(data_wide)[names(data_wide) == "cond2"] <- "second"
# 重排序
<- data_wide[, c(1, 2, 5, 3, 4)]
data_wide
data_wide#> subject sex control first second
#> 1 1 M 7.9 12.3 10.7
#> 2 2 F 6.3 10.6 11.1
#> 3 3 F 9.5 13.1 13.8
#> 4 4 M 11.5 13.4 12.9
因子水平的顺序决定了列的顺序。水平次序能够在重塑之前被改变,或者列也可以在之后重新排序。
6.18 汇总数据
6.18.1 问题
你想要按照组别汇总你的数据(均值、标准差等等)。
6.18.2 方案
有三种方法描述基于一些特定变量的分组数据,然后对每一组使用汇总函数(像均值、标准差等等)。
ddply()
函数:它比较容易使用,但需要载入 plyr 包。这种方法可能就是你要找的。summaryBy()
函数:它也比较容易使用,然而它需要载入 doBy 包。aggregate()
函数,它比较难使用一点但内置于 R 中。
假设你有以下数据并想求得每一组样本大小、均值的改变、标准差以及均值的标准误,而这里的组别是根据性别和条件指定的:F-placebo, F-aspirin, M-placebo和 M-aspirin。
<- read.table(header=TRUE, text='
data subject sex condition before after change
1 F placebo 10.1 6.9 -3.2
2 F placebo 6.3 4.2 -2.1
3 M aspirin 12.4 6.3 -6.1
4 F placebo 8.1 6.1 -2.0
5 M aspirin 15.2 9.9 -5.3
6 F aspirin 10.9 7.0 -3.9
7 F aspirin 11.6 8.5 -3.1
8 M aspirin 9.5 3.0 -6.5
9 F placebo 11.5 9.0 -2.5
10 M placebo 11.9 11.0 -0.9
11 F aspirin 11.4 8.0 -3.4
12 M aspirin 10.0 4.4 -5.6
13 M aspirin 12.5 5.4 -7.1
14 M placebo 10.6 10.6 0.0
15 M aspirin 9.1 4.3 -4.8
16 F placebo 12.1 10.2 -1.9
17 F placebo 11.0 8.8 -2.2
18 F placebo 11.9 10.2 -1.7
19 M aspirin 9.1 3.6 -5.5
20 M placebo 13.5 12.4 -1.1
21 M aspirin 12.0 7.5 -4.5
22 F placebo 9.1 7.6 -1.5
23 M placebo 9.9 8.0 -1.9
24 F placebo 7.6 5.2 -2.4
25 F placebo 11.8 9.7 -2.1
26 F placebo 11.8 10.7 -1.1
27 F aspirin 10.1 7.9 -2.2
28 M aspirin 11.6 8.3 -3.3
29 F aspirin 11.3 6.8 -4.5
30 F placebo 10.3 8.3 -2.0
')
6.18.2.1 使用 ddply
library(plyr)
# 给每一组运行长度、均值、标准差等函数
# 每一组依据性别+条件划分
<- ddply(data, c("sex", "condition"), summarise, N = length(change),
cdata mean = mean(change), sd = sd(change), se = sd/sqrt(N))
cdata#> sex condition N mean sd se
#> 1 F aspirin 5 -3.420 0.8643 0.3865
#> 2 F placebo 12 -2.058 0.5248 0.1515
#> 3 M aspirin 9 -5.411 1.1308 0.3769
#> 4 M placebo 4 -0.975 0.7805 0.3902
6.18.2.1.1 处理缺失值
如果数据中存在 NA 值,需要给每个函数添加 na.rm=TRUE
标记去除缺失值。因为函数 length()
没有 na.rm
选项,所以可以使用 sum(!is.na(...))
的方式对非缺失值进行计数。
# 给数据加些 NA 值
<- data
dataNA $change[11:14] <- NA
dataNA
<- ddply(dataNA, c("sex", "condition"), summarise,
cdata N = sum(!is.na(change)), mean = mean(change, na.rm = TRUE),
sd = sd(change, na.rm = TRUE), se = sd/sqrt(N))
cdata#> sex condition N mean sd se
#> 1 F aspirin 4 -3.425 0.9979 0.4990
#> 2 F placebo 12 -2.058 0.5248 0.1515
#> 3 M aspirin 7 -5.143 1.0675 0.4035
#> 4 M placebo 3 -1.300 0.5292 0.3055
6.18.2.1.2 自动汇总函数
不像我们刚才手动地指定想要的值然后计算标准误,这个函数可以自动处理所有的细节。它可以干以下的事情:
- 寻找均值、标准差和计数
- 寻找均值的标准误(强调,如果你处理的是被试内变量这可能不是你想要的)
- 寻找 95% 的置信区间(也可以指定其他值)
- 重命令结果数据集的变量名,这样更方便后续处理
要使用的话,把函数放你的代码中然后像下面一样调用它。
## Summarizes data. Gives count, mean, standard
## deviation, standard error of the mean, and
## confidence interval (default 95%). data: a data
## frame. measurevar: the name of a column that
## contains the variable to be summariezed groupvars:
## a vector containing names of columns that contain
## grouping variables na.rm: a boolean that indicates
## whether to ignore NA's conf.interval: the percent
## range of the confidence interval (default is 95%)
<- function(data = NULL, measurevar, groupvars = NULL,
summarySE na.rm = FALSE, conf.interval = 0.95, .drop = TRUE) {
library(plyr)
# New version of length which can handle NA's: if
# na.rm==T, don't count them
<- function(x, na.rm = FALSE) {
length2 if (na.rm)
sum(!is.na(x)) else length(x)
}
# This does the summary. For each group's data
# frame, return a vector with N, mean, and sd
<- ddply(data, groupvars, .drop = .drop, .fun = function(xx,
datac
col) {c(N = length2(xx[[col]], na.rm = na.rm), mean = mean(xx[[col]],
na.rm = na.rm), sd = sd(xx[[col]], na.rm = na.rm))
}, measurevar)
# Rename the 'mean' column
<- rename(datac, c(mean = measurevar))
datac
$se <- datac$sd/sqrt(datac$N) # Calculate standard error of the mean
datac
# Confidence interval multiplier for standard error
# Calculate t-statistic for confidence interval:
# e.g., if conf.interval is .95, use .975
# (above/below), and use df=N-1
<- qt(conf.interval/2 + 0.5, datac$N - 1)
ciMult $ci <- datac$se * ciMult
datac
return(datac)
}
举个例子使用它(用95%的置信区间)。与之前手动计算这些步骤相比 summarySE()
函数一步搞定:
summarySE(data, measurevar = "change", groupvars = c("sex",
"condition"))
#> sex condition N change sd se ci
#> 1 F aspirin 5 -3.420 0.8643 0.3865 1.0732
#> 2 F placebo 12 -2.058 0.5248 0.1515 0.3334
#> 3 M aspirin 9 -5.411 1.1308 0.3769 0.8692
#> 4 M placebo 4 -0.975 0.7805 0.3902 1.2419
# 使用 NA'的数据框, 使用 na.rm=TRUE
summarySE(dataNA, measurevar = "change", groupvars = c("sex",
"condition"), na.rm = TRUE)
#> sex condition N change sd se ci
#> 1 F aspirin 4 -3.425 0.9979 0.4990 1.5879
#> 2 F placebo 12 -2.058 0.5248 0.1515 0.3334
#> 3 M aspirin 7 -5.143 1.0675 0.4035 0.9873
#> 4 M placebo 3 -1.300 0.5292 0.3055 1.3145
6.18.2.1.3 用零填满空组合
有时候汇总的数据框中存在因子的空组合——意思是因子组合可能存在,但原始数据框里又没有实际出现。它在自动填满有 NA 值的数据框时有用。要做到这一点,当调用ddply()
或 summarySE()
时设置 .drop=FALSE
。
例子:
# 首先移除所有 Male+Placebo 条目
<- subset(data, !(sex == "M" & condition == "placebo"))
dataSub
# 如果我们汇总数据,在本来有 Male+Placebo
# 的地方会存在空行 因为这个组合已经被我们删除了
summarySE(dataSub, measurevar = "change", groupvars = c("sex",
"condition"))
#> sex condition N change sd se ci
#> 1 F aspirin 5 -3.420 0.8643 0.3865 1.0732
#> 2 F placebo 12 -2.058 0.5248 0.1515 0.3334
#> 3 M aspirin 9 -5.411 1.1308 0.3769 0.8692
# 设置 .drop=FALSE 指定不要扔掉这个组合
summarySE(dataSub, measurevar = "change", groupvars = c("sex",
"condition"), .drop = FALSE)
#> Warning in qt(conf.interval/2 + 0.5, datac$N - 1): 产生
#> 了NaNs
#> sex condition N change sd se ci
#> 1 F aspirin 5 -3.420 0.8643 0.3865 1.0732
#> 2 F placebo 12 -2.058 0.5248 0.1515 0.3334
#> 3 M aspirin 9 -5.411 1.1308 0.3769 0.8692
#> 4 M placebo 0 NaN NA NA NaN
6.18.2.2 使用summaryBy
使用 summarizeBy()
函数瓦解数据:
library(doBy)
# 给每一组运行长度、均值、标准差等函数
# 每一组依据性别+条件划分
<- summaryBy(change ~ sex + condition, data = data,
cdata FUN = c(length, mean, sd))
cdata#> sex condition change.length change.mean change.sd
#> 1 F aspirin 5 -3.420 0.8643
#> 2 F placebo 12 -2.058 0.5248
#> 3 M aspirin 9 -5.411 1.1308
#> 4 M placebo 4 -0.975 0.7805
# 重命名 change.length 为 N
names(cdata)[names(cdata) == "change.length"] <- "N"
# 计算平均值的标准误差
$change.se <- cdata$change.sd/sqrt(cdata$N)
cdata
cdata#> sex condition N change.mean change.sd change.se
#> 1 F aspirin 5 -3.420 0.8643 0.3865
#> 2 F placebo 12 -2.058 0.5248 0.1515
#> 3 M aspirin 9 -5.411 1.1308 0.3769
#> 4 M placebo 4 -0.975 0.7805 0.3902
注意,如果你有任何被试间变量,这些标准误值在比对组别差异时就没用了。
6.18.2.2.1 处理缺失值
如果数据中存在 NA
值,你需要添加 na.rm=TRUE
选项。通常你可以在 summaryBy()
函数中设置,但 length()
函数识别不了这个选项。一种解决方式是根据 length()
函数定义一个新的取长度函数去处理NA值。
# 新版的 length 函数可以处理 NA 值,如果
# na.rm=T,则不对 NA 计数
<- function(x, na.rm = FALSE) {
length2 if (na.rm)
sum(!is.na(x)) else length(x)
}
# 给数据添加一些 NA 值
<- data
dataNA $change[11:14] <- NA
dataNA
<- summaryBy(change ~ sex + condition, data = dataNA,
cdataNA FUN = c(length2, mean, sd), na.rm = TRUE)
cdataNA#> sex condition change.length2 change.mean change.sd
#> 1 F aspirin 4 -3.425 0.9979
#> 2 F placebo 12 -2.058 0.5248
#> 3 M aspirin 7 -5.143 1.0675
#> 4 M placebo 3 -1.300 0.5292
# 做些其他事情
6.18.2.2.2 自动汇总函数
注意这里的自动汇总函数与之前的不同,它是通过
summaryBy()
实现的
不像我们刚才手动地指定想要的值然后计算标准误,这个函数可以自动处理所有的细节。它可以干以下的事情:
- 寻找均值、标准差和计数
- 寻找均值的标准误
- 寻找95%的置信区间(也可以指定其他值)
- 重命令结果数据集的变量名,这样更方便后续处理
要使用的话,把函数放你的代码中然后像下面一样调用它。
## Summarizes data. Gives count, mean, standard
## deviation, standard error of the mean, and
## confidence interval (default 95%). data: a data
## frame. measurevar: the name of a column that
## contains the variable to be summariezed groupvars:
## a vector containing names of columns that contain
## grouping variables na.rm: a boolean that indicates
## whether to ignore NA's conf.interval: the percent
## range of the confidence interval (default is 95%)
<- function(data = NULL, measurevar, groupvars = NULL,
summarySE na.rm = FALSE, conf.interval = 0.95) {
library(doBy)
# New version of length which can handle NA's: if
# na.rm==T, don't count them
<- function(x, na.rm = FALSE) {
length2 if (na.rm)
sum(!is.na(x)) else length(x)
}
# Collapse the data
<- as.formula(paste(measurevar, paste(groupvars,
formula collapse = " + "), sep = " ~ "))
<- summaryBy(formula, data = data, FUN = c(length2,
datac na.rm = na.rm)
mean, sd),
# Rename columns
names(datac)[names(datac) == paste(measurevar, ".mean",
sep = "")] <- measurevar
names(datac)[names(datac) == paste(measurevar, ".sd",
sep = "")] <- "sd"
names(datac)[names(datac) == paste(measurevar, ".length2",
sep = "")] <- "N"
$se <- datac$sd/sqrt(datac$N) # Calculate standard error of the mean
datac
# Confidence interval multiplier for standard error
# Calculate t-statistic for confidence interval:
# e.g., if conf.interval is .95, use .975
# (above/below), and use df=N-1
<- qt(conf.interval/2 + 0.5, datac$N - 1)
ciMult $ci <- datac$se * ciMult
datac
return(datac)
}
举个例子使用它(用95%的置信区间)。与之前手动计算这些步骤相反,summarySE()
函数一步搞定:
summarySE(data, measurevar = "change", groupvars = c("sex",
"condition"))
#> sex condition N change sd se ci
#> 1 F aspirin 5 -3.420 0.8643 0.3865 1.0732
#> 2 F placebo 12 -2.058 0.5248 0.1515 0.3334
#> 3 M aspirin 9 -5.411 1.1308 0.3769 0.8692
#> 4 M placebo 4 -0.975 0.7805 0.3902 1.2419
# 对于含有 NA 值得数据集,使用 na.rm=TRUE
summarySE(dataNA, measurevar = "change", groupvars = c("sex",
"condition"), na.rm = TRUE)
#> sex condition N change sd se ci
#> 1 F aspirin 4 -3.425 0.9979 0.4990 1.5879
#> 2 F placebo 12 -2.058 0.5248 0.1515 0.3334
#> 3 M aspirin 7 -5.143 1.0675 0.4035 0.9873
#> 4 M placebo 3 -1.300 0.5292 0.3055 1.3145
6.18.2.2.3 用零填满空组合
有时候汇总的数据框中存在因子的空组合 - 这意思是,因子组合可能存在,但原始数据框里又没有实际出现。它在自动填满有 NA 值的数据框时有用。
这个例子将会用 0 填满缺失的组合:
<- function(df, factors, measures) {
fillMissingCombs
# 创建含因子水平组合的列表
<- list()
levelList for (f in factors) {
<- levels(df[, f])
levelList[[f]]
}
<- expand.grid(levelList)
fullFactors
<- merge(fullFactors, df, all.x = TRUE)
dfFull
# 将 measure 变量中的 NA 都替换为 0
for (m in measures) {
is.na(dfFull[, m]), m] <- 0
dfFull[
}
return(dfFull)
}
使用例子:
# 首先移除所有 Male+Placebo 条目
<- subset(data, !(sex == "M" & condition == "placebo"))
dataSub
# 如果我们汇总数据,在本来有 Male+Placebo
# 的地方会存在空行 因为这个组合已经被我们删除了
<- summarySE(dataSub, measurevar = "change", groupvars = c("sex",
cdataSub "condition"))
cdataSub#> sex condition N change sd se ci
#> 1 F aspirin 5 -3.420 0.8643 0.3865 1.0732
#> 2 F placebo 12 -2.058 0.5248 0.1515 0.3334
#> 3 M aspirin 9 -5.411 1.1308 0.3769 0.8692
# 设置 .drop=FALSE 指定不要扔掉这个组合
fillMissingCombs(cdataSub, factors = c("sex", "condition"),
measures = c("N", "change", "sd", "se", "ci"))
#> [1] sex condition N change sd
#> [6] se ci
#> <0 行> (或0-长度的row.names)
6.18.2.3 使用 aggregate()
aggregate()
函数比较难用,但它内置于 R,所以不需要安装其他包。
# 对每个目录 (sex*condition) 中的对象计数
<- aggregate(data["subject"], by = data[c("sex", "condition")],
cdata FUN = length)
cdata#> sex condition subject
#> 1 F aspirin 5
#> 2 M aspirin 9
#> 3 F placebo 12
#> 4 M placebo 4
# 重命名 'subject' 列为 'N'
names(cdata)[names(cdata) == "subject"] <- "N"
cdata#> sex condition N
#> 1 F aspirin 5
#> 2 M aspirin 9
#> 3 F placebo 12
#> 4 M placebo 4
# 按性别排序
<- cdata[order(cdata$sex), ]
cdata
cdata#> sex condition N
#> 1 F aspirin 5
#> 3 F placebo 12
#> 2 M aspirin 9
#> 4 M placebo 4
# 我们也保留 before 和 after列:
# 得到性别和条件下的平均影响大小 Get the average
# effect size by sex and condition
<- aggregate(data[c("before", "after", "change")],
cdata.means by = data[c("sex", "condition")], FUN = mean)
cdata.means#> sex condition before after change
#> 1 F aspirin 11.06 7.640 -3.420
#> 2 M aspirin 11.27 5.856 -5.411
#> 3 F placebo 10.13 8.075 -2.058
#> 4 M placebo 11.47 10.500 -0.975
# 合并数据框
<- merge(cdata, cdata.means)
cdata
cdata#> sex condition N before after change
#> 1 F aspirin 5 11.06 7.640 -3.420
#> 2 F placebo 12 10.13 8.075 -2.058
#> 3 M aspirin 9 11.27 5.856 -5.411
#> 4 M placebo 4 11.47 10.500 -0.975
# 得到标准差
<- aggregate(data["change"], by = data[c("sex",
cdata.sd "condition")], FUN = sd)
# 重命名列
names(cdata.sd)[names(cdata.sd) == "change"] <- "change.sd"
cdata.sd#> sex condition change.sd
#> 1 F aspirin 0.8643
#> 2 M aspirin 1.1308
#> 3 F placebo 0.5248
#> 4 M placebo 0.7805
# 合并
<- merge(cdata, cdata.sd)
cdata
cdata#> sex condition N before after change change.sd
#> 1 F aspirin 5 11.06 7.640 -3.420 0.8643
#> 2 F placebo 12 10.13 8.075 -2.058 0.5248
#> 3 M aspirin 9 11.27 5.856 -5.411 1.1308
#> 4 M placebo 4 11.47 10.500 -0.975 0.7805
# 计算标准误
$change.se <- cdata$change.sd/sqrt(cdata$N)
cdata
cdata#> sex condition N before after change change.sd
#> 1 F aspirin 5 11.06 7.640 -3.420 0.8643
#> 2 F placebo 12 10.13 8.075 -2.058 0.5248
#> 3 M aspirin 9 11.27 5.856 -5.411 1.1308
#> 4 M placebo 4 11.47 10.500 -0.975 0.7805
#> change.se
#> 1 0.3865
#> 2 0.1515
#> 3 0.3769
#> 4 0.3902
如果你有 NA
值想要跳过,设置 na.rm=TRUE
:
<- aggregate(data[c("before", "after", "change")],
cdata.means by = data[c("sex", "condition")], FUN = mean, na.rm = TRUE)
cdata.means#> sex condition before after change
#> 1 F aspirin 11.06 7.640 -3.420
#> 2 M aspirin 11.27 5.856 -5.411
#> 3 F placebo 10.13 8.075 -2.058
#> 4 M placebo 11.47 10.500 -0.975
6.19 数据框与列联表互换
6.19.1 问题
你想要在多种情况的一个数据框、每种情况类型计数的数据框以及一个列联表之间转换。
6.19.2 方案
这里有 3 种数据结构代表相同的信息,但是形式不同:
cases
: 每一行代表一个情况的数据框ctable
: 一个列联表counts
: 计数的数据框,每行代表每一种组合的数目
# 每一行代表一种情况
<- data.frame(Sex = c("M", "M", "F", "F", "F"), Color = c("brown",
cases "blue", "brown", "brown", "brown"))
cases#> Sex Color
#> 1 M brown
#> 2 M blue
#> 3 F brown
#> 4 F brown
#> 5 F brown
# 一个列联表
<- table(cases)
ctable
ctable#> Color
#> Sex blue brown
#> F 0 3
#> M 1 1
# 一个每种组合计数的表格
<- data.frame(Sex = c("F", "M", "F", "M"), Color = c("blue",
counts "blue", "brown", "brown"), Freq = c(0, 1, 3, 1))
counts#> Sex Color Freq
#> 1 F blue 0
#> 2 M blue 1
#> 3 F brown 3
#> 4 M brown 1
6.19.2.1 将情况记录转为列联表
将情况记录转为列联表(上面已经展示了):
<- table(cases)
ctable
ctable#> Color
#> Sex blue brown
#> F 0 3
#> M 1 1
# 如果你使用两个向量调用table函数,它将不会给维度添加名字(Sex和Color)
table(cases$Sex, cases$Color)
#>
#> blue brown
#> F 0 3
#> M 1 1
# 维度名可以通过`dnn`选项手动指定
table(cases$Sex, cases$Color, dnn = c("Sex", "Color"))
#> Color
#> Sex blue brown
#> F 0 3
#> M 1 1
6.19.2.2 将情况记录转为计数
它可以用一个数据框代表每一种组合的计数。注意它被转换并存储在 countdf
对象中:
# Cases to Counts
<- as.data.frame(table(cases))
countdf
countdf#> Sex Color Freq
#> 1 F blue 0
#> 2 M blue 1
#> 3 F brown 3
#> 4 M brown 1
6.19.2.3 countsToCases() 函数
这个函数使用在上面的例子中:
# Convert from data frame of counts to data frame of
# cases. `countcol` is the name of the column
# containing the counts
<- function(x, countcol = "Freq") {
countsToCases # Get the row indices to pull from x
<- rep.int(seq_len(nrow(x)), x[[countcol]])
idx
# Drop count column
<- NULL
x[[countcol]]
# Get the rows from x
x[idx, ] }
6.19.2.4 列联表转为情况记录
countsToCases(as.data.frame(ctable))
#> Sex Color
#> 2 M blue
#> 3 F brown
#> 3.1 F brown
#> 3.2 F brown
#> 4 M brown
注意,countsToCases()
函数定义在上面。
6.19.2.5 列联表转为计数
as.data.frame(ctable)
#> Sex Color Freq
#> 1 F blue 0
#> 2 M blue 1
#> 3 F brown 3
#> 4 M brown 1
从这里我们可以看到上一个代码的中间效果
6.19.2.6 计数转为情况记录
countsToCases(countdf)
#> Sex Color
#> 2 M blue
#> 3 F brown
#> 3.1 F brown
#> 3.2 F brown
#> 4 M brown
这相当于是列联表转为情况记录的第二步。
6.19.2.7 计数转为列联表
xtabs(Freq ~ Sex + Color, data = countdf)
#> Color
#> Sex blue brown
#> F 0 3
#> M 1 1
6.20 计算移动平均数
6.20.1 问题
你想要计算移动平均数。
6.20.2 解决方案
假设你的数据是带缺失值的噪声正弦波。
set.seed(993)
<- 1:300
x <- sin(x/20) + rnorm(300, sd = 0.1)
y 251:255] <- NA y[
filter()
函数可以用来计算移动平均数。
# 绘制未平滑的数据(灰色)
plot(x, y, type = "l", col = grey(0.5))
# 绘制网格
grid()
# 延迟平滑 当前样本和之前 19 个样本的平均数(红色)
<- rep(1/20, 20)
f20
f20#> [1] 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05
#> [11] 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05
<- filter(y, f20, sides = 1)
y_lag lines(x, y_lag, col = "red")
# 对称性平滑:
# 计算当前样本,时间上将来样本和过去10个样本的平均数(蓝色)
<- rep(1/21, 21)
f21
f21#> [1] 0.04762 0.04762 0.04762 0.04762 0.04762 0.04762
#> [7] 0.04762 0.04762 0.04762 0.04762 0.04762 0.04762
#> [13] 0.04762 0.04762 0.04762 0.04762 0.04762 0.04762
#> [19] 0.04762 0.04762 0.04762
<- filter(y, f21, sides = 2)
y_sym lines(x, y_sym, col = "blue")
filter()
会在遭遇缺失值时留下空缺,就像上面图中显示的一样。
一种处理缺失值的方法是简单地忽略它,不把它包含在平均数的计算中。这个功能可以由下面的函数实现:
# x: the vector n: the number of samples centered: if
# FALSE, then average current sample and previous
# (n-1) samples if TRUE, then average symmetrically in
# past and future. (If n is even, use one more sample
# from future.)
<- function(x, n = 1, centered = FALSE) {
movingAverage
if (centered) {
<- floor((n - 1)/2)
before <- ceiling((n - 1)/2)
after else {
} <- n - 1
before <- 0
after
}
# Track the sum and count of number of non-NA items
<- rep(0, length(x))
s <- rep(0, length(x))
count
# Add the centered data
<- x
new # Add to count list wherever there isn't a
<- count + !is.na(new)
count # Now replace NA_s with 0_s and add to total
is.na(new)] <- 0
new[<- s + new
s
# Add the data from before
<- 1
i while (i <= before) {
# This is the vector with offset values to add
<- c(rep(NA, i), x[1:(length(x) - i)])
new
<- count + !is.na(new)
count is.na(new)] <- 0
new[<- s + new
s
<- i + 1
i
}
# Add the data from after
<- 1
i while (i <= after) {
# This is the vector with offset values to add
<- c(x[(i + 1):length(x)], rep(NA, i))
new
<- count + !is.na(new)
count is.na(new)] <- 0
new[<- s + new
s
<- i + 1
i
}
# return sum divided by count
/count
s
}
# 用比较厚的线条绘制和之前一样的图
plot(x, y, type = "l", col = grey(0.5))
grid()
<- filter(y, rep(1/20, 20), sides = 1)
y_lag lines(x, y_lag, col = "red", lwd = 4) # 用红色表示延迟平均
<- filter(y, rep(1/21, 21), sides = 2)
y_sym lines(x, y_sym, col = "blue", lwd = 4) # 用蓝色表示对称平均
# 用上面定义的函数计算延迟平均
<- movingAverage(y, 20)
y_lag_na.rm lines(x, y_lag_na.rm, col = "green", lwd = 2)
# 用上面定义的函数计算对称性平均
<- movingAverage(y, 21, TRUE)
y_sym_na.rm lines(x, y_sym_na.rm, col = "green", lwd = 2)
6.21 窗口平滑
6.21.1 问题
你想要计算序列给定窗口长度的平均数。这是平滑数据的一种方式。
6.21.2 方案
假设你有一个数值向量并且想要找出第一个连续四个数、第二个连续四个数等等如此的平均数(就像用一个窗口在序列上移动,移动一次,计算一次)。
# 生成有22个(0-99)以内的随机数向量
set.seed(123)
<- floor(runif(22) * 100)
x
x#> [1] 28 78 40 88 94 4 52 89 55 45 95 45 67 57 10 89 24
#> [18] 4 32 95 88 69
# 将向量长度取整到最接近的4的倍数
<- ceiling(length(x)/4) * 4
newlength
newlength#> [1] 24
# 给新增的数值取NA值
<- NA
x[newlength]
x#> [1] 28 78 40 88 94 4 52 89 55 45 95 45 67 57 10 89 24
#> [18] 4 32 95 88 69 NA NA
# 将它转换为4行的矩阵
<- matrix(x, nrow = 4)
x
x#> [,1] [,2] [,3] [,4] [,5] [,6]
#> [1,] 28 94 55 67 24 88
#> [2,] 78 4 45 57 4 69
#> [3,] 40 52 95 10 32 NA
#> [4,] 88 89 45 89 95 NA
# 计算每一列的均值,忽略NA值
colMeans(x, na.rm = TRUE)
#> [1] 58.50 59.75 60.00 55.75 38.75 78.50
6.22 寻找唯一值序列
6.22.1 问题
你需要找到一个向量或者因子的唯一值。
6.22.2 方案
你可以通过在向量里迭代寻找唯一值,但这样做在 R 里是很慢的。一个比较快的方案是通过 rle()
函数:
# 示例数据
<- c("A", "A", "A", "B", "B", "B", "B", NA, NA, "C", "C",
v "B", "C", "C", "C")
v#> [1] "A" "A" "A" "B" "B" "B" "B" NA NA "C" "C" "B"
#> [13] "C" "C" "C"
<- rle(v)
vr
vr#> Run Length Encoding
#> lengths: int [1:7] 3 4 1 1 2 1 3
#> values : chr [1:7] "A" "B" NA NA "C" "B" "C"
RLE 编码的数据能够通过 inverse.rle()
转换回到一个向量。
inverse.rle(vr)
#> [1] "A" "A" "A" "B" "B" "B" "B" NA NA "C" "C" "B"
#> [13] "C" "C" "C"
一个问题是每一个缺失值 NA
都会视为长度为1,即使 NA
是相邻的,不过可以通过用别的值替代 NA
来做到。对于数值变量,Inf
或者别的数字都可使用;对于字符变量,任何字符串都能使用。当然,特殊值不得以其他方式出现在向量中。
<- v
w is.na(w)] <- "ZZZ"
w[
w#> [1] "A" "A" "A" "B" "B" "B" "B" "ZZZ"
#> [9] "ZZZ" "C" "C" "B" "C" "C" "C"
<- rle(w)
wr
wr#> Run Length Encoding
#> lengths: int [1:6] 3 4 2 2 1 3
#> values : chr [1:6] "A" "B" "ZZZ" "C" "B" "C"
# 在RLE编码数据中用NA代替ZZZ
$values[wr$values == "ZZZ"] <- NA
wr
wr#> Run Length Encoding
#> lengths: int [1:6] 3 4 2 2 1 3
#> values : chr [1:6] "A" "B" NA "C" "B" "C"
<- inverse.rle(wr)
w2
w2#> [1] "A" "A" "A" "B" "B" "B" "B" NA NA "C" "C" "B"
#> [13] "C" "C" "C"
6.22.3 处理因子
虽然因子基本上只是带有一些关于级别信息的整数向量,但是 rle()
函数不能处理因子。解决方案是手动将因子转换为整数向量或字符向量。使用整数向量速度快,内存效率高,这对于大型数据集可能很重要,但是很难理解。使用字符向量比较慢,需要更多的内存,但是更容易理解。
# 假设这是我们要处理的向量
<- factor(v)
f
f#> [1] A A A B B B B <NA> <NA> C
#> [11] C B C C C
#> Levels: A B C
# 将向量级别保存到一个新的变量
# 这个不是必须的,但是保存顺序是很有用的
<- levels(f)
f_levels
f_levels#> [1] "A" "B" "C"
<- as.character(f)
fc is.na(fc)] <- "ZZZ"
fc[
fc#> [1] "A" "A" "A" "B" "B" "B" "B" "ZZZ"
#> [9] "ZZZ" "C" "C" "B" "C" "C" "C"
<- rle(fc)
fr
fr#> Run Length Encoding
#> lengths: int [1:6] 3 4 2 2 1 3
#> values : chr [1:6] "A" "B" "ZZZ" "C" "B" "C"
# 在RLE编码数据中用 NA 代替 ZZZ
$values[fr$values == "ZZZ"] <- NA
fr
fr#> Run Length Encoding
#> lengths: int [1:6] 3 4 2 2 1 3
#> values : chr [1:6] "A" "B" NA "C" "B" "C"
# 将RLE编码数据转换成因子
<- inverse.rle(fr)
f2 <- factor(f, levels = f_levels)
f2
f2#> [1] A A A B B B B <NA> <NA> C
#> [11] C B C C C
#> Levels: A B C
6.23 用最后一个非 NA 值填充 NA
6.23.1 问题
你想要将非 NA
值代替向量或者因子中的 NA
值。
6.23.2 方案
这段代码展示了如何填充向量中的空白。如果需要重复执行此操作,请参阅下面的函数。该函数还可以用第一个确定的值填充第一个即为NA
的情况,并正确处理因子。
# 示例数据
<- c(NA, NA, "A", "A", "B", "B", "B", NA, NA, "C", NA,
x NA, NA, "A", "A", "B", NA, NA)
<- !is.na(x)
goodIdx
goodIdx#> [1] FALSE FALSE TRUE TRUE TRUE TRUE TRUE FALSE
#> [9] FALSE TRUE FALSE FALSE FALSE TRUE TRUE TRUE
#> [17] FALSE FALSE
# 这些是来自x的非NA数值
# 加入领头的NA,后边会使用。用来进行索引
<- c(NA, x[goodIdx])
goodVals
goodVals#> [1] NA "A" "A" "B" "B" "B" "C" "A" "A" "B"
# 用来自输出向量的索引填充输出向量的索引
# 这些补偿了goodVals。加1是为了避免索引为0
<- cumsum(goodIdx) + 1
fillIdx
fillIdx#> [1] 1 1 2 3 4 5 6 6 6 7 7 7 7 8 9 10 10
#> [18] 10
# 原本向量的值被填充了
6.23.2.1 填充 NA 的函数
此函数执行与上面代码相同的操作。它还可以用第一个良好的值填充第一个就是NA
的情况,并正确处理因子。
<- function(x, firstBack = FALSE) {
fillNAgaps ## 向量或因子中的 NA 被之前一个非 NA 值代替 如果
## firstBack 为 TRUE,将会对领头的 NA 填充第一个非
## NA 值,否则不会
## 如果是一个因子,保存因子的水平,并转换为整数
<- NULL
lvls if (is.factor(x)) {
<- levels(x)
lvls <- as.integer(x)
x
}
<- !is.na(x)
goodIdx
# 这些是来自于x的非NA值
# 加入领头NA或者以第一个值代替,取决于firstBack参数
if (firstBack)
<- c(x[goodIdx][1], x[goodIdx]) else goodVals <- c(NA, x[goodIdx])
goodVals
# 用来自输出向量的索引填充输出向量的索引
# 这些补偿了goodVals。加1是为了避免索引为0
<- cumsum(goodIdx) + 1
fillIdx
<- goodVals[fillIdx]
x
# 如果它最初是一个因子,那么将它转换回来
if (!is.null(lvls)) {
<- factor(x, levels = seq_along(lvls), labels = lvls)
x
}
x
}
# 示例数据
<- c(NA, NA, "A", "A", "B", "B", "B", NA, NA, "C", NA,
x NA, NA, "A", "A", "B", NA, NA)
x#> [1] NA NA "A" "A" "B" "B" "B" NA NA "C" NA NA
#> [13] NA "A" "A" "B" NA NA
fillNAgaps(x)
#> [1] NA NA "A" "A" "B" "B" "B" "B" "B" "C" "C" "C"
#> [13] "C" "A" "A" "B" "B" "B"
# 对领头的 NA 以第一个非 NA 值进行填充
fillNAgaps(x, firstBack = TRUE)
#> [1] "A" "A" "A" "A" "B" "B" "B" "B" "B" "C" "C" "C"
#> [13] "C" "A" "A" "B" "B" "B"
# 因子数据也能使用
<- factor(x)
y
y#> [1] <NA> <NA> A A B B B <NA> <NA> C
#> [11] <NA> <NA> <NA> A A B <NA> <NA>
#> Levels: A B C
fillNAgaps(y)
#> [1] <NA> <NA> A A B B B B B C
#> [11] C C C A A B B B
#> Levels: A B C
6.23.2.2 注释
改编自来自于 zoo 包的 na.locf()
函数。