R语言(常用函数与数据管理)

原文链接:https://wklchris.github.io/R-manage-data.html

本节内容可应用在数据读取之后。包括基本的运算(包括统计函数)、数据重整(排序、合并、子集、随机抽样、整合、重塑等)、字符串处理、异常值(NA/Inf/NaN)处理等内容。也包括 apply() 这种函数式编程函数的使用。

数学函数

数学运算符和一些统计学上需要的函数。

数学运算符

四则幂运算求余整除
+, -, *, /^ 或 **%%%/%

例子:

a <- 2 ^ 3b <- 5 %% 2c <- 5 %/% 2print(c(a, b, c))
[1] 8 1 2

基本数学函数

  • 绝对值:abs()

  • 平方根:sqrt()

  • 三角函数:sin(), cos(), tan(), acos(), asin(), atan()

  • 对数:

    • log(x, base=n) 以 n 为底 x 的对数

    • log10(x) 以 10 为底的对数

  • 指数:exp()

  • 取整:

    • 向上取整 ceiling()

    • 向下取整 floor()

    • 舍尾取整(绝对值减小) trunc()

    • 四舍五入到第 N 位 round(x, digits=N)

    • 四舍五入为有效数字共 N 位 singif(x, digits=N)

统计、概率与随机数

描述性统计等更多的统计内容,参考 “描述性统计”一文。

统计函数

常用的统计函数:

  • 均值:mean()

  • 中位数:median()

  • 标准差:sd()

  • 方差:var()

  • 绝对中位差:mad(x, center=median(x), constant=1.4826, …),计算式:

mad(x)=constantMedian(|xcenter|)mad(x)=constant∗Median(|x−center|)

  • 分位数:quantile(x, probs),例如 quantile(x, c(.3, 84%)) 返回 x 的 30% 和 84% 分位数。

  • 极值:min() & max()

  • 值域与极差:range(x),例如 range(c(1, 2, 3)) 结果为 c(1, 3)。极差用 diff(range(x))

  • 差分:diff(x, lag=1)。可以用 lag 指定滞后项的个数,默认 1

  • 标准化:scale(x, center=TRUE, scale=TRUE)。可以使用 scale(x) * SD + C 来获得标准差为 SD、均值为 C 的标准化结果。

概率函数

常用的概率分布函数:

  • 正态分布:norm

  • 泊松分布:pois

  • 均匀分布:unif

  • Beta 分布:beta

  • 二项分布:binom

  • 柯西分布:cauchy

  • 卡方分布:chisq

  • 指数分布:exp

  • F 分布:f

  • t 分布:t

  • Gamma 分布:gamma

  • 几何分布:geom

  • 超几何分布:hyper

  • 对数正态分布:lnorm

  • Logistic 分布:logis

  • 多项分布:multinom

  • 负二项分布:nbinom

以上各概率函数的缩写记为 abbr, 那么对应的概率函数有:

  1. 密度函数: d{abbr}(),例如对于正态就是 dnorm()

  2. 分布函数:p{abbr}()

  3. 分位数函数:q{abbr}()

  4. 生成随机数:r{abbr}(),例如常用的 runif() 生成均匀分布

例子

通过 runif() 产生 [0,1][0,1] 上的服从均匀分布的伪随机数列。通过 set.seed() 可以指定随机数种子,使得代码可以重现。不过作用域只有跟随其后的那个随机数函数。

set.seed(123)print(runif(3))
[1] 0.2875775 0.7883051 0.4089769
# 位于 1.96 左侧的标准正态分布曲线下方的面积pnorm(1.96)

0.97500210485178

# 均值为500,标准差为100 的正态分布的0.9 分位点qnorm(.9, mean=500, sd=100)

628.15515655446

# 生成 3 个均值为50,标准差为10 的正态随机数set.seed(123)print(rnorm(3, mean=50, sd=10))
[1] 44.39524 47.69823 65.58708

数据框操作

数据框是最常使用的数据类型。下面给出数据框使用中一些实用的场景,以及解决方案。

行、列操作

