使用tidyverse包处理数据
library(tidyverse)
## -- Attaching packages --------------------------------------- tidyverse 1.3.1 --
## v ggplot2 3.3.5 v purrr 0.3.4
## v tibble 3.1.3 v dplyr 1.0.7
## v tidyr 1.1.3 v stringr 1.4.0
## v readr 2.0.1 v forcats 0.5.1
## -- Conflicts ------------------------------------------ tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
Tidyverse简介与管道
- 可以直接使用函数
<- matrix(1:4, nrow = 2)
a <- t(a)
b b
## [,1] [,2]
## [1,] 1 2
## [2,] 3 4
apply(b, 1, mean)
## [1] 1.5 3.5
%>%
a t() %>%
apply(1, mean)
## [1] 1.5 3.5
- 实现管道操作的例子
<- function(x) {
f <- x^2 + 1
y return(y)
}f(1)
## [1] 2
<- function(x) {
g <- 4 * x - 1
y return(y)
}g(2)
## [1] 7
<- 1
x %>%
x f() %>%
g()
## [1] 7
- 常用的管道操作
c(1, 3, 4, 5, NA) %>% mean(., na.rm = TRUE) # na.rm表示除去缺失值
## [1] 3.25
# 或者也可以省略“.”,直接写成
c(1, 3, 4, 5, NA) %>% mean(na.rm = TRUE) # 两者的结果一致的
## [1] 3.25
数据读写
实例
library(readr)
<- read_csv(
df file = "data/sub_data/product.csv", col_names = TRUE, skip = 0,
n_max = 10
)
## Rows: 10 Columns: 4
## -- Column specification --------------------------------------------------------
## Delimiter: ","
## chr (1): date
## dbl (3): CLOSE, close, cs
##
## i Use `spec()` to retrieve the full column specification for this data.
## i Specify the column types or set `show_col_types = FALSE` to quiet this message.
df
## # A tibble: 10 x 4
## date CLOSE close cs
## <chr> <dbl> <dbl> <dbl>
## 1 2019/7/1 6274. 11.7 2.52
## 2 2019/7/2 6269. 11.6 2.52
## 3 2019/7/3 6230. 11.6 2.52
## 4 2019/7/4 6222. 11.6 2.52
## 5 2019/7/5 6197. 11.6 2.52
## 6 2019/7/8 6084. 11.4 2.52
## 7 2019/7/9 6040. 11.4 2.52
## 8 2019/7/10 6002. 11.4 2.52
## 9 2019/7/11 6016. 11.4 2.52
## 10 2019/7/12 6060. 11.5 2.52
改变参数,第一行不作为列名,并且跳过前3行
<- df <- read_csv(
df1 file = "data/sub_data/product.csv", col_names = FALSE,
skip = 3, n_max = 10
)
## Rows: 10 Columns: 4
## -- Column specification --------------------------------------------------------
## Delimiter: ","
## chr (1): X1
## dbl (3): X2, X3, X4
##
## i Use `spec()` to retrieve the full column specification for this data.
## i Specify the column types or set `show_col_types = FALSE` to quiet this message.
df1
## # A tibble: 10 x 4
## X1 X2 X3 X4
## <chr> <dbl> <dbl> <dbl>
## 1 2019/7/3 6230. 11.6 2.52
## 2 2019/7/4 6222. 11.6 2.52
## 3 2019/7/5 6197. 11.6 2.52
## 4 2019/7/8 6084. 11.4 2.52
## 5 2019/7/9 6040. 11.4 2.52
## 6 2019/7/10 6002. 11.4 2.52
## 7 2019/7/11 6016. 11.4 2.52
## 8 2019/7/12 6060. 11.5 2.52
## 9 2019/7/15 6051. 11.5 2.52
## 10 2019/7/16 6041. 11.6 2.52
观察对比可知,n_max中的参数指的是输出为值的行数
批量读取Excel表格的数据
- 首先需要导入全部Excel文件的完整路劲,可以任意嵌套,只要设置参数recursive为TRUE即可
<- list.files("data/sub_data", pattern = "xlsx", full.names = TRUE, recursive = TRUE)
files
files
## [1] "data/sub_data/animal.xlsx" "data/sub_data/class1.xlsx"
## [3] "data/sub_data/class2.xlsx" "data/sub_data/class3.xlsx"
- 接着使用map_dfr()在该路径上迭代,应用read_xlsx()到每个路径,再按行合并。
library(readxl)
<- map_dfr(files, read_xlsx)
df df
## # A tibble: 22 x 8
## type year heads 班级 姓名 性别 语文 数学
## <chr> <dbl> <dbl> <chr> <chr> <chr> <dbl> <dbl>
## 1 sheep 2016 2188 <NA> <NA> <NA> NA NA
## 2 cattle 1975 32 <NA> <NA> <NA> NA NA
## 3 camel 1985 515 <NA> <NA> <NA> NA NA
## 4 camel 2018 505 <NA> <NA> <NA> NA NA
## 5 camel 2008 206 <NA> <NA> <NA> NA NA
## 6 goat 1996 1999 <NA> <NA> <NA> NA NA
## 7 sheep 1999 2268 <NA> <NA> <NA> NA NA
## 8 cattle 2008 4952 <NA> <NA> <NA> NA NA
## 9 sheep 2006 3248 <NA> <NA> <NA> NA NA
## 10 sheep 2015 1314 <NA> <NA> <NA> NA NA
## # ... with 12 more rows
- 将数据写出到Excel文件当中去需要用到readr包中的write_csv或者writexl中的write_xlsx()。以写到csv文件为例子
write_csv(df, "data/sub_data/out.csv")
数据连接
行合并与列合并
library(dplyr) # 导入需要用到的函数包
# 首先创建两组数据
<- data.frame(
data1 namea = c("bob", "mike", "alen"),
value = c(1, 2, 3)
) data1
## namea value
## 1 bob 1
## 2 mike 2
## 3 alen 3
# 再创建一组数据
<- data.frame(
data2 namea = c("trump"),
value = c(4)
) data2
## namea value
## 1 trump 4
# 首先展示行合并,合并以上两个data,直接使用dplyr中的bind_row函数
bind_rows(
data1,
data2 )
## namea value
## 1 bob 1
## 2 mike 2
## 3 alen 3
## 4 trump 4
# 需要再创建一组和以上两个data行相同的数据进行列合并
<- data.frame(character = c("gentle", "small", "big"))
data3 data3
## character
## 1 gentle
## 2 small
## 3 big
# 对data1和data3进行合并
bind_cols(data1, data3)
## namea value character
## 1 bob 1 gentle
## 2 mike 2 small
## 3 alen 3 big
# 如果对行不同的数据进行合并,则会出现数据重复
bind_cols(data2, data3) # 所以列合并使规定行必须相同
## namea value character
## 1 trump 4 gentle
## 2 trump 4 small
## 3 trump 4 big
根据值匹配并合并数据框
<- tibble(
band name = c("MIck", "Jhon", "Paul"),
band = c("stone", "beatles", "beatles")
)<- tibble(
instrument name = c("Jhon", "Paul", "Keith"),
plays = c("guitar", "bass", "guitar")
) band
## # A tibble: 3 x 2
## name band
## <chr> <chr>
## 1 MIck stone
## 2 Jhon beatles
## 3 Paul beatles
instrument
## # A tibble: 3 x 2
## name plays
## <chr> <chr>
## 1 Jhon guitar
## 2 Paul bass
## 3 Keith guitar
- 左连接
%>%
band left_join(instrument, by = "name")
## # A tibble: 3 x 3
## name band plays
## <chr> <chr> <chr>
## 1 MIck stone <NA>
## 2 Jhon beatles guitar
## 3 Paul beatles bass
# 若两个表中的键列列名不同,用by=c(name1=name2),若根据多个键值匹配,用by=c(name1,name2)
- 右连接
%>%
band right_join(instrument, by = "name")
## # A tibble: 3 x 3
## name band plays
## <chr> <chr> <chr>
## 1 Jhon beatles guitar
## 2 Paul beatles bass
## 3 Keith <NA> guitar
- 全连接
%>%
band full_join(instrument, by = "name")
## # A tibble: 4 x 3
## name band plays
## <chr> <chr> <chr>
## 1 MIck stone <NA>
## 2 Jhon beatles guitar
## 3 Paul beatles bass
## 4 Keith <NA> guitar
- 内连接
%>%
band inner_join(instrument, by = "name")
## # A tibble: 2 x 3
## name band plays
## <chr> <chr> <chr>
## 1 Jhon beatles guitar
## 2 Paul beatles bass
- 半连接
%>%
band semi_join(instrument, by = "name")
## # A tibble: 2 x 2
## name band
## <chr> <chr>
## 1 Jhon beatles
## 2 Paul beatles
- 反链接
%>%
band anti_join(instrument, by = "name")
## # A tibble: 1 x 2
## name band
## <chr> <chr>
## 1 MIck stone
数据重塑
宽表变长表
#宽表变长表
=read_csv('data/sub_data/宽表变长表1.csv') w_chart
## Rows: 4 Columns: 4
## -- Column specification --------------------------------------------------------
## Delimiter: ","
## chr (1): 地区
## dbl (3): 2019, 2018, 2017
##
## i Use `spec()` to retrieve the full column specification for this data.
## i Specify the column types or set `show_col_types = FALSE` to quiet this message.
w_chart
## # A tibble: 4 x 4
## 地区 `2019` `2018` `2017`
## <chr> <dbl> <dbl> <dbl>
## 1 北京市 35371 33106 28015
## 2 天津市 14104 13363 18549
## 3 河北省 35105 32495 34016
## 4 黑龙江省 13613 12846 15903
library(tidyr)
%>%
w_chartpivot_longer(-地区,names_to = '年份',values_to = 'GDP')
## # A tibble: 12 x 3
## 地区 年份 GDP
## <chr> <chr> <dbl>
## 1 北京市 2019 35371
## 2 北京市 2018 33106
## 3 北京市 2017 28015
## 4 天津市 2019 14104
## 5 天津市 2018 13363
## 6 天津市 2017 18549
## 7 河北省 2019 35105
## 8 河北省 2018 32495
## 9 河北省 2017 34016
## 10 黑龙江省 2019 13613
## 11 黑龙江省 2018 12846
## 12 黑龙江省 2017 15903
长表变宽表
<- read_excel("data/sub_data/animal.xlsx")
l_chart l_chart
## # A tibble: 19 x 3
## type year heads
## <chr> <dbl> <dbl>
## 1 sheep 2016 2188
## 2 cattle 1975 32
## 3 camel 1985 515
## 4 camel 2018 505
## 5 camel 2008 206
## 6 goat 1996 1999
## 7 sheep 1999 2268
## 8 cattle 2008 4952
## 9 sheep 2006 3248
## 10 sheep 2015 1314
## 11 goat 2019 565
## 12 dog 2017 486
## 13 cattle 2007 432
## 14 cattle 2003 956
## 15 dog 1993 1336
## 16 dog 2018 1896
## 17 goat 1991 1556
## 18 sheep 1992 1887
## 19 sheep 1990 1665
使用函数使其变成宽表
%>%
l_chart pivot_wider(names_from = type, values_from = heads, values_fill = 0)
## # A tibble: 17 x 6
## year sheep cattle camel goat dog
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2016 2188 0 0 0 0
## 2 1975 0 32 0 0 0
## 3 1985 0 0 515 0 0
## 4 2018 0 0 505 0 1896
## 5 2008 0 4952 206 0 0
## 6 1996 0 0 0 1999 0
## 7 1999 2268 0 0 0 0
## 8 2006 3248 0 0 0 0
## 9 2015 1314 0 0 0 0
## 10 2019 0 0 0 565 0
## 11 2017 0 0 0 0 486
## 12 2007 0 432 0 0 0
## 13 2003 0 956 0 0 0
## 14 1993 0 0 0 0 1336
## 15 1991 0 0 0 1556 0
## 16 1992 1887 0 0 0 0
## 17 1990 1665 0 0 0 0
数据操作
读取数据
library(readxl)
=read_xlsx("data/ExamDatas_NAs.xlsx")
df df
## # A tibble: 50 x 8
## class name sex chinese math english moral science
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六1班 何娜 女 87 92 79 9 10
## 2 六1班 黄才菊 女 95 77 75 NA 9
## 3 六1班 陈芳妹 女 79 87 66 9 10
## 4 六1班 陈学勤 男 NA 79 66 9 10
## 5 六1班 陈祝贞 女 76 79 67 8 10
## 6 六1班 何小薇 女 83 73 65 8 9
## 7 六1班 雷旺 男 NA 80 68 8 9
## 8 六1班 陈欣越 男 57 80 60 9 9
## 9 六1班 黄亦婷 女 77 NA 54 8 10
## 10 六1班 陈媚 女 68 55 66 8 9
## # ... with 40 more rows
选择列
用列名或索引选择列
%>%
df select(name,sex,math) #或者select(2,3,5)
## # A tibble: 50 x 3
## name sex math
## <chr> <chr> <dbl>
## 1 何娜 女 92
## 2 黄才菊 女 77
## 3 陈芳妹 女 87
## 4 陈学勤 男 79
## 5 陈祝贞 女 79
## 6 何小薇 女 73
## 7 雷旺 男 80
## 8 陈欣越 男 80
## 9 黄亦婷 女 NA
## 10 陈媚 女 55
## # ... with 40 more rows
借助运算符选择列
- 1.用:选择连续的若干列
- 2.用!选择变量集合的余集(反选)。
- 3.&和|选择变量集合的交或并
- 4.c()合并多个选择
%>% select(2:4)#选第2至4列 df
## # A tibble: 50 x 3
## name sex chinese
## <chr> <chr> <dbl>
## 1 何娜 女 87
## 2 黄才菊 女 95
## 3 陈芳妹 女 79
## 4 陈学勤 男 NA
## 5 陈祝贞 女 76
## 6 何小薇 女 83
## 7 雷旺 男 NA
## 8 陈欣越 男 57
## 9 黄亦婷 女 77
## 10 陈媚 女 68
## # ... with 40 more rows
%>% select(!chinese)#不选语文成绩 df
## # A tibble: 50 x 7
## class name sex math english moral science
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 六1班 何娜 女 92 79 9 10
## 2 六1班 黄才菊 女 77 75 NA 9
## 3 六1班 陈芳妹 女 87 66 9 10
## 4 六1班 陈学勤 男 79 66 9 10
## 5 六1班 陈祝贞 女 79 67 8 10
## 6 六1班 何小薇 女 73 65 8 9
## 7 六1班 雷旺 男 80 68 8 9
## 8 六1班 陈欣越 男 80 60 9 9
## 9 六1班 黄亦婷 女 NA 54 8 10
## 10 六1班 陈媚 女 55 66 8 9
## # ... with 40 more rows
借助选择助手函数
%>%
df select(everything())#选择所有列
## # A tibble: 50 x 8
## class name sex chinese math english moral science
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六1班 何娜 女 87 92 79 9 10
## 2 六1班 黄才菊 女 95 77 75 NA 9
## 3 六1班 陈芳妹 女 79 87 66 9 10
## 4 六1班 陈学勤 男 NA 79 66 9 10
## 5 六1班 陈祝贞 女 76 79 67 8 10
## 6 六1班 何小薇 女 83 73 65 8 9
## 7 六1班 雷旺 男 NA 80 68 8 9
## 8 六1班 陈欣越 男 57 80 60 9 9
## 9 六1班 黄亦婷 女 77 NA 54 8 10
## 10 六1班 陈媚 女 68 55 66 8 9
## # ... with 40 more rows
%>%
df select(last_col())#选择最后一列
## # A tibble: 50 x 1
## science
## <dbl>
## 1 10
## 2 9
## 3 10
## 4 10
## 5 10
## 6 9
## 7 9
## 8 9
## 9 10
## 10 9
## # ... with 40 more rows
%>% select(contains("a"))#选择包含“a”的列名 df
## # A tibble: 50 x 4
## class name math moral
## <chr> <chr> <dbl> <dbl>
## 1 六1班 何娜 92 9
## 2 六1班 黄才菊 77 NA
## 3 六1班 陈芳妹 87 9
## 4 六1班 陈学勤 79 9
## 5 六1班 陈祝贞 79 8
## 6 六1班 何小薇 73 8
## 7 六1班 雷旺 80 8
## 8 六1班 陈欣越 80 9
## 9 六1班 黄亦婷 NA 8
## 10 六1班 陈媚 55 8
## # ... with 40 more rows
删除列(“-”)
%>% select(-c(name,chinese,science))#或者select(-ends_with("e")) df
## # A tibble: 50 x 5
## class sex math english moral
## <chr> <chr> <dbl> <dbl> <dbl>
## 1 六1班 女 92 79 9
## 2 六1班 女 77 75 NA
## 3 六1班 女 87 66 9
## 4 六1班 男 79 66 9
## 5 六1班 女 79 67 8
## 6 六1班 女 73 65 8
## 7 六1班 男 80 68 8
## 8 六1班 男 80 60 9
## 9 六1班 女 NA 54 8
## 10 六1班 女 55 66 8
## # ... with 40 more rows
%>% select(math,everything(),-ends_with("e"))#依次进行对应操作 df
## # A tibble: 50 x 5
## math class sex english moral
## <dbl> <chr> <chr> <dbl> <dbl>
## 1 92 六1班 女 79 9
## 2 77 六1班 女 75 NA
## 3 87 六1班 女 66 9
## 4 79 六1班 男 66 9
## 5 79 六1班 女 67 8
## 6 73 六1班 女 65 8
## 7 80 六1班 男 68 8
## 8 80 六1班 男 60 9
## 9 NA 六1班 女 54 8
## 10 55 六1班 女 66 8
## # ... with 40 more rows
调整列的顺序
列是根据被选择的顺序排列:
%>% select(ends_with("e"),math,name,class,sex) df
## # A tibble: 50 x 6
## name chinese science math class sex
## <chr> <dbl> <dbl> <dbl> <chr> <chr>
## 1 何娜 87 10 92 六1班 女
## 2 黄才菊 95 9 77 六1班 女
## 3 陈芳妹 79 10 87 六1班 女
## 4 陈学勤 NA 10 79 六1班 男
## 5 陈祝贞 76 10 79 六1班 女
## 6 何小薇 83 9 73 六1班 女
## 7 雷旺 NA 9 80 六1班 男
## 8 陈欣越 57 9 80 六1班 男
## 9 黄亦婷 77 10 NA 六1班 女
## 10 陈媚 68 9 55 六1班 女
## # ... with 40 more rows
everything()返回未被选择的所有列,将某一列移到第一列时很方便:
%>% select(math,everything()) df
## # A tibble: 50 x 8
## math class name sex chinese english moral science
## <dbl> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 92 六1班 何娜 女 87 79 9 10
## 2 77 六1班 黄才菊 女 95 75 NA 9
## 3 87 六1班 陈芳妹 女 79 66 9 10
## 4 79 六1班 陈学勤 男 NA 66 9 10
## 5 79 六1班 陈祝贞 女 76 67 8 10
## 6 73 六1班 何小薇 女 83 65 8 9
## 7 80 六1班 雷旺 男 NA 68 8 9
## 8 80 六1班 陈欣越 男 57 60 9 9
## 9 NA 六1班 黄亦婷 女 77 54 8 10
## 10 55 六1班 陈媚 女 68 66 8 9
## # ... with 40 more rows
用relocate()函数,将选择的列移到某列之前或之后,基本语法为:relocate(.data,…,.before,.after)
%>%
df relocate(where(is.numeric),.after=name)#将数值列移到name列的后面
## # A tibble: 50 x 8
## class name chinese math english moral science sex
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
## 1 六1班 何娜 87 92 79 9 10 女
## 2 六1班 黄才菊 95 77 75 NA 9 女
## 3 六1班 陈芳妹 79 87 66 9 10 女
## 4 六1班 陈学勤 NA 79 66 9 10 男
## 5 六1班 陈祝贞 76 79 67 8 10 女
## 6 六1班 何小薇 83 73 65 8 9 女
## 7 六1班 雷旺 NA 80 68 8 9 男
## 8 六1班 陈欣越 57 80 60 9 9 男
## 9 六1班 黄亦婷 77 NA 54 8 10 女
## 10 六1班 陈媚 68 55 66 8 9 女
## # ... with 40 more rows
重命名列
set_names()为所有列设置新列名:
%>%
df set_names("班级","姓名","性别","语文",
"数学","英语","品德","科学")
## # A tibble: 50 x 8
## 班级 姓名 性别 语文 数学 英语 品德 科学
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六1班 何娜 女 87 92 79 9 10
## 2 六1班 黄才菊 女 95 77 75 NA 9
## 3 六1班 陈芳妹 女 79 87 66 9 10
## 4 六1班 陈学勤 男 NA 79 66 9 10
## 5 六1班 陈祝贞 女 76 79 67 8 10
## 6 六1班 何小薇 女 83 73 65 8 9
## 7 六1班 雷旺 男 NA 80 68 8 9
## 8 六1班 陈欣越 男 57 80 60 9 9
## 9 六1班 黄亦婷 女 77 NA 54 8 10
## 10 六1班 陈媚 女 68 55 66 8 9
## # ... with 40 more rows
rename()只修改部分列名,格式为:新名=旧名
%>% rename(数学=math,科学=science) df
## # A tibble: 50 x 8
## class name sex chinese 数学 english moral 科学
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六1班 何娜 女 87 92 79 9 10
## 2 六1班 黄才菊 女 95 77 75 NA 9
## 3 六1班 陈芳妹 女 79 87 66 9 10
## 4 六1班 陈学勤 男 NA 79 66 9 10
## 5 六1班 陈祝贞 女 76 79 67 8 10
## 6 六1班 何小薇 女 83 73 65 8 9
## 7 六1班 雷旺 男 NA 80 68 8 9
## 8 六1班 陈欣越 男 57 80 60 9 9
## 9 六1班 黄亦婷 女 77 NA 54 8 10
## 10 六1班 陈媚 女 68 55 66 8 9
## # ... with 40 more rows
修改列
创建新列
用dplyr包中的mutate()创建或修改列,返回原数据框并增加新列;若改用transmute()则只返回增加的新列。
若只给新列1个值,则循环使用得到值相同的一列:
%>%
df mutate(new_col=5)
## # A tibble: 50 x 9
## class name sex chinese math english moral science new_col
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六1班 何娜 女 87 92 79 9 10 5
## 2 六1班 黄才菊 女 95 77 75 NA 9 5
## 3 六1班 陈芳妹 女 79 87 66 9 10 5
## 4 六1班 陈学勤 男 NA 79 66 9 10 5
## 5 六1班 陈祝贞 女 76 79 67 8 10 5
## 6 六1班 何小薇 女 83 73 65 8 9 5
## 7 六1班 雷旺 男 NA 80 68 8 9 5
## 8 六1班 陈欣越 男 57 80 60 9 9 5
## 9 六1班 黄亦婷 女 77 NA 54 8 10 5
## 10 六1班 陈媚 女 68 55 66 8 9 5
## # ... with 40 more rows
正常是以长度等于行数的向量赋值:
%>%
df mutate(new_col=1:n())#n()返回当前分组的样本数,未分组则为总行数
## # A tibble: 50 x 9
## class name sex chinese math english moral science new_col
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <int>
## 1 六1班 何娜 女 87 92 79 9 10 1
## 2 六1班 黄才菊 女 95 77 75 NA 9 2
## 3 六1班 陈芳妹 女 79 87 66 9 10 3
## 4 六1班 陈学勤 男 NA 79 66 9 10 4
## 5 六1班 陈祝贞 女 76 79 67 8 10 5
## 6 六1班 何小薇 女 83 73 65 8 9 6
## 7 六1班 雷旺 男 NA 80 68 8 9 7
## 8 六1班 陈欣越 男 57 80 60 9 9 8
## 9 六1班 黄亦婷 女 77 NA 54 8 10 9
## 10 六1班 陈媚 女 68 55 66 8 9 10
## # ... with 40 more rows
计算新列
用数据框的列计算新列,若修改当前列,只需要赋值给原列名。
%>%
df mutate(total=chinese+math+english+moral+science)#计算总分,注意不能使用sum、mean
## # A tibble: 50 x 9
## class name sex chinese math english moral science total
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六1班 何娜 女 87 92 79 9 10 277
## 2 六1班 黄才菊 女 95 77 75 NA 9 NA
## 3 六1班 陈芳妹 女 79 87 66 9 10 251
## 4 六1班 陈学勤 男 NA 79 66 9 10 NA
## 5 六1班 陈祝贞 女 76 79 67 8 10 240
## 6 六1班 何小薇 女 83 73 65 8 9 238
## 7 六1班 雷旺 男 NA 80 68 8 9 NA
## 8 六1班 陈欣越 男 57 80 60 9 9 215
## 9 六1班 黄亦婷 女 77 NA 54 8 10 NA
## 10 六1班 陈媚 女 68 55 66 8 9 206
## # ... with 40 more rows
在同一个mutate()中可以同时创建或计算多个列,它们是从前往后依次计算, 所以可以使用前面新创建的列,例如:
- 计算df中math列的中位数
- 创建标记math是否大于中位数的逻辑值列。
- 用as.numeric()将TRUE/FALSE转化为1/0
%>%
df mutate(med=median(math,na.rm=TRUE),
label=math>med,
label=as.numeric(label))#使用了前面创建的列label
## # A tibble: 50 x 10
## class name sex chinese math english moral science med label
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六1班 何娜 女 87 92 79 9 10 73 1
## 2 六1班 黄才菊 女 95 77 75 NA 9 73 1
## 3 六1班 陈芳妹 女 79 87 66 9 10 73 1
## 4 六1班 陈学勤 男 NA 79 66 9 10 73 1
## 5 六1班 陈祝贞 女 76 79 67 8 10 73 1
## 6 六1班 何小薇 女 83 73 65 8 9 73 0
## 7 六1班 雷旺 男 NA 80 68 8 9 73 1
## 8 六1班 陈欣越 男 57 80 60 9 9 73 1
## 9 六1班 黄亦婷 女 77 NA 54 8 10 73 NA
## 10 六1班 陈媚 女 68 55 66 8 9 73 0
## # ... with 40 more rows
修改多列
结合across()和选择列语法可以应用函数到多列,从而实现同时修改多列。
%>%
df mutate(across(everything(),as.character))#将所有列转化为字符型
## # A tibble: 50 x 8
## class name sex chinese math english moral science
## <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
## 1 六1班 何娜 女 87 92 79 9 10
## 2 六1班 黄才菊 女 95 77 75 <NA> 9
## 3 六1班 陈芳妹 女 79 87 66 9 10
## 4 六1班 陈学勤 男 <NA> 79 66 9 10
## 5 六1班 陈祝贞 女 76 79 67 8 10
## 6 六1班 何小薇 女 83 73 65 8 9
## 7 六1班 雷旺 男 <NA> 80 68 8 9
## 8 六1班 陈欣越 男 57 80 60 9 9
## 9 六1班 黄亦婷 女 77 <NA> 54 8 10
## 10 六1班 陈媚 女 68 55 66 8 9
## # ... with 40 more rows
= function(x) {#对所有数值列做归一化
rescale =range(x,na.rm=TRUE)
rng-rng[1])/(rng[2]-rng[1])
(x
}%>%
df mutate(across(where(is.numeric),rescale))
## # A tibble: 50 x 8
## class name sex chinese math english moral science
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六1班 何娜 女 0.843 0.974 1 0.875 1
## 2 六1班 黄才菊 女 1 0.776 0.926 NA 0.833
## 3 六1班 陈芳妹 女 0.686 0.908 0.759 0.875 1
## 4 六1班 陈学勤 男 NA 0.803 0.759 0.875 1
## 5 六1班 陈祝贞 女 0.627 0.803 0.778 0.75 1
## 6 六1班 何小薇 女 0.765 0.724 0.741 0.75 0.833
## 7 六1班 雷旺 男 NA 0.816 0.796 0.75 0.833
## 8 六1班 陈欣越 男 0.255 0.816 0.648 0.875 0.833
## 9 六1班 黄亦婷 女 0.647 NA 0.537 0.75 1
## 10 六1班 陈媚 女 0.471 0.487 0.759 0.75 0.833
## # ... with 40 more rows
替换NA
replace_na():实现用某个值替换一列中的所有NA值,该函数接受一个命名列表,其成分为列名=替换值:
%>%
starwars replace_na(list(hair_color="UNKNOWN",
height=mean(.$height,na.rm=TRUE)))
## # A tibble: 87 x 14
## name height mass hair_color skin_color eye_color birth_year sex gender
## <chr> <dbl> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr>
## 1 Luke S~ 172 77 blond fair blue 19 male mascu~
## 2 C-3PO 167 75 UNKNOWN gold yellow 112 none mascu~
## 3 R2-D2 96 32 UNKNOWN white, bl~ red 33 none mascu~
## 4 Darth ~ 202 136 none white yellow 41.9 male mascu~
## 5 Leia O~ 150 49 brown light brown 19 fema~ femin~
## 6 Owen L~ 178 120 brown, grey light blue 52 male mascu~
## 7 Beru W~ 165 75 brown light blue 47 fema~ femin~
## 8 R5-D4 97 32 UNKNOWN white, red red NA none mascu~
## 9 Biggs ~ 183 84 black light brown 24 male mascu~
## 10 Obi-Wa~ 182 77 auburn, wh~ fair blue-gray 57 male mascu~
## # ... with 77 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## # films <list>, vehicles <list>, starships <list>
fill():用前一个(或后一个)非缺失值填充NA。有些表在记录时,会省略与上一条记录相同的内容,如下表:
load("data/gap_data.rda")
::kable(gap_data,align="c") knitr
site | species | sample_num | bees_present |
---|---|---|---|
Bilpin | A. longiforlia | 1 | TRUE |
NA | NA | 2 | TRUE |
NA | NA | 3 | TRUE |
NA | A. elongata | 1 | TRUE |
NA | NA | 2 | FALSE |
NA | NA | 3 | TRUE |
Grose Vale | A. terminalis | 1 | FALSE |
NA | NA | 2 | FALSE |
NA | NA | 2 | TRUE |
tidyr包中的fill()适合处理这种结构的缺失值,默认是向下填充,即用上一个非缺失值填充
%>%
gap_data fill(site,species)
## # A tibble: 9 x 4
## site species sample_num bees_present
## <chr> <chr> <dbl> <lgl>
## 1 Bilpin A. longiforlia 1 TRUE
## 2 Bilpin A. longiforlia 2 TRUE
## 3 Bilpin A. longiforlia 3 TRUE
## 4 Bilpin A. elongata 1 TRUE
## 5 Bilpin A. elongata 2 FALSE
## 6 Bilpin A. elongata 3 TRUE
## 7 Grose Vale A. terminalis 1 FALSE
## 8 Grose Vale A. terminalis 2 FALSE
## 9 Grose Vale A. terminalis 2 TRUE
重新编码
%>%
df mutate(sex=if_else(sex=="男","M","F"))#用if_else()作是/否决策以确定用哪个值做重新编码
## # A tibble: 50 x 8
## class name sex chinese math english moral science
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六1班 何娜 F 87 92 79 9 10
## 2 六1班 黄才菊 F 95 77 75 NA 9
## 3 六1班 陈芳妹 F 79 87 66 9 10
## 4 六1班 陈学勤 M NA 79 66 9 10
## 5 六1班 陈祝贞 F 76 79 67 8 10
## 6 六1班 何小薇 F 83 73 65 8 9
## 7 六1班 雷旺 M NA 80 68 8 9
## 8 六1班 陈欣越 M 57 80 60 9 9
## 9 六1班 黄亦婷 F 77 NA 54 8 10
## 10 六1班 陈媚 F 68 55 66 8 9
## # ... with 40 more rows
%>%
df mutate(math=case_when(math>=75~"High",#用case_when()做更多条件下的重新编码,避免使用很多if_else()嵌套
>=60~"Middle",
mathTRUE~"Low"))
## # A tibble: 50 x 8
## class name sex chinese math english moral science
## <chr> <chr> <chr> <dbl> <chr> <dbl> <dbl> <dbl>
## 1 六1班 何娜 女 87 High 79 9 10
## 2 六1班 黄才菊 女 95 High 75 NA 9
## 3 六1班 陈芳妹 女 79 High 66 9 10
## 4 六1班 陈学勤 男 NA High 66 9 10
## 5 六1班 陈祝贞 女 76 High 67 8 10
## 6 六1班 何小薇 女 83 Middle 65 8 9
## 7 六1班 雷旺 男 NA High 68 8 9
## 8 六1班 陈欣越 男 57 High 60 9 9
## 9 六1班 黄亦婷 女 77 Low 54 8 10
## 10 六1班 陈媚 女 68 Low 66 8 9
## # ... with 40 more rows
case_when()中用的是公式形式
- 1.左边是返回TRUE或FALSE的表达式或函数
- 2.右边是若左边表达式为TRUE,则重新编码的值,也可以是表达式或函数
- 3.每个分支条件将从上到下的计算,并接受第一个TURE条件
- 4.最后一个分支直接用TRUE表示若其它条件都不为TRUE时怎么做
筛选行
即按行选择数据子集,包括删除行、对行切片、过滤行
set.seed(123)
=df %>%
df_dupslice_sample(n=60,replace = TRUE)#创建一个包含重复行的数据框(有放回抽取69行)
删除行
%>%
df_dup distinct()#删除重复行(只保留第1个,删除其余)
## # A tibble: 35 x 8
## class name sex chinese math english moral science
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六4班 周婵 女 92 94 77 10 9
## 2 六2班 杨远芸 女 93 80 68 9 10
## 3 六2班 陈华健 男 92 84 70 9 10
## 4 六1班 陈芳妹 女 79 87 66 9 10
## 5 六5班 陆曼 女 88 84 69 8 10
## 6 六5班 胡玉洁 女 74 61 52 9 6
## 7 六5班 容唐 女 83 71 56 9 7
## 8 六4班 关小孟 男 84 78 49 8 5
## 9 六3班 洪琦希 男 NA 31 69 6 4
## 10 六3班 刘虹均 男 72 23 74 3 6
## # ... with 25 more rows
%>%
df_dup distinct(sex,math,.keep_all=TRUE)#只根据sex和math判定重复
## # A tibble: 32 x 8
## class name sex chinese math english moral science
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六4班 周婵 女 92 94 77 10 9
## 2 六2班 杨远芸 女 93 80 68 9 10
## 3 六2班 陈华健 男 92 84 70 9 10
## 4 六1班 陈芳妹 女 79 87 66 9 10
## 5 六5班 陆曼 女 88 84 69 8 10
## 6 六5班 胡玉洁 女 74 61 52 9 6
## 7 六5班 容唐 女 83 71 56 9 7
## 8 六4班 关小孟 男 84 78 49 8 5
## 9 六3班 洪琦希 男 NA 31 69 6 4
## 10 六3班 刘虹均 男 72 23 74 3 6
## # ... with 22 more rows
#默认只返回选择的列,要返回所有列,需要设置参数.keep_all=TRUE
%>%
df_dup drop_na()#删除所有包含NA的行
## # A tibble: 38 x 8
## class name sex chinese math english moral science
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六4班 周婵 女 92 94 77 10 9
## 2 六2班 杨远芸 女 93 80 68 9 10
## 3 六2班 陈华健 男 92 84 70 9 10
## 4 六1班 陈芳妹 女 79 87 66 9 10
## 5 六5班 陆曼 女 88 84 69 8 10
## 6 六5班 胡玉洁 女 74 61 52 9 6
## 7 六5班 容唐 女 83 71 56 9 7
## 8 六4班 关小孟 男 84 78 49 8 5
## 9 六2班 陈华健 男 92 84 70 9 10
## 10 六3班 刘虹均 男 72 23 74 3 6
## # ... with 28 more rows
%>%
df_dup drop_na(sex:math)#也可以只删除某些列包含NA的行
## # A tibble: 50 x 8
## class name sex chinese math english moral science
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六4班 周婵 女 92 94 77 10 9
## 2 六2班 杨远芸 女 93 80 68 9 10
## 3 六2班 陈华健 男 92 84 70 9 10
## 4 六1班 陈芳妹 女 79 87 66 9 10
## 5 六5班 陆曼 女 88 84 69 8 10
## 6 六5班 胡玉洁 女 74 61 52 9 6
## 7 六5班 容唐 女 83 71 56 9 7
## 8 六4班 关小孟 男 84 78 49 8 5
## 9 六2班 陈华健 男 92 84 70 9 10
## 10 六3班 刘虹均 男 72 23 74 3 6
## # ... with 40 more rows
对行切片:slice_*() ####
%>%
df slice_max(math,n=5)#选择math列值中前5大的行
## # A tibble: 5 x 8
## class name sex chinese math english moral science
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六4班 周婵 女 92 94 77 10 9
## 2 六4班 陈丽丽 女 87 93 NA 8 6
## 3 六1班 何娜 女 87 92 79 9 10
## 4 六5班 符苡榕 女 85 89 76 9 NA
## 5 六2班 黄祖娜 女 94 88 75 10 10
%>%
df slice_sample(n=3)#随机选择3行
## # A tibble: 3 x 8
## class name sex chinese math english moral science
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六3班 刘虹均 男 72 23 74 3 6
## 2 六4班 周婵 女 92 94 77 10 9
## 3 六2班 雷开茂 男 83 NA 45 9 7
用filter()根据值或条件筛选行
%>%
df_dup filter(sex=="男",math>80)#多个条件之间用","隔开,相当于and
## # A tibble: 8 x 8
## class name sex chinese math english moral science
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六2班 陈华健 男 92 84 70 9 10
## 2 六2班 陈华健 男 92 84 70 9 10
## 3 六4班 <NA> 男 84 85 52 9 8
## 4 六2班 陈华健 男 92 84 70 9 10
## 5 六4班 李小龄 男 90 87 69 10 10
## 6 六4班 李小龄 男 90 87 69 10 10
## 7 六4班 杨昌晟 男 84 85 64 8 10
## 8 <NA> 徐达政 男 90 86 72 9 10
%>%
df_dup filter(sex=="女",(is.na(english)|math>80))
## # A tibble: 11 x 8
## class name sex chinese math english moral science
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六4班 周婵 女 92 94 77 10 9
## 2 六1班 陈芳妹 女 79 87 66 9 10
## 3 六5班 陆曼 女 88 84 69 8 10
## 4 六5班 陆曼 女 88 84 69 8 10
## 5 六2班 徐雅琦 女 92 86 72 NA 9
## 6 六5班 陆曼 女 88 84 69 8 10
## 7 六5班 符苡榕 女 85 89 76 9 NA
## 8 六2班 徐雅琦 女 92 86 72 NA 9
## 9 六4班 陈丽丽 女 87 93 NA 8 6
## 10 六3班 江佳欣 女 80 69 NA 6 5
## 11 六5班 符苡榕 女 85 89 76 9 NA
%>%
df_dup filter(between(math,70,80))#闭区间
## # A tibble: 15 x 8
## class name sex chinese math english moral science
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六2班 杨远芸 女 93 80 68 9 10
## 2 六5班 容唐 女 83 71 56 9 7
## 3 六4班 关小孟 男 84 78 49 8 5
## 4 六1班 陈祝贞 女 76 79 67 8 10
## 5 六1班 陈欣越 男 57 80 60 9 9
## 6 六1班 雷旺 男 NA 80 68 8 9
## 7 六4班 林传顺 男 85 75 52 NA 9
## 8 六2班 林师满 男 70 74 25 8 10
## 9 六5班 容唐 女 83 71 56 9 7
## 10 六2班 杨远芸 女 93 80 68 9 10
## 11 六1班 雷旺 男 NA 80 68 8 9
## 12 六1班 雷旺 男 NA 80 68 8 9
## 13 六1班 陈祝贞 女 76 79 67 8 10
## 14 六1班 陈欣越 男 57 80 60 9 9
## 15 六2班 杨远芸 女 93 80 68 9 10
在限定范围内根据条件筛选
结合across()和选择列语法,可以在限定列范围内,根据应用函数得到的结果作为条件筛选行。
4:6]%>%
df[,filter(across(everything(),~.x>75))#选出所有列范围内,所有值都>75的行
## # A tibble: 3 x 3
## chinese math english
## <dbl> <dbl> <dbl>
## 1 87 92 79
## 2 92 94 77
## 3 85 89 76
%>%
df filter(if_any(where(is.numeric),~.x>90))#选出数列范围内,存在值>90的行
## # A tibble: 8 x 8
## class name sex chinese math english moral science
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六1班 何娜 女 87 92 79 9 10
## 2 六1班 黄才菊 女 95 77 75 NA 9
## 3 六2班 黄祖娜 女 94 88 75 10 10
## 4 六2班 徐雅琦 女 92 86 72 NA 9
## 5 六2班 陈华健 男 92 84 70 9 10
## 6 六2班 杨远芸 女 93 80 68 9 10
## 7 六4班 周婵 女 92 94 77 10 9
## 8 六4班 陈丽丽 女 87 93 NA 8 6
对行进行排序
使用arrange()进行排序,默认是递增
%>%
df_dup arrange(math,sex)
## # A tibble: 60 x 8
## class name sex chinese math english moral science
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六3班 邹嘉伟 男 67 18 62 8 NA
## 2 六3班 刘虹均 男 72 23 74 3 6
## 3 六3班 刘虹均 男 72 23 74 3 6
## 4 六3班 黄凯丽 女 70 23 61 4 4
## 5 六3班 黄凯丽 女 70 23 61 4 4
## 6 六3班 黄凯丽 女 70 23 61 4 4
## 7 六3班 黄凯丽 女 70 23 61 4 4
## 8 六3班 黄凯丽 女 70 23 61 4 4
## 9 六3班 陈逾革 男 47 24 67 2 5
## 10 六3班 陈逾革 男 47 24 67 2 5
## # ... with 50 more rows
%>%
df_dup arrange(desc(math))#递减排序
## # A tibble: 60 x 8
## class name sex chinese math english moral science
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六4班 周婵 女 92 94 77 10 9
## 2 六4班 陈丽丽 女 87 93 NA 8 6
## 3 六5班 符苡榕 女 85 89 76 9 NA
## 4 六5班 符苡榕 女 85 89 76 9 NA
## 5 六1班 陈芳妹 女 79 87 66 9 10
## 6 六4班 李小龄 男 90 87 69 10 10
## 7 六4班 李小龄 男 90 87 69 10 10
## 8 六2班 徐雅琦 女 92 86 72 NA 9
## 9 六2班 徐雅琦 女 92 86 72 NA 9
## 10 <NA> 徐达政 男 90 86 72 9 10
## # ... with 50 more rows
分组汇总
分组汇总,相当于Excel的透视表功能
当数据框被分组时,上述一些操作就是分别在每个分组上独立执行;
可以认为数据框被分组,相当于被拆分成更小的数据框(并没有实际拆分); #### 创建分组 #### 用group_by()创建分组,只是对数据框增加了分组信息(用group_keys()查看),并不是真的将数据分割为多个数据框。
=df %>%
df_grpgroup_by(sex)
group_keys(df_grp)#分组键值(唯一识别分组)
## # A tibble: 3 x 1
## sex
## <chr>
## 1 男
## 2 女
## 3 <NA>
group_indices(df_grp)#查看每一行属于哪一分组
## [1] 2 2 2 1 2 2 1 1 2 2 2 2 1 1 2 1 1 1 2 1 2 2 2 1 1 1 2 1 1 2 2 1 2 1 1 1 1 2
## [39] 1 2 2 2 2 2 1 1 3 1 1 2
group_rows(df_grp)#查看每一组包含哪些行
## <list_of<integer>[3]>
## [[1]]
## [1] 4 7 8 13 14 16 17 18 20 24 25 26 28 29 32 34 35 36 37 39 45 46 48 49
##
## [[2]]
## [1] 1 2 3 5 6 9 10 11 12 15 19 21 22 23 27 30 31 33 38 40 41 42 43 44 50
##
## [[3]]
## [1] 47
ungroup(df_grp)#解除分组
## # A tibble: 50 x 8
## class name sex chinese math english moral science
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六1班 何娜 女 87 92 79 9 10
## 2 六1班 黄才菊 女 95 77 75 NA 9
## 3 六1班 陈芳妹 女 79 87 66 9 10
## 4 六1班 陈学勤 男 NA 79 66 9 10
## 5 六1班 陈祝贞 女 76 79 67 8 10
## 6 六1班 何小薇 女 83 73 65 8 9
## 7 六1班 雷旺 男 NA 80 68 8 9
## 8 六1班 陈欣越 男 57 80 60 9 9
## 9 六1班 黄亦婷 女 77 NA 54 8 10
## 10 六1班 陈媚 女 68 55 66 8 9
## # ... with 40 more rows
其他分组
- 真正将数据框分割为多个分组:group_split(),返回列表,其每个成分是一个分组数据框
- 将数据框分组(group_by),再做嵌套(nest),生成嵌套数据框:group_nest()
%>%
iris group_nest(Species)
## # A tibble: 3 x 2
## Species data
## <fct> <list<tibble[,4]>>
## 1 setosa [50 x 4]
## 2 versicolor [50 x 4]
## 3 virginica [50 x 4]
purrr风格的分组迭代:将函数.f依次应用到分组数据框.data的每个分组上
group_map(.data,.f,…):返回列表
group_walk(.data,.f,…):不返回,只关心副作用
group_modify(.data,.f,…):返回修改后的分组数据框 #### 分组汇总 #### 对数据框做分组最主要的目的就是做分组汇总,汇总就是以某种方式组合行,用dplyr包中的summarise()函数实现,结果只保留唯一值和新创建的汇总列。 summarise()
n():观测数
n_distinct(var):变量var的唯一值数目
sum(var),max(var),min(var),…
mean(var),median(var),sd(var),IQR(var),…
%>%
df group_by(sex) %>%
summarise(n=n(),
math_avg=mean(math,na.rm=TRUE),
math_med=median(math))
## # A tibble: 3 x 4
## sex n math_avg math_med
## <chr> <int> <dbl> <dbl>
## 1 男 24 64.6 NA
## 2 女 25 70.8 NA
## 3 <NA> 1 85 85
函数summarise(),配合across()可以对所选择的列做汇总。好处是可以借助辅助选择器或判断条件选择多列,还能在这些列上执行多个函数,只需要将它们放入一个列表。
对某些列做汇总
%>%
df group_by(class,sex) %>%
summarise(across(contains("h"),mean,na.rm=TRUE))#对列名包含h的列汇总求均值
## `summarise()` has grouped output by 'class'. You can override using the `.groups` argument.
## # A tibble: 12 x 5
## # Groups: class [6]
## class sex chinese math english
## <chr> <chr> <dbl> <dbl> <dbl>
## 1 六1班 男 57 79.7 64.7
## 2 六1班 女 80.7 77.2 67.4
## 3 六2班 男 75.4 68.8 42.6
## 4 六2班 女 92.2 73.8 63.8
## 5 六3班 男 66 30.4 67.6
## 6 六3班 女 68.4 49.2 67.8
## 7 六4班 男 84.8 79.2 55.7
## 8 六4班 女 85.2 74 63.7
## 9 六5班 男 66.5 64.5 65.3
## 10 六5班 女 80.4 75.4 63.4
## 11 六5班 <NA> 58 85 48
## 12 <NA> 男 90 86 72
对所有列做汇总
%>%
df select(-name) %>% #删除姓名列
group_by(class,sex) %>%
summarise(across(everything(),mean,na.rm=TRUE))#对所有列做汇总
## `summarise()` has grouped output by 'class'. You can override using the `.groups` argument.
## # A tibble: 12 x 7
## # Groups: class [6]
## class sex chinese math english moral science
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六1班 男 57 79.7 64.7 8.67 9.33
## 2 六1班 女 80.7 77.2 67.4 8.33 9.57
## 3 六2班 男 75.4 68.8 42.6 8.8 9.25
## 4 六2班 女 92.2 73.8 63.8 8.33 9
## 5 六3班 男 66 30.4 67.6 4.6 4.75
## 6 六3班 女 68.4 49.2 67.8 6.25 7.2
## 7 六4班 男 84.8 79.2 55.7 8.6 8.5
## 8 六4班 女 85.2 74 63.7 8.75 7.75
## 9 六5班 男 66.5 64.5 65.3 8.25 8.25
## 10 六5班 女 80.4 75.4 63.4 8.75 8.25
## 11 六5班 <NA> 58 85 48 9 10
## 12 <NA> 男 90 86 72 9 10
对满足条件的列做多种汇总
=df %>%
df_grpgroup_by(class) %>%
summarise(across(where(is.numeric),
list(sum=sum,mean=mean,min=min),na.rm=TRUE))#对所有数值列求和、均值、最小值
df_grp
## # A tibble: 6 x 16
## class chinese_sum chinese_mean chinese_min math_sum math_mean math_min
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六1班 622 77.8 57 702 78 55
## 2 六2班 746 82.9 66 570 71.2 41
## 3 六3班 606 67.3 44 349 38.8 18
## 4 六4班 850 85 72 771 77.1 54
## 5 六5班 726 72.6 58 720 72 59
## 6 <NA> 90 90 90 86 86 86
## # ... with 9 more variables: english_sum <dbl>, english_mean <dbl>,
## # english_min <dbl>, moral_sum <dbl>, moral_mean <dbl>, moral_min <dbl>,
## # science_sum <dbl>, science_mean <dbl>, science_min <dbl>
可读性不好,再来个宽变长:
%>%
df_grp pivot_longer(-class,names_to=c("Vars",".value"),names_sep="_")
## # A tibble: 30 x 5
## class Vars sum mean min
## <chr> <chr> <dbl> <dbl> <dbl>
## 1 六1班 chinese 622 77.8 57
## 2 六1班 math 702 78 55
## 3 六1班 english 666 66.6 54
## 4 六1班 moral 76 8.44 8
## 5 六1班 science 95 9.5 9
## 6 六2班 chinese 746 82.9 66
## 7 六2班 math 570 71.2 41
## 8 六2班 english 468 52 25
## 9 六2班 moral 69 8.62 6
## 10 六2班 science 73 9.12 7
## # ... with 20 more rows
summarise()以前只支持一个返回值的汇总函数,如sum,mean等。现在也支持多返回值(返回向量值、甚至是数据框)的汇总函数,如range(),quantile()等。
=c(0.25,0.5,0.75)
qs
=df %>%
df_qgroup_by (sex) %>%
summarise(math_qs=quantile(math,qs,na.rm=TRUE),q=qs)
## `summarise()` has grouped output by 'sex'. You can override using the `.groups` argument.
df_q
## # A tibble: 9 x 3
## # Groups: sex [3]
## sex math_qs q
## <chr> <dbl> <dbl>
## 1 男 57.5 0.25
## 2 男 69 0.5
## 3 男 80 0.75
## 4 女 55 0.25
## 5 女 73 0.5
## 6 女 86.5 0.75
## 7 <NA> 85 0.25
## 8 <NA> 85 0.5
## 9 <NA> 85 0.75
可读性不好,再来个长变宽:
%>%
df_q pivot_wider(names_from=q,values_from=math_qs,names_prefix="q_")
## # A tibble: 3 x 4
## # Groups: sex [3]
## sex q_0.25 q_0.5 q_0.75
## <chr> <dbl> <dbl> <dbl>
## 1 男 57.5 69 80
## 2 女 55 73 86.5
## 3 <NA> 85 85 85
分组计数
用count()按分类变量class和sex分组,并按分组大小排序:
%>%
df count(class,sex,sort=TRUE)
## # A tibble: 12 x 3
## class sex n
## <chr> <chr> <int>
## 1 六1班 女 7
## 2 六4班 男 6
## 3 六2班 男 5
## 4 六3班 男 5
## 5 六3班 女 5
## 6 六5班 女 5
## 7 六2班 女 4
## 8 六4班 女 4
## 9 六5班 男 4
## 10 六1班 男 3
## 11 六5班 <NA> 1
## 12 <NA> 男 1
对已分组的数据框,用tally()计数:
%>%
df group_by(math_level=cut(math,breaks=c(0,60,75,80,100),
right=FALSE)) %>%
tally()
## # A tibble: 5 x 2
## math_level n
## <fct> <int>
## 1 [0,60) 14
## 2 [60,75) 11
## 3 [75,80) 5
## 4 [80,100) 17
## 5 <NA> 3
count()和tally()都有参数wt设置加权计数。
用add_count()和add_tally()可为数据集增加一列按分组变量分组的计数:
%>%
df add_count(class,sex)
## # A tibble: 50 x 9
## class name sex chinese math english moral science n
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <int>
## 1 六1班 何娜 女 87 92 79 9 10 7
## 2 六1班 黄才菊 女 95 77 75 NA 9 7
## 3 六1班 陈芳妹 女 79 87 66 9 10 7
## 4 六1班 陈学勤 男 NA 79 66 9 10 3
## 5 六1班 陈祝贞 女 76 79 67 8 10 7
## 6 六1班 何小薇 女 83 73 65 8 9 7
## 7 六1班 雷旺 男 NA 80 68 8 9 3
## 8 六1班 陈欣越 男 57 80 60 9 9 3
## 9 六1班 黄亦婷 女 77 NA 54 8 10 7
## 10 六1班 陈媚 女 68 55 66 8 9 7
## # ... with 40 more rows
其他数据操作
按行汇总
通常的数据操作逻辑都是按列方式(colwise),这使得按行汇总很困难。 dplyr包提供了rowwise()函数为数据框创建按行方式(rowwise),使用rowwise()后并不是真的改变数据框,只是创建了按行元信息,改变了数据框的操作逻辑:
= df %>%
rf rowwise()
%>%
rf mutate(total = sum( c (chinese,math,english),na.rm =TRUE))
## # A tibble: 50 x 9
## # Rowwise:
## class name sex chinese math english moral science total
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六1班 何娜 女 87 92 79 9 10 258
## 2 六1班 黄才菊 女 95 77 75 NA 9 247
## 3 六1班 陈芳妹 女 79 87 66 9 10 232
## 4 六1班 陈学勤 男 NA 79 66 9 10 145
## 5 六1班 陈祝贞 女 76 79 67 8 10 222
## 6 六1班 何小薇 女 83 73 65 8 9 221
## 7 六1班 雷旺 男 NA 80 68 8 9 148
## 8 六1班 陈欣越 男 57 80 60 9 9 197
## 9 六1班 黄亦婷 女 77 NA 54 8 10 131
## 10 六1班 陈媚 女 68 55 66 8 9 189
## # ... with 40 more rows
函数 c_across()是为按行方式(rowwise)在选定的列范围汇总数据而设计的,它没有提供.fns参数,只能选择列。
%>%
rf mutate(total = sum(c_across(where(is.numeric))))
## # A tibble: 50 x 9
## # Rowwise:
## class name sex chinese math english moral science total
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六1班 何娜 女 87 92 79 9 10 277
## 2 六1班 黄才菊 女 95 77 75 NA 9 NA
## 3 六1班 陈芳妹 女 79 87 66 9 10 251
## 4 六1班 陈学勤 男 NA 79 66 9 10 NA
## 5 六1班 陈祝贞 女 76 79 67 8 10 240
## 6 六1班 何小薇 女 83 73 65 8 9 238
## 7 六1班 雷旺 男 NA 80 68 8 9 NA
## 8 六1班 陈欣越 男 57 80 60 9 9 215
## 9 六1班 黄亦婷 女 77 NA 54 8 10 NA
## 10 六1班 陈媚 女 68 55 66 8 9 206
## # ... with 40 more rows
若只是做按行求和或均值,直接用rowSums() /rowMeans()速度更快(不需要分割-汇总-合并),这里的rowwise行化后提供可以做更多的按行汇总的可能。
%>%
df mutate(total=rowSums(across(where(is.numeric))))
## # A tibble: 50 x 9
## class name sex chinese math english moral science total
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 六1班 何娜 女 87 92 79 9 10 277
## 2 六1班 黄才菊 女 95 77 75 NA 9 NA
## 3 六1班 陈芳妹 女 79 87 66 9 10 251
## 4 六1班 陈学勤 男 NA 79 66 9 10 NA
## 5 六1班 陈祝贞 女 76 79 67 8 10 240
## 6 六1班 何小薇 女 83 73 65 8 9 238
## 7 六1班 雷旺 男 NA 80 68 8 9 NA
## 8 六1班 陈欣越 男 57 80 60 9 9 215
## 9 六1班 黄亦婷 女 77 NA 54 8 10 NA
## 10 六1班 陈媚 女 68 55 66 8 9 206
## # ... with 40 more rows
按行方式(rowwise)可以理解为一种特殊的分组:每一行作为一组。为rowwise()提供行ID,用summarise()做汇总更能体会这一点:
%>%
df rowwise(name) %>%
summarise(total = sum(c_across(where(is.numeric))))
## `summarise()` has grouped output by 'name'. You can override using the `.groups` argument.
## # A tibble: 50 x 2
## # Groups: name [50]
## name total
## <chr> <dbl>
## 1 何娜 277
## 2 黄才菊 NA
## 3 陈芳妹 251
## 4 陈学勤 NA
## 5 陈祝贞 240
## 6 何小薇 238
## 7 雷旺 NA
## 8 陈欣越 215
## 9 黄亦婷 NA
## 10 陈媚 206
## # ... with 40 more rows
窗口函数
汇总函数如sum()和 mean()接受n个输入,返回1个值。而窗口函数是汇总函数的变体:接受n个输入,返回n个值。
例如,cumsum( )、 cummean ( )、rank()、 lead()、 lag()等。 #### 排名和排序函数 #### 共有6个排名函数,只介绍最常用的min_rank():从小到大排名(ties.method=“min”),若要从大到小排名需要套一个desc()
%>%
df mutate(ranks = min_rank(desc(math))) %>%
arrange(ranks)
## # A tibble: 50 x 9
## class name sex chinese math english moral science ranks
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <int>
## 1 六4班 周婵 女 92 94 77 10 9 1
## 2 六4班 陈丽丽 女 87 93 NA 8 6 2
## 3 六1班 何娜 女 87 92 79 9 10 3
## 4 六5班 符苡榕 女 85 89 76 9 NA 4
## 5 六2班 黄祖娜 女 94 88 75 10 10 5
## 6 六1班 陈芳妹 女 79 87 66 9 10 6
## 7 六4班 李小龄 男 90 87 69 10 10 6
## 8 六2班 徐雅琦 女 92 86 72 NA 9 8
## 9 <NA> 徐达政 男 90 86 72 9 10 8
## 10 六4班 杨昌晟 男 84 85 64 8 10 10
## # ... with 40 more rows
移位函数
- lag()取前一个值;
- lead()取后一个值;
library(lubridate)
##
## 载入程辑包:'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
= tibble(
dt day = as_date( "2019-08-30") + c(0,4:6),wday = weekdays(day) ,
#生成日期字符串并生成对应星期字符串
sales = c(2,6,2,3),
balance = c(30,25,-40,30))
%>%
dt mutate(sales_lag = lag(sales), sales_delta = sales - lag(sales))
## # A tibble: 4 x 6
## day wday sales balance sales_lag sales_delta
## <date> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 2019-08-30 星期五 2 30 NA NA
## 2 2019-09-03 星期二 6 25 2 4
## 3 2019-09-04 星期三 2 -40 6 -4
## 4 2019-09-05 星期四 3 30 2 1
累计汇总
%>%
dt filter ( cumany (balance < 0))#选择第一次透支之后的所有行
## # A tibble: 2 x 4
## day wday sales balance
## <date> <chr> <dbl> <dbl>
## 1 2019-09-04 星期三 2 -40
## 2 2019-09-05 星期四 3 30
%>%
dt filter(cumall( ! (balance < 0)))#选择所有行直到第一次透支
## # A tibble: 2 x 4
## day wday sales balance
## <date> <chr> <dbl> <dbl>
## 1 2019-08-30 星期五 2 30
## 2 2019-09-03 星期二 6 25
滑窗迭代
“窗口函数”术语来自sQL,意味着逐窗口浏览数据,将某函数重复应用于数据的每个“窗口”。窗口函数的典型应用包括滑动平均、累计和以及更复杂如滑动回归。
安装第三方包slider
install.packages("slider")
library(slider)
## Warning: 程辑包'slider'是用R版本4.1.2 来建造的
%>%
dt mutate(avg_3 = slide_dbl(sales,mean,.before = 1,.after = 1))
## # A tibble: 4 x 5
## day wday sales balance avg_3
## <date> <chr> <dbl> <dbl> <dbl>
## 1 2019-08-30 星期五 2 30 4
## 2 2019-09-03 星期二 6 25 3.33
## 3 2019-09-04 星期三 2 -40 3.67
## 4 2019-09-05 星期四 3 30 2.5
#查看连续三值滑动与连续3日滑动窗口
%>%
dt mutate(avg_3 = slide_index_dbl(sales,day,mean,.before = 1,.after = 1))
## # A tibble: 4 x 5
## day wday sales balance avg_3
## <date> <chr> <dbl> <dbl> <dbl>
## 1 2019-08-30 星期五 2 30 2
## 2 2019-09-03 星期二 6 25 4
## 3 2019-09-04 星期三 2 -40 3.67
## 4 2019-09-05 星期四 3 30 2.5
整洁计算
tidyverse代码之所以这么“整洁、优雅”,访问列只需要提供列名,不需要加引号,不需要加数据框环境df$,这是因为它内部采用了一套整洁计算(tidy evaluation)框架。
如果我们也想自定义这样的“整洁、优雅”函数,即在自定义函数中页这样“整洁、优雅”地传递参数,就需要掌握一点整洁计算的技术。
整洁计算的两种基本形式是:
- 数据屏蔽:使得可以不用带数据框(环境变量)名字,就能使用数据框内的变量(数据变量),便于在数据集内计算值
- 整洁选择:即各种选择列语法,便于使用数据集中的列
数据屏蔽为直接使用带来了代码简洁,但作为函数参数时的间接使用,正常是环境变量,要想作为数据变量使用,则需要用两个大括号括起来{{var}}:
= function(data,var) {
var_summary %>%
data summarise(n = n( ), mean = mean({{var}}))
}%>%
mtcars group_by(cyl) %>%
var_summary(mpg)
## # A tibble: 3 x 3
## cyl n mean
## <dbl> <int> <dbl>
## 1 4 11 26.7
## 2 6 7 19.7
## 3 8 14 15.1
同样地,整洁选择作为函数参数时的间接使用,也需要用两个大括号括起来{{vars}}:
= function(data,vars) {
summarise_mean %>%
data summarise(n = n(), across({{vars}}, mean))
}%>%
mtcars group_by(cyl)%>%
summarise_mean(where(is.numeric))
## # A tibble: 3 x 12
## cyl n mpg disp hp drat wt qsec vs am gear carb
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 4 11 26.7 105. 82.6 4.07 2.29 19.1 0.909 0.727 4.09 1.55
## 2 6 7 19.7 183. 122. 3.59 3.12 18.0 0.571 0.429 3.86 3.43
## 3 8 14 15.1 353. 209. 3.23 4.00 16.8 0 0.143 3.29 3.5
引用与反引用
创建tidyverse风格的整洁函数,另一种做法是使用引用与反引用机制。这需要额外的两个步骤:
- 用enquo()让函数自动引用其参数。
- 用!!反引用该参数
以自定义计算分组均值函数为例:
= function(data,summary_var, group_var) {
grouped_mean = enquo(summary_var)
summary_var = enquo(group_var)
group_var %>%
data group_by( ! ! group_var) %>%
summarise(mean = mean( ! ! summary_var))
}grouped_mean(mtcars,mpg,cyl)
## # A tibble: 3 x 2
## cyl mean
## <dbl> <dbl>
## 1 4 26.7
## 2 6 19.7
## 3 8 15.1
数据处理神器:data.table包
data.table包是data.frame的高性能版本,不依赖其它包就能胜任各种数据操作,速度超快,让个人电脑都能轻松处理几G甚至几十G的数据。data.table的高性能来源于内存管理(引用语法)、并行化和大量精细优化。
但是,与tidyverse一次用一个函数做一件事,通过管道依次连接整洁地完成复杂事情的理念截然不同data.table语法高度抽象、简洁、一致:
dt[i,j,by]:用i选择行,用j操作列,根据by分组 其中j表达式非常强大和灵活,可以选择、修改、汇总、计算新列,甚至可以接受表达式最关键一点是:只要返回等长元素或长度为1元素的list,每个list元素将转化为结果data.table 的一列。
通用语法
创建data.table
安装第三方包data.table
install.packages("data.table")
library(data.table)
##
## 载入程辑包:'data.table'
## The following objects are masked from 'package:lubridate':
##
## hour, isoweek, mday, minute, month, quarter, second, wday, week,
## yday, year
## The following objects are masked from 'package:dplyr':
##
## between, first, last
## The following object is masked from 'package:purrr':
##
## transpose
=data.table(
dtx=1:2,
y=c("A","B")
) dt
## x y
## 1: 1 A
## 2: 2 B
使用as.data.table()可将数据框、列表、矩阵等转化为data.table #### 引用语法 #### 高效计算的编程都支持引用语法,也叫浅拷贝。
浅拷贝,只是拷贝列指针向量(对应数据框的列),而实际数据在内存中不做物理拷贝;而相对的概念:深拷贝,则拷贝整个数据到内存中的另一位置,深拷贝这种冗余的拷贝极大地影响性能,特别是大数据的情形。
data.table使用:=算符,做整列或部分列替换时都不做任何拷贝,因为:=算符是通过引用就地(in-place)更新data.table的列。
若想要复制不想按引用(修改数据本身),使用DT2 = copy(DT1).
键与索引
data.table支持设置键和索引,使得选择行、做数据连接更加方便快速(快170倍)。
- 键:一级有序索引;
- 索引:自动二级索引;
setkey(dt,v1,v3) #设置键
setindex(dt,v1,v3) #设置索引
二者的主要不同:
- 1.使用键时,数据在内存中做物理重排序;而使用索引时,顺序只是保存为属性;
- 2.键是显式定义的;索引可以手动创建,也可以运行时创建(比如用==或%in%时)
- 3.索引与参数on连用;键的使用是可选的,但为了可读性建议使用键。
特殊符号
data.table提供了一些辅助操作的特殊符号:
- .():代替list()
- :=:按引用方式增加、修改列。
- .N:行数
- .sD:每个分组的数据子集,除了by或 keyby的列。
- .SDcols: 与.sD连用,用来选择包含在.SD中的列。
- .BY:包含所有by分组变量的list
- .I:整数向量seq_len(nrow(x)),例如DT[,.I[ which.max (somecol)],by=grp]
- .GRP:分组索引,1代表第1分组,2代表第2分组,…
- .NGRP:分组数
- .EACHI:用于by/keyby = .EACHI表示根据i表达式的每一行分组
链式操作
data.table也有自己专用的管道操作,成为链式操作:
#或者写开为
DT[...][...]
DT[
...
][
... ]
数据读写
函数fread()和 fwrite()是data.table最强大的函数之二。它们最大的优势,仍是读取大数据时速度超快(100倍),且非常稳健,分隔符、列类型、行数都是自动检测;它们非常通用,可以处理不同的文件格式(但不能直接读取Excel文件),还可以接受URLs甚至是操作系统指令。
读入数据
fread( "DT.csv")
fread("DT.txt",sep = "\t")#选择部分行列读取
fread ("DT.csv", select = c("V1","V4"))fread("DT.csv",drop = "V4",nrows = 100)# 读取压缩文件
fread(cmd = "unzip -cq myfile.zip")fread ("myfile.gz")
# 批量读取
c ( "DT.csv", "DT.csv") %>%
lapply(fread) %>%
rbindlist() #多个数据框/列表按行合并
写出数据
fwrite(DT,"DT.csv")
fwrite(DT,"DT.csv", append = TRUE) #追加内容
fwrite(DT,"DT.txt",sep = "\t")
fwrite(setDT(list(o,list(1:5))),"DT2.csv")#支持写出列表列
fwrite(DT,"myfile.csv.gz",compress = "gzip")#写出到压缩文件
数据连接
data.table提供了简单的按行合并函数:
- rbind(DT1,DT2,…):按行堆叠多个data.table
- rbindlist(DT_list,idcol):堆叠多个data.table构成的list
最常用的六种数据连接:左连接、右连接、内连接、全连接、半连接、反连接,前四种连接又称为修改连接,后两种连接又称为过滤连接。 #### 左连接 #### 外连接至少保留一个数据表中的所有观测,分为左连接、右连接、全连接,其中最常用的是左连接:保留x所有行,合并匹配的y中的列。
= "v1"] #注意是以×为左表
y[x,on #若v1是键
y[x] merge(x, y,all.x = TRUE,by = "v1")
右连接
保留y所有行,合并匹配的x中的列:
merge(x, y,all.y = TRUE,by = "v1")
内连接
内连接是保留两个数据表中所共有的观测:只保留x中与y匹配的行,合并匹配的y 中的列:
merge(x, y, by = "v1")
全连接
保留x和y中的所有行,合并匹配的列: merge(x, y, all = TRUE,by = “v1”) #### 半连接 #### 根据在y 中,来筛选x中的行:
$v1,on = "v1",nomatch = ] x[y
反连接
根据不在y中,来筛选x中的行:
!y, on = "v1"] x[
集合运算
fintersect(x, y)
fsetdiff(x,y)
funion(x,y)
fsetequal(x,y)
滚动连接
滚动连接也是很有用的一种数据连接,往往涉及日期时间,特别是处理在时间上有先后关联的两个事件。基本语法为:
= .(id = id,date = date), roll = TRUE]#同roll = inf x[y, on
根据id等值匹配行,date是滚动匹配,匹配与左表y日期最接近的前一个日期,匹配成功的列合并进来;roll = -inf 则匹配最接近的后一个日期。
数据重塑
宽变长
宽表的特点是:表比较宽,本来该是“值”的,却出现在“变量(名”中。这就需要给它变到“值”中,新起个列名存为一列,这就是所谓的宽表变长表。
- 每一行只有1个观测的情形
= fread( "data/分省年度GDP. csv", encoding = "UTF-8") DT
## Warning in (if (.Platform$OS.type == "unix") system
## else shell)(paste0("(", : '(data/分省年度GDP. csv) > C:
## \Users\WENKUA~1\AppData\Local\Temp\RtmpS2s7JL\file224026bf1312'运行失败,错误码
## 为1
## Warning in fread("data/分省年度GDP. csv", encoding = "UTF-8"): File 'C:
## \Users\WENKUA~1\AppData\Local\Temp\RtmpS2s7JL\file224026bf1312' has size 0.
## Returning a NULL data.table.
%>%
DT melt(measure = 2:4,variable = "年份",value = "GDP")
## Null data.table (0 rows and 0 cols)
参数measure是用整数向量指定要变形的列,也可以使用正则表达式patterns(“年$”),也可以改用参数id指定不变形的列;若需要忽略缺失值,设置参数na.rm =TRUE.
对比tidyr : : pivot_longer ()实现:
%>%
DT pivot_longer(-地区,names_to = "年份",values_to = "GDP")
两种语法基本相同,都是指定要变形的列、为存放变形列的列名中的“值”指定新列名、为存放变形列中的“值”指定新列名。 - 每一行有多个观测的情形
load ( "data/family.rda")
= as.data.table(family)
DT #family数据
%>%
DT melt(measure = patterns ( "^dob","Agender"),
value = c( "dob", "gender") , na.rm = TRUE)
长变宽
长表的特点是:表比较长。
有时候需要将分类变量的若干水平值,变成变量(列名)。这就是长表变宽表,它与宽表变长表正好相反(二者互逆)。
- 只有1个列名列和1个值列的情形
load ( "data/ animals.rda")
= as.data.table(animals)#农场动物数据
DT %>%
DT dcast(Year ~ Type,value = "Heads",fill = 0)
数据分割与合并
函数split(DT,by)可将data.table分割为list,然后就可以接map_*()函数逐分组迭代。
- 拆分列
= as.data.table(table3)
DT #将case列拆分为两列,并删除原列
c("cases", "population") := tstrsplit(DT$rate,split = "/")][,
DT[,:= NULL] rate
- 合并列
= as.data.table(table5)
DT # 将century和year列合并为新列new,并删除原列
:= paste0(century,year)][,c("century", "year") := NULL] DT[,new
数据操作
选择行
用i表达式,选择行。
- 根据索引
3:4,]#或dt[3:4] dt[
## x y
## 1: NA <NA>
## 2: NA <NA>
!3:7,]#反选,或dt[-(3:7)] dt[
## x y
## 1: NA <NA>
## 2: NA <NA>
## 3: NA <NA>
## 4: NA <NA>
## 5: NA <NA>
- 根据逻辑表达式
>5]
dt[v2 %chin% c("A", "C")] #比%in6更快
dt[v4 ==1 & v4=="A"] dt[v1
- 删除重复行
unique(dt)
unique(dt,by = c("v1" , "v4"))#返回所有列
- 删除包含NA的行
na.omit(dt,cols = 1:4)
- 行切片
sample(.N,3)]#随机抽取3 行
dt[sample(.N,.N * 0.5)]#随机抽取506的行
dt[frankv(-v1,ties.method = "dense") < 2]# v1 值最大的行 dt[
- 其它
%like% "^B"]# v4值以B开头
dt[v4 %between% c(3,5)]#闭区间
dt[v2 between(v2,3,5,incbounds = FALSE)]#开区间
dt[%inrange% list(-1:1,1:3)]# v2值属于多个区间的某个
dt[v2 inrange(v2,-1:1,1:3,incbounds = TRUE)] #同上 dt[
排序行
order(v1)]#默认按v1从小到大
dt[order(-v1)]#按v1从大到小
dt[order(v1,-v2)]#按v1从小到大,v2从大到小 dt[
若按引用对行重排序:
setorder(DT,V1,-V2)
操作列
用j表达式操作列
- 选择一列或多列
#根据索引
3]] #或dt[ [ "v3"]],dt$v3,返回向量
dt[[3] #或dt[ , "v3"],返回data.table
dt[,#根据列名
#或dt[ , list(v3)]
dt[,.(v3)]
dt[,.(v2,v3,v4)]:v4]
dt[, v2!c("v2" , "v3")]#反选列 dt[,
- 调整列序
= rev ( names(DT))#或其它列序
cols setcolorder(DT, cols)
修改列名
setnames(DT,old,new)
修改因子水平
setattr(sex, "levels",c("M","F"))] DT[,
tidyverse是用mutate()修改列,不修改原数据框,必须赋值结果;data.table修改列,是用列赋值符号:=(不执行复制),直接对原数据框修改。
- 修改或增加一列
:= v1 ^ 2][]#修改列,加[]输出结果
dt[, v1 := log(v1)]#增加新列
dt[, v2 v2 = log(v1), v3 = v2 + 1)]#只保留新列 dt[, .(
# 使用不带NA 的考试成绩数据
= readxl::read_xlsx("data/ExamDatas.xlsx")%>%
DT as.data.table()
#应用函数到所有列
lapply(.SD,as.character)]
DT[,#应用函数到满足条件的列
lapply (.sD,rescale), #rescale()为自定义的归一化函数
DT[,= is.numeric]
.SDcols #应用函数到指定列
= as.data.table(iris)
DT * 10,.SDcols = patterns( " (Length) | (width)")] DT[,.SD
删除列
:= NULL]
dt[,v1 c("v2" , "v3") := NULL]
dt[,= c("v2" , "v3")
cols := NULL] #注意不是dt[, cols := NULL] dt[,(cols)
- 重新编码
#一分支
< 4,v1 := 0]
dt[v1 #二分支
:= fifelse(v1 < 0,-v1,v1)]
dt[, vl #多分支
:= fcase(v2 <4,"low",
dt[, v2 < 7, "middle",
v2default = "high")]
- 前移/后移运算
shift(x,n = 1,fill = NA,type = "lag") #1,2,3 ->NA,1,2
shift(x,n = 1,fill = NA,type = "lead") #1,2,3 ->2,3,NA
分组汇总
用by表达式指定分组。
data.table是根据by或keyby分组,区别是,keyby会排序结果并创建键,使得更快地访问子集。
未分组数据框相当于整个数据框作为1组,数据操作是在整个数据框上进行,汇总是得到1个结果。
分组数据框,相当于整个数据框分成了m个数据框,数据操作是分别在每个数据框上进行,汇总是得到m个结果。
#使用带NA值的考试成绩数据
= readxl::read_xlsx("data/ExamDatas_NAs.xlsx") %>%
DT as.data.table()
- 未分组汇总
math_avg = mean(math,na.rm = TRUE))] DT[,.(
## math_avg
## 1: 68.04255
- 简单的分组汇总
n = .N,
DT[,.(math_avg = mean(math,na.rm = TRUE),
math_med = median(math)),
= sex] by
## sex n math_avg math_med
## 1: 女 25 70.78261 NA
## 2: 男 24 64.56522 NA
## 3: <NA> 1 85.00000 85
可以直接在by中使用判断条件或表达式,特别是根据整合单位的日期时间汇总:
= as.IDate( "2021-01-01") +1:50
date = data.table(date, a = 1:50)
DT mean(a),by = list(mon = month(date))]# 按月平均 DT[,
## mon V1
## 1: 1 15.5
## 2: 2 40.5
data.table提供快速处理日期时间的IDateTime类,更多信息可查阅帮助。
- 对某些列做汇总
lapply (.sD,mean),.sDcols = patterns("h"),
DT[,= .(class,sex)]#或用by = c ( "class", "sex") by
- 对所有列做汇总
:= NULL][,lapply (.SD,mean,na.rm = TRUE),
DT[,name = .( class,sex)] by
- 对满足条件的列做汇总
lapply(.sD,mean,na.rm = TRUE), by = class,
DT[,= is.numeric] .SDcols
- 分组计数
= na.omit(DT)
DT = .(class,cut(math,c(0,60,100)))]%>%
DT[,.N,by print(topn = 2)