在前面的章节中,我们学习了如何编写 SQL 语句,在关系型数据库(如 SQLite 和
MySQL )中查询数据。我们可能会想,有没有一种方法,能够直接使用 SQL 进行数据框
查询,就像数据框是关系型数据库中的表一样呢?sqldf 包给了我们肯定的答案。
这个包吸收了 SQLite 的轻量结构和易于嵌入 R 会话的优势。如果还没有这个包,运行
以下命令进行安装:
install.packages("sqldf")
首先,加载包:
library(sqldf)
## Loading required package: gsubfn
## Loading required package: proto
## Loading required package: RSQLite
## Loading required package: DBI
注意到,在加载 sqldf 包时,其他相关的几个包也会被自动载入。因为 sqldf 包的
实现依赖于这些包,它基本上就是在 R 和 SQLite 之间传输数据和转换数据类型。
然后,读入前面使用的产品表格:
product_info <- read_ _csv("data/product-info.csv")
product_stats <- read_ _csv("data/product-stats.csv")
product_tests <- read_ _csv("data/product-tests.csv")
toy_tests <- read_ _csv("data/product-toy-tests.csv")
这个包的神奇之处在于,我们可以使用 SQL 语句直接查询工作环境中的数据框。例如,
选择 product_info 中的所有记录:
sqldf("select * from product_info")
## Loading required package: tcltk
## 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 id, material, size / weight as density from product_stats")
## id material density
## 1 T01 Metal 12.000000
## 2 T02 Metal 7.777778
## 3 M01 Plastics NA
## 4 M02 Plastics 28.333333
## 5 M03 Wood NA
## 6 M04 Wood 26.666667
还可以根据特定的列对记录进行排序:
sqldf("select * from product_stats order by size desc")
## 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
这个包也支持查询多个数据框,如 join。在以下代码中,我们将 product_info 和
product_ stats 按照 id 合并,与之前用 merge( ) 一样:
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
同时,它还支持嵌套查询。下面的代码中,我们选出 product_info 中所有的木制
产品记录:
sqldf("select * from product_info where id in
(select id from product_stats where material = 'Wood')")
## id name type class released
## 1 M03 Runner model people yes
## 2 M04 Dancer model people no
除此之外,相同的 where 条件,使用 join 也可以达到相同的目标。对于许多关系型
数据库,在数据量比较大的时候,join 通常运行得比 in 快:
sqldf("select * from product_info join product_stats using (id)
where material = 'Wood'")
## id name type class released material size weight
## 1 M03 Runner model people yes Wood 15 NA
## 2 M04 Dancer model people no Wood 16 0.6
除了join,还可以轻松地对数据进行分组汇总。例如,按照waterproof 把product_
tests 分成两组:yes 和no。对于每一组,计算quality 和durability 的均值,如下:
sqldf("select waterproof, avg(quality), avg(durability) from product_tests
group by waterproof")
## waterproof avg(quality) avg(durability)
## 1 no 10.00 9.5
## 2 yes 5.75 5.0
对于 toy_tests 数据,很容易根据每个产品对数据进行加总。例如,计算每个产品
的 quality 和 durability 在时间上的均值:
sqldf("select id, avg(quality), avg(durability) from toy_tests
group by id")
## id avg(quality) avg(durability)
## 1 T01 9.25 9.25
## 2 T02 8.50 8.50
为了返回更多有价值的信息,我们可以把 product_info 和分组汇总表连接起来,
这样就可以一起展示产品信息和平均指标了:
sqldf("select * from product_info join
(select id, avg(quality), avg(durability) from toy_tests
group by id) using (id)")
## id name type class released avg(quality) avg(durability)
## 1 T01 SupCar toy vehicle yes 9.25 9.25
## 2 T02 SupPlane toy vehicle no 8.50 8.50
使用 sqldf 和 SQL 对数据框进行查询看起来很方便,但是局限性也很明显。
第一,sqldf 默认基于SQLite,SQLite 数据库的局限性,自然也是这个包的局限性,也就
是说内置的分组汇总函数是有限的。官方页面 (https://sqlite.org/lang_aggfunc.html)提供了一系
列函数:avg( )、count( )、group_concat( )、max( )、sum( ) 和 total( )。如
果需要更多函数,如 quantile( ),就不是那么容易了。在 R 中,可以使用更多高级方法来加总列。
第二,查询数据时,需要提供一串选择语句,但是当语句的某部分是由 R 变量决定时,
动态生成 SQL 语句就不那么方便了。
第三,SQL 的限制性也是 sqldf 的限制性。很难使用更加复杂的方法计算新列。例如,
使用现有列计算出一个排序列,就没有那么简单了。然而,在 R 中,只需要调用 order( ) 就
能实现了。另外,实现更加复杂的筛选操作(如基于排序的筛选)会很难或者非常复杂。
例如,你想如何实现这样的筛选呢:按照 size 降序排列,再按 material 分组,然后提
取第 1 个或前两个产品?确实,实现类似的查询需要许多思考和技巧。
然而,如果使用 plyr 包,类似的问题就是小菜一碟。如果还没有安装它,请运行以
下代码:
install.packages("plyr")
为了体验它的便捷之处,我们使用 plyr::ddply 来实现上述筛选。这里将 material
作为数据分离器,即 product_stats 会根据 material 进行分组。此外,这里还自定义
了一个函数,将输入的数据框(的每个部分)转换为新的数据框。之后,ddply( ) 函数
将这些数据框组合在一起:
plyr::ddply(product_stats, "material",
function(x) {
head(x[order(x$size, decreasing = TRUE),], 1L)
})
## id material size weight
## 1 T02 Metal 350 45.0
## 2 M02 Plastics 85 3.0
## 3 M04 Wood 16 0.6
值得说明的是,匿名函数的调用是基于分组的 product_stats 的,也就是说,根据
material 对 product_stats 进行分组,每一组对应的 material 取值唯一。
另一个例子是选出样本数最多的两个测试结果:
plyr::ddply(toy_tests, "id",
function(x) {
head(x[order(x$sample, decreasing = TRUE), ], 2)
})
## id date sample quality durability
## 1 T01 20160405 180 9 10
## 2 T01 20160302 150 10 9
## 3 T02 20160403 90 9 8
## 4 T02 20160502 85 10 9
其中,匿名函数的调用是基于 toy_tests 的两个部分,即 id 为 T01 和 T02 的数据框。
每个数据框子集都根据 sample 降序排列,并取出前两条记录。这个任务很容易就完成了。
另外,ddply 和 plyr 针对很多输入输出的数据类型组合提供了多种多样的函数。想
要了解更多,请访问 http://had.co.nz/plyr/ 和 https://github.com/hadley/plyr。

通过 sqldf 包使用 SQL 查询数据框的更多相关文章

  1. SQL反模式学习笔记18 减少SQL查询数据,避免使用一条SQL语句解决复杂问题

    目标:减少SQL查询数据,避免使用一条SQL语句解决复杂问题 反模式:视图使用一步操作,单个SQL语句解决复杂问题 使用一个查询来获得所有结果的最常见后果就是产生了一个笛卡尔积.导致查询性能降低. 如 ...

  2. SQL查询数据并插入新表

    SQL查询数据并插入新表 --如果接受数据导入的表已经存在 insert into 表 select * from tablename --如果导入数据并生成表 select * into 表 fro ...

  3. SQL查询数据总结

    SQL查询数据 完整语法 Select [select选项] 字段列表[字段别名]/* from 数据源 [where条件子句] [group by子句] [having子句] [order by子句 ...

  4. 基于Spring Boot,使用JPA动态调用Sql查询数据

    在<基于Spring Boot,使用JPA操作Sql Server数据库完成CRUD>,<基于Spring Boot,使用JPA调用Sql Server数据库的存储过程并返回记录集合 ...

  5. 20150221—LINQ to SQL 查询数据

    LINQ to SQL 可以快捷的查询基于SQL的数据,直接在VS中包括基本的Object/relation映射器,O/R映射器可以快速的将基于SQL的数据源映射为CLR对象,之后就可以使用LINQ查 ...

  6. Oracle表明明存在SQL查询数据提示表不存在异常

    今天同事遇到一个很奇怪的问题,恢复了一个数据库,表明明存在,用PLSQL和sqlplus都试过了,SQL语句select * from 表名,查询数据,却提示表名不存在异常 然而,使用select * ...

  7. Hibernate原生SQL查询数据转换为HQL查询数据方法

    HQL形式:(构造方法不支持timestamp类型) public List<Device> queryByMatherBoardId(String matherBoardId) { St ...

  8. SQL查询数据的几大方法

    有你,查询数据我什么都不怕.快快掌握!! 出大招的工具: 1.使用LIKE.BETWEEN.IN进行模糊查询 eg1: SELECT * FROM Students WHERE 姓名 like '张% ...

  9. SQl查询数据库库名,表名、表的列名

    查询数据库 select * From master.dbo.sysdatabases where name='数据库名' and status<>512   --读取库中的所有表名 (当 ...

随机推荐

  1. iOS UI基础-2.0按钮操作与形变

    按钮状态 1.normal:默认状态 Default 对应的枚举常量:UIControlStateNormal   2.highlighted(高亮状态) 按钮被按下去的时候(未松开) 对应的枚举常量 ...

  2. Android常用权限permission列表摘录

    一个Android应用程序需要权限才能调用某些android系统的功能:一个android应用也可能被其他应用调用,因此也需要声明调用自身所需要的权限.除了平时常用的权限记得比较熟悉,还有很多的权限一 ...

  3. VMWARE安装centos6 http://www.centoscn.com/image-text/setup/2013/0816/1263.html

    http://www.centoscn.com/image-text/setup/2013/0816/1263.html

  4. kendo 级联加带搜索的下拉框以及js赋值

    1‘.js给下拉框赋值 $("#UserRole").data("kendoDropDownList").value(dataItem.RoleName); $ ...

  5. Python 读写文件中w与wt, r与rt的区别

    w和wt是一们的,r和rt是一样的,t是默认参数,可以省略的,help(open)就能看到open的参数的详细说明. w,r,wt,rt都是python里面文件操作的模式.w是写模式,r是读模式.t是 ...

  6. Python: 反方向迭代一个序列

    使用内置的reversed()函数 >>> a = [1, 2, 3, 4] >>> for x in reversed(a): ... print(x) out ...

  7. Java EE企业应用发展

    新形式下的企业应用特点企业应用系统从封闭走向开放,由局域网转到互联网,随着涉众面的极大扩展,新的企业应用要求多浏览器支持,国际化支持,全球业务的互联互通.企业需求提升.除了功能性需求,客户对于安全,性 ...

  8. MATERIALIZED VIEW-物化视图

     Oracle的实体化视图提供了强大的功能,可以用在不同的环境中,实体化视图和表一样可以直接进行查询.实体化视图可以基于分区表,实体化视图本身也可以分区. 主要用于预先计算并保存表连接或聚集等耗时较多 ...

  9. C/C++之全局、static对象/变量的初始化问题

    关于全局.static对象/变量的初始化问题 1. 全局变量.static变量的初始化时机:main()函数执行之前(或者说main中第一个用户语句执行之前). 2. 初始化顺序. 1)全局对象.外部 ...

  10. keepalived+lvs高可用集群

    LVS+Keepalived 介绍 LVS LVS是Linux Virtual Server的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统.本项目在1998年5月由章文嵩博士成立,是中国 ...