3.4 应用3.4.1 简写转换全拼字符串匹配为创建查询表提供了强大的方法比如将简写转换成全拼# 比如m是male的简写f是female的简写u是unknown的简写 # 将简写字符快速转换为全拼 gender - c(m, f, f, u, m) lookup - c(mmale, ffemale, uunknown) lookup[gender]m f f u m“male” “female” “female” “unknown” “male”# 用unname可隐去元素名称 unname(lookup[gender])[1] “male” “female” “female” “unknown” “male”3.4.2 表格字段匹配根据分数级别来匹配评价表格的对应区间进而返回该分数对应的评语、是否及格等多列# 这是多个考试级别A级最高C级最低 grades - c(A,C,B,A) # 评级表格 info - data.frame(gradec(A,B,C), descc(棒极了,不错,需努力), passc(T,T,F)) # match()函数可返回第1个参数中的元素首次出现在第2个参数中的位置 (index - match(grades, info$grade))[1] 1 3 2 1# 再通过子集选取的方式返回各分数对应的评价 info[index, ]grade desc pass1 A 棒极了 TRUE3 C 需努力 FALSE2 B 不错 TRUE1.1 A 棒极了 TRUE还有一种方式将分数级别作为评价表的行名称再用行筛选子集# 评价表的行名称设置为分数级别 rownames(info) - info$grade infograde desc passA A 棒极了 TRUEB B 不错 TRUEC C 需努力 FALSE# 再筛选子集 info[grades, ]grade desc passA A 棒极了 TRUEC C 需努力 FALSEB B 不错 TRUEA.1 A 棒极了 TRUE如果有多列多条件匹配方案之一是将多列合并成一列字符串或因子然后再用上面的方法匹配表格# 分数评级(grade)与科目(subject)任意两两配对成为返回因子的水平标签 # 而标签的整数取值按顺序一一配对若有一个长度不足则自动循环 grades2 - data.frame(gradec(A,C,B), subjectc(语文,数学,英语)) (grades2.factor - interaction(grades2$grade, grades2$subject))[1] A.语文 C.数学 B.英语Levels: A.数学 B.数学 C.数学 A.英语 B.英语 C.英语 A.语文 B.语文 C.语文# 评级表格其中语文、数学、英语各有A/B/C三档分数评级 # 同样地interaction()会将grade与subject任意两两配对并返回因子 info2 - data.frame(gradec(A,B,C), subjectc(语文,语文,语文, 数学,数学,数学, 英语,英语,英语), descc(棒极了,不错,需努力), passc(T,T,F)) (info2.factor - interaction(info2$grade, info2$subject))[1] A.语文 B.语文 C.语文 A.数学 B.数学 C.数学 A.英语 B.英语 C.英语Levels: A.数学 B.数学 C.数学 A.英语 B.英语 C.英语 A.语文 B.语文 C.语文# 多条件合并成一个后可以再使用match匹配每个分数评级在评分表中的行索引 (index2 - match(grades2.factor, info2.factor))[1] 1 6 8# 最后跟取子集的方法相同取出对应的记录 info2[index2,]grade subject desc pass1 A 语文 棒极了 TRUE6 C 数学 需努力 FALSE8 B 英语 不错 TRUE多个条件合并成一个条件除了合并为因子外也可以合并为字符串更简单直观# 将分数框中的分数评级和科目按顺序合并成一个字符串之间用罕见的分割符分开 (grades2.str - paste(grades2$grade, grades2$subject, sep|))[1] “A|语文” “C|数学” “B|英语”(info2.str - paste(info2$grade, info2$subject, sep|))[1] “A|语文” “B|语文” “C|语文” “A|数学” “B|数学” “C|数学” “A|英语” “B|英语”[9] “C|英语”# 后面的查找索引和匹配子集原理相同 (index2.str - match(grades2.str, info2.str))[1] 1 6 8info2[index2.str,]grade subject desc pass1 A 语文 棒极了 TRUE6 C 数学 需努力 FALSE8 B 英语 不错 TRUE3.4.3 随机抽取样本统计或建模中从向量或数据框中随机抽取一定量的样本是常见操作实现思路sample()函数可随机生成指定个数的索引向量再以其为索引从母集子选取子集# sample()是随机抽样函数默认不放回抽样下面代码指从myFrame4的总行数中插取5个行号 myFrame4 - data.frame(aseq(from1, to8, length.out10), brep(1:5, each2), cletters[1:10]) index4 - sample(nrow(myFrame4), 5) myFrame4[index4, ]a b c6 4.888889 3 f3 2.555556 2 c1 1.000000 1 a9 7.222222 5 i5 4.111111 3 e在统计分析中有时样本量过少可使用自助法即有放回重复抽样# sample()的参数rep取T指有放回抽样 (index4 - sample(nrow(myFrame4), 5, repT))[1] 9 9 3 7 9myFrame4[index4,]a b c9 7.222222 5 i9.1 7.222222 5 i3 2.555556 2 c7 5.666667 4 g9.2 7.222222 5 isample()不指定抽样个数时默认全量抽样相当于对原数据随机排列# 对母集随机排列 (index4 - sample(nrow(myFrame4)))[1] 10 4 1 3 6 7 9 5 2 8myFrame4[index4, ]a b c10 8.000000 5 j4 3.333333 2 d1 1.000000 1 a3 2.555556 2 c6 4.888889 3 f7 5.666667 4 g9 7.222222 5 i5 4.111111 3 e2 1.777778 1 b8 6.444444 4 h增加一点难度如何随机抽取 3 行但 3 行必须是连续的# 为方便对比先按a列升序排列 (myFrame4 - myFrame4[order(myFrame4$a), order(names(myFrame4))])a b c1 1.000000 1 a2 1.777778 1 b3 2.555556 2 c4 3.333333 2 d5 4.111111 3 e6 4.888889 3 f7 5.666667 4 g8 6.444444 4 h9 7.222222 5 i10 8.000000 5 j# 随机抽取1个起始索引位置 (start - sample(nrow(myFrame4), 1))[1] 7# 起始位置再往后挪动2个位置就是结尾位置但结尾位置有可能溢出向量长度 # 既要保证位置连续又要确保位置不溢出 # 可以在位置索引末尾拼接倒数第8、第9位索引就像回文一般选取3个数字只需回文2个 (index - c(1:nrow(myFrame4), nrow(myFrame4)-2, nrow(myFrame4)-1))[1] 1 2 3 4 5 6 7 8 9 10 8 9# 从起始位置再往后挪动2位置便得到所需要的索引 (dest - index[start:(start2)])[1] 7 8 9# 按升序筛选记录 # sort()返回排序后的新向量 myFrame4[sort(dest),]a b c7 5.666667 4 g8 6.444444 4 h9 7.222222 5 i3.4.4 排序order()函数返回对向量排序后的索引默认升序排列# 创建向量x (x - c(5.1, 3.2, 4.3, 1.4, 2.5))[1] 5.1 3.2 4.3 1.4 2.5# 这是排序后的索引 # 最小值是1.4索引位置是4 # 最大值是5.1索引位置是1 order(x)[1] 4 5 2 3 1# 有了排序后的索引就能用取子集的方式获取排序后的向量 x[order(x)][1] 1.4 2.5 3.2 4.3 5.1可以使用 decreasingT 参数令其倒序排列# 倒序排列 x[order(x, decreasingT)][1] 5.1 4.3 3.2 2.5 1.4默认情况下NA 总是排在最末尾# 向量x增加两个NA值后随机排序 x2 - c(x,NA,NA) (x2 - x2[sample(x2)])[1] 3.2 2.5 1.4 4.3 NA 5.1 NA# 对x升序排列, NA默认在最末尾 x2[order(x2)][1] 1.4 2.5 3.2 4.3 5.1 NA NA# 可以通过na.lastF参数将NA排在最前面 x2[order(x2, na.lastF)][1] NA NA 1.4 2.5 3.2 4.3 5.1还可以设置 na.lastNA 来排除 NA 值# 排序时排除NA值 x2[order(x2, na.lastNA)][1] 1.4 2.5 3.2 4.3 5.1二维或更高维数据order()同样可以对其行排序# 先对数据框的行和列乱序排列 (myFrame4 - myFrame4[sample(nrow(myFrame4)), c(b,c,a)])b c a3 2 c 2.5555565 3 e 4.11111110 5 j 8.0000007 4 g 5.6666671 1 a 1.0000006 3 f 4.8888899 5 i 7.2222222 1 b 1.7777788 4 h 6.4444444 2 d 3.333333# 对数据框的a列降序排列 myFrame4[order(myFrame4$a, decreasingT), ]b c a10 5 j 8.0000009 5 i 7.2222228 4 h 6.4444447 4 g 5.6666676 3 f 4.8888895 3 e 4.1111114 2 d 3.3333333 2 c 2.5555562 1 b 1.7777781 1 a 1.000000对数据框的 c 列按字母顺序排列的方法是相同的# 对数据框的c列按字母顺序排列 myFrame4[order(myFrame4$c), ]b c a1 1 a 1.0000002 1 b 1.7777783 2 c 2.5555564 2 d 3.3333335 3 e 4.1111116 3 f 4.8888897 4 g 5.6666678 4 h 6.4444449 5 i 7.22222210 5 j 8.000000甚至还能重新排列列名# 对列名进行排序 myFrame4[, order(names(myFrame4))]a b c3 2.555556 2 c5 4.111111 3 e10 8.000000 5 j7 5.666667 4 g1 1.000000 1 a6 4.888889 3 f9 7.222222 5 i2 1.777778 1 b8 6.444444 4 h4 3.333333 2 d相反地如何随机排列列名呢# 巧用sample()全量不放回抽样来乱序排列列名 myFrame4[, sample(names(myFrame4))]c b a3 c 2 2.5555565 e 3 4.11111110 j 5 8.0000007 g 4 5.6666671 a 1 1.0000006 f 3 4.8888899 i 5 7.2222222 b 1 1.7777788 h 4 6.4444444 d 2 3.333333order()返回的是排序后的索引而 sort()返回的是排序后的新向量# 排序后的向量 sort(x)[1] 1.4 2.5 3.2 4.3 5.13.4.5 展开重复记录有时拿到的数据框是整理汇总过的比如下表N 代表该行重复的数量其中语文 80 分有 3 条记录有些情况下我们需要将记录展开比如语文 80 分重复出现 3 行ABN语文803数学922英语854思路是第一行语文有 3 条记录那么就用 rep()函数把索引 1 复制 3 次同理第二行数学有 2 条记录则把索引 2 复制 2 次# 先创建分数汇总表 scores - data.frame(Ac(语文,数学,英语), Bc(80,92,85), Nc(3,2,4)) # 按N列复制索引 (index - rep(1:nrow(scores), timesscores$N))[1] 1 1 1 2 2 3 3 3 3# 再用子集筛选同时不要出现N列即可重复出现相同记录 scores[index, -3]A B1 语文 801.1 语文 801.2 语文 802 数学 922.1 数学 923 英语 853.1 英语 853.2 英语 853.3 英语 853.4.6 剔除数据框中的列方法 1将不需要的列设置为 NULL# 删除N列 scores$N - NULL scoresA B1 语文 802 数学 923 英语 85方法 2只选择需要的列# 只要A列 scores[c(A)]A1 语文2 数学3 英语方法 3排除掉不需要的列# 排除B列setdiff()返回除B之外的其它列名 scores[setdiff(names(scores),B)]A1 语文2 数学3 英语3.4.2 多条件筛选行组合多列的条件最常用的方法是逻辑子集选取方法条件 1“和”条件 2 同时成立与条件 1“或”条件 2 任意一个成立需注意运算符的使用运算符说明场景条件 1 “和” 条件 2 同时成立结果才是成立的生成的布尔运算符是向量可以有多个元素常用于子集筛选条件 1 “或” 条件 2 任意一个成立结果就是成立的条件 1 “和” 条件 2 同时成立结果才是成立的短路标量生成的布尔运算符只有一个元素常用于 if 判断# 筛选出gear为5且cyl为4的记录 data(mtcars) mtcars[mtcars$gear5 mtcars$cyl4, ]mpg cyl disp hp drat wt qsec vs am gear carbPorsche 914-2 26.0 4 120.3 91 4.43 2.140 16.7 0 1 5 2Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.9 1 1 5 2布尔运算符还可以组合!(X Y)等价于!X | !Y!(X | Y)等价于!X !Ysubset()是专用于数据框子集筛选的简写函数对列进行条件判断时可不用再写数据框名称# gear和cyl是mtcars的列但无需再写前缀“mtcars$” subset(mtcars, gear5 cyl4)mpg cyl disp hp drat wt qsec vs am gear carbPorsche 914-2 26.0 4 120.3 91 4.43 2.140 16.7 0 1 5 2Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.9 1 1 5 23.4.8 集合运算与布尔代数集合运算指通过整数索引选取子集而布尔代数指通过逻辑值选取子集在以下场景下集合运算的效率更高且占用内存更低整数索引天然存在位置已知而不是通过 which()将布尔值转换为索引提取数量较少应用场景提取第一个/最后一个 TRUE 对应的元素、判断条件中只有少量 TRUE集合运算与布尔代数可以相互转换which()可将布尔值转换为整数索引# 创建1至10的向量从中挑选出偶数 # which()函数返回TRUE对应的索引 even - 11:20 (index.bool - even %% 2 0)[1] FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE(index.int - which(index.bool))[1] 2 4 6 8 10even[index.int][1] 12 14 16 18 20通常情况下除非确信有必要否则尽量不要将已存在的逻辑索引转换为整数索引后再选取子集因为有时会发生意想不到的结果。比如x[-which(y)]与 x[!y]是不等价的其中 y 是逻辑索引如果 y 全部是 FALSEwhich(y)的结果是 integer(0)则-which(y)的结果也是 integer(0)导致 x[-which(y)]无法选取任何值而 x[!y]却能选取全部值没有现成函数将整数索引转换为布尔向量但可以很方便自定义# 自定义将整数索引转换为布尔向量的函数 # xTRUE对应的整数索引 # n向量总长度 unwhich - function(x, n) { result - rep_len(F, n) # 创建长度为n值全为FALSE的向量 result[x] - T # 索引x所在的元素修改为TRUE result # 返回结果向量 } # 将上面的整数索引转换为布尔向量 (index2.bool - unwhich(index.int, length(even)))[1] FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE# 再用布尔向量筛选出偶数值 even[index2.bool][1] 12 14 16 18 20下面是几个综合了上节及本节知识点的案例以及求两个向量之间的交集/并集/差集/异或案例一整数 1 至 10 中挑选出既是偶数又能被 3 整除的数字# 首先挑选出偶数的逻辑索引 (index1 - even %% 2 0)[1] FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE# 然后挑选出能被3整除的逻辑索引 (index2 - even %% 3 0)[1] FALSE TRUE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE# 接着选取index1和index2两个逻辑向量同时成立的返回值 # 也即对应元素同时为TRUE才返回TRUE任意一方不是TRUE则返回FALSE (index3 - index1 index2)[1] FALSE TRUE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE# 最后根据逻辑索引选取子集 even[index3][1] 12 18案例二求两个向量的交集并去重# intersect()函数返回两个向量的交集去重 # 也即同时存在于两个向量中的元素并去重 (num1 - sample(even, 5))[1] 20 18 14 11 15(num2 - sample(even, 5))[1] 18 20 14 17 19intersect(num1, num2)[1] 20 18 14案例三求两个向量的并集并去重# union()函数返回两个向量的并集去重 # 也即存在于任意一个向量中的元素并去重 union(num1, num2)[1] 20 18 14 11 15 17 19案件四求两个向量的差集在 x 中而不在 y 中# setdiff()返回存在于第1个参数中但不在第2个参数中的元素 setdiff(num1, num2)[1] 11 15案件五筛选出有 5 个档位或 4 个汽缸的汽车且排除同时存在 5 个档位和 4 个汽缸的汽车# xor()是异或函数当其中一个布尔值为TRUE而另一个为FALSE时返回TRUE # 也即两个值不同时返回TRUE # 若两个布尔值相同同为TRUE或同为FALSE则返回FALSE # 等价于(gear5 | cyl4) !(gear5 cyl4) mtcars[xor(mtcars$gear5, mtcars$cyl4), ]mpg cyl disp hp drat wt qsec vs am gear carbDatsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2Fiat 128 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1Honda Civic 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2Toyota Corolla 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1Toyota Corona 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1Fiat X1-9 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4 1Ford Pantera L 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5 4Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6Maserati Bora 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5 8Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2特别注意xor()属于逻辑运算家族、|、!等而不是集合运算intersect、union、setdiff本文是我阅读和学习《高级R语言编程指南》作者Hadley Wickham机械工业出版社出版后的读书笔记大部分内容与例子引用原书并加上自己的一部分理解与补充。推荐关注我的个人博客R数据分析不定期发布R语言经典书籍的读书笔记、数据分析案例欢迎交流~