通过 sqldf 包使用 SQL 查询数据框
在前面的章节中,我们学习了如何编写 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 查询数据框的更多相关文章
- SQL反模式学习笔记18 减少SQL查询数据,避免使用一条SQL语句解决复杂问题
目标:减少SQL查询数据,避免使用一条SQL语句解决复杂问题 反模式:视图使用一步操作,单个SQL语句解决复杂问题 使用一个查询来获得所有结果的最常见后果就是产生了一个笛卡尔积.导致查询性能降低. 如 ...
- SQL查询数据并插入新表
SQL查询数据并插入新表 --如果接受数据导入的表已经存在 insert into 表 select * from tablename --如果导入数据并生成表 select * into 表 fro ...
- SQL查询数据总结
SQL查询数据 完整语法 Select [select选项] 字段列表[字段别名]/* from 数据源 [where条件子句] [group by子句] [having子句] [order by子句 ...
- 基于Spring Boot,使用JPA动态调用Sql查询数据
在<基于Spring Boot,使用JPA操作Sql Server数据库完成CRUD>,<基于Spring Boot,使用JPA调用Sql Server数据库的存储过程并返回记录集合 ...
- 20150221—LINQ to SQL 查询数据
LINQ to SQL 可以快捷的查询基于SQL的数据,直接在VS中包括基本的Object/relation映射器,O/R映射器可以快速的将基于SQL的数据源映射为CLR对象,之后就可以使用LINQ查 ...
- Oracle表明明存在SQL查询数据提示表不存在异常
今天同事遇到一个很奇怪的问题,恢复了一个数据库,表明明存在,用PLSQL和sqlplus都试过了,SQL语句select * from 表名,查询数据,却提示表名不存在异常 然而,使用select * ...
- Hibernate原生SQL查询数据转换为HQL查询数据方法
HQL形式:(构造方法不支持timestamp类型) public List<Device> queryByMatherBoardId(String matherBoardId) { St ...
- SQL查询数据的几大方法
有你,查询数据我什么都不怕.快快掌握!! 出大招的工具: 1.使用LIKE.BETWEEN.IN进行模糊查询 eg1: SELECT * FROM Students WHERE 姓名 like '张% ...
- SQl查询数据库库名,表名、表的列名
查询数据库 select * From master.dbo.sysdatabases where name='数据库名' and status<>512 --读取库中的所有表名 (当 ...
随机推荐
- [LeetCode] 415. Add Strings_Easy tag: String
Given two non-negative integers num1 and num2 represented as string, return the sum of num1 and num2 ...
- C语言头文件、库文件的查找路径
在 程序设计中,文件包含是很有用的.一个大的程序可以分为多个模块,由多个程序员分别编程.有些公用的符号常量或宏定义等可单独组成一个文件,在其它文件的开头用包含命令包含该文件即可使用.这样,可避免在每个 ...
- Summary: rand5构造rand7
给一个方法,比如 rand5(), 它能够等概率生成 1-5 之间的整数. 所谓等概率就是1,2,3,4,5 生产的概率均为 0.2 .现在利用rand5(), 构造一个能够等概率生成 1- 7 的方 ...
- win10下的iis的配置(服务于asp.net)
win10下的iis的配置和win7下的是类似的. 1.右键开始,打开控制面板,进入卸载程序中,勾上如下图所示的项目,即可装上iis. 这里写图片描述 2.重启后搜索iis,进入iis配置中.点击网站 ...
- SSM三层模型之间的参数传递
Controller接收各种形式的传参: RequestMapping()包含三部分信息: 表单传参: 1.表单action请求 1:参数名称和顺序都要和表单字段名称一致,直接接收(适合字 ...
- Unirest-拼装http请求发送rest接口
public static Integer getInfo(String name) { HttpResponse<Integer> httpResponse = null; try { ...
- 5Lambda表达式
C++11中的Lambda表达式用于定义并创建匿名的函数对象,以简化编程工作.首先看一下Lambda表达式的基本构成: [函数对象参数](操作符重载函数参数)mutable或exception -&g ...
- HTML5 -canvas拖拽、移动 绘制图片可操作移动,拖动
关于canvas 的基础知识就不多说了,可以进这个网址学习 http://www.w3school.com.cn/html5/html_5_canvas.asp 对于canvas 和 SVG 其实一开 ...
- 简化document.createElement("div")动态生成层方法
我们在WEB开发时,很多时候往往需要我们 JavaScript 来动态建立 html 元素,动态的设置相关的属性.比方说我们想要建立一個 div 层,则可以使用以下代码实现. 一.直接建立functi ...
- JavaScript中几种 获取元素的方式
1.根据id获取元素 document.getElementById("id属性的值"); 2.根据标签名字获取元素 document.getElementsByTagName(& ...