R语言中tidyverse基础知识汇总

tidyverse

group_by 分组统计

gather()和spread()

简单地说,gather()是列转行,而spread()是行转列。
请看下面的示例:

> df
   id class grade
1   1     a    81
2   2     b    82
3   3     a    83
4   4     b    84
5   5     a    85
6   6     b    86
7   7     a    87
8   8     b    88
9   9     a    89
10 10     b    90

*可以使用spread()将class中的某个字段的取值拆分成多个列。

> df_2 = df %>% spread(key = 'class', value = 'grade')
> df_2
  id  a  b
1  1 81 82
2  2 83 84
3  3 85 86
4  4 87 88
5  5 89 90

*也可以使用gather()将df_2中的a列和b列转换成某字段的值,就是把多列字段聚合在一起。

> df_2 %>% gather("a", "b", key = 'class', value = 'grade')
   id class grade
1   1     a    81
2   2     a    83
3   3     a    85
4   4     a    87
5   5     a    89
6   1     b    82
7   2     b    84
8   3     b    86
9   4     b    88
10  5     b    90

unite()和separate()

这个一般用于字符串,也可以用于数字,就是把一列分成两列,或者把两列拼成一列。
unite()默认使用"_"来拼接,也可以用sep来指定符号。

> df_3 = df_2 %>% unite('ab', 'a', 'b', sep='*')
  id    ab
1  1 81*82
2  2 83*84
3  3 85*86
4  4 87*88
5  5 89*90

separate()中一般用非数字字符的符号作为分割符,也可以用sep指定分割符,支持正则。

> df_4 = df_3 %>% separate("ab", into=c("a", "b"))
  id  a  b
1  1 81 82
2  2 83 84
3  3 85 86
4  4 87 88
5  5 89 90

注意,这里的数字都会被处理成字符串:

> str(df_4)
'data.frame':   5 obs. of  3 variables:
 $ id: num  1 2 3 4 5
 $ a : chr  "81" "83" "85" "87" ...
 $ b : chr  "82" "84" "86" "88" ...
> str(df_2)
'data.frame':   5 obs. of  3 variables:
 $ id: num  1 2 3 4 5
 $ a : int  81 83 85 87 89
 $ b : int  82 84 86 88 90

tibble

可以使用as_tibble把dataframe转换成tibble格式,也可以直接构造一个tibble。
如果你已经熟悉了data.frame(),请注意:tibble永远不会改变输入的类型(例如,它永远不会将字符串转换为因子!)

> a = tibble(a = c("yang", "y"), b = c(1,2))
> a
# A tibble: 2 x 2
  a         b
  <chr> <dbl>
1 yang      1
2 y         2
#rep()中的each参数是让每个数重复多少次,times参数是整体重复多少次。
> tibble(a = rep(1:3, each = 3))
# A tibble: 9 x 1
      a
  <int>
1     1
2     1
3     1
4     2
5     2
6     2
7     3
8     3
9     3

在引用字段时,可以使用a$a 或者 a[['a']]。

> a[['a']]
[1] "yang" "y"   

在管道符号中,要注意加个点,如'.$x' 或者'.[['a']]'。

> a %>% .[['a']]
[1] "yang" "y"

注意,这种表示方法在管道符中比较好用,因为可以用变量名代替字段名,这点很重要,以前不知道。

> x = 'a'
> a %>% .[[x]] #否则会把变量名直接当字段名,即认为字段为"x"
[1] "yang" "y" 

连接

 过滤连接 semi_join, anti_join

用于对数据进行筛选

扫描二维码关注公众号,回复: 4031072 查看本文章
> m
  id value
1  1     2
2  2     3
3  3     4
> n
  id value
1  0     2
2  2     3
3  3     5
#可以在向量中指定两边连接的字段,一个等号。
#对m表进行筛选,只保留匹配到的记录。
> m %>% semi_join(n, c("id" = "id")) 
  id value
1  2     3
2  3     4
#对m表进行筛选,只保留匹配不到的记录。
> m %>% anti_join(n, "id")
  id value
1  1     2

常用连接