新建

创建一个新的列(变量)是很常见的操作。比如我们现在有数据框 df ,想要在右侧新建一个列,使其等于左侧两列的和。

df = data.frame(x1=c(1, 3, 5), x2=c(2, 4, 6))# 直接用美元符声明一个新列df$sumx <- df$x1 + df$x2df
x1x2sumx
123
347
5611
# 或者使用 transform 函数df <- transform(df, sumx2=x1+x2)df
x1x2sumxsumx2
1233
3477
561111
重命名
colnames(df)[4] <- "SUM"print(colnames(df))
[1] "x1"   "x2"   "sumx" "SUM" 
选取/剔除: subset()
# 选取前两列df[,1:2]  # 或者 df[c("x1", "x2")]
x1x2
12
34
56
# 剔除列 sumxdf <- df[!names(df) == "sumx"]df
x1x2SUM
123
347
5611
# 剔除第三列df <- df[-c(3)]  # 或者 df[c(-3)]df
x1x2
12
34
56

至于选取行,与列的操作方式是类似的:

# 选取 x1>2 且 x2为偶数的观测(行)df[df$x1 > 2 & df$x2 %% 2 ==0,]
#x1x2
234
356

再介绍一个 subset() 指令,非常简单粗暴。先来一个复杂点的数据集:

DF <- data.frame(age    = c(22, 37, 28, 33, 43),
                 gender = c(1, 2, 1, 2, 1),
                 q1     = c(1, 5, 3, 3, 2),
                 q2     = c(4, 4, 5, 3, 1),
                 q3     = c(3, 2, 4, 3, 1))DF$gender <- factor(DF$gender, labels=c("Male", "Female"))DF
agegenderq1q2q3
22Male143
37Female542
28Male354
33Female333
43Male211
# 选中年龄介于 25 与 40 之间的观测# 并只保留变量 age 到 q2subset(DF, age > 25 & age < 40, select=age:q2)
#agegenderq1q2
237Female54
328Male35
433Female33
横向合并

如果你有两个行数相同的数据框,你可以使用 merge() 将其进行内联合并(inner join),他们将通过一个或多个共有的变量进行合并。

df1 <- data.frame(ID=c(1, 2, 3), Sym=c("A", "B", "C"), Oprtr=c("x", "y", "z"))df2 <- data.frame(ID=c(1, 3, 2), Oprtr=c("x", "y", "z"))# 按 ID 列合并merge(df1, df2, by="ID")
IDSymOprtr.xOprtr.y
1Axx
2Byz
3Czy
# 由于 ID 与 Oprtr 一致的只有一行,因此其余的都舍弃merge(df1, df2, by=c("ID", "Oprtr"))
IDOprtrSym
1xA

或者直接用 cbind() 函数组合。

# 直接组合。注意:列名相同的话,在按列名调用时右侧的会被忽略cbind(df1, df2)
IDSymOprtrIDOprtr
1Ax1x
2By3y
3Cz2z
纵向合并

相当于追加观测。两个数据框必须有相同的变量,尽管顺序可以不同。如果两个数据框变量不同请:

  • 删除多余变量;

  • 在缺少变量的数据框中,追加同名变量并将其设为缺失值 NA。

df1 <- data.frame(ID=c(1, 2, 3), Sym=c("A", "B", "C"), Oprtr=c("x", "y", "z"))df2 <- data.frame(ID=c(1, 3, 2), Oprtr=c("x", "y", "z"))df2$Sym <- NArbind(df1, df2)
IDSymOprtr
1Ax
2By
3Cz
1NAx
3NAy
2NAz

逻辑型筛选

通过逻辑判断来过滤数据,或者选取数据子集,或者将子集作统一更改。在前面的一些例子中已经使用到了。

df$x3 <- c(7, 8, 9)# 把列 x3 中的奇数换成 NAdf$x3[df$x3 %% 2 == 1] <- NAdf
x1x2x3
12NA
348
56NA
df$y <- c(7, 12, 27)# 把所有小于 3 的标记为 NaN# 把所有大于 10 的数按奇偶标记为正负Infdf[df < 3] <- NaNdf[df > 10 & df %% 2 == 1] <- Infdf[df > 10 & df %% 2 == 0] <- -Infdf
x1x2x3y
NaNNaNNA7
348-Inf
56NAInf

