R语言入门学习笔记(三)
文章目录
前言
目前为止,我们学习了R对象,这可以用于暂存所需要处理的数据。但是,在数据分析的过程中,我们可能随时需要调取这些R对象中的部分数据进行分析,我们要如何对这些数据进行选择和处理呢?这次的学习将会学习到R中取值以及修改数值的方法,这样我们就可以对R中的数据进行提取和修改了。
一、值的选取
R中有一套索引体系,可以用于提取R对象中的值。如下所示
data[x, y]
可以从名为data的数据框中提取第x行,第y列的数值。对于R来说,在编写索引的时候,有6种方式可供选择:正整数、负整数、零、空格、逻辑值和名称。
1.正整数
正整数索引方式如同上面提到的,xy均为正整数的情况下,索引的为x行y列的数值。需要注意的是R的索引是从1开始的(与python从0开始不同)。这种索引可以同时对多行多列进行索引:
df <- data.frame(A = c(1, 1, 1), B = c(2, 2, 2), C = c(3, 3, 3))
df
#> A B C
#> 1 1 2 3
#> 2 1 2 3
#> 3 1 2 3
df[1, 2]
#> [1] 2
df[1, 2, drop = FALSE]
#> B
#> 1 2
df[c(1:3), 1]
#> [1] 1 1 1
df[1, c(1:3)]
#> A B C
#> 1 1 2 3
df[c(1, 1), c(1:3)]
#> A B C
#> 1 1 2 3
#> 1.1 1 2 3
这里注意,在只索引1列的时候会返回一个向量,在索引2列及以上的时候会返回数据框,如果想要都返回数据框,可以在索引的时候添加参数drop = FALSE
。另外,如果在索引种重复某个数值,R就会在相应位置重复提取相同的数据,然后在返回值中重复出现。
这种索引方法不只是适用于数据框,可以适用于任何R对象,只要该对象在相应维度有索引值即可。
2.负整数
负整数的索引值是不包含的意思。R对象将不返回负整数索引所对应的元素,可以将负号看作减的意思。
df[-1, -1]
#> B C
#> 2 2 3
#> 3 2 3
不能在同一个索引位置同时出现正负索引,但不同索引位置是可以的。
3.零
零索引会返回空对象,实际并没有什么意义。
df[0, 1]
#> numeric(0)
df[0, 0]
#> data frame with 0 columns and 0 rows
4.空格
如果在某个索引位置不键入任何数字,或者键入一个空格,则会索引该位置对应的所有数据。
5.逻辑值
逻辑值为TRUE
和FALSE
,将会索引所有TRUE
元素对应的位置的值。索引数量与对应位置元素的数量相同时,会按照逻辑值对应进行索引。索引数量小于对应位置元素的数量时,会自动重复索引对该位置元素进行索引。索引数量大于对应位置元素的数量应该避免。
# 索引长度少于被索引元素长度
df[1, T]
#> A B C
#> 1 1 2 3
df[1, c(T,F)]
#> A C
#> 1 1 3
vec <- c(1, 2, 3, 4, 5, 6)
vec[c(T,F)]
#> [1] 1 3 5
# 索引长度超出被索引元素长度
vec[c(T,F,F,T,T,F,T,T,F)]
#> [1] 1 4 5 NA NA
df[1, c(T,F,T,T,T)]
#> Error in `[.data.frame`(df, 1, c(T, F, T, T, T)) : 选择了未定义的列
6.名称
如果R对象有名称属性(关于名称属性,请移步至R语言入门学习笔记(二)),就可以根据对应位置的名称进行索引。
df[1, "A"]
#> [1] 1
df[, c("A", "C")]
#> A C
#> 1 1 3
#> 2 1 3
#> 3 1 3
7.$与[[ ]]
R中还有另外的索引体系:$
与[[ ]]
。$用于提取数据框或列表中对象中的值。
数据框中可以直接用$索引某列的值:
df$A
#> [1] 1 1 1
它会返回一个向量,向量便于直接进行计算。
mean(df$A)
#> [1] 1
在对列表索引的方法类似:
lst <- list(num = c(1:20), word = "R语言真好玩", log = c(TRUE, FALSE))
lst$num
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
在介绍[[ ]]
的索引方式,首先看看[]
索引:
lst[2]
#> $word
#> [1] "R语言真好玩"
typeof(lst[2])
#> [1] "list"
此时经过索引得到的是一个只包含了一个字符串的list。如果想要直接索引到内部的字符串就要用到双中括号了:
lst[[2]]
#> [1] "R语言真好玩"
typeof(lst[[2]])
#> [1] "character"
lst$word
#> [1] "R语言真好玩"
双中括号在列表没有名字属性的时候可以代替$符号使用。
引用书中的一幅图来梳理一下中括号间的索引关系:
二、数值更改
在进行数值更改前,最好留好数据副本,以免更改的面目全非后不能恢复。
1.直接改值
可以使用R的索引体系直接在数据上进行单独或批量的改值。
vec[1] <- 100
vec
#> [1] 100 2 3 4 5 6
vec[c(1, 3, 5)] <- c(0, 0, 0)
vec
#> [1] 0 2 0 4 0 6
vec[c(1, 3, 5)] <- vec[c(1, 3, 5)] + 1
vec
#> [1] 1 2 1 4 1 6
另外,还可以创建原来不存在的新值:
vec[7] <- 100
vec
#> [1] 1 2 1 4 1 6 100
vec <- vec[-7]
vec
#> [1] 1 2 1 4 1 6
如果想要数据框中删除某些列,可以将其赋值为NULL
:
df[,"C"] <- NULL
# df$C <- NULL也可以
df
#> A B
#> 1 1 2
#> 2 1 2
#> 3 1 2
2.逻辑值取子集
在R的索引中可以利用逻辑值进行对应的索引,从而筛选出想要的数据。但是要打出那么多的TRUE
和FALSE
也不现实。幸运的是,不需要这么做也没有意义,可以使用逻辑测试来生成逻辑值。
在R中有如下逻辑测试方法:
运算符 | 语法 | 解释 |
---|---|---|
> | a > b | a是否大于b? |
< | a < b | a是否小于b? |
>= | a >= b | a是否大于等于b? |
<= | a <= b | a是否小于等于b? |
== | a == b | a是否等于b? |
!= | a != b | a是否不等于b? |
%in% | a %in% c(a, b, c) | a是否含于c(a, b, c)? |
除了%in%
外,其他运算符都是进行左右的一一对比:
1 > c(0, 1, 2)
#> [1] TRUE FALSE FALSE
c(1, 2, 3) == c(3, 2, 1)
#> [1] FALSE TRUE FALSE
这些运算符都与其他算术运算符遵循的规则一致,即如果运算符两个向量的长度不等,R会重读较短的向量以达到较长的向量(见R语言入门学习笔记(一)):
1 > c(0, 1, 2)
#> [1] TRUE FALSE FALSE
# 等价于c(1, 1, 1) > c(0, 1, 2)
c(1, 2, 3, 4) == c(1, 2)
#> [1] TRUE TRUE FALSE FALSE
# 等价于c(1, 2, 3, 4) == c(1, 2, 1, 2)
而%in%
运算符则是将左侧每个元素单独与右侧整体进行对比得到判断,得到逻辑值个数与左侧元素个数相同:
c(1, 5) %in% c(0, 1, 2)
#> [1] TRUE FALSE
注意不要将 =
与 ==
弄混,=
是赋值符号相当于 <-
,逻辑判断需要用 ==
。
逻辑值取值法的应用十分广泛,比如:
df <- data.frame(A = c(1, 3, 5), B = c(2, 4, 6), C = c(1, 1, 3))
df$C == 1
#> [1] TRUE TRUE FALSE
可以通过sum()
函数统计其中TRUE
逻辑值的个数,因为此时计算时会触发R的强制转换,将逻辑值转换为数字,TRUE
会被转换为1。这对快速了解数据特征有帮助。
sum(df$C == 1)
#> [1] 2
在逻辑值取值后就可以将其作为一个索引来选取一些数据,然后通过赋值,对选到的数据进行更改:
# 选择C列中值为1的数据对应的B列的值(逻辑值取值)
df$B[df$C == 1]
#> [1] 2 4
# 看一眼原始df数据框理解一下
df
#> A B C
#> 1 1 2 1
#> 2 3 4 1
#> 3 5 6 3
# 通过赋值修改B列的值
df$B[df$C == 1] <- 10
df
#> A B C
#> 1 1 10 1
#> 2 3 10 1
#> 3 5 6 3
3.布尔运算符
如果需要将逻辑运算符组合起来就要用到布尔运算符了,这是一组关于与和或(等等)的运算符。它们将最终输出一个逻辑值。
R中的布尔运算符如下:
运算符 | 语法 | 解释 |
---|---|---|
& | event1 & event2 | event1和event2是否同时为真? |
| | event1 | event2 | event1和event2是否至少一个为真? |
xor | xor(event1, event2) | event1和event2是否只有一个为真? |
! | !event1 | event1是否为假?(event1的反结果) |
any | any(event1, event2, event3) | 所有事件中是否至少有一个为真? |
all | all(event1, event2, event3) | 所有时间是否同时为真? |
遇到布尔逻辑符时,R会先计算所有事件的逻辑值,然后根据布尔运算逻辑组合在进行逻辑值计算。
三、缺失信息
在操作R数据中值的时候,有时会遇到缺失值。这些值可能是由于丢失或者并未测量等等原因而不存在了。
在R中会使用NA
来存储缺失值,R中处理缺失值的方法与我们是不一样的。比如1+NA
在R中会返回NA
作为结果,因为缺失值不能用0简单替代。在进行逻辑测试的时候NA == 1
同样会返回NA
。
1.na.rm
缺失值的存在会比较棘手,因为在进行统计分析时,由于NA值得存在会使得结果也是NA。但这可能并不是我们期待得结果。大多数R函数都有一个可选参数na.rm
它表示移除NA值,可以通过添加na.rm = TRUE
让R在计算时忽略NA。
2.is.na
有些时候,可能想要通过逻辑测试来定位NA。但是NA == NA
的结果也是NA。
c(1, 2, 3, NA) == NA
#> [1] NA NA NA NA
因此R提供了is.na
函数来测试某个值是否为NA。
is.na(NA)
#> TRUE
is.na(c(1, 2, 3, NA))
#> [1] FALSE FALSE FALSE TRUE
总结
通过本节学习,我们学会了在R中定向的索引数据。可以使用多种方式对R对象中元素进行索引。进行索引后,我们就可以使用赋值函数对R对象中的数据进行修改,注意有需要时在修改前保存数据副本。为了更好的对数据进行定向修改,学习了逻辑值取子集的方法。另外,在处理数据的过程中,缺失值会是我们的一个困扰,所以在R中也提供了一些特定的函数来处理这些值。