ошибка rsqlite_fetch в R

У меня есть набор данных формата «CSV» с 28 переменными и 7400 наблюдениями. Я импортирую набор данных в R с именем «фильмы» и хочу его очистить. Таким образом, я использую библиотеки «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

В чем проблема?


person Farzad    schedule 08.12.2017    source источник
comment
Одна проблема: вы ошибочно принимаете warning за error. Другая проблема: dbGetQuery предполагает, что есть данные для возврата, но вы просто обновляете; вместо этого рассмотрите dbExecute (или dbSendQuery и dbClearResult).   -  person r2evans    schedule 09.12.2017
comment
Спасибо. Я использовал dbExecute, но он просто создает значение d, которое показывает количество цветов, и мой набор данных (пленка) не обновляется.   -  person Farzad    schedule 09.12.2017
comment
Вы говорите, что ожидаете, что ваше обновление таблицы db будет отражено в переменной films в R?   -  person r2evans    schedule 09.12.2017
comment
Да. В яблочко. Я хочу изменить все colors на color в моем наборе данных films.   -  person Farzad    schedule 09.12.2017


Ответы (1)


Здесь есть несколько проблем на работе.

Первый:

# 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 является непосредственным и постоянным представлением таблицы базы данных. (Существуют аналогичные методологии с использованием dbplyr, которые делают сделайте это, грубо говоря.)

Чтобы обеспечить этот тип связи между R и некоторой формой запросов SQL, существует sqldf, который позволяет вам запрашивать переменные R, как если бы они были реальными таблицами SQL. Когда вы делаете такой запрос, он захватывает data.frame в том виде, в котором он выглядит в данный момент, вставляет его во временную таблицу базы данных (будь то RSQLite или другую), запускает код SQL, а затем возвращает то, что вам нужно.

В-третьих: несмотря на эту очевидную связь, он вполне функционален, поскольку не создает побочных эффектов в среде R. Это означает, что если вы хотите сохранить результирующие данные так, как их может использовать R, вам нужно явно записать новую таблицу в переменную 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

Вы можете получить данные по отдельности или в одной строке, включив select * from ... в свой вызов sqldf:

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. Вы создаете дескриптор db для прямых запросов SQL (не sqldf), но никогда его не используете. Как вы увидите позже, либо (а) используйте dbExecute (и связанные функции) с дескриптором db, либо (б) используйте sqldf, нет необходимости в dbConnect и его друзьях.

  2. При использовании sqldf при каждом и каждом вызове sqldf выполняется полная копия текущего экземпляра переменной в базу данных. (Это и полезно, и иногда неэффективно. С небольшими наборами данных потерянное время, вероятно, не ощущается, но все же...) Поэтому, когда вы продолжаете ссылаться на таблицу 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: всегда ссылайтесь/перезаписывайте исходную переменную 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, он копирует весь набор данных во временную таблицу. Каждый. Время. Вы можете уменьшить часть накладных расходов, объединив все строки запроса в один вызов, например:

    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:

    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)
    })
    
person r2evans    schedule 08.12.2017
comment
Спасибо. Я использую код: sqldf('update films set director_name=lower(director_name)'), но снова получаю ту же ошибку. - person Farzad; 09.12.2017
comment
Я думаю, вы меня не слышите: это предупреждение, а не ошибка. Команда SQL выполняется отлично... но поскольку таблица существует только до тех пор, пока sqldf работает, когда ваш один update запрос завершен, данные отбрасываются. Предупреждение не имеет ничего общего с вашим желанием обновлять переменную R при изменении таблицы базы данных, для чего я также предложил обходной путь. Делать просто sqldf("update ...") бессмысленно; для этого вы должны сделать что-то вроде sqldf(c("update ...", "select * from ...")). - person r2evans; 09.12.2017
comment
Мне очень жаль, что я не полностью понимаю ваши пункты. На самом деле я пробую второй формат, который вы рекомендуете d<- sqldf(c("update ...", "select * from ...")), но только для первой команды, таблица создана, но для следующих команд я получаю то же самое предупреждение, и мой набор данных не делает любые обновления и изменения. - person Farzad; 09.12.2017
comment
Мой код у меня работает, включая обновление локальной переменной. Я предлагаю вам отредактировать свой вопрос и включить точный код, который вы используете. Вам также необходимо включить в свой вопрос репрезентативные данные (возможно, образец ваших реальных данных), чтобы мы могли видеть, что то, что вы ожидаете, является логически правильным, а также программно правильным. (Возможно, только dput(head(d)), включая достаточно строк, чтобы содержать как некоторые с "Colores", так и некоторые без них.) - person r2evans; 09.12.2017
comment
Я отредактировал свой вопрос с точным кодом, который я использовал. - person Farzad; 09.12.2017
comment
Если это ответ на ваш вопрос, пожалуйста, примите ответ, установив галочку слева от него. (Это общепринятый этикет на подобных форумах, и он дает очки репутации за время/усилия, потраченные на ответ.) - person r2evans; 09.12.2017
comment
Еще раз спасибо за ваше время и любезное руководство. - person Farzad; 09.12.2017
comment
sqldf имеет средство для постоянных соединений. См. пример 10 на главной странице. github.com/ggrothendieck/ - person G. Grothendieck; 10.12.2017
comment
Кроме того, если вы действительно измерите скорость, вы обнаружите, что sqldf во многих случаях выгодно отличается от базового R. Хотя он был написан не с целью скорости, а больше для того, чтобы позволить легко использовать SQL без настройки операторов создания таблицы, неожиданно было обнаружено, что некоторые пользователи использовали его, чтобы получить преимущество в скорости по сравнению с базовым R. - person G. Grothendieck; 10.12.2017
comment
@GGrothendieck Я задавался вопросом, не согласитесь ли вы с какими-либо оговорками (с моей стороны). Я знал, что есть что-то с постоянными соединениями, но, должно быть, предположил, что это для H2 или чего-то подобного, спасибо, что разъяснили мне правду. Мой комментарий о накладных расходах/скорости больше применим к большим наборам данных, где время вставки таблицы измеримо, но я должен провести больше тестов, прежде чем выбрасывать это туда, извините. - person r2evans; 10.12.2017
comment
Не так. Некоторые пользователи, использующие его для скорости, используют его для больших наборов данных. Один пользователь сказал, что ему удалось устроиться на работу, занимающую всю ночь до часа. - person G. Grothendieck; 10.12.2017