排序

排序使用 order() 命令。

df <- data.frame(age   =c(22, 37, 28, 33, 43),
                 gender=c(1, 2, 1, 2, 1))df$gender <- factor(df$gender, labels=c("Male", "Female"))# 按gender升序排序,各gender内按age降序排序df[order(df$gender, -df$age),]
#agegender
543Male
328Male
122Male
237Female
433Female

随机抽样

从已有的数据集中随机抽选样本是常见的做法。例如,其中一份用于构建预测模型,另一份用于验证模型。

# 无放回地从 df 的所有观测中,抽取一个大小为 3 的样本
df[sample(1:nrow(df), 3, replace=F)]

随机抽样的 R 包有 sampling 与 survey,如果可能我会在本系列下另建文章介绍。

SQL语句

在 R 中,借助 sqldf 包可以直接用 SQL 语句操作数据框(data.frame)。一个来自书中的例子:

newdf <- sqldf("select * from mtcars where carb=1 order by mpg", row.names=TRUE)

这里就不过多涉及了。

字符串处理

R 中的字符串处理函数有以下几种:

通用函数

函数含义
nchar(x)计算字符串的长度
substr(x, start, stop)提取子字符串
grep(pattern, x, ignore.case=FALSE, fixed=FALSE)正则搜索,返回为匹配的下标。如果 fixed=T,则按字符串而不是正则搜索。
grepl()类似 grep(),只不过返回值是逻辑值向量。
sub(pattern, replacement, x, ignore.base=FALSE, fixed=FALSE)在 x 中搜索正则式,并以 replacement 将其替换。如果 fixed=T,则按字符串而不是正则搜索
strsplit(x, split, fixed=FALSE)在 split 处分割字符向量 x 中的元素,返回一个列表。
paste(x1, x2, …, sep=””)连接字符串,连接符为 sep。也可以连接重复字串:paste("x", 1:3, sep="")
toupper(x)转换字符串为全大写
tolower(x)转换字符串为全小写

一些例子。首先是正则表达式的使用:

streg <- c("abc", "abcc", "abccc", "abc5")re1 <- grep("abc*", streg)re2 <- grep("abc\\d", streg)  # 注意反斜杠要双写来在 R 中转义re3 <- sub("[a-z]*", "Hey", streg)re4 <- sub("[a-z]*\\d", "NEW", streg)print(list(re1, re2, re3, re4))
[[1]]
[1] 1 2 3 4

[[2]]
[1] 4

[[3]]
[1] "Hey"  "Hey"  "Hey"  "Hey5"

[[4]]
[1] "abc"   "abcc"  "abccc" "NEW"  

然后是字符串分割与连接。注意这里的 paste() 有非常巧妙的用法:

splt <- strsplit(streg, "c")  # 结果中不含分隔符 "c"cat1 <- paste("a", "b", "c", sep="-")cat2 <- paste("x", 1:3, sep="")  # 生成列名时非常有用print(list(splt, cat1, cat2))
[[1]]
[[1]][[1]]
[1] "ab"

[[1]][[2]]
[1] "ab" ""  

[[1]][[3]]
[1] "ab" ""   ""  

[[1]][[4]]
[1] "ab" "5" 


[[2]]
[1] "a-b-c"

[[3]]
[1] "x1" "x2" "x3"

日期型字符串

与其他类型相似,日期型字符串能够通过 as.Date() 函数处理。各格式字符的含义如下:

符号含义通用示例中文示例
%d日(1~31)2222
%a缩写星期Mon周一
%A全写星期Monday星期一
%m月(1~12)1010
%b缩写月Jan1月
%B全写月January一月
%y两位年1717
%Y四位年20172017
# 对字符串数据 x,用法:as.Date(x, format=, ...)dates <- as.Date("01-28-2017", format="%m-%d-%Y")print(dates)
[1] "2017-01-28"

