第2章 不同类型向量处理

每种不同的“类”或者“类型”的向量,都有对应的专用工具来处理。

2.1 数值型

参考

https://mp.weixin.qq.com/s?__biz=MzA3MTM3NTA5Ng==&mid=2651054585&idx=1&sn=bb74da8762b4577421e63a94a82e1b92&scene=1&srcid=080887FTZWXCJ5hGipnDoNVb&key=8dcebf9e179c9f3aa36717a83e5e4b481a517865ea2228e37ee250d6573e168a1a28ef673134e92c9bc54b3016ce0801&ascene=0&uin=MTE0NTUzNQ%3D%3D&devicetype=iMac16%2C2+OSX+OSX+10.11.6+build(15G31)&version=11020201&pass_ticket=8qEIgb5P8KxjMN82kEUhSvsPVljtxTKPuOCmLOuNPsI%3D

http://blog.sina.com.cn/s/blog_72ef7bea0101cgq9.html

2.1.1 常用的数学函数

x <- c(1, 2, 3, 4, 5, -7, -1.6, 3.876)

# 绝对值
abs(x)
## [1] 1.000 2.000 3.000 4.000 5.000 7.000 1.600 3.876
# 平方根
sqrt(abs(x))
## [1] 1.000000 1.414214 1.732051 2.000000 2.236068 2.645751 1.264911 1.968756
x^(0.5)
## [1] 1.000000 1.414214 1.732051 2.000000 2.236068      NaN      NaN 1.968756
# 不小于x的最大整数
ceiling(x)
## [1]  1  2  3  4  5 -7 -1  4
# 不大于x的最大整数
floor(x)
## [1]  1  2  3  4  5 -7 -2  3
# 向0的方向截取x的整数部分
trunc(x)
## [1]  1  2  3  4  5 -7 -1  3
# 将x舍入为指定位数的小数
round(x, digits = 2)
## [1]  1.00  2.00  3.00  4.00  5.00 -7.00 -1.60  3.88
# 将x舍入为指定有效数字位数
signif(x, digits = 2)
## [1]  1.0  2.0  3.0  4.0  5.0 -7.0 -1.6  3.9
# 余弦函数
cos(x)
## [1]  0.54030231 -0.41614684 -0.98999250 -0.65364362  0.28366219  0.75390225
## [7] -0.02919952 -0.74222805
# 正弦函数
sin(x)
## [1]  0.8414710  0.9092974  0.1411200 -0.7568025 -0.9589243 -0.6569866 -0.9995736
## [8] -0.6701474
# 正切函数
tan(x)
## [1]  1.5574077 -2.1850399 -0.1425465  1.1578213 -3.3805150 -0.8714480 34.2325327
## [8]  0.9028861
# 反余弦函数
y <- seq(from = -1, to = 1, by = 0.2)
acos(y)
##  [1] 3.1415927 2.4980915 2.2142974 1.9823132 1.7721542 1.5707963 1.3694384
##  [8] 1.1592795 0.9272952 0.6435011 0.0000000
# 反正弦函数
asin(y)
##  [1] -1.5707963 -0.9272952 -0.6435011 -0.4115168 -0.2013579  0.0000000
##  [7]  0.2013579  0.4115168  0.6435011  0.9272952  1.5707963
# 反正切函数
atan(y)
##  [1] -0.7853982 -0.6747409 -0.5404195 -0.3805064 -0.1973956  0.0000000
##  [7]  0.1973956  0.3805064  0.5404195  0.6747409  0.7853982
# 双曲余弦函数
cosh(x)
## [1]   1.543081   3.762196  10.067662  27.308233  74.209949 548.317035   2.577464
## [8]  24.125819
# 双曲正弦函数
sinh(x)
## [1]    1.175201    3.626860   10.017875   27.289917   74.203211 -548.316123
## [7]   -2.375568   24.105086
# 双曲正切函数
tanh(x)
## [1]  0.7615942  0.9640276  0.9950548  0.9993293  0.9999092 -0.9999983 -0.9216686
## [8]  0.9991406
z <- 1:10

# 反双曲余弦函数
acosh(z)
##  [1] 0.000000 1.316958 1.762747 2.063437 2.292432 2.477889 2.633916 2.768659
##  [9] 2.887271 2.993223
# 反双曲正弦函数
asinh(z)
##  [1] 0.8813736 1.4436355 1.8184465 2.0947125 2.3124383 2.4917799 2.6441208
##  [8] 2.7764723 2.8934440 2.9982230
# 反双曲正切函数
atanh(y)
##  [1]       -Inf -1.0986123 -0.6931472 -0.4236489 -0.2027326  0.0000000
##  [7]  0.2027326  0.4236489  0.6931472  1.0986123        Inf
# 对 x 取以 2 为底的对数
log(abs(x), base = 2) 
## [1] 0.0000000 1.0000000 1.5849625 2.0000000 2.3219281 2.8073549 0.6780719
## [8] 1.9545686
# 没有 base 则默认底数为自然对数
log(abs(x))
## [1] 0.0000000 0.6931472 1.0986123 1.3862944 1.6094379 1.9459101 0.4700036
## [8] 1.3548037
log(10)
## [1] 2.302585
# 常用对数
log10(abs(x))
## [1] 0.0000000 0.3010300 0.4771213 0.6020600 0.6989700 0.8450980 0.2041200
## [8] 0.5883838
log10(10)
## [1] 1
# 指数函数
exp(x)
## [1] 2.718282e+00 7.389056e+00 2.008554e+01 5.459815e+01 1.484132e+02
## [6] 9.118820e-04 2.018965e-01 4.823091e+01

2.1.2 统计分析中常用的函数与作用

x <- mtcars$mpg

