R 中的错​​误 rsqlite_fetch

error rsqlite_fetch in R

我有一个 "CSV" 格式的数据集,其中包含 28 个变量和 7400 个观察值。我在 R 中导入名为 "films" 的数据集,我想清理它。这样,我使用了 "sqldf" 和 "SQLite" 库。但是当我使用 "sqldf" 时,我收到了警告,因此我的数据集也没有更新:

color   director_name   num_critic_for_reviews  duration
Color   James Cameron   723                       178
Color   Gore Verbinski  302                       169
Color   Sam Mendes      602                       148

我用过的代码如下:

library(RSQLite) 
library(sqldf)
db <- dbConnect(SQLite(), "tempdb")  

dbWriteTable(db,"films",films, overwrite=TRUE)

d <- sqldf(c('update films set movie_title=lower(movie_title)', 'select * from films'))
d <- sqldf(c('update films set actor_3_name=lower(actor_3_name)', 'select * from films'))
d <- sqldf(c('update films set actor_2_name=lower(actor_2_name)', 'select * from films'))
d <- sqldf(c('update films set actor_1_name=lower(actor_1_name)', 'select * from films'))
d <- sqldf(c('update films set director_name=lower(director_name)', 'select * from films'))


# Warning message:
# In rsqlite_fetch(res@ptr, n = n) :
#   Don't need to call dbFetch() for statements, only for queries

问题是什么?

这里有几个问题。

第一个:

# Warning message:
# In rsqlite_fetch(res@ptr, n = n) :
#   Don't need to call dbFetch() for statements, only for queries

这是警告,不是错误。事实上,它在 RSQLite 中似乎是一个新问题,并在一个未解决的问题中被引用:https://github.com/r-dbi/RSQLite/issues/227.

(为了简洁起见,我将从本答案其余部分的输出中删除它,尽管每个非 select 查询都会出现它。)

其次RSQLite处理数据库,句号。它对 R 环境中的变量没有意见或意识,因此没有暗示 R 中的变量是数据库的即时和永久表示 table。 (粗略地说,有使用 dbplyr 的类似方法 do 这样做。)

为了在 R 和某种形式的 SQL 查询之间提供这种类型的联系,sqldf 允许您查询 R 变量,就好像它们是实际的 SQL table秒。当你执行这样的查询时,它会抓取当前看起来的 data.frame,将其插入临时数据库 table(无论是 RSQLite 还是其他),运行 SQL 代码, 然后 returns 你需要什么。

第三个:尽管有这种明显的联系,但它非常实用,因为它不会在 R 环境中产生 side-effects。这意味着如果你想以 R 可以使用的方式存储结果数据,你需要明确地将新的 table 捕获到 R 变量中。

例如:

library(sqldf)
(mt <- mtcars[1:5,1:5])
#                    mpg cyl disp  hp drat
# Mazda RX4         21.0   6  160 110 3.90
# Mazda RX4 Wag     21.0   6  160 110 3.90
# Datsun 710        22.8   4  108  93 3.85
# Hornet 4 Drive    21.4   6  258 110 3.08
# Hornet Sportabout 18.7   8  360 175 3.15

更新后,原数据不变

sqldf('update mt set cyl=5 where cyl>5')
mt
#                    mpg cyl disp  hp drat
# Mazda RX4         21.0   6  160 110 3.90
# Mazda RX4 Wag     21.0   6  160 110 3.90
# Datsun 710        22.8   4  108  93 3.85
# Hornet 4 Drive    21.4   6  258 110 3.08
# Hornet Sportabout 18.7   8  360 175 3.15

您可以通过在对 sqldf:

的调用中包含 select * from ... 来单独或在同一行中获取数据
mt2 <- sqldf(c('update mt set cyl=5 where cyl>5', 'select * from mt'))
mt2
#    mpg cyl disp  hp drat
# 1 21.0   5  160 110 3.90
# 2 21.0   5  160 110 3.90
# 3 22.8   4  108  93 3.85
# 4 21.4   5  258 110 3.08
# 5 18.7   5  360 175 3.15

(在这种情况下,我将其保存为 mt2,但您可以轻松地覆盖它。)

所有这些都在 sqldf FAQ 8、“8. 为什么我在更新时遇到问题?”

中以各种形式进行了讨论

编辑