要想获得当前的日期或时间,有两种格式可以参考,并可以用 format() 函数辅助输出。

# Sys.Date() 返回一个精确到日的标准日期格式dates1 <- Sys.Date()format(dates1, format="%A")  # 可以指定输出格式

‘星期六’

# date() 返回一个精确到秒的详细的字串dates2 <- date()dates2

‘Sat Apr 22 15:30:54 2017’

函数 difftime() 提供了计算时间差的方式。其中计量单位可以是以下之一:”auto”, “secs”, “mins”, “hours”, “days”, “weeks”。

截至本文最后更新,我有 1100+ 周大。唔……这好像听起来没什么感觉

dates1 <- as.Date("1994-11-23")dates2 <- Sys.Date()difftime(dates2, dates1, units="weeks")
Time difference of 1169.429 weeks

异常值处理

异常值包括三类:

  • NA:缺失值。

  • Inf:正无穷。用 -Inf 表示负无穷。无穷与数可以比较大小,比如 -Inf < 3 为真。

  • NaN:非可能值。比如 0/0。

使用 is.na() 函数判断数据集中是否存在 NA 或者 NaN,并返回矩阵。注意 NaN 会被判断为缺失值。

is.na(df)
agegender
FALSEFALSE
FALSEFALSE
FALSEFALSE
FALSEFALSE
FALSEFALSE

另外也有类似的函数来判断 Inf 与 NaN,但只能对一维数据集使用:

print(c(is.infinite(c(Inf, -Inf)), is.nan(NA)))
[1]  TRUE  TRUE FALSE

在进行数据处理之前,处理 NA 缺失值是必须的步骤。如果某些数值过于离群,你也可能需要将其标记为 NA 。行移除是最简单粗暴的处理方法。

# NA 行移除df <- na.omit(df)df
agegender
22Male
37Female
28Male
33Female
43Male

整合与重构

转置

常见的转置方法是 t() 函数:

df = matrix(1:6, nrow=2, ncol=3)t(df)
12
34
56

整合:aggregate()

这个函数是非常强大的。语法:

aggregate(x, by=list(), FUN)

其中 x 是待整合的数据对象,by 是分类依据的列,FUN 是待应用的标量函数。

# 这个例子改编自 R 的官方帮助 aggregate()df <- data.frame(v1 = c(1,3,5,7,8,3,5,NA,4,6,7,9),
                     v2 = c(11,33,55,77,88,33,55,NA,44,55,77,99) )by1 <- c("red", "blue", 1, 2, NA, "big", 1, 2, "red", 1, NA, 12)by2 <- c("wet", "dry", 99, 95, NA, "damp", 95, 99, "red", 99, NA, NA)# 按照 by1 & by2 整合原数据 testDF# 注意(by1, by2)=(1, 99) 对应 (v1, v2)=(5, 55) 与 (6,55) 两条数据# 因此第三行的 v1 = mean(c(5, 6)) = 5.5aggregate(x = df, by = list(b1=by1, b2=by2), FUN = "mean")
b1b2v1v2
1955.055
2957.077
1995.555
299NANA
bigdamp3.033
bluedry3.033
redred4.044
redwet1.011
# 用公式筛选原数据的列,仅整合这些列# 注意:v1中的一个含 NA 的观测被移除aggregate(cbind(df$v1) ~ by1+by2, FUN = "mean")
by1by2V1
1955.0
2957.0
1995.5
bigdamp3.0
bluedry3.0
redred4.0
redwet1.0

还有一个强大的整合包 reshape2,这里就不多介绍了。

函数式编程:apply 函数族

函数式编程是每个科学计算语言中的重要内容;操作实现的优先级依次是矢量运算(例如 df+1)、函数式书写,最后才是循环语句。在 R 中,函数式编程主要是由 apply 函数族承担。R 中的 apply 函数族包括:

  • apply():指定轴向。传入 data.frame,返回 vector.

  • tapply():

  • vapply():

  • lapply():

  • sapply():

  • mapply():

  • rapply():

  • eapply():