inner_join(x, y)    merge(x, y)
left_join(x, y)     merge(x, y, all.x = TRUE)
right_join(x, y)    merge(x, y, all.y = TRUE),
full_join(x, y)     merge(x, y, all.x = TRUE, all.y = TRUE)

dplyr中的连接速度比merge()快很多。

集合操作

针对表中每行记录进行操作。
intersect(x, y):仅返回在两表中共同的行。
union(x, y):返回两表记录的并集。
setdiff(x, y):返回存在于x表中,但不在y表中的记录。(semi_join只考虑某个字段)

> union(m,n)
  id value
1  1     2
2  3     5
3  3     4
4  0     2
5  2     3
> intersect(m, n)
  id value
1  2     3
> setdiff(m,n)
  id value
1  1     2
2  3     4

字符串

library(tidyverse)
library(stringr)
R中的基础函数也可以处理字符串,但不好记忆,我每次都要上网查找,所以用stringr包,这个包要单独加载。
这个包中的函数一般以str_开头。

显示字符串原来内容,考虑转义。

> writeLines("\"")
"
> print("\"")
[1] "\""

长度

str_length(c("a", "R for data science", NA))
#> [1]  1 18 NA

拼接

可以对单个字符串拼接,也可以对含有多个字符串的向量拼接。

str_c("x", "y", sep = ", ")
#> [1] "x, y"

> name = c("yang", "jian")
> str_c(name, c("@", "$"))
[1] "yang@" "jian$"

> str_c("_", name, "_")
[1] "_yang_" "_jian_"

#str_c()是矢量化的,如果两向量的元素个数不相等,会循环执行,并抛出警告。
> str_c(name, c("@", "$", "#"))
[1] "yang@" "jian$" "yang#"
Warning message:
In stri_c(..., sep = sep, collapse = collapse, ignore_null = TRUE) :
  longer object length is not a multiple of shorter object length

# NA值处理
> str_c("a", NA)
[1] NA

> str_replace_na(NA)
[1] "NA" #字符串NA
> class(str_replace_na(NA))
[1] "character"

# 加入条件判断。空串不显示。
> a = 20
> str_c("a", "b", '', if(a>=18) "yj")
[1] "abyj"

#要将字符串向量折叠为单个字符串,请使用collapse:
str_c(c("x", "y", "z"), collapse = ", ")
#> [1] "x, y, z"

截取字符串

x <- c("Apple", "Banana", "Pear")
str_sub(x, 1, 3)
#> [1] "App" "Ban" "Pea"

# 修改和替换
str_sub(x, 1, 1) <- str_to_lower(str_sub(x, 1, 1))
x
#> [1] "apple"  "banana" "pear"

大小写及排序。(考虑语言环境)

str_to_lower()
str_to_upper()
str_to_title() #首字母大写
> str_sort(c("apple", "banana", "pear" ))
[1] "apple"  "banana" "pear"  
> str_order(c("apple", "banana", "pear" ))
[1] 1 2 3

正则表达式

str_view()和str_detect()
str_view()的返回结果不知道是啥?
str_detect()是检测与正则是否匹配,返回逻辑结果。
str_extract() 提取匹配。

> s = c("abc", "dc", "aa")
> str_detect(s, 'c$')
[1]  TRUE  TRUE FALSE
> s[str_detect(s, 'c$')]
[1] "abc" "dc"

#或者直接用下面这种方法
> str_subset(s,"c$")
[1] "abc" "dc" 

#还可以在数据框中进行筛选,直接作为管道符中的函数,类似于sum(),n()等。
> df_3
  id    ab
1  1 81*82
2  2 83*84
3  3 85*86
4  4 87*88
5  5 89*90
> df_3 %>% filter(str_detect(ab, "2$"))
  id    ab
1  1 81*82

#统计匹配的数量
> x <- c("apple", "banana", "pear")
> str_count(x, "an")
[1] 0 2 0
#统计ab列中8的次数。
> df_3 %>% mutate( count = str_count(ab,"8"))
  id    ab count
1  1 81*82     2
2  2 83*84     2
3  3 85*86     2
4  4 87*88     3
5  5 89*90     1