# 最大值
max(x)
## [1] 33.9
# 最小值
min(x)
## [1] 10.4
# 返回最大元素所在下标
which.max(x)
## [1] 20
# 返回最小元素所在下标
which.min(x)
## [1] 15
# 均值
mean(x)
## [1] 20.09062
# 中位数
median(x)
## [1] 19.2
# 中位绝对离差
mad(x)
## [1] 5.41149
# 方差
var(x)
## [1] 36.3241
# 标准差
sd(x)
## [1] 6.026948
# 级差,最小值 最大值 
range(x)
## [1] 10.4 33.9
# 四分位数级差
IQR(x)
## [1] 7.375
# 分位数函数,默认为五分位数;prob 参数而已指定任意分位数
quantile(x)
##     0%    25%    50%    75%   100% 
## 10.400 15.425 19.200 22.800 33.900
quantile(x, prob = c(0.15, 0.89))
##    15%    89% 
## 14.895 29.129
# 常用描述性统计量,6个值,分别是:最小值、1/4分为数、中位数、平均值、3/4分位数、最大值)
summary(x)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   10.40   15.43   19.20   20.09   22.80   33.90
# 返回向量长度
length(x)
## [1] 32
# 总和(累计求和)
sum(x)
## [1] 642.9
# 总积(累计乘积)
prod(x)
## [1] 1.264241e+41
# 逆序,将向量逆序排列(按出现的顺序,索引)
rev(x)
##  [1] 21.4 15.0 19.7 15.8 30.4 26.0 27.3 19.2 13.3 15.2 15.5 21.5 33.9 30.4 32.4
## [16] 14.7 10.4 10.4 15.2 17.3 16.4 17.8 19.2 22.8 24.4 14.3 18.1 18.7 21.4 22.8
## [31] 21.0 21.0
# 向量排序,参数 decreasing 默认为 FASLE (正序,从小到大),将 decreasing 设置为 TRUE,则按倒序排序(从大到小)
sort(x)
##  [1] 10.4 10.4 13.3 14.3 14.7 15.0 15.2 15.2 15.5 15.8 16.4 17.3 17.8 18.1 18.7
## [16] 19.2 19.2 19.7 21.0 21.0 21.4 21.4 21.5 22.8 22.8 24.4 26.0 27.3 30.4 30.4
## [31] 32.4 33.9
sort(x, decreasing = TRUE)
##  [1] 33.9 32.4 30.4 30.4 27.3 26.0 24.4 22.8 22.8 21.5 21.4 21.4 21.0 21.0 19.7
## [16] 19.2 19.2 18.7 18.1 17.8 17.3 16.4 15.8 15.5 15.2 15.2 15.0 14.7 14.3 13.3
## [31] 10.4 10.4
# 返回向量每个元素的“秩”,默认是按正序排列时的第几个(排名); 将 decreasing 设置为 TRUE,则返回向量每个元素倒序排序的“秩”
order(x)
##  [1] 15 16 24  7 17 31 14 23 22 29 12 13 11  6  5 10 25 30  1  2  4 32 21  3  9
## [26]  8 27 26 19 28 18 20
order(x, decreasing = TRUE)
##  [1] 20 18 19 28 26 27  8  3  9 21  4 32  1  2 30 10 25  5  6 11 13 12 29 22 14
## [26] 23 31 17  7 24 15 16
# 返回向量的“秩” ?
rank(x)
##  [1] 19.5 19.5 24.5 21.5 15.0 14.0  4.0 26.0 24.5 16.5 13.0 11.0 12.0  7.5  1.5
## [16]  1.5  5.0 31.0 29.5 32.0 23.0  9.0  7.5  3.0 16.5 28.0 27.0 29.5 10.0 18.0
## [31]  6.0 21.5
# 分步累计函数,每个元素值是上个元素值与当前元素值的运算结果,返回相同长度的向量
x <- c(3, 1, 4, 15, 92)

# 累计求和
cumsum(x)
## [1]   3   4   8  23 115
# 累计乘积
cumprod(x)
## [1]     3     3    12   180 16560
# 累计比较最小值
cummin(x)
## [1] 3 1 1 1 1
# 累计比较最大值
cummax(x)
## [1]  3  3  4 15 92
x <- c(3, 1, 4, 15, 92)
y <- 2:6

# 方差
var(x,y)
## [1] 48
# 协方差
cov(x,y)
## [1] 48
# 相关系数
cor(x,y)
## [1] 0.779304
# 外积
outer(x,y)
##      [,1] [,2] [,3] [,4] [,5]
## [1,]    6    9   12   15   18
## [2,]    2    3    4    5    6
## [3,]    8   12   16   20   24
## [4,]   30   45   60   75   90
## [5,]  184  276  368  460  552
# 滞后差分
diff(x, lag = 1)
## [1] -2  3 11 77
# 为x进行中心化或标准化
scale(x, center = TRUE, scale = TRUE)
##            [,1]
## [1,] -0.5134116
## [2,] -0.5647527
## [3,] -0.4877410
## [4,] -0.2053646
## [5,]  1.7712699
## attr(,"scaled:center")
## [1] 23
## attr(,"scaled:scale")
## [1] 38.9551

2.2 概率分布函数特点

在 R 中,概率函数形如:

[dpqr]distribution+abbreviation()

其中第一个字母表示其所指分布的某一方面:

d = 密度函数(desity function)

p = 分布函数(distribution function)

q = 分位数函数(quantile function)

r = 生成随机数函数(random function)

2.2.1 概率分布函数表格

概率分布(维基百科)

————- | ————- 分布名称 | 缩写 ————- | ————- Beta分布|beta 二项分布|binorm 柯西分布|cauchy (非中心)卡方分布|chisq 指数分布|exp F分布|f Gamma分布|gamma 几何分布|geom 超几何分布|hyper 对数正态分布|lnorm Logistic分布|logis 多项分布|multinom 负二项分布|nbinom 正态分布|norm 泊松分布|pois Wilcoxon符号秩分布|signrank 负二项分布|nbinom t分布|t 均匀分布|unif Weibull分布|weibull Wilcoxon秩和分布|wilcox

2.3 字符串 stringr

2.3.1 base R 基础包中常用的字符处理函数

统计字符个数

x <- c("abcd", "R语言", "数据分析")
nchar(x) # 默认 type = "chars" # 人类可读的字符数
## [1] 4 3 4
nchar(x, type = "bytes") # 字节数(机器可读)
## [1]  4  7 12

连接字符串

paste("abcd", "efg", "xyz", sep = ",") # sep 是连接符号
## [1] "abcd,efg,xyz"
paste0("abcd","xyz") # paste0 是 paste 的快捷函数,等价于 sep = ""
## [1] "abcdxyz"

将字符向量中的每个元素收尾相连

(nth <- paste0(1:12, c("st", "nd", "rd", rep("th", 9))))
##  [1] "1st"  "2nd"  "3rd"  "4th"  "5th"  "6th"  "7th"  "8th"  "9th"  "10th"
## [11] "11th" "12th"
paste(nth, collapse = ", ")
## [1] "1st, 2nd, 3rd, 4th, 5th, 6th, 7th, 8th, 9th, 10th, 11th, 12th"

截取字符子窜

(x <- c("abcdefg", "uvwxyz"))
## [1] "abcdefg" "uvwxyz"
substr(x, 2, 5) # substr(x, start, stop)
## [1] "bcde" "vwxy"
substring(x, 1:2, c(3, 5)) # 起始和结束使用位置向量
## [1] "abc"  "vwxy"

字符串拆分为向量(与“将字符向量中的每个元素收尾相连”相反)

strsplit(x, split, fixed = FALSE, perl = FALSE, useBytes = FALSE)

  • split 是用来拆分的字符,默认是使用正则表达式,除非 fixed 设置为 TRUE

strsplit()返回的是列表,因为一个字符串(一个字符向量元素)可能包含多个用来拆分的字符,每个字符元素将会返回一个字符向量,而每个字符向量的长度可能不太一样。

(x <- c(as = "asfef", qu = "qwerty", "yuiop[", "b", "stuff.blah.yech"))
##                as                qu                                     
##           "asfef"          "qwerty"          "yuiop["               "b" 
##                   
## "stuff.blah.yech"
strsplit(x, "e") # 用字符 "e" 来拆分字符串
## $as
## [1] "asf" "f"  
## 
## $qu
## [1] "qw"  "rty"
## 
## [[3]]
## [1] "yuiop["
## 
## [[4]]
## [1] "b"
## 
## [[5]]
## [1] "stuff.blah.y" "ch"
strsplit("a.b.c", ".", fixed = TRUE) # 将 "." 作为非正则符号来使用 
## [[1]]
## [1] "a" "b" "c"

2.3.2 Regular Expressions 正则表达式在R用的使用