下面依次介绍。

apply():指定多维对象的轴

在 R 中,通过 apply() 可以将函数运用于多维对象。基本语法是:

apply(d, N, FUN, ...)

其中,N 用于指定将函数 FUN 应用于数据 d 的第几维(1为行,2为列)。省略号中可以传入 function 的参数。

df <- data.frame(x=c(1, 2, 3), y=c(5, 4, 2), z=c(8, 6, 9), s=c(3, 7, 4))df
xyzs
1583
2467
3294
# 计算 df 各列的中位数colmean <- apply(df, 2, median)# 计算 df 各行的 25 分位数rowquan <- apply(df, 1, quantile, probs=.25)print(list(colmean, rowquan))
[[1]]
x y z s 
2 4 8 4 

[[2]]
[1] 2.50 3.50 2.75

lapply():列表式应用

lapply 函数的本意是对 list 对象进行操作。返回值是 list 类型。

lst <- list(a=c(0,1), b=c(1,2), c=c(3,4))lapply(lst, function(x) {sum(x^2)})
  • $a

  • 1

  • $b

  • 5

  • $c

  • 25

但同样可以作用于 DataFrame 对象的各个列(因为 DataFrame 对象是类似于各列组成的 list):

lapply(df, sum)
  • $x

  • 6

  • $y

  • 11

  • $z

  • 23

  • $s

  • 14

sapply()/vapply():变种 lapply()

sapply() 实质上是一种异化的 lapply(),返回值可以转变为 vector 而不是 list 类型。

class(sapply(lst, function(x) {sum(x^2)}))class(lapply(lst, function(x) {sum(x^2)}))

‘numeric’

‘list’

print(sapply(df, sum))
 x  y  z  s 
 6 11 23 14 

参数 simplify=TRUE 是默认值,表示返回 vector 而不是 list。如果改为 FALSE,就退化为 lapply() 函数。

sapply(df, sum, simplify=FALSE)
  • $x

  • 6

  • $y

  • 11

  • $z

  • 23

  • $s

  • 14

vapply() 函数可以通过 FUN.VALUE 参数传入行名称,但这一步往往可以借助 lapply()/sapply() 加上外部的 row.names() 函数完成。

mapply():多输入值的应用

mapply() 函数支持多个输入值:

mapply(FUN, [input1, input2, ...], MoreArgs=NULL)

其中各 input 的长度应该相等或互为整数倍数。该函数的用处在于避免了事先将数据合并。

print(mapply(min, seq(0, 2, by=0.5), -2:7))
 [1] -2.0 -1.0  0.0  1.0  2.0  0.0  0.5  1.0  1.5  2.0

tapply():分组应用

tapply() 函数可以借助 factor 的各水平进行分组,然后进行计算。类似于 group by 操作:

tapply(X, idx, FUN)

其中 X 是数据,idx 是分组依据。

df <- data.frame(x=1:6, groups=rep(c("a", "b"), 3))print(tapply(df$x, df$groups, cumsum))
$a
[1] 1 4 9

$b
[1]  2  6 12

其他的 apply() 函数很少用到,在此就不介绍了。

其他实用函数

在本系列的 “数据读写操作”一文 中,也介绍了一些实用的函数,可以参考。

此外还有:

函数含义
seq(from=N, to=N, by=N, [length.out=N, along.with=obj])生成数列。参数分别是起、止、步长、数列长、指定数列长度与某对象等长。
rep(x, N)重复组合。比如 rep(1:2, 2) 会生成一个向量 c(1, 2, 1, 2)
cut(x, N, [ordered_result=F])分割为因子。 将连续变量 x 分割为有 N 个水平的因子,可以指定是否有序。
pretty(x, N)美观分割。将连续变量 x 分割为 N 个区间(N+1 个端点),并使端点为取整值。 绘图中使用。
cat(obj1, obj2, …, [file=, append=])连接多个对象,并输出到屏幕或文件。

猜你可能喜欢

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值