str_extract()仅提取第一个匹配项。我们可以通过首先选择具有多于1个匹配的所有句子来最容易地看到。
str_extract_all() 以列表形式返回所有匹配到的内容。
str_extract_all(, simplify = T) 以矩阵形式返回,用空串补充。

> x
[1] "apple"  "banana" "pear"  

> str_extract(x, "[a-e]")
[1] "a" "b" "e"

> str_extract_all(x, "[a-e]")
[[1]]
[1] "a" "e"

[[2]]
[1] "b" "a" "a" "a"

[[3]]
[1] "e" "a"

> str_extract_all(x, "[a-e]", simplify = T)
     [,1] [,2] [,3] [,4]
[1,] "a"  "e"  ""   ""  
[2,] "b"  "a"  "a"  "a" 
[3,] "e"  "a"  ""   "" 

str_match()给出每个单独的组件。它返回一个矩阵,而不是字符向量,其中一列用于完整匹配,后面是每个分组的匹配,也就正则表达式中小括号中匹配的内容。

> sen = c("the apple", "a pen", "the banana")
> noun <- "(a|the) ([^ ]+)" #第二个括号内容表示空格以外的任何字符,重复一次或者多次。
> str_match(sen, noun)
     [,1]         [,2]  [,3]    
[1,] "the apple"  "the" "apple" 
[2,] "a pen"      "a"   "pen"   
[3,] "the banana" "the" "banana"

str_replace()并str_replace_all()允许您用新字符串替换匹配项。最简单的用法是用固定的字符串替换模式。

x <- c("apple", "pear", "banana")
str_replace(x, "[aeiou]", "-")
#> [1] "-pple"  "p-ar"   "b-nana"
str_replace_all(x, "[aeiou]", "-")
#> [1] "-ppl-"  "p--r"   "b-n-n-"

随着str_replace_all()您可以通过提供一个名为向量执行多个替换:

x <- c("1 house", "2 cars", "3 people")
str_replace_all(x, c("1" = "one", "2" = "two", "3" = "three"))
#> [1] "one house"    "two cars"     "three people"

可以使用反向引用来插入匹配的组件,而不是使用固定字符串替换。

> head(sentences )
[1] "The birch canoe slid on the smooth planks." 
[2] "Glue the sheet to the dark blue background."
[3] "It's easy to tell the depth of a well."     
[4] "These days a chicken leg is a rare dish."   
[5] "Rice is often served in round bowls."       
[6] "The juice of lemons makes fine punch."  
# 匹配到每句的前三个单词,用\\1方式引用和处理。    
> sentences %>% 
+     str_replace("([^ ]+) ([^ ]+) ([^ ]+)", "\\1 \\3 \\2") %>% 
+     head(5)
[1] "The canoe birch slid on the smooth planks." 
[2] "Glue sheet the to the dark blue background."
[3] "It's to easy tell the depth of a well."     
[4] "These a days chicken leg is a rare dish."   
[5] "Rice often is served in round bowls." 

> sentences %>% 
+     str_replace("([^ ]+) ([^ ]+) ([^ ]+)", "\\1_\\3_\\2") %>% 
+     head(5)
[1] "The_canoe_birch slid on the smooth planks." 
[2] "Glue_sheet_the to the dark blue background."
[3] "It's_to_easy tell the depth of a well."     
[4] "These_a_days chicken leg is a rare dish."   
[5] "Rice_often_is served in round bowls."  

str_split()最多将一个字符串分解成多个。例如,我们可以将句子分成单词。
返回一个列表, 您可以使用simplify = TRUE返回矩阵。

> "the apple is red" %>% str_split(" ")
[[1]]
[1] "the"   "apple" "is"    "red" 

str_locate()和str_locate_all()给你查找匹配项的开始和结束位置。当其他任何功能都没有完全符合您的要求时,这些功能特别有用。您可以使用它str_locate()来查找匹配的模式,str_sub()以提取和/或修改它们。

> str_locate("app_cpp", "pp")
     start end
[1,]     2   3
> str_locate_all("app_cpp", "pp")
[[1]]
     start end
