Загрузка данных с помощью RSQLite, в котором указаны значения в кавычках

Я пытаюсь загрузить файл csv большого размера в базу данных SQL lite с помощью пакета RSQLite (я также пробовал пакет sqldf). Файл содержит все почтовые индексы Великобритании и различные поисковые значения для них.

Я хотел не загружать его в R, а просто загружать напрямую в базу данных. Хотя это не является строго необходимым для этой задачи, я хочу сделать это, чтобы подготовить методику для больших файлов, которые не поместятся в памяти, если мне придется обрабатывать их в будущем.

К сожалению, csv предоставляет значения в двойных кавычках, и функция dbWriteTable, похоже, не может разделить их или игнорировать их в любой форме. Вот место загрузки файла: http://ons.maps.arcgis.com/home/item.html?id=3548d835cff740de83b527429fe23ee0

Вот мой код:

# Load library
library("RSQLite")

# Create a temporary directory
tmpdir <- tempdir()

# Set the file name
file <- "data\\ONSPD_MAY_2017_UK.zip"

# Unzip the ONS Postcode Data file
unzip(file, exdir = tmpdir )

# Create a path pointing at the unzipped csv file
ONSPD_path <- paste0(tmpdir,"\\ONSPD_MAY_2017_UK.csv")

# Create a SQL Lite database connection
db_connection <- dbConnect(SQLite(), dbname="ons_lkp_db")

# Now load the data into our SQL lite database
dbWriteTable(conn = db_connection,
              name = "ONS_PD",
              value = ONSPD_path,
              row.names = FALSE,
              header = TRUE,
              overwrite = TRUE
              )

# Check the data upload
dbListTables(db_connection)
dbGetQuery(db_connection,"SELECT pcd, pcd2, pcds from ONS_PD  LIMIT 20")

