26. R 数据整理(一:base R 的数据处理函数)

2021-12-17 09:21:01 浏览数 (1)

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."

0 人点赞