'R'unthrough
向量计算
在命令行中, 输出总是以一个方括号和其中的数字开头, 这个数字指本行第一个输出的数组下标; 所以, R语言数组/向量的第一项竟然是0, 这也太反人类了! 与数学里的向量和numpy数组类似, R中的向量也可以与等长/倍长向量和标量进行各种运算, 大部分内置函数也可以接收向量作为传入参数(向量化)
生成
除了 -c
以外还有很多方式可以生成向量, 如:
a:b
类似于np.arange, 生成一个下界为a, 上界为b的整数数组(两边均为闭区间)seq(a,b)
同a:b
, 不过seq还可以额外增加一个参数length,代表生成数组的元素个数, 则此时生成的数组就不一定是整数数组了, 类似于np.linspace, 也由参数by, 即类似步长- R支持复数, 而复数本身即是向量, 构造一个复数向量可使用
complex(real=a, imagine=b)
不过这里a, b可以为数组(等长), 那最后生成的也是一组虚数s, 虚数向量支持如Re
求实部,Im
求虚部,Mod
或Abs
求模长,Conj
求共轭等
修改与读取
同其他语言的数组一样, R中的数组也可以以 <数组名>[索引值]
的形式进行读取, 不过注意第一项是1不是0
rep(c(a,b),c(c,d))
可以实现复制的功能, 可将生成一个由c个a,d个b组成的新向量(第二个参数也可以只传一个值, 会以向量为整体进行复制,若想按元素复制,需用each参数)textrep(c(1,2),2) #(1,2,1,2) rep(c(1,2),each=2) #(1,1,2,2)
- numpy数组可以实现的绝大部分统计学功能R语言中的数组也均可实现
- R语言还可以对向量中的元素进行排序, 例:
text你还可以输出排序后的函数的原索引值
a <- c(3,5,1) sort(x) #[1] 1 3 5 rev(sort(x)) #[1] 5 3 1
texta <- c(3,5,1) sort(x) #[1] 1 3 5 order(x) #[1] 3 1 2
布尔型向量计算
R中布尔型(R的官方名字叫logical)包括'TRUE' 'FALSE' 和 'NA'(缺失值), 'TRUE' 和 'FALSE' 支持分别以1和0参与到算术运算中;
有NA参与的表达式均会返回NA,包括比较(NA == NA
也会返回NA),和算数运算得到的结果均为NA
除了常见的 > < >= <= ==
以外,R语言比较特有的运算符还有 %in%
其可以接受两个向量作为参数, 其可以逐个判断第一个向量中的每一个元素是否在第二个元素中出现
c(1,1,2,NA) %in% c(-1:1,NA) #[1] TRUE TRUE FALSE TRUE
与 %in%
类似的还有 match
, 不过其返回值是查找数在被查找数组中找到的的第一个数的索引值, 若不存在则返回NA
match(c(1,2),c(1,1,3)) #[1] 1 NA
R语言中同样有逻辑运算符, 如和 & 或 &&
,或| 或 ||
,异或xor,
,非!
等, R语言也有部分特性:
- 包含NA时:
- 在异或运算中, 若存在NA则结果为NA, !NA=NA
- 在或/和运算中, 若另一个运算符可以确定返回值, 则返回相应返回值, 否则返回NA,例子:
NA | TRUE 结果为TRUE
FALSE & NA 结果为FALSE
- 短路原则:
&
和|
无短路原则,&&
和||
有短路原则,例子:textFALSE & sqrt(-1) #FALSE TRUE || sqrt(-1) #ERROR
更多布尔运算
any
all
类似于|
&
的并联, 对NA的判断也相似which()
返回真值对应的索引值identical() 与 all.equal()
可以比较两个参数是否相同, 前者会考虑数据类型, 后者会返回一条信息讲述为什么不一样dupilcate()
可以判断对应元素是否首次出现, 也可以用unique()
去除重复项
字符型
字符型元素均以字符串的形式出现, 使用""和''都可以, 另外注意空字符串''不直接等同于NA, R自带一些处理字符型向量和字符串元素的函数:
paste(a,b)
可以连接两个字符串,如text可选参数:paste(c(1,2),c('a','b')) #[1] "1 a" "2 b"
sep=
可以指定分隔符, 默认为空格,collapse=
可以合并新字符串的元素textpaste(c(1,2),c('a','b') seq="") #[1] "1a" "2b" paste(c(1,2),c('a','b') collapse=" ") #[1] "1 a 2 b"
toupper(x)
将字符串转化为大写,tolower
将字符串转化为小写substr(x,a,b)
即为取子串的操作, x可以是单个字符型变量或者向量, a为子串开始字符, b为字串结束字符, b默认为结束as.numeric
将一个字符型的数值转化为对应数值gsub(org,new,text,fixed=TRUE)
是R语言可以替换字符串部分字符的函数, 其可将text中满足org的替换为new, 此处使用fixed=TRUE表示不将org解释为正则表达式textgsub('Risapieceofshit','I love R',c(a,b,Risapeiceofshit,c),fixed=TRUE) # [1] a,b,I love R,c
下标控制
注意下标从1开始!不是从0开始 若传入索引值为0则返回一个同类型的空向量, 若传入索引值则返回所有元素, 支持传入负索引值 传入超过向量长度的索引值会返回缺失值, 试图给超限区域赋值时也会自动延长向量长度并对为赋值的部分赋空值, 虽然这不会报错但也不是常规的手段😖 同样支持逻辑下标
映射
可以建立不同向量之间的映射关系, 如:
a <- c('a','b','c')
b <- c(1,3,2,1,2,3,)
a[b] # [1] 'a' 'c' 'b' 'a' 'b' 'c'
当然也可以以此借此关系来取名
a <- c(12,34,56)
b <- c("name_a","name_b","name_c)
a <- setNames(a,b)
然后就可以通过名字来查找元素了, 如a["name_b"]会得到34 若需清除名字可使用unname(a)或者 names(a) <- NULL
更多
- 查找
可使用
which()
来查找满足特定要求的元素, 后返回其索引值;特别的,which.max()
和which.min()
可以返回最大与最小值的索引值 - 集合
intersec(a,b)
求a,b的交集union(a,b)
求a,b的并集setdiff(a,b)
求a,b的差集(a中不属于b的部分)setequal(a,b)
判断两集合元素是否相等, 即不考虑顺序和重复
类属
类型与相互转换
R的默认属性包括int, double(前两者可合称为numeric), logical, character等, 可用 typeof()
查看或者使is.<type>来判断, 在没有显式说明的情况下数字通常以double的形式处理储存, 若需要存储为整数需在数字后面加'L', 注意对部分函数如 identical
等, int和double会被视为不同的数;
R中还有特殊值, 如NA, 严格来说不同的数据类型有不同的NA, 如NAinterger, NAreal(double型NA), 不过一般都视为NA, 除此之外, 还有NaN, Inf, -Inf等, 这三个都是double型特殊值, 分别为'Not a number'(在0/0等时候触发)和正负无穷, NaN同样被视为缺失值, 还有特殊类型NULL, 其只有一个取值NULL, NULL不是上述提到的任何一种类型;
在部分函数, 如运算, 生成时, 会自动将类型统一为最复杂的类型, 具体转化顺序为 logical < interger < double < character , 如 typeof(c(1,TRUE,"HELLO"))
的值为 "character"
上述这些基本类型以及这些基于其得到的如factor, table, list, vector等数据结构均为R的不同类属(其实也就类似c++里的类), 基于此设计也衍生出了如重载等功能, 使R具有部分面向对象的特性
属性
属性有点类似与类和实例中的部分变量, 部分属性有自己本身自带的一些属性, 查询属性可以用 attributes()
来查询, 如table就有一个属性"class", 其值为"table", 除了自带的属性以外, 也可以用 attr()
来手动添加属性, 如:
x <- c(1,2,3)
attributes(x)
# NULL
attr(x,"test_attr") <- "test"
attributes(x)
# $test_attr
# [1] "test"
日期
默认的R语言储存和处理时间的数据类型通常有两种: POSIXct 和 POSIXlt, POSIXct 本质上存储的是现在到1970.1.1的间隔秒数, 在输出时会被自动转化为需要的格式, 而POSIXlt通过一个列表来储存年月日时分的数值;
除了R语言自带的日期功能以外, lubridate
包也经常用于日期的处理
获取与生成
- 可使用
lubridate::today()
得到当前系统日期,lubridate::now()
得到当前系统日期和时间 - 可使用mdy("mm-dd-yy(yy)")将一个字符型的日期转化为专有的日期型变量, 注意格式, 如dmy则转化"dd-mm-yy(yy)"的字符串, 也可以在之后加上h,m,s, 则会额外存储具体的时间
- 可使用
makedate
和makedatetime
来直接通过数值生成日期, 如make_datetime(1998, 3, 16, 13, 15, 45.2)
就会得到 "1998-03-16 13:15:45 UTC"的时间变量 format()
可以将时间转化为字符串, 一般来说有一个可选参数format规定转化之后的效果, 如;textformat函数可以执行各种类型的相互转化, 这只是其中一种x <- as.POSIXct('2024-03-19') format(x,format="%Y年%m-%d") # [1] '2024年03-19'
读取与修改
- lubridate包的如下函数可以取出日期型或日期时间型数据中的组成部分:
year()
取出年,month()
取出月份数值,mday()
取出日数值,yday()
取出日期在一年中的序号,元旦为1,wday()
取出日期在一个星期内的序号, 但是一个星期从星期天开始, 星期天为1,星期一为2,星期六为7;hour()
取出小时,minute()
取出分钟,second()
取出秒, 同样也可以通过这些函数进行赋值, 这样也会改变原有时间 - lubridate同样提供了日期的取整, 也包括floor, ceiling, round三种模式, 如:
text
x <- ymd_hms("2024-03-19 22:19:43") floor_date(x, unit="10 minutes") # [1] "2024-03-19 22:20:00 UTC"
Factor
生成
Factor数据类型通常用来统计一个向量中不同元素的出现次数, 特有一level属性:
x <- factor(c('a','b','c','a','f'))
x
# [1] a b c a f
# Levels: a b c f
levels(x) # [1] "a" "b" "c" "f"
除了上面这种最基础的生成方式, 还可以将连续取值的变量通过 cut()
来生成factor
cut(1:10,breaks=(0,1,5,10))
cut(1:10,breaks=4)
若breaks传入的为单个数n,则表明将等间距分为n组
生成了一个levels为(0,1], (1,5], (5,10], 元素为1~10的factor,注意breaks的取值不建议小于原始生成的向量的取值
若想实现各组所含元素量的平均, 可以使用 quantile()
函数
统计
table()
函数可用于统计每组所有元素的个数texttable(x) # a b c f # 2 1 1 1
tapply()
用于在已有factor的情况下继续根据factor的分类方式来统计和记录新的数据, 例如, 已有一学生factor, 分别记录了各自的生理性别'F'或'M', 然后我们新建了一个向量h来记录各自的身高, 那么我们可以用tapply来计算'F'组和'M'组的对应的身高数据textstu=factor(c('F','M','F','F')) h <- c(167.3,176.3,159.7,165.3) tapply(h,stu,mean) #将h一一对应分为'F'组和'M'组计算各自的平均身高
forcats
除了自带的处理factor的函数以外, forcats包也包含了大量的处理factor的函数
fct_relevel(factor,level)
可将factor中的一个level提至最前, 也可以通过添加额外变量来进行其他移动- 在之前已经使用过了一次tapply之后, 一个向量就会和一个factor绑定在一起, 此时如果想更深一步, 可以用
fct_reorder(x,factor)
使factor中的level按x的数据来排序 - 可以通过
fct_recode(factor,level_name=new_level_name)
来对某个level重命名, 可同时对多个level重命名, 也可以通过这种方式将多个旧水平合并
列表
相较于只能存储单种数据类型的向量, 列表最大的优势则是可以存储不同的数据类型, 与py中的dict有点相似
list()
即可创建一个列表, 如:textx <- list("Name"="Bob", "Age"=18, "GPA"=3.3)
- list既可以像py中的dict一样用元素名访问, 也有索引值:
text
x[[1]] # "Bob" x$age # 18
- 可以通过c()合并两个list
dataframe
大部分数据都是以表格的形式存储的, 这些外部表格通常就是在读入R语言时被转化为了dataframe或者其改良版本tibble, dataframe的本质是list的衍生物
- 创建: 一般可以按列创建
text此处可以理解为先创建了一个三个元素的list,然后每个list的元素是一个向量, 因此我们也可以知道, 在dataframe里面同列的数据类型是相等的
df <- data.frame( Name = c("Alice", "Bob", "Charlie"), Age = c(23, 25, 27), Score = c(85, 92, 88) )
- 访问: 可以通过[]或者$访问列或者单个数据, 如:
text
df$Age # 访问df中的"Age"一行 df[2,"Name"] # 访问第二行的name列
- 修改: 修改最基础的就是通过重新赋值的操作, 这样既可以单独修改某个数据也可以修改整行整列, 同样也可以通过赋NULL值来删除行/列/某处的值
若需新增行, 可以采用
df[n+1,] <- ...
的方式在最后一行的下一行添加元素; 若需新增列, 也可以直接赋值一个新列df$new_col <- ...
- 筛选: df[]同样可以传入条件语句,如可以传入
df.[df$Age>=25,]
将返回所有年龄值>=25的行
矩阵
- 生成: 矩阵通常由
matrix(arr,nrow,ncol,byrow=TRUE/FALSE)
生成, 即nrow和ncol规定矩阵的规模, arr为一个数字向量, 将向量的内容依次(默认byrow为false即按列填入, 当然也可以按行填入), 矩阵拥有一个dim属性, 反映其规模 - 读取: 同样可以通过[]的方式来读取矩阵的某行, 某列, 某个元素或者某个子矩阵, 具体规则与其他数据类型相似, 也支持逻辑下表
- 组合: 可以使用cbind将多个向量/矩阵视作大矩阵的不同列, 然后将其组合形成一个大矩阵, 或者使用rbind按行拼成大矩阵
- 运算: 矩阵支持与标量运算, +-*均适用, 但是注意此处若矩阵*矩阵不是线性代数上的矩阵相乘, 而是单纯的每一个元素与对应元素相乘, 矩阵乘法可用
%*%
对一维向量, 有内积为sum(x*y)
和外纪%o%
, 除此之外, 还有一个特殊衍生函数outer(x,y,f), 函数接收两个向量,并对两个向量假设为 和 则最后生成一m*n矩阵, 矩阵的第[i][j]项即为f(x[i],y[j])