似乎对 sqldf 等存在一些误解。

  1. 您正在为直接 SQL 查询(而非 sqldf)创建一个 db 句柄,但您从未使用过它。正如您稍后将看到的,(a) 使用 dbExecute(和相关函数)和 db 句柄,或者 (b) 使用 sqldf,不需要 dbConnect和朋友。

  2. 使用 sqldf 时,在 每次 调用 sqldf 时,它都会执行 完整复制 将当前实例变量存入数据库。 (这既有用又有时效率低下。对于较小的数据集,可能感觉不到时间损失,但仍然......)所以当你继续参考 table films,它忽略了你创建的 d,因为它无法推断你在它的调用之外想要做什么……它只是复制、查询和丢弃。

    # assuming this is something like what you do ... but it doesn't matter
    films <- read.csv("films.csv", ...)
    #    `-<---<---<---<---<---<---<---<---<---<---<---<---<---<---<-+-<--.
    db <- dbConnect(SQLite(), "tempdb") # not used in sqldf          ^     \
    dbWriteTable(db, "films", films, overwrite=TRUE) # never used    ^      \
    #                             `--- is referring to --->--->--->--'       \
    d <- sqldf(c('update films set movie_title=lower(movie_title)', #         \
                                 'select * from films')) #                     \
    #                        \                      `--- (internal to sqldf)    ^
    #                         `--- refers to the original 'films' --->--->--->--'
    

    选项 1,使用 RSQLite 函数,而不是 sqldf:

    db <- dbConnect(SQLite(), "tempdb")
    dbWriteTable(db,"films",films, overwrite=TRUE)
    dbExecute(db, 'update films set actor_3_name=lower(actor_3_name)')
    #        `--- repeat for all updates
    films <- dbGetQuery(db, 'select * from films')
    

    选项 2,(不是我的首选)使用在上一行创建的变量:

    films <- read.csv("films.csv", ...)
    #   `--<---<---<---<---<---<---<---<---<---<---<---<---<---<---<---<---<-.
    d <- sqldf(c('update films set movie_title=lower(movie_title)', #         \
                                 'select * from films')) #                     \
    #\                        \                      `--- (internal to sqldf)    ^
    # \                        `--- refers to original 'films' --->--->--->--->--'
    #  `--<---<---<---<---<---<---<---<---<---<---<---<---<---<---<---<--.
    d <- sqldf(c('update d set actor_3_name=lower(actor_3_name)', #       \
                                 'select * from d')) #                     \
    #                    \                      `--- (internal to sqldf)    ^
    #                     `--- refers to previously-created 'd' --->--->--->'
    #                         (repeat for other updates)
    

    选项 3,始终 reference/overwrite 原始 films 变量:

    films <- read.csv("films.csv", ...)
    #   `--<---<---<---<---<---<---<---<---<---<---<---+--<---<---<---<---<---.
    films <- sqldf(c('update films set movie_title=lower(movie_title)', #      \
                                  'select * from films')) #                     \
    #   \                        \                   `--- (internal to sqldf)    ^
    #    \                        ` --- refers to the first 'films' -->--->--->--'
    #     `-<---<---<---<---<---<---<---<---<---<---<---+--<---<---<---<---<--.
    films <- sqldf(c('update films set actor_3_name=lower(actor_3_name)', #    \
                                  'select * from films')) #                     \
    #                            \                   `--- (internal to sqldf)    ^
    #                             ` --- refers to the second 'films' -->--->--->-'
    #                              (repeat for other updates)
    
  3. sqldf 效率低下。每次调用 sqldf 时,它都会将整个数据集复制到临时 table 中。每一个。时间。您可以通过将所有查询字符串合并到一个调用中来减少一些开销,如下所示:

    films <- read.csv("films.csv", ...)
    films <- sqldf(c('update films set actor_3_name=lower(actor_3_name)',
                     'update films set actor_2_name=lower(actor_2_name)',
                     'update films set actor_1_name=lower(actor_1_name)',
                     'update films set director_name=lower(director_name)',
                     'select * from films'))
    
  4. SQL 效率低下。您的原始代码可能会针对该问题进行简化(这很好),但如果没有,那么就可以了。由于您似乎根本没有调整更新,因此您可以将数据清理合并到一个更新中。 (这也可以与 dbExecute 一起使用。)

    films <- read.csv("films.csv", ...)
    films <- sqldf(c('update films set actor_3_name=lower(actor_3_name),
                                       actor_3_name=lower(actor_3_name),
                                       actor_2_name=lower(actor_2_name),
                                       actor_1_name=lower(actor_1_name),
                                       director_name=lower(director_name)',
                     'select * from films'))
    
  5. 你真的需要SQL吗?这在 R:

    中完全可以做到 easily/quickly
    films <- read.csv("films.csv", ...)
    films <- within(films, {
      actor_3_name <- tolower(actor_3_name)
      actor_2_name <- tolower(actor_2_name)
      actor_1_name <- tolower(actor_1_name)
      director_name <- tolower(director_name)
    })