[1,]     2   3
[2,]     6   7

您可以使用其他参数regex()来控制匹配的详细信息。
ignore_case = TRUE,允许字符匹配大写或小写形式。这始终使用当前区域设置。
multiline = TRUE,允许^和$匹配每一行的开头和结尾,而不是整个字符串的开头和结尾。

x <- "Line 1\nLine 2\nLine 3"
str_extract_all(x, "^Line")[[1]]
#> [1] "Line"
str_extract_all(x, regex("^Line", multiline = TRUE))[[1]]
#> [1] "Line" "Line" "Line"

comments = TRUE,允许您使用注释和空格来使复杂的正则表达式更容易理解。
dotall = TRUE,允许.匹配所有内容,包括\n。

其他

apropos()搜索全局环境中可用的所有对象。如果您不太记得函数的名称,这将非常有用。
dir()列出目录中的所有文件。该pattern参数采用正则表达式,仅返回与模式匹配的文件名。

> dir(pattern='.*xlsx')
[1] "yj.xlsx.xlsx"

因子factors

library(tidyverse)
library(forcats)

#如果对月份排序,默认是按字母顺序排列的。如果按月份排序,应该使用因子。
x1 <- c("Dec", "Apr", "Jan", "Mar")
sort(x1)
#[1] "Apr" "Dec" "Jan" "Mar"
month_levels <- c(
  "Jan", "Feb", "Mar", "Apr", "May", "Jun", 
  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
)
y1 <- factor(x1, levels = month_levels)
sort(y1)
#[1] Jan Mar Apr Dec
# Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec

日期和时间

本章将重点介绍lubridate包,它使得更容易处理R. Rubridate中的日期和时间不是核心整数的一部分,因为您只需要在处理日期/时间时使用它。我们还需要nycflights13来练习数据。

library(tidyverse)
library(lubridate)
library(nycflights13)

字符串转换成日期

ymd("2017-01-31")
#> [1] "2017-01-31"
mdy("January 31st, 2017")
#> [1] "2017-01-31"
dmy("31-Jan-2017")
#> [1] "2017-01-31"
ymd(20170131)
#> [1] "2017-01-31"
ymd_hms("2017-01-31 20:11:59")
#> [1] "2017-01-31 20:11:59 UTC"
mdy_hm("01/31/2017 08:01")
#> [1] "2017-01-31 08:01:00 UTC"

如果用as.Date(),

> as.Date("2018-10-30")
[1] "2018-10-30"
> as.Date("20181030")
Error in charToDate(x) : 字符串的格式不够标准明确

日期和时间转换。

as_datetime(today())
#> [1] "2018-10-25 UTC"
as_date(now())
#> [1] "2018-10-25"

读取日期和时间组件

#"2018-11-08",周四
> t = today()
[1] "2018-11-08"
> year(t)
[1] 2018
> month(t)
[1] 11
> week(t)
[1] 45
> day(t)
[1] 8
> yday(t)
[1] 312
> mday(t)
[1] 8
> wday(t)
[1] 5 
> wday(ymd('20181111'))
[1] 1
> wday(ymd('20181111'), label = T)
[1] 周日
Levels: 周日 < 周一 < 周二 < 周三 < 周四 < 周五 < 周六
> wday(ymd('20181111'), label = T, abbr = F)
[1] 星期日
Levels: 星期日 < 星期一 < 星期二 < 星期三 < 星期四 < 星期五 < 星期六
> second(now())
[1] 6.943398
> hour(now())
[1] 16
> minute(now())
[1] 27

日期计算
时间跨度(duration)
在R中,当你减去两个日期时,你得到一个difftime对象。

> h_age <- today() - ymd(19791014)
> h_age
Time difference of 14270 days
# 以秒计算时间跨度。
> as.duration(h_age)
[1] "1232928000s (~39.07 years)"
> m = ymd('20181101')
> day(m) = day(m) -5
> m
[1] "2018-10-27"
> diff = ymd('20181101') - ymd('20181031')
> diff
Time difference of 1 days

可以用函数直接构造时间跨度函数,用于计算。