关于正则表达式可参考 http://stringr.tidyverse.org/articles/regular-expressions.html stringr 中的介绍文档,案例也使用该包中的函数。

library(stringr)

匹配普通字符

x <- c("apple", "banana", "pear")
str_extract(x, "an")
## [1] NA   "an" NA

匹配字符大小写敏感

bananas <- c("banana", "Banana", "BANANA")
str_detect(bananas, "banana") # 默认是大小写敏感的
## [1]  TRUE FALSE FALSE
str_detect(bananas, regex("banana", ignore_case = TRUE)) # 可以在正则表达式中关闭大小写敏感
## [1] TRUE TRUE TRUE

转义符号

通常情况下,正则表达式中的转义符号“";在 R 语言的正则表达式中匹配模式中,需要使用“\”来转义。

str_extract(c("abc", "a.c", "bef"), "a\\.c") # 转义后"."就只代表该字符本身而没任意字符的特殊含义
## [1] NA    "a.c" NA

常用的特殊字符

  • \n 换行符
  • \r 回车符
  • \t 制表符

匹配一个任意字符

str_extract(c("abc", "1.c", "\n"), ".") # "." 匹配一个任意字符 
## [1] "a" "1" NA
str_extract(c("1abc", "2a.c", "bef"), "\\d") # "\d" 匹配一个任意数字 
## [1] "1" "2" NA
str_extract(c("1abc", "2a.c", "bef"), "[0-9]") # "[0-9]"是匹配0到9数字中的一个, 与 "\d" 匹配任意一个数字等价
## [1] "1" "2" NA
str_extract(c("1abc", "2a.c", "bef"), "\\D") # "\D" 匹配一个任意非数字 
## [1] "a" "a" "b"
str_extract(c("1abc", ",a.c", "bef"), "\\w") # "\w" 匹配一个任意字母、标点符号、数字 
## [1] "1" "a" "b"
str_extract(c("1abc", "2f.c", "Bef"), "[a-zA-Z]") # "[a-z]"是匹配从a-z的26个小写字母, [A-Z]"是匹配从A-Z的26个大写字母
## [1] "a" "f" "B"
str_extract(c("1abc", ".fc", "bef"), "[abc]") # "[abc]" 匹配从“a","b","c"的中任意一个字符
## [1] "a" "c" "b"
str_extract(c("1abc", ".fc", "bef"), "[^abc]") # "[^abc]" 匹配非“a","b","c"的中任意一个字符
## [1] "1" "." "e"
str_extract(c("1a bc", "2\tc", "3b\nf", "w\rd", "tt\fd"), "\\s") # "\s" 匹配一个空白符,包含 空格 换行符 回车符 制表符等
## [1] " "  "\t" "\n" "\r" "\f"
str_extract(c("1a bc", "2\tc", "3b\nf", "w\rd", "tt\fd"), "\\S") # "\S" 匹配非空白符
## [1] "1" "2" "3" "w" "t"

预定义的字符匹配(非正则表达式)

  • [:punct:]: punctuation. 标点符号
  • [:alpha:]: letters. 字母,包含大小写
  • [:lower:]: lowercase letters. 小写字母
  • [:upper:]: upperclass letters. 大写字母
  • [:digit:]: digits. 数字
  • [:xdigit:]: hex digits. 十六进制数字
  • [:alnum:]: letters and numbers. 字母和数字
  • [:cntrl:]: control characters. 控制字符
  • [:graph:]: letters, numbers, and punctuation. 字母,数字,标点符号
  • [:print:]: letters, numbers, punctuation, and whitespace. 字母,数字,表达符号和空白符号
  • [:space:]: space characters (basically equivalent to ). 空白
  • [:blank:]: space and tab. 空格和制表符
str_extract(c("1abc", ",a.c", "Bef"), "[:alpha:]") # "\s" 匹配一个空白符,包含 空格 换行符 回车符 制表符等
## [1] "a" "a" "B"

交替与分组

| 交替是逻辑条件,()分组是表达式优先级

str_extract(c("1abc", ",a.c", "Bef"), "1|a") # "1|a" 匹配 “1”或者“a”中任意一个字符
## [1] "1" "a" NA
str_extract(c("abc", "a1c", "E1c"), "a(1|b)c") # "a(1|b)c" 匹配 三个字符,开通是“a”,结尾是”c",中间是“1”或者“b”
## [1] "abc" "a1c" NA

定位符

^以什么开头(放在要匹配的字符之前),$以什么结尾(放在匹配字符之后)。

str_extract(c("abc", "cba"), "^a") # "^a" 匹配以"a"作为开头的字符
## [1] "a" NA
str_extract(c("abc", "cba"), "a$") # "$a" 匹配以"a"作为结尾的字符
## [1] NA  "a"

2.3.3 正则表达式

http://mp.weixin.qq.com/s/Jx0RF4UukEYB0ImPxE2eMA

