1. 数据汇总
- summary
对一个数据框 d,用 summary(d) 可以获得每个连续型变量的基本统计量,和每个离散取值变量的频率。以及分类变量的各种类型的统计结果。如:
代码语言:javascript复制> summary(CO2)
Plant Type Treatment conc
Qn1 : 7 Quebec :42 nonchilled:42 Min. : 95
Qn2 : 7 Mississippi:42 chilled :42 1st Qu.: 175
Qn3 : 7 Median : 350
Qc1 : 7 Mean : 435
Qc3 : 7 3rd Qu.: 675
Qc2 : 7 Max. :1000
(Other):42
uptake
Min. : 7.70
1st Qu.:17.90
Median :28.30
Mean :27.21
3rd Qu.:37.12
Max. :45.50
- str
对数据框 d,用 str(d) 可以获得各个变量的类型和取值样例。如:
代码语言:javascript复制> str(CO2)
Classes ‘nfnGroupedData’, ‘nfGroupedData’, ‘groupedData’ and 'data.frame': 84 obs. of 5 variables:
$ Plant : Ord.factor w/ 12 levels "Qn1"<"Qn2"<"Qn3"<..: 1 1 1 1 1 1 1 2 2 2 ...
$ Type : Factor w/ 2 levels "Quebec","Mississippi": 1 1 1 1 1 1 1 1 1 1 ...
$ Treatment: Factor w/ 2 levels "nonchilled","chilled": 1 1 1 1 1 1 1 1 1 1 ...
$ conc : num 95 175 250 350 500 675 1000 95 175 250 ...
$ uptake : num 16 30.4 34.8 37.2 35.3 39.2 39.7 13.6 27.3 37.1 ...
- attr(*, "formula")=Class 'formula' language uptake ~ conc | Plant
.. ..- attr(*, ".Environment")=<environment: R_EmptyEnv>
- attr(*, "outer")=Class 'formula' language ~Treatment * Type
.. ..- attr(*, ".Environment")=<environment: R_EmptyEnv>
- attr(*, "labels")=List of 2
..$ x: chr "Ambient carbon dioxide concentration"
..$ y: chr "CO2 uptake rate"
- attr(*, "units")=List of 2
..$ x: chr "(uL/L)"
..$ y: chr "(umol/m^2 s)"
还有一个pillar::glimpse 函数,更简洁:
代码语言:javascript复制> glimpse(CO2)
Rows: 84
Columns: 5
$ Plant <ord> Qn1, Qn1, Qn1, Qn1, Qn1, Qn1, Qn1, Qn2, Qn2, Qn2, Qn…
$ Type <fct> Quebec, Quebec, Quebec, Quebec, Quebec, Quebec, Queb…
$ Treatment <fct> nonchilled, nonchilled, nonchilled, nonchilled, nonc…
$ conc <dbl> 95, 175, 250, 350, 500, 675, 1000, 95, 175, 250, 350…
$ uptake <dbl> 16.0, 30.4, 34.8, 37.2, 35.3, 39.2, 39.7, 13.6, 27.3…
- head, tail
看数据框前后几行。
概括函数
连续性变量:对连续取值的变量 x,可以用 mean, std, var, sum, prod, min, max 等函数获取基本统计量。加 na.rm=TRUE 选 项可以仅对非缺失值计算。sort(x) 返回排序后的结果。rev(x) 把 x 所有元素次序颠倒后返回。quantile(x, c(0.05, 0.95)) 可以求 x 的样本分位数。rank(x) 对 x 求秩得分(即名次,但从最小到最大排列)。
分类变量:table 统计频数。prop.table() 将频数转为百分比。table 还可以接受两个参数,实现列联表:
对于 table() 的结果列联表,可以用 addmargins() 函数增加行和与列和:
数据框概括
用 colMeans() 对数据框或矩阵的每列计算均值,用 colSums() 对数据框或矩阵的每列计算总和。用 rowMeans() 和 rowSums() 对矩阵的每行计算均值或总和。
2. 分类概括
- tapply
分组概括:
代码语言:javascript复制tapply(X, INDEX, FUN)
## F M
## 113.2354 108.1214
with(
d.cancer,
tapply(v0, sex, mean))
- aggregate
可以指定某一列或几列(用list 传递)分组,对指定数据框进行统计计算:
代码语言:javascript复制aggregate(
d.cancer[, c("age", "v0", "v1")], list(sex=d.cancer[["sex"]]), mean, na.rm=TRUE)
##sex age v0 v1 ## 1 F 66.14286 113.2354 42.65538 ## 2 M 63.25000 108.1214 45.95524
aggregate() 第一个参数是数据框,第二个参数是列表,列表元素是用来分组或交叉分组的变量,第三个参数是概 括用的函数,概括用的函数的选项可以在后面给出。
- split
split 函数可以把数据框的各行按照一个或几个分组变量分为子集的列表,然后可以用 sapply() 或 vapply() 对每组进行概括。如:
代码语言:javascript复制 sp <- split(d.cancer[,c("v0","v1")], d.cancer[["sex"]]) sapply(sp, colMeans)
顾名思义,字符处理函数就是用来处理文本型数据的。可以是从文本型数据中抽取信息,也可以修改内容,亦或是重设格式。
3. 字符串处理函数
常用的函数如下:
代码语言:javascript复制length(x) # 计算对象x 中的长度
nchar(x) # 计算x 中的字符数量(区别于length(),它返回的是向量中的元素数量)
seq(from, to, by) # 生成一个序列,从from 到 to 以by 为间隔。
rep(x, time = n) # 将序列重复n次,默认为time,使用each 参数,会重复序列中的每个元素n 次,再将它们合并在一起
# > rep(1:3, each = 3)
# [1] 1 1 1 2 2 2 3 3 3
# > rep(1:3, time = 3)
# [1] 1 2 3 1 2 3 1 2 3
cut(x, breaks, labels, order_result, include.lowest = T) # 用来切割连续性变量,可以将其按照breaks 中的向量区间分割
# 可以通过labels 参数指定向量,使其元素作为breaks 分割后的新值,ordered_result 默认True,返回有序型因子
# include.lowest 设定切割后是否包括指定的breaks 的最小值,默认为F,不包括
> a = sample(1:100, 20)
> a
[1] 80 22 88 54 52 19 65 56 25 99 2 59 79 82 23 7 87 61 49 55
> cut(a, breaks = breaks, labels = labels, include.lowest = T)
[1] 好 差 好 中 中 差 良 良 中 好 差 良 良 好 差 差 好 良 中 中
Levels: 差 中 良 好
pretty(x, n) # 创建美观的分割点,将连续性向量x 分割为n 个区间,通过选取n 1 个等间距的取整值。
cat(x1, n1, x2, n2... file = "xxx", 'n') # 可以连接任意数目的字符串和指定的对象,但需要是一维,将其连接起来。
# 可以在末尾使用'n' 否则不会换行
substr(x, start, stop)
# 从start 到stop 位置提取x 字符中的子串
# substr('abc', 1, 2) 返回'ab'
toupper(x) # 将x 全部大写转换
tolower(x) # 将x 全部小写转换
paste(x, sep='')
# 用于连接字符串,sep 为连接的分隔符。
# paste("x", 1:3, sep='-'),返回 "x-1" "x-2" "x-3"
# 我们还可以通过paste 将向量连接为一个字符串,通过collapse 参数设定连接的分隔符。
trimws 提供了处理空白字符的操作:
cut()和pretty()
这里我非常想再care 一下cut 函数:
代码语言:javascript复制cut(x, breaks, labels, order_result, include.lowest = T) # 用来切割连续性变量,可以将其按照breaks 中的向量区间分割
# 可以通过labels 参数指定向量,使其元素作为breaks 分割后的新值,ordered_result 默认True,返回有序型因子
# include.lowest 设定切割后是否包括指定的breaks 的最小值,默认为F,不包括
> a = sample(1:100, 20)
> a
[1] 80 22 88 54 52 19 65 56 25 99 2 59 79 82 23 7 87 61 49 55
> cut(a, breaks = breaks, labels = labels, include.lowest = T)
[1] 好 差 好 中 中 差 良 良 中 好 差 良 良 好 差 差 好 良 中 中
Levels: 差 中 良 好
通过cut 函数,我们在处理连续型变量的切割时,就不用ifelse 一层套一层而且也不用自己设置了,比如上面的cut 后的结果,我们就可以和原本的结果合并,获得分组信息:
代码语言:javascript复制> a
[1] 80 22 88 54 52 19 65 56 25 99 2 59 79 82 23 7 87 61 49 55
> labels
[1] "差" "中" "良" "好"
> breaks = fivenum(a)
> breaks
[1] 2.0 24.0 55.5 79.5 99.0
b = cut(a, breaks = breaks, labels = labels, include.lowest = T)
> b
[1] 好 差 好 中 中 差 良 良 中 好 差 良 良 好 差 差 好 良 中 中
Levels: 差 中 良 好
> c = cbind(as.data.frame(a), b)
> head(c)
a b
1 80 好
2 22 差
3 88 好
4 54 中
5 52 中
6 19 差
另外,在设置cut 参数的breaks 时,我们除了使用fivenum() 函数获取数值的四分位数,还可以结合pretty 函数,获取指定分段长的数字,pretty 会帮助我们获得等间距的整值:
代码语言:javascript复制> pretty(a, 5)
[1] 0 20 40 60 80 100
> labels
[1] "差" "中" "良" "好" "极好"
> breaks = pretty(a, 5)
b = cut(a, breaks = breaks, labels = labels, include.lowest = T)
> b = cut(a, breaks = breaks, labels = labels, include.lowest = T)
> b
[1] 好 中 极好 良 良 差 好 良 中 极好 差 良 好 极好 中
[16] 差 极好 好 良 良
Levels: 差 中 良 好 极好
但有个小问题,pretty 并不能非常理想的分割指定的区间,比如关于1:100 数字,参数设置分割4组和5组都会返回5个区间结果:
代码语言:javascript复制> pretty(1:100, 4)
[1] 0 20 40 60 80 100
> pretty(1:100, 5)
[1] 0 20 40 60 80 100
有正则功能的函数
这部分的函数具备了正则表达式,因此有强大的搜索和匹配的功能。我们分别来介绍。
关于详细的正则表达式的使用,可以参见:https://www.yuque.com/mugpeng/python/wf7lbf
基本的正则表达式语法如下:
代码语言:javascript复制需要强调的是,正则表达式的字符范围包括大小写字母,罗马数字,以及部分符号。
. 表示任何单个字符
[] 对单个字符给出取值范围;[abc]表示a或b或c,[a-f]表a-f中的任意一个字符串。
[^ ],与[]相反,指取值范围以外字符;[^abc]表示非a非b非c。
*,前一个字符0或无限延伸;abc*表示,ab,abc,abcc...
,前一个字符1或无限延伸;abc 表示,abc,abcc,abccc...
?,前一个字符0或1次延伸;abc?表示,ab,abc。
|,左右表达式任意一个;ab|cd表示,ab或cd。
{m},扩展前一个字符串m次;ab{2}c,表示abbc。
{m,n},扩展前一个字符串m 到n次;ab{1,2}c,表示abc,abbc。
^,表示字符串开头部分;^abc,匹配abc 开头的字符串。
$,匹配字符串结尾;abc$,匹配abc 结尾的字符串。
(),分组标记,内部只可以用|;(abc)表示abc,(abc|def)表示abc, def。
d 数字,等价于[0-9]
D 非数字。
w 单词字符,等价于[A-Z], [a-z], [0-9] 及 -。
W 非单词字符。
t 制表符。
n 空行。
s 空格型内容,如t, n等。
S 非空格。
其中主要包含三个函数,grep、sub、strsplit。
这三个函数通过fixed
参数来设定正则表达式:如果是False,则匹配的为一个正则表达式;如果是True,则匹配为一个文本字符串,不带有任何的正则匹配功能。默认下为False。
另外,通过ignore.case
参数来设定是否大小写敏感:如果是False,则对大小写敏感;如果是True,则不会检查字母的大小写。默认下为False。
grep
grep 函数用于搜索,其返回值为匹配的下标,会在x 中搜索设定的pattern(正则或文本),常用参数使用及设置如下:
代码语言:javascript复制grep(pattern, x, ignore.case = F, fixed = F)
比如在文本中查找字母a:
代码语言:javascript复制> a = letters[sample(1:26, 10)]
> a
[1] "u" "a" "w" "v" "e" "k" "m" "r" "d" "l"
> grep('a', a)
[1] 2
> a[2]
[1] "a"
结果会返回 a 的下标。
我们还可以使用正则表达式试试,会极大的拓展我们可以搜索的范围:
代码语言:javascript复制> for (x in 1:100) {
i[x] = paste(letters[sample(1:26, 3)], collapse='')
}
> head(i)
[1] "mfw" "gpv" "ogf" "xnv" "pkd" "efb"
比如我们可以查看这100个三个字符的字符串中哪些是以m 开头的:
代码语言:javascript复制> grep('^m', i)
[1] 1 27 38 44 63 76 81
> length(grep('^m', i))
[1] 7
可见有7个字符都是如此呢,我们可以查看一下:
代码语言:javascript复制> i[grep('^m', i)]
[1] "mfw" "mtb" "mgk" "mio" "mup" "mxp" "mfd"
sub
不同于grep 只是查找,sub 则是会搜索x ,并直接对匹配到的pattern 进行修改,将其修改为指定的replacement,常用参数使用及设置如下:
代码语言:javascript复制sub(pattern, replacement, x, ignore.case = F, fixed = F)
同样,我们还是使用之前grep 中使用的变量:
代码语言:javascript复制> head(a)
[1] "mfw" "gpv" "ogf" "xnv" "pkd" "efb"
我们还是匹配所有m 开头的字符串,并将它们替换成指定的字符:
代码语言:javascript复制> sub('^m.*', 'cat',a)
[1] "cat" "gpv" "ogf" "xnv" "pkd" "efb" "inq" "apb" "ecr" "pjf" "tni" "jns"
[13] "bfe" "uje" "wcn" "sfj" "fla" "tqu" "fau" "ecl" "bzd" "shy" "lfr" "gzk"
[25] "znc" "xav" "cat" "nly" "kgl" "sjb" "oys" "yoz" "jxu" "fib" "neq" "qra"
[37] "kmp" "cat" "fve" "swy" "xou" "vdy" "ash" "cat" "jgs" "cym" "fxb" "cns"
[49] "hiw" "ymt" "thg" "hty" "wyh" "tqb" "qae" "jsd" "ktn" "nep" "ihf" "zey"
[61] "fdv" "wfd" "cat" "ikp" "vfd" "dlf" "lph" "bgw" "ufc" "qvz" "ovc" "dco"
[73] "qaj" "rsf" "wcr" "cat" "veq" "hmd" "rse" "czd" "cat" "kpf" "qbs" "jgd"
[85] "tcg" "yco" "joi" "ghb" "wfb" "ifg" "lun" "tag" "xzp" "bnh" "yni" "sin"
[97] "qns" "bjp" "oby" "dxv"
> grep('cat', sub('^m.*', 'cat',a))
[1] 1 27 38 44 63 76 81
都变成cat啦。
需要注意的是,sub 正则表达会只修改符合它匹配到的元素的完全匹配到的部分,因此,如果我们并没有指定匹配字符的长度,而只是设置匹配开头的字母m,则其只会修改字符只的m 字符,而不会对整个元素进行修改:
代码语言:javascript复制sub('^m', 'cat',a); a[grep('cat', a)]
[1] "catfw" "gpv" "ogf" "xnv" "pkd" "efb" "inq" "apb" "ecr"
[10] "pjf" "tni" "jns" "bfe" "uje" "wcn" "sfj" "fla" "tqu"
[19] "fau" "ecl" "bzd" "shy" "lfr" "gzk" "znc" "xav" "cattb"
[28] "nly" "kgl" "sjb" "oys" "yoz" "jxu" "fib" "neq" "qra"
[37] "kmp" "catgk" "fve" "swy" "xou" "vdy" "ash" "catio" "jgs"
[46] "cym" "fxb" "cns" "hiw" "ymt" "thg" "hty" "wyh" "tqb"
[55] "qae" "jsd" "ktn" "nep" "ihf" "zey" "fdv" "wfd" "catup"
[64] "ikp" "vfd" "dlf" "lph" "bgw" "ufc" "qvz" "ovc" "dco"
[73] "qaj" "rsf" "wcr" "catxp" "veq" "hmd" "rse" "czd" "catfd"
[82] "kpf" "qbs" "jgd" "tcg" "yco" "joi" "ghb" "wfb" "ifg"
[91] "lun" "tag" "xzp" "bnh" "yni" "sin" "qns" "bjp" "oby"
[100] "dxv"
匹配到的字符只是修改了字母m而已。
另外,sub 的会返回一个修改后的值,因此如果需要保存,可以使用变量接收结果。
strsplit
在字符串向量x 中按照split 的正则语法或正常文本搜寻,并对x 进行分割,将分割后的结果返回为一个列表:
代码语言:javascript复制strsplit(x, split, ignore.case = F, fixed = F)
尝试一下;
代码语言:javascript复制> a = c('123 456', 'good morning sir')
> strsplit(a, ' ')
[[1]]
[1] "123" "456"
[[2]]
[1] "good" "morning" "sir"
R 会对字符串向量一一进行查找,如果有符合匹配的,则会将该元素进行切割,并作为列表的元素进行存储,每一个字符串向量的元素都对应返回的列表的元素;而列表的元素则包括了符合切割的元素的切割后的结果,及未匹配的元素,且切割的内容会被删去:
代码语言:javascript复制> strsplit(a, '3')
[[1]]
[1] "12" " 456"
[[2]]
[1] "good morning sir"
字符变换表
chartr 提供了一个字符串替换表的方法可以指定一个字符对应关系,旧字符在 old 中,新字符在 new 中,x 是一个 要进行替换的字符型向量。比如,下面的例子把所有! 替换成.,把所有; 替换成,:
代码语言:javascript复制chartr("!;", ".,", c("Hi; boy!", "How do you do!"))
## [1] "Hi, boy." "How do you do."