dseconds(15)
#> [1] "15s"
dminutes(10)
#> [1] "600s (~10 minutes)"
dhours(c(12, 24))
#> [1] "43200s (~12 hours)" "86400s (~1 days)"
ddays(0:5)
#> [1] "0s"                "86400s (~1 days)"  "172800s (~2 days)"
#> [4] "259200s (~3 days)" "345600s (~4 days)" "432000s (~5 days)"
dweeks(3)
#> [1] "1814400s (~3 weeks)"
dyears(1)
#> [1] "31536000s (~52.14 weeks)"

如果要计算明天的今天,

> today() + dyears(1)
[1] "2019-11-08"

周期(periods)
由于上面的时间跨度是精确到秒的,在计算时过于精确,在考虑时区时会出问题。为了解决这个问题,lubridate提供了周期,以更直观的方式工作。

seconds(15)
#> [1] "15S"
minutes(10)
#> [1] "10M 0S"
hours(c(12, 24))
#> [1] "12H 0M 0S" "24H 0M 0S"
days(7)
#> [1] "7d 0H 0M 0S"
months(1:6)
#> [1] "1m 0d 0H 0M 0S" "2m 0d 0H 0M 0S" "3m 0d 0H 0M 0S" "4m 0d 0H 0M 0S"
#> [5] "5m 0d 0H 0M 0S" "6m 0d 0H 0M 0S"
weeks(3)
#> [1] "21d 0H 0M 0S"
years(1)
#> [1] "1y 0m 0d 0H 0M 0S"

间隔(interval)

> ymd("2016-01-01") %--% ymd('2017-01-01') / days(1)
[1] 366
> (ymd('2017-01-01')-ymd("2016-01-01")) / ddays(1)
[1] 366

时区
查看当前时区

> Sys.timezone()
[1] "Asia/Taipei"

修改时区,分两种情况。一是保持时间不变,转换成其他时区的时间;二是直接修改时区,会使时间改变。

t1 = now()
t2 = with_tz(now(), tzone = "Australia/Lord_Howe")
t3 = force_tz(now(), tzone = "Australia/Lord_Howe")
> t1
[1] "2018-11-08 17:53:38 CST"
> t2
[1] "2018-11-08 20:53:38 +11"
> t3
[1] "2018-11-08 17:53:38 +11"
#请忽略程序运行的时间差,即:t2 = t1
> t2-t1 
Time difference of 0.002995968 secs
> t3-t1
Time difference of -2.999998 hours

逻辑条件
在if()条件的计算结果必须要么TRUE或FALSE。如果它是一个向量,你会收到一条警告信息; 如果它是NA 或NULL,你会得到一个错误。但ifelse()与if()不一样,两者区别很大。
在if的条件中,尽量使用||和&&,这是惰性求值。或者用any()(存在一个TRUE就返回TRUE)、all()(所有为TRUE才返回TRUE)来把多个逻辑结果转换成一个逻辑结果。

> T | c(T,T,F)
[1] TRUE TRUE TRUE
> T || c(T,T,F)
[1] TRUE

向量、列表、循环
列表结构中,用一个中括号得到的还是一个列表,可以用双层中括号或者$符号获取元素。

> yj = list(a = 3, b = c(1,2))
> str(yj)
List of 2
 $ a: num 3
 $ b: num [1:2] 1 2
> str(yj[[1]])
 num 3
> str(yj$a)
 num 3
> str(yj[['a']])
 num 3

while和for循环
while更有用一些,可以控制循环的次数。

for (i in seq_along(x)) {
  # body
}

# Equivalent to
i <- 1
while (i <= length(x)) {
  # body
  i <- i + 1 
}

map函数,类似于apply函数,但比apply()函数快。

map()     返回列表。
map_lgl() 返回一个逻辑向量。
map_int() 返回一个整数向量。
map_dbl() 返回双精度向量。
map_chr() 返回一个字符矢量。
map_df()  返回数据框。

比较运行速度,如果循环次数少的话,绝对时间差不大;如果循环总人数多,差距巨大。

f = function(x){x + 1}

t1 = now()
map_dbl(c(1:10000000),f)
now() -t1
#Time difference of 10.76235 secs

