在前面的章节中,我们学习了如何编写 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. java中Integer 和String 之间的转换

    java中Integer 和String 之间的转换 将数组转换成字符串:char[] array = {'a','b','c','d','e'};String str = new String(ar ...

  2. Ants-穷举算法

    package java操作excel; import java.util.Scanner; /** * Ants * n只蚂蚁以每秒1cm的速度在长为Lcm的竿子上爬行,当蚂蚁爬到端点就会掉下去,竿 ...

  3. SQL Server查询中特殊字符的处理方法

    SQL Server查询中,经常会遇到一些特殊字符,比如单引号“'”等,这些字符的处理方法,是SQL Server用户都应该需要知道的. 我们都知道SQL Server查询过程中,单引号“'”是特殊字 ...

  4. OAuth 白话简明教程 3.客户端模式(Client Credentials)

    转自:http://www.cftea.com/c/2016/11/6704.asp OAuth 白话简明教程 1.简述 OAuth 白话简明教程 2.授权码模式(Authorization Code ...

  5. zw版【转发·台湾nvp系列Delphi例程】HALCON Regiongrowing

    zw版[转发·台湾nvp系列Delphi例程]HALCON Regiongrowing procedure TForm1.Button1Click(Sender: TObject);var img : ...

  6. POJ 分类

    初期: 一.基本算法:      (1)枚举. (poj1753,poj2965)      (2)贪心(poj1328,poj2109,poj2586)      (3)递归和分治法.      ( ...

  7. BabelMap 12.0.0.1 汉化版(2019年3月11日更新)

    软件简介 BabelMap 是一个免费的字体映射表工具,可辅助使用<汉字速查>程序. 该软件可使用系统上安装的所有字体浏览 Unicode 中的十万个字符,还带有拼音及部首检字法,适合文献 ...

  8. 命名空间“Microsoft.Office.Interop”中不存在类型或命名空间名称“Excel”。是否缺少程序集引用 的另一种解决方案

    一直以来都是使用tfs进行源代码管理,系统部署也是由我本机生成后发布到服务器上,某一日,进行发布操作时,报了 [命名空间“Microsoft.Office.Interop”中不存在类型或命名空间名称“ ...

  9. python实现常量const

    新建const.py: #-*-coding:UTF-8-*- #Filename: const.py # 定义一个常量类实现常量的功能 # # 该类定义了一个方法__setattr()__,和一个异 ...

  10. 2018-2019-2 20165209 《网络对抗技术》Exp5:MSF基础应用

    2018-2019-2 20165209 <网络对抗技术>Exp5:MSF基础应用 目录 一.基础问题回答和实验内容 二.攻击实例 主动攻击的实践 ms08_067 payload/gen ...