Решив эту проблему, я нашел справочное руководство (https://www.r-bloggers.com/r-and-sqlite-part-1/), который рекомендовал использовать пакет sqldf, но, к сожалению, когда я пытаюсь использовать соответствующую функцию в sqldf (read.csv.sql), я получить ту же проблему с двойными кавычками.

Это похоже на довольно распространенную проблему при импорте файлов csv в систему sql, большинство инструментов импорта могут обрабатывать двойные кавычки, поэтому я удивлен, что столкнулся с проблемой с этим (если только я не пропустил очевидный файл справки по проблеме где-то по пути).

ИЗМЕНИТЬ 1

Вот несколько примеров данных из моего файла csv в виде вывода dput таблицы SQL:

structure(list(pcd = c("\"AB1 0AA\"", "\"AB1 0AB\"", "\"AB1 0AD\"", 
"\"AB1 0AE\"", "\"AB1 0AF\""), pcd2 = c("\"AB1  0AA\"", "\"AB1  0AB\"", 
"\"AB1  0AD\"", "\"AB1  0AE\"", "\"AB1  0AF\""), pcds = c("\"AB1 0AA\"", 
"\"AB1 0AB\"", "\"AB1 0AD\"", "\"AB1 0AE\"", "\"AB1 0AF\"")), .Names = c("pcd", 
"pcd2", "pcds"), class = "data.frame", row.names = c(NA, -5L))

ИЗМЕНИТЬ 2

Вот моя попытка использовать аргумент фильтра в функции sqldf read.csv.sql (обратите внимание, что пользователям Windows потребуется rtools, установленный для этого). К сожалению, это все еще не удаляет кавычки из моих данных, хотя таинственным образом удаляет все пробелы.

library("sqldf")
sqldf("attach 'ons_lkp_db' as new")
db_connection <- dbConnect(SQLite(), dbname="ons_lkp_db")
read.csv.sql(ONSPD_path,
              sql = "CREATE TABLE ONS_PD AS SELECT * FROM file",
              dbname = "ons_lkp_db",
              filter = 'tr.exe -d ^"'
              )

dbGetQuery(db_connection,"SELECT pcd, pcd2, pcds from ONS_PD  LIMIT 5")

Кроме того, спасибо за тщательное голосование от тех, кто считал, что это не вопрос программирования в рамках Stack Overflow (?!).


person Tumbledown    schedule 17.07.2017    source источник
comment
См. FAQ № 13 на странице sqldf на github. Также файл справки для read.csv.sql. Просмотрите минимальный воспроизводимый пример.   -  person G. Grothendieck    schedule 17.07.2017
comment
Спасибо, я попробую совет по часто задаваемым вопросам, как только установлю rtools. Не могли бы вы указать мне, где мой пример нуждается в улучшении?   -  person Tumbledown    schedule 17.07.2017
comment
Для других: github. ru / ggrothendieck /   -  person Tumbledown    schedule 17.07.2017
comment
Покажите небольшое подмножество строк ввода прямо в вопросе, чтобы сделать его самодостаточным, или, если они длинные, сократите их достаточно, чтобы сохранить основную идею, не перегружая вопрос большими данными.   -  person G. Grothendieck    schedule 17.07.2017
comment
Я добавлю это чуть позже, когда у меня будет больше времени. Я только что попробовал предложение из FAQ, но мне все равно не повезло: Ошибка в utils :: read.table (value, sep = sep, header = header, skip = skip,: во вводе нет строк Ошибка в rsqlite_send_query (conn @ ptr, statement): нет такой таблицы: файл   -  person Tumbledown    schedule 17.07.2017
comment
Мой синтаксис отлично работает, если я уберу аргумент фильтра. Я обновлю свой пост позже, чтобы показать, что именно я пробовал.   -  person Tumbledown    schedule 17.07.2017
comment
Игнорируйте предыдущие комментарии, мои переменные среды не обновились. Теперь данные загружаются с помощью команды фильтра, но по-прежнему имеют кавычки.   -  person Tumbledown    schedule 17.07.2017
comment
Просто обратите внимание, что после добавления filter = 'tr.exe -d ^ к функции read.csv.sql он по-прежнему не удаляет двойные кавычки, но удаляет пробелы.   -  person Tumbledown    schedule 24.07.2017
comment
Пробуя его в Windows 10, кажется, что вам нужны одинарные кавычки вокруг ^". Также прочтите минимальный воспроизводимый пример.   -  person G. Grothendieck    schedule 24.07.2017
comment
Чтобы предоставить воспроизводимый пример в этом случае, вам нужно будет показать строки input из файла csv.   -  person G. Grothendieck    schedule 24.07.2017


Ответы (3)


Используйте read.csv.sql из пакета sqldf с аргументом filter и предоставьте любую утилиту, которая удаляет двойные кавычки или переводит их в пробелы.

Вопрос не дает полностью воспроизводимого минимального примера, но я привел его ниже. Если вы используете read.csv.sql для выбора подмножества строк или столбцов, просто добавьте для этого соответствующий аргумент sql.

Сначала настройте тестовые входные данные, а затем попробуйте любое из однострочных решений, показанных ниже. Предполагая Windows, убедитесь, что утилита tr (находится в дистрибутиве Rtools R) или сторонняя утилита csvfix (найденная здесь, а для Linux также см. this) или утилиту vbscript trquote2space.vbs ( см. примечание в конце) находится на вашем пути:

library(sqldf)
cat('a,b\n"1","2"\n', file = "tmp.csv")

# 1 - corrected from FAQ
read.csv.sql("tmp.csv", filter = "tr.exe -d '^\"'")

# 2 - similar but does not require Windows cmd quoting
read.csv.sql("tmp.csv", filter = "tr -d \\42")

# 3 - using csvfix utility (which must be installed first)
read.csv.sql("tmp.csv", filter = "csvfix echo -smq")

# 4 - using trquote2space.vbs utility as per Note at end
read.csv.sql("tmp.csv", filter = "cscript /nologo trquote2space.vbs")

любой из которых дает:

  a b
1 1 2

Вы также можете использовать любой другой подходящий язык или утилиту. Например, можно использовать ваше предложение Powershell, хотя я подозреваю, что специальные утилиты, такие как tr и csvfix, будут работать быстрее.

Первое решение выше исправлено из FAQ. (Он действительно работал в то время, когда часто задаваемые вопросы были написаны много лет назад, но при тестировании его сейчас в Windows 10, похоже, требуется указанное изменение, или, возможно, уценка не сохранилась без изменений после перехода от Google Code, где он изначально находился, к github, который использует немного другой вариант уценки.)

Для Linux tr доступен изначально, хотя цитирование отличается от Windows и может даже зависеть от оболочки. csvfix также доступен в Linux, но его необходимо установить. Показанный выше пример csvfix будет одинаково работать в Windows и Linux. vbscript явно специфичен для Windows.

Примечание. sqldf поставляется с утилитой mini-tr, написанной на vbscript. Если вы измените соответствующие строки на:

Dim sSearch  : sSearch  = chr(34)
Dim sReplace : sReplace = " "

и измените имя на trquote2space.vbs, тогда у вас будет специальная утилита Windows для замены двойных кавычек на пробелы.

person G. Grothendieck    schedule 24.07.2017
comment
Добавили еще решений и переделали. - person G. Grothendieck; 24.07.2017
comment
Отлично, ваша поправка к аргументу фильтра работает отлично. Приносим извинения за не совсем точный воспроизводимый пример и благодарим за усилия, которые вы приложили для ответа. - person Tumbledown; 25.07.2017

Импортер CSV в пакете RSQLite является производным от оболочки sqlite3, которая сама по себе не поддерживает цитируемые значения при импорте файлов CSV (Как импортировать загрузку файла .sql или .csv в SQLite?, doc < / а>). Вы можете использовать readr::read_delim_chunked():

callback <- function(data) {
  name <- "ONS_PD"
  exists <- dbExistsTable(con, name)
  dbWriteTable(con, name, data, append = exists)
}

readr::read_delim_chunked(ONSPD_path, callback, ...)

Замените ... любыми дополнительными аргументами, которые вам нужны для вашего CSV-файла.

person krlmlr    schedule 17.07.2017
comment
Спасибо за это, у вас есть какие-нибудь представления о скорости этого для больших файлов? Я надеюсь на решение проблемы с фильтрацией в функции read.csv.sql, но попробую ваше предложение, когда у меня появится такая возможность. - person Tumbledown; 24.07.2017
comment
@Tumbledown: программа чтения работает довольно быстро; в то время как fread() из data.table, как сообщается, работает еще быстрее, похоже, что у него меньше возможностей для анализа сложных файлов CSV, и я не нашел возможности добавить обратный вызов. dbWriteTable() использует подготовленные операторы и тоже работает довольно быстро. Я не знаю, как это соотносится с sqldf. - person krlmlr; 24.07.2017

Честно говоря, я не смог найти ничего, что могло бы решить эту проблему. Документация sqldf сообщает: «Итак, одно ограничение с файлами .csv заключается в том, что кавычки не считаются специальными в файлах, поэтому запятая в поле данных, например« Смит, Джеймс », будет рассматриваться как разделитель полей, а кавычки будут вводиться как часть данных, которая, вероятно, не соответствует назначению "

Итак, похоже, что решения нет, насколько я знаю.

Один из возможных субоптимальных подходов (кроме очевидного поиска и замены в текстовом редакторе) - использовать такие команды SQL.

dbSendQuery(db_connection,"UPDATE ONS_PD SET pcd = REPLACE(pcd, '\"', '')")
person Federico Manigrasso    schedule 17.07.2017
comment
Я просто пробую предложение rtools сейчас, в противном случае вы можете удалить их, вызвав сценарий Windows PowerShell в CSV перед загрузкой. Хотя все немного неловко. - person Tumbledown; 17.07.2017
comment
Эта цитата из документации применима только в том случае, если вы не используете команду filter. Если вы используете команду filter, вы должны иметь возможность преобразовывать ввод произвольным образом, поэтому, если это возможно, вы должны иметь возможность это сделать. Если вам нужна дополнительная помощь, вам нужно будет предоставить автономный воспроизводимый пример. Мы не знаем, что у вас есть и что вы сделали. - person G. Grothendieck; 18.07.2017