t1 = now()
sapply(c(1:10000000),f)
now() -t1
#Time difference of 20.59263 secs

t1 = now()
res = c()
for(x in c(1:10000000)){res = c(res, f(x))}
now() -t1
#跑了好几十分钟也跑不出来,放弃了。

map2()和pmap()
如果函数有多个参数:

> f2 = function(x,y){x+y}
> a = c(1,2,3)
> b = c(2,3,4)
> map2(a, b, f2)
[[1]]
[1] 3

[[2]]
[1] 5

[[3]]
[1] 7
> pmap(list(a, b), f2) #需要放在列表中。
[[1]]
[1] 3

[[2]]
[1] 5

[[3]]
[1] 7

invoke_map()函数,调用不同的函数。

f <- c("runif", "rnorm", "rpois")
param <- list(
  list(min = -1, max = 1), 
  list(sd = 5), 
  list(lambda = 10)
)
invoke_map(f, param, n = 5) %>% str()
#> List of 3
#>  $ : num [1:5] 0.762 0.36 -0.714 0.531 0.254
#>  $ : num [1:5] 3.07 -3.09 1.1 5.64 9.07
#>  $ : int [1:5] 9 14 8 9 7

reduce()accumulate()
reduce函数采用“二进制”函数(即具有两个主输入的函数),并将其重复应用于列表,直到只剩下一个元素。
下面示例中full_join()就有两个主输入的函数。

dfs <- list(
  age = tibble(name = "John", age = 30),
  sex = tibble(name = c("John", "Mary"), sex = c("M", "F")),
  trt = tibble(name = "Mary", treatment = "A")
)

dfs %>% reduce(full_join)
#> Joining, by = "name"
#> Joining, by = "name"
#> # A tibble: 2 x 4
#>   name    age sex   treatment
#>   <chr> <dbl> <chr> <chr>    
#> 1 John     30 M     <NA>     
#> 2 Mary     NA F     A

accumulate()与reduce()是类似的,但它保留了所有的中期结果。

> f2
function(x,y){x+y}
<bytecode: 0x0000000072c61b78>
> a = c(1,2,3,4)
> reduce(a, f2)
[1] 10
> res = accumulate(a, f2)
> res
[1]  1  3  6 10
> str(res)
 num [1:4] 1 3 6 10

谓词函数
keep()与discard(),保留或者丢弃数据;

iris %>% 
  keep(is.factor) %>% 
  str()
#> 'data.frame':    150 obs. of  1 variable:
#>  $ Species: Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...

some()和every(),返回T或者F,与any()、all()类似,用法不一样;
detect()找到谓词为true的第一个元素; detect_index()返回它的位置。

> keep(list("a", 1), is.character)
[[1]]
[1] "a"

> some(c("a", "b", 4), is.character)
[1] TRUE
> any(is.character(c("a", "b", 4)))
[1] TRUE

模型

library(tidyverse)
library(modelr)
options(na.action = na.warn)

htmlwidgets
有许多包提供htmlwidgets,包括:

dygraphs,http://rstudio.github.io/dygraphs/,用于交互式时间序列可视化。

DT,http://rstudio.github.io/DT/,用于交互式表格。

threejs,https://github.com/bwlewis/rthreejs用于交互式三维图。

DiagrammeR,http://rich-iannone.github.io/DiagrammeR/用于图表(如流程图和简单的节点链接图)。

networkD3,http://christophergandrud.github.io/networkD3/

要了解有关htmlwidgets的更多信息并查看提供它们的更完整的软件包列表,请访问http://www.htmlwidgets.org/。
leaflet: http://rstudio.github.io/leaflet/

leafletCN
https://blog.csdn.net/sinat_26917383/article/details/57083985

install.packages("leafletCN")
install.packages("rgeos")

R地图

R+大地图时代︱ leaflet/leafletCN 动态、交互式绘制地图(遍地代码图)
有中国各省市县的地图,使用非常方便,很容易得到一个不同颜色的中国地图,不同数据对应不同颜色。

猜你喜欢

转载自www.cnblogs.com/YangCool/p/9944217.html