pattern 含义
[ 回退(并删除)一个字符(backspace)
换页符
换行符
回车符
制表符(tab)
垂直制表符
任何一个数字字符,等价于[0-9]
任何一个非数字字符,等价于1
任何一个字母数字字符(大小写均可以)或下划线字符(等价于[a-zA-Z0-9])
任何一个非字母数字或下划线字符(等价于[^a-zA-Z0-9])
任何一个空白字符(等价于[)
任何一个非空白字符(等价于[^\f\n\r\t\v])
[:alnum:] 任何一个字母或数字(等价于[a-ZA-Z0-9])
[:alpha:] 任何一个字母(等价于[a-ZA-Z])
[:blank:] 空格或制表符(等价于[)    注:t后面有一个空格
[:cntrl:] ASCII控制字符(ASCII  0到31,再加上ASCII 127)
[:digit:] 任何一个数字(等价于[0-9])
[:graph:] 和[:print:]一样,但不包括空格
[:lower:] 任何一个小写字母(等价于[a-z])
[:print:] 任何一个可打印字符
[:punct:] 既不属于[:alnum:],也不属于[:cntrl:]的任何一个字符
[:space:] 任何一个空格字符,包括空格(等价于[f] 注:v后面有一个空格
[:upper:] 任何一个大写字母(等价于[A-Z])
[:xdigit:] 任何一个十六进制数字(等价于[a-fA-F0-9])
. 可以匹配任何单个的字符字母数字甚至.字符本身。同一个正则表达式允许使用多个.字符。但不能匹配换行
\ 转义字符,如果要匹配就要写成“\(\)”
^ 取非匹配
$ 放在句尾,表示一行字符串的结束
() 提取匹配的字符串,(\s*)表示连续空格的字符串
[] 选择方括号中的任意一个(如[0-2]和[012]完全等价,[Rr]负责匹配字母R和r)
{} 前面的字符或表达式的重复次数。如{5,12}表示重复的次数不能小于5,不能多于12,否则都不匹配
* 匹配零个或任意多个字符或字符集合,也可以没有匹配
+ 匹配一个或多个字符,至少匹配一次
? 匹配零个或一个字符
# http://blog.csdn.net/mark555/article/details/22379757
# 验证数字的正则表达式集 
# 验证数字:^[0-9]*$
# 验证n位的数字:^\d{n}$
# 验证至少n位数字:^\d{n,}$
# 验证m-n位的数字:^\d{m,n}$
# 验证零和非零开头的数字:^(0|[1-9][0-9]*)$
# 验证有两位小数的正实数:^[0-9]+(.[0-9]{2})?$
# 验证有1-3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$
# 验证非零的正整数:^\+?[1-9][0-9]*$
# 验证非零的负整数:^\-[1-9][0-9]*$
# 验证非负整数(正整数 + 0)  ^\d+$
# 验证非正整数(负整数 + 0)  ^((-\d+)|(0+))$
# 验证长度为3的字符:^.{3}$
# 验证由26个英文字母组成的字符串:^[A-Za-z]+$
# 验证由26个大写英文字母组成的字符串:^[A-Z]+$
# 验证由26个小写英文字母组成的字符串:^[a-z]+$
# 验证由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$
# 验证由数字、26个英文字母或者下划线组成的字符串:^\w+$
# 验证用户密码:^[a-zA-Z]\w{5,17}$ 正确格式为:以字母开头,长度在6-18之间,只能包含字符、数字和下划线。
# 验证是否含有 ^%&',;=?$\" 等字符:[^%&',;=?$\x22]+
# 验证汉字:^[\u4e00-\u9fa5],{0,}$
# 验证Email地址:^\w+[-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
# 验证InternetURL:^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$ ;^[a-zA-z]+://(w+(-w+)*)(.(w+(-w+)*))*(?S*)?$
# 验证电话号码:^(\(\d{3,4}\)|\d{3,4}-)?\d{7,8}$:--正确格式为:XXXX-XXXXXXX,XXXX-XXXXXXXX,XXX-XXXXXXX,XXX-XXXXXXXX,XXXXXXX,XXXXXXXX。
# 验证身份证号(15位或18位数字):^\d{15}|\d{}18$
# 验证一年的12个月:^(0?[1-9]|1[0-2])$ 正确格式为:“01”-“09”和“1”“12”
# 验证一个月的31天:^((0?[1-9])|((1|2)[0-9])|30|31)$    正确格式为:01、09和1、31。
# 整数:^-?\d+$
# 非负浮点数(正浮点数 + 0):^\d+(\.\d+)?$
# 正浮点数   ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$
# 非正浮点数(负浮点数 + 0) ^((-\d+(\.\d+)?)|(0+(\.0+)?))$
# 负浮点数  ^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$
# 浮点数  ^(-?\d+)(\.\d+)?
# 符号表示意义:
# 匹配除换行符以外的任意字符
# \w    匹配字母或数字或下划线或汉字
# \s    匹配任意的空白符
# \d    匹配数字
# \b    匹配单词的开始或结束
# ^     匹配字符串的开始
# $     匹配字符串的结束
# [\u4e00-\u9fa5]{2,20} 匹配2-20个汉字
# 
# *     重复零次或更多次
# +     重复一次或更多次
# ?     重复零次或一次
# {n}   重复n次
# {n,}  重复n次或更多次
# {n,m}     重复n到m次
# 
# \W    匹配任意不是字母,数字,下划线,汉字的字符
# \S    匹配任意不是空白符的字符
# \D    匹配任意非数字的字符
# \B    匹配不是单词开头或结束的位置
# [^x]  匹配除了x以外的任意字符
# [^aeiou]  匹配除了aeiou这几个字母以外的任意字符
# 
# (exp)     匹配exp,并捕获文本到自动命名的组里
# (?<name>exp)  匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name'exp)
# (?:exp)   匹配exp,不捕获匹配的文本,也不给此分组分配组号
# 零宽断言
# (?=exp)   匹配exp前面的位置
# (?<=exp)  匹配exp后面的位置
# (?!exp)   匹配后面跟的不是exp的位置
# (?<!exp)  匹配前面不是exp的位置
# 注释
# (?#comment)   这种类型的组不对正则表达式的处理产生任何影响,用于提供注释让人阅读

2.3.4 基本概念

一个正则表达式通常被称为一个模式 (pattern),为用来描述或者匹配一系列符合某个句法规则的字符串。例如:Handel、Händel 和 Haendel 这三个字符串,都可以由“H(a|ä|ae)ndel”这个模式来描述。大部分正则表达式的形式都有如下的结构:

选择 | 竖直分隔符代表选择。例如“gray|grey”可以匹配grey或gray。

数量限定 某个字符后的数量限定符用来限定前面这个字符允许出现的个数。最常见的数量限定符包括“+”、“?”和“*”(不加数量限定则代表出现一次且仅出现一次):

+ 加号代表前面的字符必须至少出现一次。(1次、或多次)。例如,“goo+gle”可以匹配google、gooogle、goooogle等;

? 问号代表前面的字符最多只可以出现一次。(0次、或1次)。例如,“colou?r”可以匹配color或者colour;

* 星号代表前面的字符可以不出现,也可以出现一次或者多次。(0次、或1次、或多次)。例如,“0*42”可以匹配42、042、0042、00042等。

匹配 圆括号可以用来定义操作符的范围和优先度。例如,“gr(a|e)y”等价于“gray|grey”,“(grand)?father”匹配father和grandfather。

上述这些构造子都可以自由组合,因此,“H(ae?|ä)ndel”和“H(a|ae|ä)ndel”是相同的。

精确的语法可能因不同的工具或程序而异。

2.3.5 常用的正则表达式

常用的正则表达式主要有以下几种:

匹配中文字符的正则表达式: [\u4e00-\u9fa5]
    评注:匹配中文还真是个头疼的事,有了这个表达式就好办了哦 
获取日期正则表达式:\d{4}[年|\-|\.]\d{1,2}[月|\-|\.] \d{1,2}日?
    评注:可用来匹配大多数年月日信息。 
匹配双字节字符(包括汉字在内):[^\x00-\xff]
    评注:可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1) 
匹配空白行的正则表达式:\n\s*\r
    评注:可以用来删除空白行 
匹配HTML标记的正则表达式:<(\S*?) [^>]*>.*?</>|<.*? />
    评注:网上流传的版本太糟糕,上面这个也仅仅能匹配部分,对于复杂的嵌套标记依旧无能为力 
匹配首尾空白字符的正则表达式:^\s*|\s*$
    评注:可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式 
匹配Email地址的正则表达式:\w+([-+.]\w+)*@\w+([-.]\w+)* \.\w+([-.]\w+)*
    评注:表单验证时很实用 
匹配网址URL的正则表达式:[a-zA-z]+://[^\s]*
    评注:网上流传的版本功能很有限,上面这个基本可以满足需求 
匹配帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z] [a-zA-Z0-9_]{4,15}$
    评注:表单验证时很实用 
匹配国内电话号码:\d{4}-\d{7}|\d{3}-\d{8}
    评注:匹配形式如 0511-4405222 或 021-87888822 
匹配腾讯QQ号:[1-9][0-9]\{4,\}
    评注:腾讯QQ号从10000开始 
匹配中国邮政编码:[1-9]\d(?!\d)
    评注:中国邮政编码为6位数字 
匹配身份证:\d{17}[\d|X]|\d{15}
    评注:中国的身份证为15位或18位 
匹配ip地址:((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3} (2[0-4]\d|25[0-5]|[01]?\d\d?)。
    评注:提取ip地址时有用 
匹配特定数字:
    ^[1-9]\d*$ //匹配正整数
    ^-[1-9]\d*$ //匹配负整数
    ^-?[1-9]\d*$ //匹配整数
    ^[1-9]\d*|0$ //匹配非负整数(正整数 + 0)
    ^-[1-9]\d*|0$ //匹配非正整数(负整数 + 0)
    ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$ //匹配正浮点数
    ^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$ //匹配负浮点数
    ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$  //匹配浮点数
    ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$ //匹配非负浮点数(正浮点数 + 0)
    ^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$  //匹配非正浮点数(负浮点数 + 0)
    评注:处理大量数据时有用,具体应用时注意修正 
匹配特定字符串:
    ^[A-Za-z]+$ //匹配由26个英文字母组成的字符串
    ^[A-Z]+$ //匹配由26个英文字母的大写组成的字符串
    ^[a-z]+$ //匹配由26个英文字母的小写组成的字符串
    ^[A-Za-z0-9]+$ //匹配由数字和26个英文字母组成的字符串
    ^\w+$ //匹配由数字、26个英文字母或者下划线组成的字符串
    评注:最基本也是最常用的一些表达式 

2.3.6 stringr: Simple, Consistent Wrappers for Common String Operations

# 转义符号 \\

library(stringr)
pattern <- "a.b"
strings <- c("abb" , "a.b")

#fixed
str_detect(strings ,pattern) #pattern 被认为是正则表达式
str_detect(strings ,fixed(pattern)) # fixed 只是匹配这个字符,不是正则


#ignore.case #忽略大小写的影响
stringr2 <- c("ABB" , "aaB" , "aab")
str_detect(stringr2 , pattern) #默认的是对大小写敏感的
str_detect(stringr2 , ignore.case(pattern)) #忽略大小写的敏感

#invert_match #取出非pattern的对象的位置信息
numbers <- "1 and 2 and 4 and 456"
num_loc <- str_locate_all(numbers , "[0-9]+")[[1]] #返回所有数字的位置,开始的位置和结束的位置
num_loc <- str_locate_all(numbers, "456")


# subsub 
# str_sub(string, start = 1L, end = -1L)
str_sub(numbers , num_loc[ , "start"] , num_loc[ , "end"]) #从原字符串中把数字取出来

hw <- "Hadley Wickham"

str_sub(hw, 1, 6)
str_sub(hw, end = 6)
str_sub(hw, 8, 14)
str_sub(hw, 8)
str_sub(hw, c(1, 8), c(6, 14))

str_sub(hw, -1) # 从最后一个往前推,第1个
str_sub(hw, -7) # 从最后一个往前推,倒数第1个到第7个
str_sub(hw, end = -7) # 在倒数第7个结束

str_sub(hw, seq_len(str_length(hw)))
str_sub(hw, end = seq_len(str_length(hw)))


text_loc <- invert_match(num_loc)
str_sub(numbers , text_loc[ , "start"] , text_loc[ , "end"])
#从原字符串中把不是数字的对象取出来

#str_c 将不同的字符串连接起来
#str_join 同 str_c

str_c("Letter:" , letters)
paste0("Letter:" , letters)

str_c("Letter" , letters , sep = ": ")
paste("Letter" , sep = ": ", letters )
str_c(letters , " is for" , "...")

str_c(letters[-26] , " comes before " , letters[-1])
str_c(letters  , collapse = "")
str_c(letters  , collapse = ", ")

#str_count 返回每个字符中有几个匹配字符
fruit <- c("apple" , "banana" , "pear" , "pineapple")
str_count(fruit ,"a")
str_count(fruit ,"p")
str_count(fruit ,c("a" , "p"))


#str_detect 是否有匹配对象存在
fruit <- c("apple" , "banana" , "pear" , "pineapple")
str_detect(fruit , "a")
str_detect(fruit , "^a")#以a开头的对象
str_detect(fruit , "a$")#以a结尾的对象
str_detect(fruit , "[aeiou]")
str_detect("aecfg" , letters)

#str_dup  将对象进行重复多次

str_dup(fruit , 2) #所有的对象都重复2次
str_dup(fruit , 1:4) #分别重复1-4次
str_c("ba" , str_dup("na" , 0:5))

#str_extract 提取出满足第一个匹配对象的字符
shopping_list <- c("apples x4" , "flour" , "sugar" , "milk x2")
str_extract(shopping_list , "\\d") #\\d表示数字,提取出所有对象中数字部分
str_extract(shopping_list , "[0-9]+")#与上式相同

str_extract(shopping_list , "[a-z]+") #取出所有纯字母部分

str_extract_all(shopping_list , "[a-z]+") #取出所有纯字母部分

str_extract(shopping_list , "[a-z]{1,4}")#取出所有纯字母字符的前四个字符
str_extract(shopping_list , "\\b[a-z]{1,4}\\b") #取出纯字母长度为4的对象

#str_extract_all 提取出所有满足匹配对象的字符
str_extract_all(shopping_list , "[a-z]+") #列表形式返回
str_extract_all(shopping_list , "\\b[a-z]+\\b")


##str_length 计算每个对象的字符长度
str_length(letters) #都是1
nchar(letters)  #与上面结果一致
str_length(c("i" , "like" , "programming" , NA)) #NA的长度还是NA

##str_locate  返回字符第一处匹配模式的位置
fruit <- c("apple" , "banana" , "pear" , "pineapple" )
str_locate(fruit , "a") #虽然banana有很多a,但只返回第一个a的位置
str_locate(fruit , "e") #如果字符没有匹配模式的地方,则返回NA
str_locate(fruit , c("a","e" , "a" , "p"))


##str_locate_all 在str_locate的基础上匹配所有的位置

str_locate_all(fruit , "a") #以列表形式返回
str_locate_all(fruit , c("a","e" , "a" , "p"))

##str_match 一个字符只能匹配一次
strings <- c(" 219 733 8965", "329-293-8753 ", "banana", "595 794 7569",
            "387 287 6718", "apple", "233.398.9187 ", "482 952 3315",
            "239 923 8115", "842 566 4692", "Work: 579-499-7527", "$1000",
            "Home: 543.355.3679")
phone <- "([2-9][0-9]{2})[- .]([0-9]{3})[- .]([0-9]{4})"
#解读phone,([2-9][0-9]{2})第一数字是2-9之间的,第二个数字是0-9之间的
#{2}是说后面两个数字都是0-9之间的,[- .]表明中间的连接符是-后者空格
#或者.都可以。后同
str_extract(strings, phone) #提取出所有匹配模式的信息
str_match(strings, phone)  #比str_extracte返回的更详细,有模式的每个部分的信息



##st_match_all 同一个字符可以匹配所有满足模式的对象

strings <- c("Home: 219 733 8965. Work: 229-293-8753 ",
            "banana pear apple", "595 794 7569 / 387 287 6718")
phone <- "([2-9][0-9]{2})[- .]([0-9]{3})[- .]([0-9]{4})"
str_extract_all(strings, phone)
str_match_all(strings, phone)

##str_pad  填充一个字符
#可以指定填充的位置
rbind(
 str_pad("hadley", 30, "left"),
 str_pad("hadley", 30, "right"),
 str_pad("hadley", 30, "both")
)
# Longer strings are returned unchanged
#width参数可以指定返回字符串的长度,一旦width值小于字符串本身
#长度,则完全返回字符串,反之则返回width长度的字符。
str_pad("hadley", 3)
str_pad("hadley", 8)
#填空的物质默认为空格,也可以换成其他单字符
str_pad("hadley", width = 20 ,pad ="0")

#str_replace 替换字符第一个匹配模式的对象

fruits <- c("one apple", "two pears", "three bananas")
str_replace(fruits, "[aeiou]", "-") #每个字符中第一个的aeiou被替换为-

str_replace_all(fruits, "[aeiou]", "-") #替换所有满足模式的对象
str_replace(fruits, "([aeiou])", "") #
str_replace(fruits, "([aeiou])", "\\1\\1")
# \\1\\1是将对象复制一次,double
str_replace(fruits, "([aeiou])", "\\1\\1\\1")
# 复制三次
str_replace(fruits, "[aeiou]", c("1", "2", "3"))
str_replace(fruits, c("a", "e", "i"), "-")


##str_split 以pattern为识别符,来拆分原字符
#n是可以指定拆分成几部分
#列表形式返回

fruits <- c(
 "apples and oranges and pears and bananas",
 "pineapples and mangos and guavas"
)
str_split(fruits, " and ")
# Specify n to restrict the number of possible matches
str_split(fruits, " and ", n = 3)
str_split(fruits, " and ", n = 2)
# If n greater than number of pieces, no padding occurs
str_split(fruits, " and ", n = 5)

##str_split_fixed
##以矩阵形式返回
##参数n就是列数量(n列),结果不够n列,则用空字符替代
fruits <- c(
 "apples and oranges and pears and bananas",
 "pineapples and mangos and guavas"
)
str_split_fixed(fruits, " and ", n = 3)
str_split_fixed(fruits, " and ", n = 4)

##str_sub 从字符向量中提取部分字符
#参数start,默认为1,如果为负数,则从最后一个字符开始数
#end也是,默认为最后一个字符
hw <- "Hadley Wickham"
str_sub(hw, 1, 6)
str_sub(hw, end = 6)
str_sub(hw, 8, 14)
str_sub(hw, 8)
str_sub(hw, c(1, 8), c(6, 14))
str_sub(hw, -1) #start是最后一个字符,end默认的也是最后一个字符
str_sub(hw, -7) #start是倒数第七个字符,end是最后一个字符,返回7个
str_sub(hw, end = -7) #start默认为第一个字符,end为倒数第7个字符
str_sub(hw, seq_len(str_length(hw)))
str_sub(hw, end = seq_len(str_length(hw)))

# 提取第一个字符
full_name <- c("刘建国", "史芸", "欧阳真真")
family_name <- str_sub(full_name, start = 1, end = 1)
str_length(full_name)

##str_sub_replace #将部分字符用一个字符向量替换
#str_sub(string, start = 1L, end = -1L) <- value

x <- "BBCDEF"
str_sub(x, 1, 1) <- "A"; x
str_sub(x, -1, -1) <- "K"; x
str_sub(x, -2, -2) <- "GHIJ"; x
str_sub(x, 2, -2) <- ""; x

##str_trim 去除每个字符前后面的空白字符
#str_pad是添加空白字符,而这个是去除
str_trim(" String with trailing and leading white space\t")
str_trim("\n\nString with trailing and leading white space\n\n")

##str_wrap 设置字符串的格式
thanks_path <- file.path(R.home("doc"), "THANKS")
thanks <- str_c(readLines(thanks_path), collapse = "\n") #一行一个换行
thanks <- word(thanks, 1, 3, fixed("\n\n"))
cat(str_wrap(thanks), "\n")
cat(str_wrap(thanks, width = 40), "\n") #每行有40个字符
cat(str_wrap(thanks, width = 60, indent = 2), "\n") #每行有60个
#字符,且首行缩进2个字符
cat(str_wrap(thanks, width = 60, exdent = 2), "\n")
#每行60个字符,且除了首行,其他行都缩进2个字符

##word:从一个句子中提出单词
#word(string, start = 1L, end = start, sep = fixed(" "))
sentences <- c("Jane saw a cat", "Jane sat down")
word(sentences, 1) #提出第一个单词
word(sentences, 2) #提取第二个单词
word(sentences, -1) #提取最后一个单词
word(sentences, 2, -1) #提取第2个单词到最后一个单词
# Also vectorised over start and end
word(sentences[1], 1:3, -1)
word(sentences[1], 1, 1:4)

# Can define words by other separators
str <-'abc.def..123.4568.999'
word(str, 1, sep = fixed('..')) #满足fixed前面的对象
word(str, 2, sep = fixed('..'))#后面的对象

2.4 因子 forcats

人生有一道难题,那就是如何使一寸光阴等于一寸生命。在数据分析中也有一道难题,那就是如何自如的操作时间数据。R语言的基础包中提供了两种类型的时间数据,一类是Date日期数据,它不包括时间和时区信息,另一类是POSIXct/POSIXlt类型数据,其中包括了日期、时间和时区信息。一般来讲,R语言中建立时序数据是通过字符型转化而来,但由于时序数据形式多样,而且R中存贮格式也是五花八门,例如Date/ts/xts/zoo/tis/fts等等。用户很容易被一系列的数据格式所迷惑,所以时序数据的转化和操作并不是非常方便。所幸的是,我们有了lubridate包。lubridate包主要有两类函数,一类是处理时点数据(time instants),另一类是处理时段数据(time spans)。

2.5 日期与时间 datetime

人生有一道难题,那就是如何使一寸光阴等于一寸生命。在数据分析中也有一道难题,那就是如何自如的操作时间数据。R语言的基础包中提供了两种类型的时间数据,一类是Date日期数据,它不包括时间和时区信息,另一类是POSIXct/POSIXlt类型数据,其中包括了日期、时间和时区信息。一般来讲,R语言中建立时序数据是通过字符型转化而来,但由于时序数据形式多样,而且R中存贮格式也是五花八门,例如Date/ts/xts/zoo/tis/fts等等。用户很容易被一系列的数据格式所迷惑,所以时序数据的转化和操作并不是非常方便。所幸的是,我们有了lubridate包。lubridate包主要有两类函数,一类是处理时点数据(time instants),另一类是处理时段数据(time spans)。

2.5.1 参考

http://lubridate.tidyverse.org/

library(lubridate)
## 
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
## 
##     date, intersect, setdiff, union

2.5.1.1 时点类函数

它包括了解析、抽取、修改。

library("lubridate")

# 从字符型数据解析时间,会自动识别各种分隔符
x <- ymd('2018-08-03')
x
## [1] "2018-08-03"
# 观察x日期是一年中的第几天
yday(x)
## [1] 215
# 修改x日期中的月份为5月
month(x) <- 5
x
## [1] "2018-05-03"

2.5.2 截取时间点

Round, floor and ceiling methods for date-time objects.

用法

round_date(x, unit = "second")

floor_date(x, unit = "seconds") # 与 SQL 中 date_trunc 函数对应

ceiling_date(x, unit = "seconds", change_on_boundary = NULL)

参数

  • x a vector of date-time objects
  • unit
    a character string specifying the time unit or a multiple of a unit to be rounded to. Valid base units are second, minute, hour, day, week, month, bimonth, quarter, halfyear, or year. Arbitrary unique English abbreviations as in period constructor are also supported. Rounding to multiple of units (except weeks) is supported from v1.6.0. 时间单位可使用单数,也可使用复数,两者相同(兼容性)。 支持时间单位倍数,使其更为灵活。

示例

x <- as.POSIXct("2018-08-03 12:01:59.23")
print(x)
## [1] "2018-08-03 12:01:59 CST"
# round 是四舍五入
round_date(x, "second")
## [1] "2018-08-03 12:01:59 CST"
round_date(x, "minute")
## [1] "2018-08-03 12:02:00 CST"
round_date(x, "5 mins")
## [1] "2018-08-03 12:00:00 CST"
round_date(x, "hour")
## [1] "2018-08-03 12:00:00 CST"
round_date(x, "2 hours")
## [1] "2018-08-03 12:00:00 CST"
round_date(x, "day")
## [1] "2018-08-04 CST"
round_date(x, "week")
## [1] "2018-08-05 CST"
round_date(x, "month")
## [1] "2018-08-01 CST"
round_date(x, "bimonth")   # 等价于 round_date(x, "2 months")
## [1] "2018-09-01 CST"
round_date(x, "quarter")   # 等价于 round_date(x, "3 months")
## [1] "2018-07-01 CST"
round_date(x, "halfyear")  # 等价于 round_date(x, "6 months")
## [1] "2018-07-01 CST"
round_date(x, "year")
## [1] "2019-01-01 CST"
# floor 是向下取整,相当于丢弃指定单位的数值
# 与 SQL 中的 trunc 用法等价,故而 floor_date() 是最常使用的函数
floor_date(x, "second")
## [1] "2018-08-03 12:01:59 CST"
floor_date(x, "minute")
## [1] "2018-08-03 12:01:00 CST"
floor_date(x, "hour")
## [1] "2018-08-03 12:00:00 CST"
floor_date(x, "day")
## [1] "2018-08-03 CST"
floor_date(x, "week")
## [1] "2018-07-29 CST"
floor_date(x, "month")
## [1] "2018-08-01 CST"
floor_date(x, "bimonth")
## [1] "2018-07-01 CST"
floor_date(x, "quarter")
## [1] "2018-07-01 CST"
floor_date(x, "halfyear")
## [1] "2018-07-01 CST"
floor_date(x, "year")
## [1] "2018-01-01 CST"
# ceiling 是上取整
ceiling_date(x, "second")
## [1] "2018-08-03 12:02:00 CST"
ceiling_date(x, "minute")
## [1] "2018-08-03 12:02:00 CST"
ceiling_date(x, "5 mins")
## [1] "2018-08-03 12:05:00 CST"
ceiling_date(x, "hour")
## [1] "2018-08-03 13:00:00 CST"
ceiling_date(x, "day")
## [1] "2018-08-04 CST"
ceiling_date(x, "week")
## [1] "2018-08-05 CST"
ceiling_date(x, "month")
## [1] "2018-09-01 CST"
ceiling_date(x, "bimonth") 
## [1] "2018-09-01 CST"
ceiling_date(x, "quarter")
## [1] "2018-10-01 CST"
ceiling_date(x, "halfyear")
## [1] "2019-01-01 CST"
ceiling_date(x, "year")
## [1] "2019-01-01 CST"

2.5.3 时段类函数

它可以处理三类对象,分别是:

  • interval:最简单的时段对象,它由两个时点数据构成。
  • duration:去除了时间两端的信息,纯粹以秒为单位计算时段的长度,不考虑闰年和闰秒,它同时也兼容基本包中的difftime类型对象。
  • period:以较长的时钟周期来计算时段长度,它考虑了闰年和闰秒,适用于长期的时间计算。

以2012年为例,duration计算的一年是标准不变的365天,而period计算的一年就会变成366天。

有了时点和时段数据,就可以进行各种计算了。

2.5.4 从两个时点生成一个interval时段数据

# y <- new_interval(x,now())
# # 从interval格式转为duration格式
# as.duration(y)
# # 时点+时段生成一个新的时点
# now() + as.duration(y)
# # 10天后的时间数据
# now() + ddays(10)
# # 下面的两条语句很容易看出duration和period的区别,dyears(1)表示duration对象的一年,它永远是365天。而years(1)表示period对象的一年,它识别出2012是闰年,它有366天,所以得到正确的时点。
# ymd('20120101') + dyears(1)
# ymd('20120101') + years(1)

为了处理时区信息,lubridate包提供了三个函数:

tz:提取时间数据的时区

with_tz:

force_tz:将时间数据的时区强制转换为另一个时区

# 输入欧洲杯决赛在乌克兰的开场时间,再转为北京时间
# eurotime <- ymd_hms('2012-07-01 21:45:00',tz='EET')
# with_tz(eurotime,tzone='asia/shanghai')

最后来玩玩股票指数作为结束吧,在金融市场中谣传着一种日历效应。即在一周的第一天或者一年的第一个月份,股票会出现不错的上涨。让我们用热图来观察一下。首先是获取上证指数数据,然后根据不同的月份和星期数,将收益率汇集到不同的组中。将该组收益率的中位数映射到图形颜色上去。可以从下图看到,似乎并不存在明显周一效应或是一月效应。

2.5.5 时间间隔

span <- interval(ymd_hms("2009-01-01 00:00:00"), ymd_hms("2010-02-02 01:01:01"))


as.period(span)
## [1] "1y 1m 1d 1H 1M 1S"
as.period(span, unit = "day")
## [1] "397d 1H 1M 1S"
as.period(span, unit = "days")
## [1] "397d 1H 1M 1S"
# as.period(span, unit = "week")
# as.period(span, unit = "weeks")
as.period(span, unit = "month")
## [1] "13m 1d 1H 1M 1S"
as.period(span, unit = "months")
## [1] "13m 1d 1H 1M 1S"
as.period(span, unit = "year")
## [1] "1y 1m 1d 1H 1M 1S"
as.period(span, unit = "years")
## [1] "1y 1m 1d 1H 1M 1S"
as.period(span, unit = "day")
## [1] "397d 1H 1M 1S"
as.period(span, unit = "days")
## [1] "397d 1H 1M 1S"
# as.period(span, unit = "week") 
# as.period(span, unit = "weeks")
as.period(span, unit = "month")
## [1] "13m 1d 1H 1M 1S"
as.period(span, unit = "months")
## [1] "13m 1d 1H 1M 1S"
as.period(span, unit = "year")
## [1] "1y 1m 1d 1H 1M 1S"
as.period(span, unit = "years")
## [1] "1y 1m 1d 1H 1M 1S"
library(quantmod)
## Loading required package: xts
## Loading required package: zoo
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
## Loading required package: TTR
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
library(ggplot2)
library(lubridate)

# 读取上证指数历史数据
# getSymbols('^SSEC',src='yahoo',from = '1997-01-01')
# time <- ymd(as.character(index(SSEC)))
# open <- as.numeric(Op(SSEC))
# high <- as.numeric(Hi(SSEC))
# low <- as.numeric(Lo(SSEC))
# close <- as.numeric(Cl(SSEC))
# volume <- as.numeric(Vo(SSEC))
# 
# # 根据收盘和开盘计算当日收益率
# profit <- (close-open)/open
# 
# # 提取时间数据中的周数和月份
# wday <- wday(time)-1
# mday <-month(time)
# data <- data.frame(time,wday,mday,profit)
# p <- ggplot(data,aes(factor(mday),factor(wday),z=profit))
# 
# # 收益率的热图,图中颜色越浅,表示汇集到这个组中的收益率中位数越高
# p +stat_summary2d(fun=function(x) median(x))+
#   opts(legend.position = "top")+ labs(x='月份',y='星期')

2.6 24小时内的时间 hms

library(hms)
## 
## Attaching package: 'hms'
## The following object is masked from 'package:lubridate':
## 
##     hms

2.6.1 hms 格式的时间

hms 包提供了24小时内的时间表示与处理方式,时间用hh:mm:ss格式表示,底层是包含 24小时也就是86400秒的数值,单位是秒。

相当于对日期时间格式yyyy-MM-dd hh:mm:ss的时间,却掉了具体的日期,只包含时分秒,故而该时分秒无法代表哪天,就是没有时间轴上的哪个点。

看起来有点类似Java或者SQL中date_format(current_timestamp, 'HH:mm:ss')把时间转为字符,只保留时分秒。而最大的差别是SQL中将其转为时分秒后就变成了字符串格式,不能计算时间差,而hms时间格式是在tibble下,是time>类型,是可以对其做24小时内的时间和数值运算的。

最长应用的场景是,在分析24小时内的统计分布较为方便,再结合ggplot2时坐标轴使用了hms对象则其标度就是24小时时间制的,可以自动表示为hh:mm:ss,这是连续坐标轴,因为该时间的本质还是数值型,相对的,无论是Excel或是Tableau等作图可视化工具,都只能将其识别为字符串。

2.6.2 常用函数

格式转化:as_hms() 可以将格式为hh:mm:ss的字符转为<time>类型,也可以将dttime日期格式的时间,转为或者可以看做是提取时分秒变成为<time>类型。

四舍五入或者截尾舍弃:round_hms()trunc_hms()round是四舍五入,trunc是截尾舍弃。舍入或者截尾的单位参数都是secs秒数,还有一个可选参数 digits是精度,也就是比秒更小的精度,其实就是毫秒。

# round
round_hms(as_hms("12:34:56"), 5) # 按最小5秒单位舍入
## 12:34:55
#> 12:34:55  # 56秒,按5秒为单位,其下是55秒,其上是60秒,四舍五入之后结果是55秒

round_hms(as_hms("12:34:58"), 5) # 按最小5秒单位舍入
## 12:35:00
#> 12:35:00  # 58秒,按5秒为单位,其下是55秒,其上是60秒,四舍五入之后结果是60秒,暨分钟单位进1,秒单位归0

round_hms(as_hms("12:34:57.499"), 5)
## 12:34:55
#> 12:34:55 # 中间值,从数值上更靠近谁就舍入到谁上
round_hms(as_hms("12:34:57.500"), 5)
## 12:35:00
#> 12:35:00 # 中间值,从数值上更靠近谁就舍入到谁上

# trunc 
# 在 `lubridate` 中有 round_date() 四舍五入, floor_date() 向下取整, ceiling_date() 向下取整 三种用法
# 在 `hms` 中的 trunc_hms() 与 `lubridate` 中的 floor_date() 是类似地,截取舍弃类似向下取整
trunc_hms(as_hms("12:34:56"), 5)
## 12:34:55
#> 12:34:55  # 56秒,按5秒为单位,其下是55秒,其上是60秒,截尾舍弃暨向下取整之后结果是55秒

trunc_hms(as_hms("12:34:58"), 5)
## 12:34:55
#> 12:34:55  # 56秒,按5秒为单位,其下是55秒,其上是60秒,截尾舍弃暨向下取整之后结果依然是55秒

# 参数单位50秒就等于1分钟
round_hms(as_hms("12:34:56"), 60)
## 12:35:00
#> 12:35:00


# 参数单位0.25秒,就表示秒单位的精度是 0.25,也即是 1/4
round_hms(as_hms("12:34:56.78"), 0.25)
## 12:34:56.75
#> 12:34:56.75  # 从这个显示的结果看,秒后面的小数点,是真的小数点,为0.75秒,显示的不是毫秒,不是 75毫秒(因为 0.25秒,换算成毫秒应该是 250毫秒)



# 精度不常用

# 精度位数为 1 位,也就是精度为秒的小数后1为,也就是 0.1 秒
round_hms(as_hms("12:34:56.78"), digits = 1)
## 12:34:56.8
#> 12:34:56.8

# 精度位数为 0 位,精度不设定,单位为秒,则精度为 1 秒
round_hms(as_hms("12:34:56.78"), digits = 0)
## 12:34:57
#> 12:34:57

# 精度位数为 -1 位,因为单位为秒,精度负一相当于精度减少一个数量级,从1秒的精度,退化到10秒的精度
round_hms(as_hms("12:34:56.78"), digits = -1)
## 12:35:00
#> 12:35:00

# 精度位数为 -2 位,因为单位为秒,精度负二相当于精度减少二个数量级,从1秒的精度,退化到100秒的精度
round_hms(as_hms("12:34:56.78"), digits = -2)
## 12:35:00
#> 12:35:00



# 60秒,等价与1分钟;这里只有秒作为单位,故而要小时、30分钟等,就要换算成秒数
trunc_hms(as_hms("12:34:56"), 60)
## 12:34:00
#> 12:34:00

2.6.3 案例

2.6.3.1 订单分布

library(readr)
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:xts':
## 
##     first, last
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(lubridate)
library(hms)
library(ggplot2)



order_time_distribution <- read_csv("data/order_time_distribution.csv", 
               col_types = cols(order_id = col_character(), 
               order_time = col_character()))



order_time_distribution <- order_time_distribution %>%
  mutate(order_time = as_datetime(order_time),
         otime = as_hms(order_time),
         odate = as_date(order_time))


# 总订单15分钟分布
order_time_distribution %>%
  ggplot(aes(x = otime)) +
  geom_histogram(bins = 96) # 24 为一小时,96为一刻钟

# 每日统计
order_time_distribution %>%
  ggplot(aes(x = odate)) +
  geom_bar()

# 日订单排序统计


order_time_distribution %>%
  group_by(odate) %>%
  summarise(order_num = n()) %>%
  arrange(desc(order_num)) %>%
  head(4)
## # A tibble: 4 × 2
##   odate      order_num
##   <date>         <int>
## 1 2022-03-19        44
## 2 2022-03-04        40
## 3 2022-03-12        34
## 4 2022-01-16        29
# 4个组织
# odate      order_num
# <date>         <int>
# 1 2022-03-19        44
# 2 2022-03-04        40
# 3 2022-03-12        34
# 4 2022-01-16        29




# 四天的直方图:30分钟分布
order_time_distribution %>%
  filter(odate %in% as_date(c("2022-03-19","2022-03-04", "2022-03-12", "2022-01-16"))) %>%
  ggplot(aes(x = otime)) +
  geom_histogram(bins = 48) +  #坐标轴是24小时制,故而 bin = 24 为一小时,48 为30分钟, 96为一刻钟
  facet_wrap(~odate)


  1. 0-9↩︎