从 SQLite 数据库中读取许多表并在 R 中组合
reading in many tables from SQLlite databases and combining in R
我正在使用一个输出结果数据库的程序。我有数百个结构相同的数据库,我想将它们组合成一个大数据库。我最感兴趣的是每个数据库中的 1 table。我不太使用 databases/sql,但它会简化流程中的其他步骤,跳过输出 csv。
之前,我通过导出 csv 并使用这些步骤合并所有 csv 来完成此操作:
所有 CSV 的向量并组合
library(DBI)
library(RSQLite)
library(dplyr)
csv_locs<- list.files(newdir, recursive = TRUE, pattern="*.csv", full.names = TRUE)
pic_dat <- do.call("rbind", lapply(csv_locs,
FUN=function(files){data.table::fread(files, data.table = FALSE)}))
如何使用 sql 类型的数据库 tables 来做到这一点?
我基本上是拉出第一个 table,然后通过循环加入其余部分。
db_locs <- list.files(directory, recursive = TRUE, pattern="*.ddb", full.names = TRUE)
# first table
con1<- DBI::dbConnect(RSQLite::SQLite(), db_locs [1])
start <- tbl(con1, "DataTable")
# open connection to location[i], get table, union, disconnect; repeat.
for(i in 2:length(db_locs )){
con <- DBI::dbConnect(RSQLite::SQLite(), db_locs[i])
y <- tbl(con, "DataTable")
start <- union(start, y, copy=TRUE)
dbDisconnect(con)
}
这太慢了!好吧,公平地说,它的大数据和 csv 也很慢。
老实说,我想我写了最慢的方法来做到这一点 :) 我做不到。call/lapply 在这里工作的选项,但也许我遗漏了一些东西。
这看起来类似于 "iterative rbind
ing of frames",因为每次您这样做 union
,它都会将整个 table 复制到一个新对象(未经证实,但这是我的直觉)。这可能适用于少数人,但扩展性很差。我建议您将所有 table 收集到一个列表中,并在最后调用 data.table::rbindlist
一次 ,然后插入 table.
没有你的数据,我想办法。因为我不能完全确定每个 sqlite3 文件是否只有一个 table,所以我将为每个数据库添加两个 table。如果你只有一个,解决方案很容易简化。
for (i in 1:3) {
con <- DBI::dbConnect(RSQLite::SQLite(), sprintf("mtcars_%d.sqlite3", i))
DBI::dbWriteTable(con, "mt1", mtcars[1:3,1:3])
DBI::dbWriteTable(con, "mt2", mtcars[4:5,4:7])
DBI::dbDisconnect(con)
}
(lof <- list.files(pattern = "*.sqlite3", full.names = TRUE))
# [1] "./mtcars_1.sqlite3" "./mtcars_2.sqlite3" "./mtcars_3.sqlite3"
现在我将遍历它们并读取 table
的内容
allframes <- lapply(lof, function(fn) {
con <- DBI::dbConnect(RSQLite::SQLite(), fn)
mt1 <- tryCatch(DBI::dbReadTable(con, "mt1"),
error = function(e) NULL)
mt2 <- tryCatch(DBI::dbReadTable(con, "mt2"),
error = function(e) NULL)
DBI::dbDisconnect(con)
list(mt1 = mt1, mt2 = mt2)
})
allframes
# [[1]]
# [[1]]$mt1
# mpg cyl disp
# 1 21.0 6 160
# 2 21.0 6 160
# 3 22.8 4 108
# [[1]]$mt2
# hp drat wt qsec
# 1 110 3.08 3.215 19.44
# 2 175 3.15 3.440 17.02
# [[2]]
# [[2]]$mt1
# mpg cyl disp
# 1 21.0 6 160
# 2 21.0 6 160
# 3 22.8 4 108
### ... repeated
从这里开始,只需将它们组合在 R 中并写入新数据库。虽然您可以使用 do.call(rbind,...)
或 dplyr::bind_rows
,但您已经提到了 data.table
,所以我会坚持使用:
con <- DBI::dbConnect(RSQLite::SQLite(), "mtcars_all.sqlite3")
DBI::dbWriteTable(con, "mt1", data.table::rbindlist(lapply(allframes, `[[`, 1)))
DBI::dbWriteTable(con, "mt2", data.table::rbindlist(lapply(allframes, `[[`, 2)))
DBI::dbGetQuery(con, "select count(*) as n from mt1")
# n
# 1 9
DBI::dbDisconnect(con)
如果您无法一次将它们全部加载到 R 中,则将它们实时附加到 table:
con <- DBI::dbConnect(RSQLite::SQLite(), "mtcars_all2.sqlite3")
for (fn in lof) {
con2 <- DBI::dbConnect(RSQLite::SQLite(), fn)
mt1 <- tryCatch(DBI::dbReadTable(con2, "mt1"), error = function(e) NULL)
if (!is.null(mt1)) DBI::dbWriteTable(con, "mt1", mt1, append = TRUE)
mt2 <- tryCatch(DBI::dbReadTable(con2, "mt2"), error = function(e) NULL)
if (!is.null(mt1)) DBI::dbWriteTable(con, "mt2", mt2, append = TRUE)
DBI::dbDisconnect(con2)
}
DBI::dbGetQuery(con, "select count(*) as n from mt1")
# n
# 1 9
这不会遭受您正在经历的迭代减速。
考虑 ATTACH
为您从中导入的数据库创建模式:
library(DBI)
file1 <- tempfile(fileext = ".sqlite")
file2 <- tempfile(fileext = ".sqlite")
con1 <- dbConnect(RSQLite::SQLite(), dbname = file1)
con2 <- dbConnect(RSQLite::SQLite(), dbname = file2)
dbWriteTable(con1, "iris", iris[1:3, ])
dbWriteTable(con2, "iris", iris[4:6, ])
# Main connection
con <- dbConnect(RSQLite::SQLite(), ":memory:")
dbExecute(con, paste0("ATTACH '", file1, "' AS con1"))
#> [1] 0
dbExecute(con, paste0("ATTACH '", file2, "' AS con2"))
#> [1] 0
dbGetQuery(con, "SELECT * FROM con1.iris UNION ALL SELECT * FROM con2.iris")
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> 1 5.1 3.5 1.4 0.2 setosa
#> 2 4.9 3.0 1.4 0.2 setosa
#> 3 4.7 3.2 1.3 0.2 setosa
#> 4 4.6 3.1 1.5 0.2 setosa
#> 5 5.0 3.6 1.4 0.2 setosa
#> 6 5.4 3.9 1.7 0.4 setosa
由 reprex package (v0.3.0)
于 2019-10-08 创建
我正在使用一个输出结果数据库的程序。我有数百个结构相同的数据库,我想将它们组合成一个大数据库。我最感兴趣的是每个数据库中的 1 table。我不太使用 databases/sql,但它会简化流程中的其他步骤,跳过输出 csv。
之前,我通过导出 csv 并使用这些步骤合并所有 csv 来完成此操作:
所有 CSV 的向量并组合
library(DBI)
library(RSQLite)
library(dplyr)
csv_locs<- list.files(newdir, recursive = TRUE, pattern="*.csv", full.names = TRUE)
pic_dat <- do.call("rbind", lapply(csv_locs,
FUN=function(files){data.table::fread(files, data.table = FALSE)}))
如何使用 sql 类型的数据库 tables 来做到这一点?
我基本上是拉出第一个 table,然后通过循环加入其余部分。
db_locs <- list.files(directory, recursive = TRUE, pattern="*.ddb", full.names = TRUE)
# first table
con1<- DBI::dbConnect(RSQLite::SQLite(), db_locs [1])
start <- tbl(con1, "DataTable")
# open connection to location[i], get table, union, disconnect; repeat.
for(i in 2:length(db_locs )){
con <- DBI::dbConnect(RSQLite::SQLite(), db_locs[i])
y <- tbl(con, "DataTable")
start <- union(start, y, copy=TRUE)
dbDisconnect(con)
}
这太慢了!好吧,公平地说,它的大数据和 csv 也很慢。
老实说,我想我写了最慢的方法来做到这一点 :) 我做不到。call/lapply 在这里工作的选项,但也许我遗漏了一些东西。
这看起来类似于 "iterative rbind
ing of frames",因为每次您这样做 union
,它都会将整个 table 复制到一个新对象(未经证实,但这是我的直觉)。这可能适用于少数人,但扩展性很差。我建议您将所有 table 收集到一个列表中,并在最后调用 data.table::rbindlist
一次 ,然后插入 table.
没有你的数据,我想办法。因为我不能完全确定每个 sqlite3 文件是否只有一个 table,所以我将为每个数据库添加两个 table。如果你只有一个,解决方案很容易简化。
for (i in 1:3) {
con <- DBI::dbConnect(RSQLite::SQLite(), sprintf("mtcars_%d.sqlite3", i))
DBI::dbWriteTable(con, "mt1", mtcars[1:3,1:3])
DBI::dbWriteTable(con, "mt2", mtcars[4:5,4:7])
DBI::dbDisconnect(con)
}
(lof <- list.files(pattern = "*.sqlite3", full.names = TRUE))
# [1] "./mtcars_1.sqlite3" "./mtcars_2.sqlite3" "./mtcars_3.sqlite3"
现在我将遍历它们并读取 table
的内容allframes <- lapply(lof, function(fn) {
con <- DBI::dbConnect(RSQLite::SQLite(), fn)
mt1 <- tryCatch(DBI::dbReadTable(con, "mt1"),
error = function(e) NULL)
mt2 <- tryCatch(DBI::dbReadTable(con, "mt2"),
error = function(e) NULL)
DBI::dbDisconnect(con)
list(mt1 = mt1, mt2 = mt2)
})
allframes
# [[1]]
# [[1]]$mt1
# mpg cyl disp
# 1 21.0 6 160
# 2 21.0 6 160
# 3 22.8 4 108
# [[1]]$mt2
# hp drat wt qsec
# 1 110 3.08 3.215 19.44
# 2 175 3.15 3.440 17.02
# [[2]]
# [[2]]$mt1
# mpg cyl disp
# 1 21.0 6 160
# 2 21.0 6 160
# 3 22.8 4 108
### ... repeated
从这里开始,只需将它们组合在 R 中并写入新数据库。虽然您可以使用 do.call(rbind,...)
或 dplyr::bind_rows
,但您已经提到了 data.table
,所以我会坚持使用:
con <- DBI::dbConnect(RSQLite::SQLite(), "mtcars_all.sqlite3")
DBI::dbWriteTable(con, "mt1", data.table::rbindlist(lapply(allframes, `[[`, 1)))
DBI::dbWriteTable(con, "mt2", data.table::rbindlist(lapply(allframes, `[[`, 2)))
DBI::dbGetQuery(con, "select count(*) as n from mt1")
# n
# 1 9
DBI::dbDisconnect(con)
如果您无法一次将它们全部加载到 R 中,则将它们实时附加到 table:
con <- DBI::dbConnect(RSQLite::SQLite(), "mtcars_all2.sqlite3")
for (fn in lof) {
con2 <- DBI::dbConnect(RSQLite::SQLite(), fn)
mt1 <- tryCatch(DBI::dbReadTable(con2, "mt1"), error = function(e) NULL)
if (!is.null(mt1)) DBI::dbWriteTable(con, "mt1", mt1, append = TRUE)
mt2 <- tryCatch(DBI::dbReadTable(con2, "mt2"), error = function(e) NULL)
if (!is.null(mt1)) DBI::dbWriteTable(con, "mt2", mt2, append = TRUE)
DBI::dbDisconnect(con2)
}
DBI::dbGetQuery(con, "select count(*) as n from mt1")
# n
# 1 9
这不会遭受您正在经历的迭代减速。
考虑 ATTACH
为您从中导入的数据库创建模式:
library(DBI)
file1 <- tempfile(fileext = ".sqlite")
file2 <- tempfile(fileext = ".sqlite")
con1 <- dbConnect(RSQLite::SQLite(), dbname = file1)
con2 <- dbConnect(RSQLite::SQLite(), dbname = file2)
dbWriteTable(con1, "iris", iris[1:3, ])
dbWriteTable(con2, "iris", iris[4:6, ])
# Main connection
con <- dbConnect(RSQLite::SQLite(), ":memory:")
dbExecute(con, paste0("ATTACH '", file1, "' AS con1"))
#> [1] 0
dbExecute(con, paste0("ATTACH '", file2, "' AS con2"))
#> [1] 0
dbGetQuery(con, "SELECT * FROM con1.iris UNION ALL SELECT * FROM con2.iris")
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> 1 5.1 3.5 1.4 0.2 setosa
#> 2 4.9 3.0 1.4 0.2 setosa
#> 3 4.7 3.2 1.3 0.2 setosa
#> 4 4.6 3.1 1.5 0.2 setosa
#> 5 5.0 3.6 1.4 0.2 setosa
#> 6 5.4 3.9 1.7 0.4 setosa
由 reprex package (v0.3.0)
于 2019-10-08 创建