本文内容:
- 基础函数操作数据框
- sqldf包使用 SQL 查询数据框
- data.table包操作数据
- dplyr管道操作处理数据
- rlist包处理嵌套数据结构
使用内置函数操作数据框
数据框的本质是一个由向量构成的列表,由于列长度相同,所以可以当做矩阵进行访问和操作。比如选择满足特定条件的行,使用 []
符号,第一个参数提供一个逻辑向量,第二个参数留空。
本文大部分的代码都是基于一组产品的虚拟数据。我们先将数据载入,然后学习怎么用不同的方法操作数据。
if(!require(readr)) install.packages("readr") product_info = read_csv("../../R/dataset/product-info.csv") #> Parsed with column specification: #> cols( #> id = col_character(), #> name = col_character(), #> type = col_character(), #> class = col_character(), #> released = col_character() #> ) product_info #> # A tibble: 6 x 5 #> id name type class released #> <chr> <chr> <chr> <chr> <chr> #> 1 T01 SupCar toy vehicle yes #> 2 T02 SupPlane toy vehicle no #> 3 M01 JeepX model vehicle yes #> 4 M02 AircraftX model vehicle yes #> 5 M03 Runner model people yes #> 6 M04 Dancer model people no
当数据以数据框的形式载入内存后,我们可以使用下面的代码查看每一列的类型:
sapply(product_info, class) #> id name type class released #> "character" "character" "character" "character" "character"
注意 read_csv
函数载入的数据框与内置函数 read.csv
函数是不同的,主要体现在不会将字符串转换为因子变量,当然前者的速度要快得多。
接下来我们正式学习用R内置的函数操作数据框进行分析和统计的一些方法。
内置函数操作数据框
选取 type
为 toy
的行:
product_info[product_info$type == "toy", ] #> # A tibble: 2 x 5 #> id name type class released #> <chr> <chr> <chr> <chr> <chr> #> 1 T01 SupCar toy vehicle yes #> 2 T02 SupPlane toy vehicle no
或选取 released
为 no
的行:
product_info[product_info$released == "no", ] #> # A tibble: 2 x 5 #> id name type class released #> <chr> <chr> <chr> <chr> <chr> #> 1 T02 SupPlane toy vehicle no #> 2 M04 Dancer model people no
对列进行筛选需要将第1个参数留空,给第2个参数提供字符向量。
product_info[, c("id", "name", "type")] #> # A tibble: 6 x 3 #> id name type #> <chr> <chr> <chr> #> 1 T01 SupCar toy #> 2 T02 SupPlane toy #> 3 M01 JeepX model #> 4 M02 AircraftX model #> 5 M03 Runner model #> 6 M04 Dancer model
行列筛选也是可以的,我们只要组合前面的两种情况即可。
product_info[product_info$type == "toy", c("name", "class", "released")] #> # A tibble: 2 x 3 #> name class released #> <chr> <chr> <chr> #> 1 SupCar vehicle yes #> 2 SupPlane vehicle no
内置函数 subset()
可以简化取子集操作的过程:
subset(product_info, subset = type == "model" & released == "yes", select = name:class) #> # A tibble: 3 x 3 #> name type class #> <chr> <chr> <chr> #> 1 JeepX model vehicle #> 2 AircraftX model vehicle #> 3 Runner model people
使用 with()
函数在数据框的语义中计算表达式,即可以直接使用数据框的列名,而不必重复指定数据框:
with(product_info, name[released == "no"]) #> [1] "SupPlane" "Dancer"
除了构建子集,表达式还可以用来统计每列各个可能值出现的频数。
with(product_info, table(type[released == "yes"])) #> #> model toy #> 3 1
除了产品信息表,还有一张产品属性的统计表:
product_stats = read_csv("../../R/dataset/product-stats.csv") #> Parsed with column specification: #> cols( #> id = col_character(), #> material = col_character(), #> size = col_integer(), #> weight = col_double() #> ) product_stats #> # A tibble: 6 x 4 #> id material size weight #> <chr> <chr> <int> <dbl> #> 1 T01 Metal 120 10 #> 2 T02 Metal 350 45 #> 3 M01 Plastics 50 NA #> 4 M02 Plastics 85 3 #> 5 M03 Wood 15 NA #> 6 M04 Wood 16 0.6
如果现在要获取尺寸最大的前3个产品的名字该怎么办?
一种方法是将 product_stats
按尺寸降序排列,选择前3个记录的id,然后用id值筛选 product_info
的行:
top3_id = unlist(product_stats[order(product_stats$size, decreasing = TRUE), "id"])[1:3] product_info[product_info$id %in% top3_id, ] #> # A tibble: 3 x 5 #> id name type class released #> <chr> <chr> <chr> <chr> <chr> #> 1 T01 SupCar toy vehicle yes #> 2 T02 SupPlane toy vehicle no #> 3 M02 AircraftX model vehicle yes
我们用比较冗长的方式完成了任务。但仔细在想想,两个数据框是通过 id
连接到一起的,我们可以把它们合并到一起,然后执行提取操作:
product_table = merge(product_info, product_stats, by = "id") product_table #> id name type class released material size weight #> 1 M01 JeepX model vehicle yes Plastics 50 NA #> 2 M02 AircraftX model vehicle yes Plastics 85 3.0 #> 3 M03 Runner model people yes Wood 15 NA #> 4 M04 Dancer model people no Wood 16 0.6 #> 5 T01 SupCar toy vehicle yes Metal 120 10.0 #> 6 T02 SupPlane toy vehicle no Metal 350 45.0
现在通过合并的数据框,我们可以根据任意一列 排序 数据框,而不需要处理其他的表格数据:
product_table[order(product_table$size), ] #> id name type class released material size weight #> 3 M03 Runner model people yes Wood 15 NA #> 4 M04 Dancer model people no Wood 16 0.6 #> 1 M01 JeepX model vehicle yes Plastics 50 NA #> 2 M02 AircraftX model vehicle yes Plastics 85 3.0 #> 5 T01 SupCar toy vehicle yes Metal 120 10.0 #> 6 T02 SupPlane toy vehicle no Metal 350 45.0
前面的问题我们也可以利用合并的数据框加以解决:
product_table[order(product_table$size, decreasing = TRUE), "name"][1:3] #> [1] "SupPlane" "SupCar" "AircraftX"
有时候我们需要生成新数据框来对原始数据基础上进行调整和处理,从而避免破坏原始数据。 transform()
函数可以帮助我们完成这类任务,例如:
transform(product_table, released = ifelse(released == "yes", TRUE, FALSE), density = weight / size) #> id name type class released material size weight density #> 1 M01 JeepX model vehicle TRUE Plastics 50 NA NA #> 2 M02 AircraftX model vehicle TRUE Plastics 85 3.0 0.0353 #> 3 M03 Runner model people TRUE Wood 15 NA NA #> 4 M04 Dancer model people FALSE Wood 16 0.6 0.0375 #> 5 T01 SupCar toy vehicle TRUE Metal 120 10.0 0.0833 #> 6 T02 SupPlane toy vehicle FALSE Metal 350 45.0 0.1286
前面数据中我们看到有一些缺失值(用 NA
表示),很多时候我们不希望数据出现任何缺失值,因此需要某种办法处理它们。为了演示处理的方法,我们再载入一张包含缺失值的表,包含每件产品的质量、耐久性、防水性的测试结果。
product_tests = read_csv("../../R/dataset/product-tests.csv") #> Parsed with column specification: #> cols( #> id = col_character(), #> quality = col_integer(), #> durability = col_integer(), #> waterproof = col_character() #> ) product_tests #> # A tibble: 6 x 4 #> id quality durability waterproof #> <chr> <int> <int> <chr> #> 1 T01 NA 10 no #> 2 T02 10 9 no #> 3 M01 6 4 yes #> 4 M02 6 5 yes #> 5 M03 5 NA yes #> 6 M04 6 6 yes
na.omit()
可以删除所有包含缺失值的行:
na.omit(product_tests) #> # A tibble: 4 x 4 #> id quality durability waterproof #> <chr> <int> <int> <chr> #> 1 T02 10 9 no #> 2 M01 6 4 yes #> 3 M02 6 5 yes #> 4 M04 6 6 yes
另外,函数 complete.cases()
可以返回逻辑向量,表明某行是否完整。
complete.cases(product_tests) #> [1] FALSE TRUE TRUE TRUE FALSE TRUE
利用该函数可以筛选数据框,比如获得不含缺失值的 id
值:
product_tests[complete.cases(product_tests), "id"] #> # A tibble: 4 x 1 #> id #> <chr> #> 1 T02 #> 2 M01 #> 3 M02 #> 4 M04
前面给出的3个表格有共同的 id
列,可惜R里面内置函数只能一次合并2个数据框:
product_full = merge(product_table, product_tests, by = "id") product_full #> id name type class released material size weight quality #> 1 M01 JeepX model vehicle yes Plastics 50 NA 6 #> 2 M02 AircraftX model vehicle yes Plastics 85 3.0 6 #> 3 M03 Runner model people yes Wood 15 NA 5 #> 4 M04 Dancer model people no Wood 16 0.6 6 #> 5 T01 SupCar toy vehicle yes Metal 120 10.0 NA #> 6 T02 SupPlane toy vehicle no Metal 350 45.0 10 #> durability waterproof #> 1 4 yes #> 2 5 yes #> 3 NA yes #> 4 6 yes #> 5 10 no #> 6 9 no
对完全合并好的表格,我们利用 tapply()
函数( apply
家族成员)可以进行统计,该函数专门用于处理表格数据,使用某些方法根据某列队另一列的数据进行统计。
例如根据type列计算quality列的均值:
mean_quality1 = tapply(product_full$quality, list(product_full$type), mean, na.rm=TRUE) mean_quality1 #> model toy #> 5.75 10.00
返回的结果看起来是个数值向量,我们使用 str()
看看:
str(mean_quality1) #> num [1:2(1d)] 5.75 10 #> - attr(*, "dimnames")=List of 1 #> ..$ : chr [1:2] "model" "toy"
实际上,这是个一维数组:
is.array(mean_quality1) #> [1] TRUE
tapply()
返回的是一个数组,而不是简单的数值向量,因此可以方便地计算多组操作。
例如计算每一对 type
和 class
组合的 quality
均值:
mean_quality2 = tapply(product_full$quality, list(product_full$type, product_full$class), mean, na.rm = TRUE) mean_quality2 #> people vehicle #> model 5.5 6 #> toy NA 10
对于二维数组,我们可以使用两个参数来获取其中的值:
typeof(mean_quality2) #> [1] "double" class(mean_quality2) #> [1] "matrix" mean_quality2["model", "vehicle"] #> [1] 6
同理我们可以根据多列分组,使用 with()
可以避免反复输入 product_full
:
mean_quality3 = with(product_full, tapply(quality, list(type, material, released), mean, na.rm = TRUE)) mean_quality3 #> , , no #> #> Metal Plastics Wood #> model NA NA 6 #> toy 10 NA NA #> #> , , yes #> #> Metal Plastics Wood #> model NA 6 5 #> toy NaN NA NA
使用3个参数可以获取单元格中的值:
mean_quality3["model", "Wood", "yes"] #> [1] 5
reshape2重塑数据框
前面我们学习了如何筛选、排序、合并和汇总数据框,有时候我们需要做些更复杂的操作。
例如下面数据包含两种产品不同日期的质量和耐久性的测试结果:
toy_tests = read_csv("../../R/dataset/product-toy-tests.csv") #> Parsed with column specification: #> cols( #> id = col_character(), #> date = col_integer(), #> sample = col_integer(), #> quality = col_integer(), #> durability = col_integer() #> ) toy_tests #> # A tibble: 8 x 5 #> id date sample quality durability #> <chr> <int> <int> <int> <int> #> 1 T01 20160201 100 9 9 #> 2 T01 20160302 150 10 9 #> 3 T01 20160405 180 9 10 #> 4 T01 20160502 140 9 9 #> 5 T02 20160201 70 7 9 #> 6 T02 20160303 75 8 8 #> 7 T02 20160403 90 9 8 #> 8 T02 20160502 85 10 9
如果需要同时比较两种产品的质量和耐久性,这种格式就比较麻烦,如果是下面的格式就好了:
date T01 T02 20160201 9 9 2016 10 9
reshape2
包就是用来搞定这种任务的,如果没有安装,运行下面代码:
install.packages("reshape2")
安装成功后,我们就可以使用 dcast()
来转换数据,用于比较:
library(reshape2) #> #> 载入程辑包:'reshape2' #> The following object is masked from 'package:tidyr': #> #> smiths toy_quality = dcast(toy_tests, date ~ id, value.var = "quality") toy_quality #> date T01 T02 #> 1 20160201 9 7 #> 2 20160302 10 NA #> 3 20160303 NA 8 #> 4 20160403 NA 9 #> 5 20160405 9 NA #> 6 20160502 9 10
上述代码重塑了 toy_tests
让 date
列被共享, id
值被单独分割为列,每个 date
与 id
对应的值是 quality
。
可以看到数据中存在缺失值,有一种叫 末次观测值结转法(LOCF) 可以填补缺失值,当非缺失值后面紧跟一个缺失值时,就用该缺失值填补后面的缺失值,直到所有缺失值都被填满。 zoo
包提供了LOCF的一个实现,使用下面代码安装:
install.packages("zoo")
下面用一组简单的向量演示:
library(zoo) #> #> 载入程辑包:'zoo' #> The following objects are masked from 'package:base': #> #> as.Date, as.Date.numeric na.locf(c(1, 2, NA, NA, 3, 1, NA, 2, NA)) #> [1] 1 2 2 2 3 1 1 2 2
同样的方法我们可以应用于现在处理的数据:
na.locf(toy_quality$T01) #> [1] 9 10 10 10 9 9
如果需要填补的数据很多,包含上千个产品,更好的做法是使用 lapply
进行自动分配:
toy_quality[-1] = lapply(toy_quality[-1], na.locf ) toy_quality #> date T01 T02 #> 1 20160201 9 7 #> 2 20160302 10 7 #> 3 20160303 10 8 #> 4 20160403 10 9 #> 5 20160405 9 9 #> 6 20160502 9 10
这里数据虽然已经没有了缺失值,但每一行数据的含义却发生了变化。原始数据中产品T01在20160303这天并没有测试,所以这一天的值应该被解释为在此之前的最后一次quality的测试值。另一个问题是两种产品都是按月测试的,但重塑后的数据框没有以固定的频率对其date。
下面方法进问题进行修正。
toy_tests$ym = substr(toy_tests$date, 1, 6) toy_tests #> # A tibble: 8 x 6 #> id date sample quality durability ym #> <chr> <int> <int> <int> <int> <chr> #> 1 T01 20160201 100 9 9 201602 #> 2 T01 20160302 150 10 9 201603 #> 3 T01 20160405 180 9 10 201604 #> 4 T01 20160502 140 9 9 201605 #> 5 T02 20160201 70 7 9 201602 #> 6 T02 20160303 75 8 8 201603 #> 7 T02 20160403 90 9 8 201604 #> 8 T02 20160502 85 10 9 201605
我们只提取年月信息,然后利用它进行重塑。
toy_quality = dcast(toy_tests, ym ~ id, value.var = "quality") toy_quality #> ym T01 T02 #> 1 201602 9 7 #> 2 201603 10 8 #> 3 201604 9 9 #> 4 201605 9 10
现在,两种产品每月的质量得分自然地展示出来,而且每月缺失值。
有时候,我们需要将 许多列合并为1列 ,用于表示被测量的对象,另外用1列存储对应的结果值。下面用 melt()
函数将原始数据两种测量组合到一起:
toy_tests2 = melt(toy_tests, id.vars = c("id", "ym"), measure.vars = c("quality", "durability"), variable.name = "measure") toy_tests2 #> id ym measure value #> 1 T01 201602 quality 9 #> 2 T01 201603 quality 10 #> 3 T01 201604 quality 9 #> 4 T01 201605 quality 9 #> 5 T02 201602 quality 7 #> 6 T02 201603 quality 8 #> 7 T02 201604 quality 9 #> 8 T02 201605 quality 10 #> 9 T01 201602 durability 9 #> 10 T01 201603 durability 9 #> 11 T01 201604 durability 10 #> 12 T01 201605 durability 9 #> 13 T02 201602 durability 9 #> 14 T02 201603 durability 8 #> 15 T02 201604 durability 8 #> 16 T02 201605 durability 9
这种格式正是 ggplot2
所喜爱的长格式数据,我们可以来画图:
library(ggplot2) ggplot(toy_tests2, aes(x = ym, y = value)) + geom_point() + facet_grid(id ~ measure)
我们得到了按照产品id和measure分组,以ym为x轴,以value为y轴的散点图,可以清晰对比分组后两种产品质量差异(以年月)。
我们还可以用不同的颜色来表示产品,下图可以给出与上图相同的信息:
ggplot(toy_tests2, aes(x = ym, y = value, color = id)) + geom_point() + facet_grid(. ~ measure)
通过sqldf包使用SQL查询数据框
有没有一种方法,能够直接使用SQL进行数据框查询,就像数据框是关系型数据库中的表一样呢? sqldf
包给出肯定答案。该包吸收了 SQLite 轻量结构和易于嵌入R会话的优点,可以用下面代码安装:
install.packages("sqldf")
首先加载包:
library(sqldf) #> 载入需要的程辑包:gsubfn #> 载入需要的程辑包:proto #> 载入需要的程辑包:RSQLite
注意加载 sqldf
包时,几个依赖包会自动加载进来。 sql
包的实现依赖这些包,它基本上是在R和SQLite之间传输数据和转换数据类型 。
读入前面使用的产品表格:
product_info = read_csv("../../R/dataset/product-info.csv") #> Parsed with column specification: #> cols( #> id = col_character(), #> name = col_character(), #> type = col_character(), #> class = col_character(), #> released = col_character() #> ) product_stats = read_csv("../../R/dataset/product-stats.csv") #> Parsed with column specification: #> cols( #> id = col_character(), #> material = col_character(), #> size = col_integer(), #> weight = col_double() #> ) product_tests = read_csv("../../R/dataset/product-tests.csv") #> Parsed with column specification: #> cols( #> id = col_character(), #> quality = col_integer(), #> durability = col_integer(), #> waterproof = col_character() #> ) toy_tests = read_csv("../../R/dataset/product-toy-tests.csv") #> Parsed with column specification: #> cols( #> id = col_character(), #> date = col_integer(), #> sample = col_integer(), #> quality = col_integer(), #> durability = col_integer() #> )
sqldf
包的神奇之处在于我们可以使用SQL语句查询工作环境中的数据框,例如:
sqldf("select * from product_info") #> id name type class released #> 1 T01 SupCar toy vehicle yes #> 2 T02 SupPlane toy vehicle no #> 3 M01 JeepX model vehicle yes #> 4 M02 AircraftX model vehicle yes #> 5 M03 Runner model people yes #> 6 M04 Dancer model people no
sqldf与SQLite一样,支持简单的选择性请求。
比如选择特定列:
sqldf("select id, name, class from product_info") #> id name class #> 1 T01 SupCar vehicle #> 2 T02 SupPlane vehicle #> 3 M01 JeepX vehicle #> 4 M02 AircraftX vehicle #> 5 M03 Runner people #> 6 M04 Dancer people
根据条件筛选记录:
sqldf("select id, name from product_info where released = 'yes' ") #> id name #> 1 T01 SupCar #> 2 M01 JeepX #> 3 M02 AircraftX #> 4 M03 Runner
除了基本的数据库操作和分组统计,该包还支持查询多个数据框,比如:
sqldf("select * from product_info join product_stats using (id)") #> id name type class released material size weight #> 1 T01 SupCar toy vehicle yes Metal 120 10.0 #> 2 T02 SupPlane toy vehicle no Metal 350 45.0 #> 3 M01 JeepX model vehicle yes Plastics 50 NA #> 4 M02 AircraftX model vehicle yes Plastics 85 3.0 #> 5 M03 Runner model people yes Wood 15 NA #> 6 M04 Dancer model people no Wood 16 0.6
不过sqldf包的缺点也很明显:
- sqldf默认基于SQLite,因此SQLite的局限性就是该包的局限性,比如内置的分组汇总函数是有限的,而R本身的统计汇总函数要多得多
- 不方便动态编程
- SQL的限制性也限制了该包,我们难以像操作dplyr包一样用sqldf进行表格数据的操作、变换等等
如果你喜欢这个包并想用起来,阅读sqldf更多操作例子: https://github.com/ggrothendieck/sqldf#examples
使用data.table包操作数据
data.table
包提供了一个加强版的 data.frame
,它运行效率极高,而且能够处理适合内存的大数据集,它使用 []
实现了一种自然地数据操作语法 。使用下面命令进行安装:
install.packages("data.table")
载入包:
library(data.table) #> #> 载入程辑包:'data.table' #> The following objects are masked from 'package:reshape2': #> #> dcast, melt #> The following objects are masked from 'package:dplyr': #> #> between, first, last #> The following object is masked from 'package:purrr': #> #> transpose
注意, data.table
包提供了加强版的 dcast()
和 melt()
,它们的功能更强大、性能更高,内存使用也更高效。
创建 data.table
与创建 data.frame
类似:
dt = data.table(x = 1:3, y = rnorm(3), z = letters[1:3]) dt #> x y z #> 1: 1 0.819 a #> 2: 2 0.426 b #> 3: 3 0.289 c
检查它的结构:
str(dt) #> Classes 'data.table' and 'data.frame': 3 obs. of 3 variables: #> $ x: int 1 2 3 #> $ y: num 0.819 0.426 0.289 #> $ z: chr "a" "b" "c" #> - attr(*, ".internal.selfref")=<externalptr>
可以看到, dt
的类是 data.table
和 data.frame
,也就是说 data.table
继承了 data.frame
的一些行为,但增强了其他部分。
** data.table
的基本语法是 dt[i, j, by],简单说就是使用
i 选择行,用
by 分组,然后计算
j **。接下来我们看看
data.table`继承了什么,增强了什么。
首先,我们仍然载入之前用到的产品数据,不过这里我们使用 data.table
包提供的 fread()
函数,它非常高效和智能,默认返回 data.table
。
product_info = fread("../../R/dataset/product-info.csv") product_stats = fread("../../R/dataset/product-stats.csv") product_tests = fread("../../R/dataset/product-tests.csv") toy_tests = fread("../../R/dataset/product-toy-tests.csv")
如果查看表格信息,你会发现它和 data.frame
没什么两样:
product_info #> id name type class released #> 1: T01 SupCar toy vehicle yes #> 2: T02 SupPlane toy vehicle no #> 3: M01 JeepX model vehicle yes #> 4: M02 AircraftX model vehicle yes #> 5: M03 Runner model people yes #> 6: M04 Dancer model people no
再看结构:
str(product_info) #> Classes 'data.table' and 'data.frame': 6 obs. of 5 variables: #> $ id : chr "T01" "T02" "M01" "M02" ... #> $ name : chr "SupCar" "SupPlane" "JeepX" "AircraftX" ... #> $ type : chr "toy" "toy" "model" "model" ... #> $ class : chr "vehicle" "vehicle" "vehicle" "vehicle" ... #> $ released: chr "yes" "no" "yes" "yes" ... #> - attr(*, ".internal.selfref")=<externalptr>
与 data.frame
不同,如果只提供一个参数用来构建子集, data.table
是选择行而不是列:
product_info[1] #> id name type class released #> 1: T01 SupCar toy vehicle yes product_info[1:3] #> id name type class released #> 1: T01 SupCar toy vehicle yes #> 2: T02 SupPlane toy vehicle no #> 3: M01 JeepX model vehicle yes
如果提供的是负数,那么将删除指定的行:
product_info[-1] #> id name type class released #> 1: T02 SupPlane toy vehicle no #> 2: M01 JeepX model vehicle yes #> 3: M02 AircraftX model vehicle yes #> 4: M03 Runner model people yes #> 5: M04 Dancer model people no
data.table提供了许多特殊符号,它们是data.table的重要组成。 .N
是最常用的符号之一,它表示当前分组中,对象的数目(就不用调用 nrow
函数啦)。在 []
使用它指提取最后一行。
product_info[.N] #> id name type class released #> 1: M04 Dancer model people no
product_info[c(1, .N)] #> id name type class released #> 1: T01 SupCar toy vehicle yes #> 2: M04 Dancer model people no
在对 data.table
构建子集时,能够自动根据语义计算表达式,因此可以直接使用列名,像 with()
和 subset()
那样。
比如:
product_info[released == "yes"] #> id name type class released #> 1: T01 SupCar toy vehicle yes #> 2: M01 JeepX model vehicle yes #> 3: M02 AircraftX model vehicle yes #> 4: M03 Runner model people yes
方括号内的第1个参数是行筛选器,第2个则对筛选后的数据进行适当的计算。
例如提取列:
product_info[released == "yes", id] #> [1] "T01" "M01" "M02" "M03"
在这里使用 "id"
结果不同,返回的必然是个data.table。
product_info[released == "yes", "id"] #> id #> 1: T01 #> 2: M01 #> 3: M02 #> 4: M03
第二个参数可以是表达式,例如生成一张表,反应每种 type
和 class
组合中 released
取 yes
的数量:
product_info[released == "yes", table(type, class)] #> class #> type people vehicle #> model 1 2 #> toy 0 1
要注意,给第2个参数提供list(),结果仍然转换为data.table:
product_info[released == "yes", list(id, name)] #> id name #> 1: T01 SupCar #> 2: M01 JeepX #> 3: M02 AircraftX #> 4: M03 Runner
我们可以替换原有列,生成新的data.table:
product_info[, list(id, name, released = released == "yes")] #> id name released #> 1: T01 SupCar TRUE #> 2: T02 SupPlane FALSE #> 3: M01 JeepX TRUE #> 4: M02 AircraftX TRUE #> 5: M03 Runner TRUE #> 6: M04 Dancer FALSE
还可以创建新列:
product_stats[, list(id, material, size, weight, density = size/weight)] #> id material size weight density #> 1: T01 Metal 120 10.0 12.00 #> 2: T02 Metal 350 45.0 7.78 #> 3: M01 Plastics 50 NA NA #> 4: M02 Plastics 85 3.0 28.33 #> 5: M03 Wood 15 NA NA #> 6: M04 Wood 16 0.6 26.67
为了简化,data.table使用.()作为list()的缩写,这两者等价:
product_info[, .(id, name, type, class)] #> id name type class #> 1: T01 SupCar toy vehicle #> 2: T02 SupPlane toy vehicle #> 3: M01 JeepX model vehicle #> 4: M02 AircraftX model vehicle #> 5: M03 Runner model people #> 6: M04 Dancer model people product_info[released == "yes", .(id, name)] #> id name #> 1: T01 SupCar #> 2: M01 JeepX #> 3: M02 AircraftX #> 4: M03 Runner
提供排序索引可以对记录排序:
product_stats[order(size, decreasing = TRUE)] #> id material size weight #> 1: T02 Metal 350 45.0 #> 2: T01 Metal 120 10.0 #> 3: M02 Plastics 85 3.0 #> 4: M01 Plastics 50 NA #> 5: M04 Wood 16 0.6 #> 6: M03 Wood 15 NA
前面都是在构建子集后,又创建新的data.table。这样挺麻烦的,因此 data.table
包提供了对列进行原地赋值的符号 :=
,例如 product_stats
开始是这样的:
product_stats #> id material size weight #> 1: T01 Metal 120 10.0 #> 2: T02 Metal 350 45.0 #> 3: M01 Plastics 50 NA #> 4: M02 Plastics 85 3.0 #> 5: M03 Wood 15 NA #> 6: M04 Wood 16 0.6
使用 :=
直接在上面数据框创建新列:
product_stats[, density := size / weight]
虽然没有任何返回,但数据已经被修改了:
product_stats #> id material size weight density #> 1: T01 Metal 120 10.0 12.00 #> 2: T02 Metal 350 45.0 7.78 #> 3: M01 Plastics 50 NA NA #> 4: M02 Plastics 85 3.0 28.33 #> 5: M03 Wood 15 NA NA #> 6: M04 Wood 16 0.6 26.67
使用 :=
替换已有的列:
product_info[, released := released == "yes"] product_info #> id name type class released #> 1: T01 SupCar toy vehicle TRUE #> 2: T02 SupPlane toy vehicle FALSE #> 3: M01 JeepX model vehicle TRUE #> 4: M02 AircraftX model vehicle TRUE #> 5: M03 Runner model people TRUE #> 6: M04 Dancer model people FALSE
使用键获取值
索引支持是data.table另一个独特功能,即我们可以创建键(key),使用键获取记录及其高效。
例如,使用 setkey()
将 id
设置为 product_info
中的一个键:
setkey(product_info, id)
同样的,函数无任何返回,但我们已经为原始数据设置了键,而且原来的数据看起来也没变化:
product_info #> id name type class released #> 1: M01 JeepX model vehicle TRUE #> 2: M02 AircraftX model vehicle TRUE #> 3: M03 Runner model people TRUE #> 4: M04 Dancer model people FALSE #> 5: T01 SupCar toy vehicle TRUE #> 6: T02 SupPlane toy vehicle FALSE
但键已生成:
key(product_info) #> [1] "id"
现在我们可以用它来获取数据了,比如提供一个id值:
product_info["M01"] #> id name type class released #> 1: M01 JeepX model vehicle TRUE
也可以使用 setkeyv()
来设置键,但它只接受字符向量:
setkeyv(product_stats, "id")
当key是一个动态变化的向量时,这个函数会非常好用。
product_stats["M02"] #> id material size weight density #> 1: M02 Plastics 85 3 28.3
如果两个表格有相同的键,我们可以轻松把他们连接到一起:
product_info[product_stats] #> id name type class released material size weight density #> 1: M01 JeepX model vehicle TRUE Plastics 50 NA NA #> 2: M02 AircraftX model vehicle TRUE Plastics 85 3.0 28.33 #> 3: M03 Runner model people TRUE Wood 15 NA NA #> 4: M04 Dancer model people FALSE Wood 16 0.6 26.67 #> 5: T01 SupCar toy vehicle TRUE Metal 120 10.0 12.00 #> 6: T02 SupPlane toy vehicle FALSE Metal 350 45.0 7.78
data.table的键可以不止一个。例如使用 id
和 date
定位 toy_tests
中的记录:
setkey(toy_tests, id, date)
现在提供key中的两个元素就可以获取记录了
toy_tests[.("T01", 20160201)] #> id date sample quality durability #> 1: T01 20160201 100 9 9
如果提供第一个元素,会返回匹配的多个值:
toy_tests["T01"] #> id date sample quality durability #> 1: T01 20160201 100 9 9 #> 2: T01 20160302 150 10 9 #> 3: T01 20160405 180 9 10 #> 4: T01 20160502 140 9 9
key不能错序,因此不能单独提供第2个元素以及反序排列。
toy_tests[20160201] #> id date sample quality durability #> 1: <NA> NA NA NA NA toy_tests[.(20160202,"T01")] #> Error in bmerge(i, x, leftcols, rightcols, io, xo, roll, rollends, nomatch, : x.'id' is a character column being joined to i.'V1' which is type 'double'. Character columns must join to factor or character columns.
对数据进行分组汇总
by
是data.table中另一个重要参数(即方括号内的第3个参数),它可以将数据按照 by
值进行分组,并对分组计算第2个参数。
接下来,我们学习如何通过by以简便的方式实现数据的分组汇总。
最简单的用法是计算每组的记录条数:
product_info[, .N, by = released] #> released N #> 1: TRUE 4 #> 2: FALSE 2
分组的变量可以不止一个,例如由 type
和 class
确定一个分组:
product_info[, .N, by = .(type, class)] #> type class N #> 1: model vehicle 2 #> 2: model people 2 #> 3: toy vehicle 2
可以对每个分组进行统计计算,这里计算防水和非防水产品的质量得分均值:
product_tests[, mean(quality, na.rm = TRUE), by = .(waterproof)] #> waterproof V1 #> 1: no 10.00 #> 2: yes 5.75
可以看到结果存储在V1列中,我们可以手动指定列名:
product_tests[, .(mean_quality = mean(quality, na.rm = TRUE)), by = .(waterproof)] #> waterproof mean_quality #> 1: no 10.00 #> 2: yes 5.75
注意操作需要�放在 list
中进行( .()
)。
我们可以将多个[]按顺序连接起来,形成工作流(类似管道 %>%
)。
下面的例子中,首先使用通用键id将product_info和product_tests连接起来,然后筛选已发布的产品,再按type和class进行分组,最后计算每组的quality和durability的均值。
type_class_test0 = product_info[product_tests][released == TRUE, .(mean_quality = mean(quality, na.rm=TRUE), mean_durability = mean(durability, na.rm=TRUE)), by = .(type, class)] type_class_test0 #> type class mean_quality mean_durability #> 1: toy vehicle NaN 10.0 #> 2: model vehicle 6 4.5 #> 3: model people 5 NaN
在返回的data.table中,by所对应的组合中的值是唯一的,虽然实现了目标,但结果中没有设置键:
key(type_class_test0) #> NULL
这种情况下,我们可以使用keyby来确保结果的data.table自动将keyby对应的分组向量设置为键。一般data.table会保持原来的顺序返回,有时候我们想要设定排序,keyby也可以实现,所以是一举两得:
type_class_test = product_info[product_tests][released == TRUE, .(mean_quality = mean(quality, na.rm = TRUE), mean_durability = mean(durability, na.rm = TRUE)), keyby = .(type, class)] type_class_test #> type class mean_quality mean_durability #> 1: model people 5 NaN #> 2: model vehicle 6 4.5 #> 3: toy vehicle NaN 10.0
key(type_class_test) #> [1] "type" "class"
下面可以直接用键来获取值:
type_class_test[.("model", "vehicle"), mean_quality] #> [1] 6
对大数据集使用键进行搜索,能够比迭代使用逻辑比较快得多,因为键搜索利用了二进制搜索,而迭代在不必要的计算上浪费了时间。
下面举例说明,首先创建有1000万行的数据,其中一列是索引列id,其他两列是随机数:
n = 10000000 test1 = data.frame(id = 1:n, x = rnorm(n), y = rnorm(n))
现在查找id为876543的行,看要花多少时间:
system.time(row <- test1[test1$id == 876543, ]) #> 用户 系统 流逝 #> 0.152 0.020 0.172
作为对比,我们使用 data.table
来完成这个任务,使用 setDT()
将数据框转换为 data.table
,该函数可以原地转换,不需要复制,并可以设定键。
setDT(test1, key = "id") class(test1) #> [1] "data.table" "data.frame"
现在我们搜索相同的元素:
system.time(row <- test1[.(876543)]) #> 用户 系统 流逝 #> 0.001 0.000 0.001
结果一致,但data.table用的时间要少得多。
重塑data.table
data.table扩展包为data.table对象提供了更强更快得 dcast()
和 melt()
函数。
例如将toy_tests的每个产品质量得分按照年和月进行对齐
toy_tests[, ym := substr(date, 1, 6)] toy_quality = dcast(toy_tests, ym ~ id, value.var = "quality") toy_quality #> ym T01 T02 #> 1: 201602 9 7 #> 2: 201603 10 8 #> 3: 201604 9 9 #> 4: 201605 9 10
data.table::dcast()
提供了更强大的多变量支持:
toy_tests2 = data.table::dcast(toy_tests, ym ~ id, value.var = c("quality", "durability")) toy_tests2 #> ym quality_T01 quality_T02 durability_T01 durability_T02 #> 1: 201602 9 7 9 9 #> 2: 201603 10 8 9 8 #> 3: 201604 9 9 10 8 #> 4: 201605 9 10 9 9
看到没,data.table可以自动将id值与质量分类连接起来。
此时 ym
是键:
key(toy_tests2) #> [1] "ym"
我们可以利用它提取数据:
toy_tests2["201602"] #> ym quality_T01 quality_T02 durability_T01 durability_T02 #> 1: 201602 9 7 9 9
使用原地设置函数
我们知道R存在复制修改机制,这在进行大数据计算时开销很大, data.table
提供了一系列支持语义的 set
函数,它们可以原地修改data.table,因此避免不必要的复制。
仍以 product_stats
为例,我们可以使用 setDF()
函数不要任何复制就可以将data.table变成data.frame。
product_stats #> id material size weight density #> 1: M01 Plastics 50 NA NA #> 2: M02 Plastics 85 3.0 28.33 #> 3: M03 Wood 15 NA NA #> 4: M04 Wood 16 0.6 26.67 #> 5: T01 Metal 120 10.0 12.00 #> 6: T02 Metal 350 45.0 7.78 setDF(product_stats) class(product_stats) #> [1] "data.frame"
setDT()
可以将任意的data.frame转换为data.table,并设置键。
setDT(product_stats, key = "id") class(product_stats) #> [1] "data.table" "data.frame"
使用 setnames()
可以对列重命名:
setnames(product_stats, "size", "volume") product_stats #> id material volume weight density #> 1: M01 Plastics 50 NA NA #> 2: M02 Plastics 85 3.0 28.33 #> 3: M03 Wood 15 NA NA #> 4: M04 Wood 16 0.6 26.67 #> 5: T01 Metal 120 10.0 12.00 #> 6: T02 Metal 350 45.0 7.78
如果给行添加索引,使用:
product_stats[, i := .I] product_stats #> id material volume weight density i #> 1: M01 Plastics 50 NA NA 1 #> 2: M02 Plastics 85 3.0 28.33 2 #> 3: M03 Wood 15 NA NA 3 #> 4: M04 Wood 16 0.6 26.67 4 #> 5: T01 Metal 120 10.0 12.00 5 #> 6: T02 Metal 350 45.0 7.78 6
为方便,索引一般在第1列,所以我们要修改列的顺序:
setcolorder(product_stats, c("i", "id", "material", "weight", "volume", "density")) product_stats #> i id material weight volume density #> 1: 1 M01 Plastics NA 50 NA #> 2: 2 M02 Plastics 3.0 85 28.33 #> 3: 3 M03 Wood NA 15 NA #> 4: 4 M04 Wood 0.6 16 26.67 #> 5: 5 T01 Metal 10.0 120 12.00 #> 6: 6 T02 Metal 45.0 350 7.78
data.table的动态作用域
我们不仅可以直接使用列,也可以提前定义注入 .N
、 .I
和 .SD
来指代数据中的重要部分。
为演示,我们先创建新的data.table,命名为 market_data
,其中date列是连续的。
market_data = data.table(date = as.Date("2015-05-01") + 0:299) head(market_data) #> date #> 1: 2015-05-01 #> 2: 2015-05-02 #> 3: 2015-05-03 #> 4: 2015-05-04 #> 5: 2015-05-05 #> 6: 2015-05-06
向调用函数一样,我们给data.table添加数据列:
set.seed(123) market_data[, `:=`( price = round(30 * cumprod(1 + rnorm(300, 0.001, 0.05)), 2), volume = rbinom(300, 5000, 0.8) )]
注意这里的price和volumn都是服从正态分布的随机数:
head(market_data) #> date price volume #> 1: 2015-05-01 29.2 4021 #> 2: 2015-05-02 28.9 4000 #> 3: 2015-05-03 31.2 4033 #> 4: 2015-05-04 31.3 4036 #> 5: 2015-05-05 31.5 3995 #> 6: 2015-05-06 34.3 3955
我们以图形的方式展示数据:
plot(price ~ date, data = market_data, type = "l", main = "Market data")
数据准备好后,我们看看动态作用域如何让事情变得简单。
看下时间范围:
market_data[, range(date)] #> [1] "2015-05-01" "2016-02-24"
将数据整合缩减为月度数据:
monthly = market_data[, .(open = price[[1]], high = max(price), low = min(price), close = price[[.N]]), keyby = .(year = year(date), month = month(date))] head(monthly) #> year month open high low close #> 1: 2015 5 29.2 37.7 26.1 28.4 #> 2: 2015 6 28.1 37.6 28.1 37.2 #> 3: 2015 7 36.3 41.0 32.1 41.0 #> 4: 2015 8 41.5 50.0 30.9 30.9 #> 5: 2015 9 30.5 34.5 22.9 27.0 #> 6: 2015 10 25.7 33.2 24.6 29.3
计算过程为: 先根据by表达式将原始数据分割,分割后的每个部分都是原始数据的一个子集,并且原始数据和子集都是data.table。然后在每个子集data.table的语义中计算j表达式 。
下面代码没有按组聚合数据,而是画了每年的价格图:
oldpar = par(mfrow = c(1, 2)) market_data[, { plot(price ~ date, type = "l", main = sprintf("Market data (%d)", year)) }, by = .(year = year(date))] par(oldpar)
这里我们没有为 plot()
设定data参数,图像也成功绘制,这是因为该操作是在data.table的语义中进行的。
此外,j表达式还可以用于构建模型的代码,下面是一个批量拟合线性模型的例子。这里使用 diamonds
数据集。
data("diamonds", package = "ggplot2") setDT(diamonds) head(diamonds) #> carat cut color clarity depth table price x y z #> 1: 0.23 Ideal E SI2 61.5 55 326 3.95 3.98 2.43 #> 2: 0.21 Premium E SI1 59.8 61 326 3.89 3.84 2.31 #> 3: 0.23 Good E VS1 56.9 65 327 4.05 4.07 2.31 #> 4: 0.29 Premium I VS2 62.4 58 334 4.20 4.23 2.63 #> 5: 0.31 Good J SI2 63.3 58 335 4.34 4.35 2.75 #> 6: 0.24 Very Good J VVS2 62.8 57 336 3.94 3.96 2.48
该数据集包含超过5万条钻石信息的记录,每条记录了钻石的10个属性,现在我们队cut列中的每种切割类型都你拟合一个线性回归模型,由此观察每种切割类型中carat与depth是如何反映log(price)的信息。
diamonds[, { m = lm(log(price) ~ carat + depth) as.list(coef(m)) }, keyby = .(cut)] #> cut (Intercept) carat depth #> 1: Fair 7.73 1.26 -0.01498 #> 2: Good 7.08 1.97 -0.01460 #> 3: Very Good 6.29 2.09 -0.00289 #> 4: Premium 5.93 1.85 0.00594 #> 5: Ideal 8.50 2.13 -0.03808
动态作用域允许我们组合使用data.table内部或外部预定义的符号。举例,我们定义一个函数,计算market_data中由用户定义的列的年度均值:
average = function(column){ market_data[, .(average = mean(.SD[[column]])), by = .(year = year(date))] }
这里我们使用 .SD[[x]]
提取x列的值,这跟通过名字从列表中提取成分或元素相同。
下面计算每年的平均价格:
average("price") #> year average #> 1: 2015 32.3 #> 2: 2016 32.4
每年平均数量:
average("volume") #> year average #> 1: 2015 4000 #> 2: 2016 4003
我们可以利用此包专门的语法创造一个列数动态变化的组合,并且组合中的列是由动态变化的名称决定的。
这里我们假设添加额外的3列数据,每一列都是原始价格加了随机噪声生成的。不用重复调用 market_date[, price1 := ...]
,而是使用 market_data[, (columns) := list(...)]
来动态设定列,其中 columns
是一个包含列名的字符向量, list(...)
是每个列对应的值:
price_cols = paste0("price", 1:3) market_data[, (price_cols) := lapply(1:3, function(i) round(price + rnorm(.N, 0, 5), 2))] head(market_data) #> date price volume price1 price2 price3 #> 1: 2015-05-01 29.2 4021 30.6 27.4 33.2 #> 2: 2015-05-02 28.9 4000 29.7 20.4 36.0 #> 3: 2015-05-03 31.2 4033 34.3 26.9 27.2 #> 4: 2015-05-04 31.3 4036 29.3 29.0 28.0 #> 5: 2015-05-05 31.5 3995 36.0 32.1 34.8 #> 6: 2015-05-06 34.3 3955 30.1 31.0 35.2
另一方面,如果表格有很多列,并且需要对它们的子集进行一些计算,也可以用类似的语法来解决。
举例,我们现在需要对每个价格列调用 na.locf()
以去掉缺失值,先获取所有的价格列:
cols = colnames(market_data) price_cols = cols[grep("^price", cols)] price_cols #> [1] "price" "price1" "price2" "price3"
然后我们用类似的语法,并添加一个参数 .SDcols = price_cols
,这是为了让 .SD
中的列只是我们想要的那些价格列。
market_data[, (price_cols) := lapply(.SD, zoo::na.locf), .SDcols = price_cols] head(market_data) #> date price volume price1 price2 price3 #> 1: 2015-05-01 29.2 4021 30.6 27.4 33.2 #> 2: 2015-05-02 28.9 4000 29.7 20.4 36.0 #> 3: 2015-05-03 31.2 4033 34.3 26.9 27.2 #> 4: 2015-05-04 31.3 4036 29.3 29.0 28.0 #> 5: 2015-05-05 31.5 3995 36.0 32.1 34.8 #> 6: 2015-05-06 34.3 3955 30.1 31.0 35.2
最后,更多操作请前往 https://github.com/Rdatatable/data.table/wiki 查看完整功能列表。
使用dplyr操作数据框
关于dplyr的基本操作我已经写过很多笔记了,不再赘述,想学习的读者请参阅下面几篇文章,这部分我只挑没接触过的学习下。
- r<-基础|分析】初学者学习tidyverse
- 【r<-数据分析】使用dplyr(1):介绍与filter操作 及后续
- 高级操作: 【r<-高级|dplyr】dplyr编程,quote与unquote
与 data.table
类似, dplyr
也提供了 do()
函数来对每组数据进行任意操作。
例如将 diamonds
按 cut
分组,每组都按 log(price) ~ carat
拟合一个线性模型。和 data.table
不同的是,我们需要为操作指定一个名称,以便将结果存储在列中。而且 do()
表达式不能直接在分组数据的语义下计算 ,我们需要使用 .
来表示数据。
data("diamonds", package = "ggplot2") models = diamonds %>% group_by(cut) %>% do(lmod = lm(log(price) ~ carat, data = .)) models #> Source: local data frame [5 x 2] #> Groups: <by row> #> #> # A tibble: 5 x 2 #> cut lmod #> * <ord> <list> #> 1 Fair <S3: lm> #> 2 Good <S3: lm> #> 3 Very Good <S3: lm> #> 4 Premium <S3: lm> #> 5 Ideal <S3: lm>
注意结果创建了一个新列,该列不是典型的原子向量,每个元素都是模型的结果,包含线性回归对象的列表。我们可以通过索引来提取模型结果:
models$lmod[[1]] #> #> Call: #> lm(formula = log(price) ~ carat, data = .) #> #> Coefficients: #> (Intercept) carat #> 6.78 1.25
在需要完成高度定制的操作时, do()
的优势非常明显 。下面举例。
假如我们需要分析 toy_tests
数据,要对每种产品的质量和耐久性进行汇总。如果只需要样本数最多的3个测试记录,并且每个产品的质量和耐久性是经样本数加权的平均数 ,下面是做法。
toy_tests %>% group_by(id) %>% arrange(desc(sample)) %>% do(head(., 3)) %>% summarise( quality = sum(quality * sample) / sum(sample), durability = sum(durability * sample) / sum(sample) ) #> # A tibble: 2 x 3 #> id quality durability #> <chr> <dbl> <dbl> #> 1 T01 9.32 9.38 #> 2 T02 9.04 8.34
为了查看中间结果,可以运行 do()
之前的代码:
toy_tests %>% group_by(id) %>% arrange(desc(sample)) #> # A tibble: 8 x 6 #> # Groups: id [2] #> id date sample quality durability ym #> <chr> <int> <int> <int> <int> <chr> #> 1 T01 20160405 180 9 10 201604 #> 2 T01 20160302 150 10 9 201603 #> 3 T01 20160502 140 9 9 201605 #> 4 T01 20160201 100 9 9 201602 #> 5 T02 20160403 90 9 8 201604 #> 6 T02 20160502 85 10 9 201605 #> 7 T02 20160303 75 8 8 201603 #> 8 T02 20160201 70 7 9 201602
使用rlist包处理嵌套数据结构
在R中,最常见的嵌套数据结构是列表对象,之前关注的都是操作表格数据,这部分我们一起玩转 rlist
包,它是针对非表格数据设计的包 。
rlist
的设计与 dplyr
非常相似,它提供了针对列表对象的映射、筛选、选择、排序和聚合功能。
安装:
install.packages("rlist")
载入:
library(rlist)
为了把数据以列表形式载入R,我们需要用 jsonlite::fromJSON()
或者直接使用 rlist
提供的 list.load()
函数 (这里我找不到原作者的数据文件,所以自己谷歌找了类似的学习下):
products = list.load("../../R/dataset/products.json")
products
的每个成分都包含产品所有信息,使用 list.map()
可以在每个成分的语义中计算表达式:
str(list.map(products, id)) #> List of 66 #> $ : chr "amazonite" #> $ : chr "amber" #> $ : chr "amethyst" #> $ : chr "ametrine" #> $ : chr "apophyllite" #> $ : chr "aquamarine" #> $ : chr "aragonite" #> $ : chr "azurite" #> $ : chr "barite" #> $ : chr "bismuth" #> $ : chr "black_tourmaline" #> $ : chr "bloodstone" #> $ : chr "blue_calcite" #> $ : chr "kyanite" #> $ : chr "carnelian" #> $ : chr "celestite" #> $ : chr "chrysocolla" #> $ : chr "chrysoprase" #> $ : chr "citrine" #> $ : chr "copper" #> $ : chr "dalmatian_stone" #> $ : chr "danburite" #> $ : chr "desert_rose" #> $ : chr "emerald" #> $ : chr "epidote" #> $ : chr "fluorite" #> $ : chr "garnet" #> $ : chr "green_calcite" #> $ : chr "hematite" #> $ : chr "honey_calcite" #> $ : chr "howlite" #> $ : chr "jasper" #> $ : chr "jet" #> $ : chr "kunzite" #> $ : chr "labradorite" #> $ : chr "lapis_lazuli" #> $ : chr "lemon_quartz" #> $ : chr "lemurian_quartz" #> $ : chr "lepidolite" #> $ : chr "libyan_gold_tektite" #> $ : chr "magnesite" #> $ : chr "malachite" #> $ : chr "meteorite" #> $ : chr "moldavite" #> $ : chr "moonstone" #> $ : chr "orange_calcite" #> $ : chr "peridot" #> $ : chr "pyrite" #> $ : chr "quartz" #> $ : chr "rhodochrosite" #> $ : chr "rhodonite" #> $ : chr "rose_quartz" #> $ : chr "ruby" #> $ : chr "scolecite" #> $ : chr "selenite" #> $ : chr "serpentine" #> $ : chr "shungite" #> $ : chr "smoky_quartz" #> $ : chr "sodalite" #> $ : chr "tektite" #> $ : chr "tibetan_black_quartz" #> $ : chr "tigers_eye" #> $ : chr "titanium_quartz" #> $ : chr "tourmalined_quartz" #> $ : chr "turquoise" #> $ : chr "vanadinite"
list.mapv()
简化这个列表,返回一个向量:
list.mapv(products, name) #> [1] "Amazonite" "Amber" "Amethyst" #> [4] "Ametrine" "Apophyllite" "Aquamarine" #> [7] "Aragonite" "Azurite" "Barite" #> [10] "Bismuth" "Black Tourmaline" "Bloodstone" #> [13] "Blue Calcite" "Blue Kyanite" "Carnelian" #> [16] "Celestite" "Chrysocolla" "Chrysoprase" #> [19] "Citrine" "Copper" "Dalmatian Stone" #> [22] "Danburite" "Desert Rose" "Emerald" #> [25] "Epidote" "Fluorite" "Garnet" #> [28] "Green Calcite" "Hematite" "Honey Calcite" #> [31] "Howlite" "Jasper" "Jet" #> [34] "Kunzite" "Labradorite" "Lapis Lazuli" #> [37] "Lemon Quartz" "Lemurian Quartz" "Lepidolite" #> [40] "Libyan Gold Tektite" "Magnesite" "Malachite" #> [43] "Meteorite" "Moldavite" "Moonstone" #> [46] "Orange Calcite" "Peridot" "Pyrite" #> [49] "Quartz" "Rhodochrosite" "Rhodonite" #> [52] "Rose Quartz" "Ruby" "Scolecite" #> [55] "Selenite" "Serpentine" "Shungite" #> [58] "Smoky Quartz" "Sodalite" "Tektite" #> [61] "Tibetan Black Quartz" "Tiger Eye" "Titanium Quartz" #> [64] "Tourmalined Quartz" "Turquoise" "Vanadinite"
为了对products进行筛选,我们可以使用 list.filter()
,只有所有条件都为 TRUE
的 products
元素才会被返回:
liveTRUE = list.filter(products, livemode = TRUE) list.mapv(liveTRUE, name) #> [1] "Amazonite" "Amber" "Amethyst" #> [4] "Ametrine" "Apophyllite" "Aquamarine" #> [7] "Aragonite" "Azurite" "Barite" #> [10] "Bismuth" "Black Tourmaline" "Bloodstone" #> [13] "Blue Calcite" "Blue Kyanite" "Carnelian" #> [16] "Celestite" "Chrysocolla" "Chrysoprase" #> [19] "Citrine" "Copper" "Dalmatian Stone" #> [22] "Danburite" "Desert Rose" "Emerald" #> [25] "Epidote" "Fluorite" "Garnet" #> [28] "Green Calcite" "Hematite" "Honey Calcite" #> [31] "Howlite" "Jasper" "Jet" #> [34] "Kunzite" "Labradorite" "Lapis Lazuli" #> [37] "Lemon Quartz" "Lemurian Quartz" "Lepidolite" #> [40] "Libyan Gold Tektite" "Magnesite" "Malachite" #> [43] "Meteorite" "Moldavite" "Moonstone" #> [46] "Orange Calcite" "Peridot" "Pyrite" #> [49] "Quartz" "Rhodochrosite" "Rhodonite" #> [52] "Rose Quartz" "Ruby" "Scolecite" #> [55] "Selenite" "Serpentine" "Shungite" #> [58] "Smoky Quartz" "Sodalite" "Tektite" #> [61] "Tibetan Black Quartz" "Tiger Eye" "Titanium Quartz" #> [64] "Tourmalined Quartz" "Turquoise" "Vanadinite"
注意, rlist
的设计与 dplyr
函数非常类似,我们可以利用管道符号将数据往下传递:
products %>% list.filter(livemode = TRUE) %>% list.mapv(name) #> [1] "Amazonite" "Amber" "Amethyst" #> [4] "Ametrine" "Apophyllite" "Aquamarine" #> [7] "Aragonite" "Azurite" "Barite" #> [10] "Bismuth" "Black Tourmaline" "Bloodstone" #> [13] "Blue Calcite" "Blue Kyanite" "Carnelian" #> [16] "Celestite" "Chrysocolla" "Chrysoprase" #> [19] "Citrine" "Copper" "Dalmatian Stone" #> [22] "Danburite" "Desert Rose" "Emerald" #> [25] "Epidote" "Fluorite" "Garnet" #> [28] "Green Calcite" "Hematite" "Honey Calcite" #> [31] "Howlite" "Jasper" "Jet" #> [34] "Kunzite" "Labradorite" "Lapis Lazuli" #> [37] "Lemon Quartz" "Lemurian Quartz" "Lepidolite" #> [40] "Libyan Gold Tektite" "Magnesite" "Malachite" #> [43] "Meteorite" "Moldavite" "Moonstone" #> [46] "Orange Calcite" "Peridot" "Pyrite" #> [49] "Quartz" "Rhodochrosite" "Rhodonite" #> [52] "Rose Quartz" "Ruby" "Scolecite" #> [55] "Selenite" "Serpentine" "Shungite" #> [58] "Smoky Quartz" "Sodalite" "Tektite" #> [61] "Tibetan Black Quartz" "Tiger Eye" "Titanium Quartz" #> [64] "Tourmalined Quartz" "Turquoise" "Vanadinite"
使用 list.select
可以筛选字段以及创建新的字段:
products %>% list.filter(livemode = TRUE) %>% list.select(id, name, caption) %>% str() #> List of 66 #> $ :List of 3 #> ..$ id : chr "amazonite" #> ..$ name : chr "Amazonite" #> ..$ caption: chr "love,calm,communication" #> $ :List of 3 #> ..$ id : chr "amber" #> ..$ name : chr "Amber" #> ..$ caption: chr "cleansing,vitality,protection" #> $ :List of 3 #> ..$ id : chr "amethyst" #> ..$ name : chr "Amethyst" #> ..$ caption: chr "cleansing,protection,clarity" #> $ :List of 3 #> ..$ id : chr "ametrine" #> ..$ name : chr "Ametrine" #> ..$ caption: chr "Clarity,motivation,confidence" #> $ :List of 3 #> ..$ id : chr "apophyllite" #> ..$ name : chr "Apophyllite" #> ..$ caption: chr "insight,intuition,vitality" #> $ :List of 3 #> ..$ id : chr "aquamarine" #> ..$ name : chr "Aquamarine" #> ..$ caption: chr "Clarity,calm,cleansing" #> $ :List of 3 #> ..$ id : chr "aragonite" #> ..$ name : chr "Aragonite" #> ..$ caption: chr "balance,strength,confidence" #> $ :List of 3 #> ..$ id : chr "azurite" #> ..$ name : chr "Azurite" #> ..$ caption: chr "Calm,balance,dreamwork" #> $ :List of 3 #> ..$ id : chr "barite" #> ..$ name : chr "Barite" #> ..$ caption: chr "Dreamwork,insight,intuition" #> $ :List of 3 #> ..$ id : chr "bismuth" #> ..$ name : chr "Bismuth" #> ..$ caption: chr "Transformation,vitality,focus" #> $ :List of 3 #> ..$ id : chr "black_tourmaline" #> ..$ name : chr "Black Tourmaline" #> ..$ caption: chr "grounding,protection,cleansing" #> $ :List of 3 #> ..$ id : chr "bloodstone" #> ..$ name : chr "Bloodstone" #> ..$ caption: chr "courage,cleansing,vitality,strength,manifestation,transformation" #> $ :List of 3 #> ..$ id : chr "blue_calcite" #> ..$ name : chr "Blue Calcite" #> ..$ caption: chr "Creativity,clarity,intuition,calm,protection,dreamwork" #> $ :List of 3 #> ..$ id : chr "kyanite" #> ..$ name : chr "Blue Kyanite" #> ..$ caption: chr "communication,intuition,insight" #> $ :List of 3 #> ..$ id : chr "carnelian" #> ..$ name : chr "Carnelian" #> ..$ caption: chr "confidence,courage,strength,passion,motivation,vitality" #> $ :List of 3 #> ..$ id : chr "celestite" #> ..$ name : chr "Celestite" #> ..$ caption: chr "Intuition,protection,calm" #> $ :List of 3 #> ..$ id : chr "chrysocolla" #> ..$ name : chr "Chrysocolla" #> ..$ caption: chr "communication,strength,insight" #> $ :List of 3 #> ..$ id : chr "chrysoprase" #> ..$ name : chr "Chrysoprase" #> ..$ caption: chr "Balance,joy,transformation" #> $ :List of 3 #> ..$ id : chr "citrine" #> ..$ name : chr "Citrine" #> ..$ caption: chr "confidence,love,passion,manifestation,intuition,creativity" #> $ :List of 3 #> ..$ id : chr "copper" #> ..$ name : chr "Copper" #> ..$ caption: chr "balance,vitality,grounding" #> $ :List of 3 #> ..$ id : chr "dalmatian_stone" #> ..$ name : chr "Dalmatian Stone" #> ..$ caption: chr "protection,calm,joy" #> $ :List of 3 #> ..$ id : chr "danburite" #> ..$ name : chr "Danburite" #> ..$ caption: chr "Calm,joy,communication" #> $ :List of 3 #> ..$ id : chr "desert_rose" #> ..$ name : chr "Desert Rose" #> ..$ caption: chr "insight,courage,clarity" #> $ :List of 3 #> ..$ id : chr "emerald" #> ..$ name : chr "Emerald" #> ..$ caption: chr "Love,Vitality,Balance" #> $ :List of 3 #> ..$ id : chr "epidote" #> ..$ name : chr "Epidote" #> ..$ caption: chr "Transformation,manifestation,motivation" #> $ :List of 3 #> ..$ id : chr "fluorite" #> ..$ name : chr "Fluorite" #> ..$ caption: chr "focus,clarity,insight" #> $ :List of 3 #> ..$ id : chr "garnet" #> ..$ name : chr "Garnet" #> ..$ caption: chr "Creativity,manifestation,passion" #> $ :List of 3 #> ..$ id : chr "green_calcite" #> ..$ name : chr "Green Calcite" #> ..$ caption: chr "Calm,Balance,Love" #> $ :List of 3 #> ..$ id : chr "hematite" #> ..$ name : chr "Hematite" #> ..$ caption: chr "balance,grounding,manifestation" #> $ :List of 3 #> ..$ id : chr "honey_calcite" #> ..$ name : chr "Honey Calcite" #> ..$ caption: chr "confidence,motivation,manifestation" #> $ :List of 3 #> ..$ id : chr "howlite" #> ..$ name : chr "Howlite" #> ..$ caption: chr "focus,calm,creativity" #> $ :List of 3 #> ..$ id : chr "jasper" #> ..$ name : chr "Jasper" #> ..$ caption: chr "Grounding,Balance,Strength" #> $ :List of 3 #> ..$ id : chr "jet" #> ..$ name : chr "Jet" #> ..$ caption: chr "Grounding,cleansing,vitality" #> $ :List of 3 #> ..$ id : chr "kunzite" #> ..$ name : chr "Kunzite" #> ..$ caption: chr "Love,joy,vitality" #> $ :List of 3 #> ..$ id : chr "labradorite" #> ..$ name : chr "Labradorite" #> ..$ caption: chr "Creativity,joy,vitality" #> $ :List of 3 #> ..$ id : chr "lapis_lazuli" #> ..$ name : chr "Lapis Lazuli" #> ..$ caption: chr "insight,calm,transformation" #> $ :List of 3 #> ..$ id : chr "lemon_quartz" #> ..$ name : chr "Lemon Quartz" #> ..$ caption: chr "Joy,Balance,Vitality" #> $ :List of 3 #> ..$ id : chr "lemurian_quartz" #> ..$ name : chr "Lemurian Quartz" #> ..$ caption: chr "Love,strength,balance" #> $ :List of 3 #> ..$ id : chr "lepidolite" #> ..$ name : chr "Lepidolite" #> ..$ caption: chr "Calm,Balance,Cleansing" #> $ :List of 3 #> ..$ id : chr "libyan_gold_tektite" #> ..$ name : chr "Libyan Gold Tektite" #> ..$ caption: chr "Motivation,confidence,manifestation,protection,creativity,strength" #> $ :List of 3 #> ..$ id : chr "magnesite" #> ..$ name : chr "Magnesite" #> ..$ caption: chr "balance,calm,insight" #> $ :List of 3 #> ..$ id : chr "malachite" #> ..$ name : chr "Malachite" #> ..$ caption: chr "creativity,strength,confidence" #> $ :List of 3 #> ..$ id : chr "meteorite" #> ..$ name : chr "Meteorite" #> ..$ caption: chr "Vitality,Intuition,Insight" #> $ :List of 3 #> ..$ id : chr "moldavite" #> ..$ name : chr "Moldavite" #> ..$ caption: chr "Cleansing,Manifestation,Vitality,Insight,Transformation,Dreamwork" #> $ :List of 3 #> ..$ id : chr "moonstone" #> ..$ name : chr "Moonstone" #> ..$ caption: chr "insight,intuition,dreamwork" #> $ :List of 3 #> ..$ id : chr "orange_calcite" #> ..$ name : chr "Orange Calcite" #> ..$ caption: chr "Creativity,vitality,passion" #> $ :List of 3 #> ..$ id : chr "peridot" #> ..$ name : chr "Peridot" #> ..$ caption: chr "manifestation,vitality,transformation" #> $ :List of 3 #> ..$ id : chr "pyrite" #> ..$ name : chr "Pyrite" #> ..$ caption: chr "manifestation,creativity,confidence,passion,strength,focus" #> $ :List of 3 #> ..$ id : chr "quartz" #> ..$ name : chr "Quartz" #> ..$ caption: chr "clarity,transformation,cleansing" #> $ :List of 3 #> ..$ id : chr "rhodochrosite" #> ..$ name : chr "Rhodochrosite" #> ..$ caption: chr "Love,courage,joy" #> $ :List of 3 #> ..$ id : chr "rhodonite" #> ..$ name : chr "Rhodonite" #> ..$ caption: chr "Love,transformation,clarity" #> $ :List of 3 #> ..$ id : chr "rose_quartz" #> ..$ name : chr "Rose Quartz" #> ..$ caption: chr "love,calm,creativity" #> $ :List of 3 #> ..$ id : chr "ruby" #> ..$ name : chr "Ruby" #> ..$ caption: chr "Passion,vitality,courage,strength,protection,motivation" #> $ :List of 3 #> ..$ id : chr "scolecite" #> ..$ name : chr "Scolecite" #> ..$ caption: chr "calm,insight,dreamwork" #> $ :List of 3 #> ..$ id : chr "selenite" #> ..$ name : chr "Selenite" #> ..$ caption: chr "Clarity,cleansing,motivation" #> $ :List of 3 #> ..$ id : chr "serpentine" #> ..$ name : chr "Serpentine" #> ..$ caption: chr "Manifestation,transformation,vitality" #> $ :List of 3 #> ..$ id : chr "shungite" #> ..$ name : chr "Shungite" #> ..$ caption: chr "clarity,transformation,cleansing" #> $ :List of 3 #> ..$ id : chr "smoky_quartz" #> ..$ name : chr "Smoky Quartz" #> ..$ caption: chr "calm,clarity,protection" #> $ :List of 3 #> ..$ id : chr "sodalite" #> ..$ name : chr "Sodalite" #> ..$ caption: chr "Insight,Intuition,Dreamwork" #> $ :List of 3 #> ..$ id : chr "tektite" #> ..$ name : chr "Tektite" #> ..$ caption: chr "Communication,transformation,vitality" #> $ :List of 3 #> ..$ id : chr "tibetan_black_quartz" #> ..$ name : chr "Tibetan Black Quartz" #> ..$ caption: chr "Protection,balance,cleansing" #> $ :List of 3 #> ..$ id : chr "tigers_eye" #> ..$ name : chr "Tiger Eye" #> ..$ caption: chr "Balance,confidence,strength" #> $ :List of 3 #> ..$ id : chr "titanium_quartz" #> ..$ name : chr "Titanium Quartz" #> ..$ caption: chr "strength,focus,confidence" #> $ :List of 3 #> ..$ id : chr "tourmalined_quartz" #> ..$ name : chr "Tourmalined Quartz" #> ..$ caption: chr "Cleansing,protection,clarity" #> $ :List of 3 #> ..$ id : chr "turquoise" #> ..$ name : chr "Turquoise" #> ..$ caption: chr "strength,balance,communication" #> $ :List of 3 #> ..$ id : chr "vanadinite" #> ..$ name : chr "Vanadinite" #> ..$ caption: chr "creativity,motivation,vitality"
创建新字段:
products %>% list.filter(livemode = TRUE) %>% list.select(id, name, caption) %>% list.select(connect = paste(id, name, sep = "-")) %>% str() #> List of 66 #> $ :List of 1 #> ..$ connect: chr "amazonite-Amazonite" #> $ :List of 1 #> ..$ connect: chr "amber-Amber" #> $ :List of 1 #> ..$ connect: chr "amethyst-Amethyst" #> $ :List of 1 #> ..$ connect: chr "ametrine-Ametrine" #> $ :List of 1 #> ..$ connect: chr "apophyllite-Apophyllite" #> $ :List of 1 #> ..$ connect: chr "aquamarine-Aquamarine" #> $ :List of 1 #> ..$ connect: chr "aragonite-Aragonite" #> $ :List of 1 #> ..$ connect: chr "azurite-Azurite" #> $ :List of 1 #> ..$ connect: chr "barite-Barite" #> $ :List of 1 #> ..$ connect: chr "bismuth-Bismuth" #> $ :List of 1 #> ..$ connect: chr "black_tourmaline-Black Tourmaline" #> $ :List of 1 #> ..$ connect: chr "bloodstone-Bloodstone" #> $ :List of 1 #> ..$ connect: chr "blue_calcite-Blue Calcite" #> $ :List of 1 #> ..$ connect: chr "kyanite-Blue Kyanite" #> $ :List of 1 #> ..$ connect: chr "carnelian-Carnelian" #> $ :List of 1 #> ..$ connect: chr "celestite-Celestite" #> $ :List of 1 #> ..$ connect: chr "chrysocolla-Chrysocolla" #> $ :List of 1 #> ..$ connect: chr "chrysoprase-Chrysoprase" #> $ :List of 1 #> ..$ connect: chr "citrine-Citrine" #> $ :List of 1 #> ..$ connect: chr "copper-Copper" #> $ :List of 1 #> ..$ connect: chr "dalmatian_stone-Dalmatian Stone" #> $ :List of 1 #> ..$ connect: chr "danburite-Danburite" #> $ :List of 1 #> ..$ connect: chr "desert_rose-Desert Rose" #> $ :List of 1 #> ..$ connect: chr "emerald-Emerald" #> $ :List of 1 #> ..$ connect: chr "epidote-Epidote" #> $ :List of 1 #> ..$ connect: chr "fluorite-Fluorite" #> $ :List of 1 #> ..$ connect: chr "garnet-Garnet" #> $ :List of 1 #> ..$ connect: chr "green_calcite-Green Calcite" #> $ :List of 1 #> ..$ connect: chr "hematite-Hematite" #> $ :List of 1 #> ..$ connect: chr "honey_calcite-Honey Calcite" #> $ :List of 1 #> ..$ connect: chr "howlite-Howlite" #> $ :List of 1 #> ..$ connect: chr "jasper-Jasper" #> $ :List of 1 #> ..$ connect: chr "jet-Jet" #> $ :List of 1 #> ..$ connect: chr "kunzite-Kunzite" #> $ :List of 1 #> ..$ connect: chr "labradorite-Labradorite" #> $ :List of 1 #> ..$ connect: chr "lapis_lazuli-Lapis Lazuli" #> $ :List of 1 #> ..$ connect: chr "lemon_quartz-Lemon Quartz" #> $ :List of 1 #> ..$ connect: chr "lemurian_quartz-Lemurian Quartz" #> $ :List of 1 #> ..$ connect: chr "lepidolite-Lepidolite" #> $ :List of 1 #> ..$ connect: chr "libyan_gold_tektite-Libyan Gold Tektite" #> $ :List of 1 #> ..$ connect: chr "magnesite-Magnesite" #> $ :List of 1 #> ..$ connect: chr "malachite-Malachite" #> $ :List of 1 #> ..$ connect: chr "meteorite-Meteorite" #> $ :List of 1 #> ..$ connect: chr "moldavite-Moldavite" #> $ :List of 1 #> ..$ connect: chr "moonstone-Moonstone" #> $ :List of 1 #> ..$ connect: chr "orange_calcite-Orange Calcite" #> $ :List of 1 #> ..$ connect: chr "peridot-Peridot" #> $ :List of 1 #> ..$ connect: chr "pyrite-Pyrite" #> $ :List of 1 #> ..$ connect: chr "quartz-Quartz" #> $ :List of 1 #> ..$ connect: chr "rhodochrosite-Rhodochrosite" #> $ :List of 1 #> ..$ connect: chr "rhodonite-Rhodonite" #> $ :List of 1 #> ..$ connect: chr "rose_quartz-Rose Quartz" #> $ :List of 1 #> ..$ connect: chr "ruby-Ruby" #> $ :List of 1 #> ..$ connect: chr "scolecite-Scolecite" #> $ :List of 1 #> ..$ connect: chr "selenite-Selenite" #> $ :List of 1 #> ..$ connect: chr "serpentine-Serpentine" #> $ :List of 1 #> ..$ connect: chr "shungite-Shungite" #> $ :List of 1 #> ..$ connect: chr "smoky_quartz-Smoky Quartz" #> $ :List of 1 #> ..$ connect: chr "sodalite-Sodalite" #> $ :List of 1 #> ..$ connect: chr "tektite-Tektite" #> $ :List of 1 #> ..$ connect: chr "tibetan_black_quartz-Tibetan Black Quartz" #> $ :List of 1 #> ..$ connect: chr "tigers_eye-Tiger Eye" #> $ :List of 1 #> ..$ connect: chr "titanium_quartz-Titanium Quartz" #> $ :List of 1 #> ..$ connect: chr "tourmalined_quartz-Tourmalined Quartz" #> $ :List of 1 #> ..$ connect: chr "turquoise-Turquoise" #> $ :List of 1 #> ..$ connect: chr "vanadinite-Vanadinite"
用 list.sort()
函数,可以按照指定字段或值对列表元素进行排序,再用 list.stack()
将所有元素变成数据框:
products %>% list.filter(livemode = TRUE) %>% list.select(id, name, caption) %>% list.sort(id) %>% list.stack() #> id name #> 1 amazonite Amazonite #> 2 amber Amber #> 3 amethyst Amethyst #> 4 ametrine Ametrine #> 5 apophyllite Apophyllite #> 6 aquamarine Aquamarine #> 7 aragonite Aragonite #> 8 azurite Azurite #> 9 barite Barite #> 10 bismuth Bismuth #> 11 black_tourmaline Black Tourmaline #> 12 bloodstone Bloodstone #> 13 blue_calcite Blue Calcite #> 14 carnelian Carnelian #> 15 celestite Celestite #> 16 chrysocolla Chrysocolla #> 17 chrysoprase Chrysoprase #> 18 citrine Citrine #> 19 copper Copper #> 20 dalmatian_stone Dalmatian Stone #> 21 danburite Danburite #> 22 desert_rose Desert Rose #> 23 emerald Emerald #> 24 epidote Epidote #> 25 fluorite Fluorite #> 26 garnet Garnet #> 27 green_calcite Green Calcite #> 28 hematite Hematite #> 29 honey_calcite Honey Calcite #> 30 howlite Howlite #> 31 jasper Jasper #> 32 jet Jet #> 33 kunzite Kunzite #> 34 kyanite Blue Kyanite #> 35 labradorite Labradorite #> 36 lapis_lazuli Lapis Lazuli #> 37 lemon_quartz Lemon Quartz #> 38 lemurian_quartz Lemurian Quartz #> 39 lepidolite Lepidolite #> 40 libyan_gold_tektite Libyan Gold Tektite #> 41 magnesite Magnesite #> 42 malachite Malachite #> 43 meteorite Meteorite #> 44 moldavite Moldavite #> 45 moonstone Moonstone #> 46 orange_calcite Orange Calcite #> 47 peridot Peridot #> 48 pyrite Pyrite #> 49 quartz Quartz #> 50 rhodochrosite Rhodochrosite #> 51 rhodonite Rhodonite #> 52 rose_quartz Rose Quartz #> 53 ruby Ruby #> 54 scolecite Scolecite #> 55 selenite Selenite #> 56 serpentine Serpentine #> 57 shungite Shungite #> 58 smoky_quartz Smoky Quartz #> 59 sodalite Sodalite #> 60 tektite Tektite #> 61 tibetan_black_quartz Tibetan Black Quartz #> 62 tigers_eye Tiger Eye #> 63 titanium_quartz Titanium Quartz #> 64 tourmalined_quartz Tourmalined Quartz #> 65 turquoise Turquoise #> 66 vanadinite Vanadinite #> caption #> 1 love,calm,communication #> 2 cleansing,vitality,protection #> 3 cleansing,protection,clarity #> 4 Clarity,motivation,confidence #> 5 insight,intuition,vitality #> 6 Clarity,calm,cleansing #> 7 balance,strength,confidence #> 8 Calm,balance,dreamwork #> 9 Dreamwork,insight,intuition #> 10 Transformation,vitality,focus #> 11 grounding,protection,cleansing #> 12 courage,cleansing,vitality,strength,manifestation,transformation #> 13 Creativity,clarity,intuition,calm,protection,dreamwork #> 14 confidence,courage,strength,passion,motivation,vitality #> 15 Intuition,protection,calm #> 16 communication,strength,insight #> 17 Balance,joy,transformation #> 18 confidence,love,passion,manifestation,intuition,creativity #> 19 balance,vitality,grounding #> 20 protection,calm,joy #> 21 Calm,joy,communication #> 22 insight,courage,clarity #> 23 Love,Vitality,Balance #> 24 Transformation,manifestation,motivation #> 25 focus,clarity,insight #> 26 Creativity,manifestation,passion #> 27 Calm,Balance,Love #> 28 balance,grounding,manifestation #> 29 confidence,motivation,manifestation #> 30 focus,calm,creativity #> 31 Grounding,Balance,Strength #> 32 Grounding,cleansing,vitality #> 33 Love,joy,vitality #> 34 communication,intuition,insight #> 35 Creativity,joy,vitality #> 36 insight,calm,transformation #> 37 Joy,Balance,Vitality #> 38 Love,strength,balance #> 39 Calm,Balance,Cleansing #> 40 Motivation,confidence,manifestation,protection,creativity,strength #> 41 balance,calm,insight #> 42 creativity,strength,confidence #> 43 Vitality,Intuition,Insight #> 44 Cleansing,Manifestation,Vitality,Insight,Transformation,Dreamwork #> 45 insight,intuition,dreamwork #> 46 Creativity,vitality,passion #> 47 manifestation,vitality,transformation #> 48 manifestation,creativity,confidence,passion,strength,focus #> 49 clarity,transformation,cleansing #> 50 Love,courage,joy #> 51 Love,transformation,clarity #> 52 love,calm,creativity #> 53 Passion,vitality,courage,strength,protection,motivation #> 54 calm,insight,dreamwork #> 55 Clarity,cleansing,motivation #> 56 Manifestation,transformation,vitality #> 57 clarity,transformation,cleansing #> 58 calm,clarity,protection #> 59 Insight,Intuition,Dreamwork #> 60 Communication,transformation,vitality #> 61 Protection,balance,cleansing #> 62 Balance,confidence,strength #> 63 strength,focus,confidence #> 64 Cleansing,protection,clarity #> 65 strength,balance,communication #> 66 creativity,motivation,vitality
除此之外, list.group()
函数可以用指定变量字段值对元素分组, list.table()
提供了一个加强版的 table()
用于处理嵌套列表的表格处理。
更多 rlist
函数,请阅读 rlist
教程( https://renkun.me/rlist-tutorial ),另外 purrr
是基于其他理念处理嵌套数据结构的包,有兴趣可以看看。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- C# 数据操作系列 - 1. SQL基础操作
- 并发环境下,先操作数据库还是先操作缓存?
- elasticsearch(二)---基本数据操作
- SpringBoot数据库操作
- MongoDB 操作数据库
- [ Laravel 5.7 文档 ] 数据库操作 —— 数据填充
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。