Добавить столбец в таблицу SQLite в R с помощью RSQLite - PullRequest
1 голос
/ 08 июля 2011

У меня есть два больших фрейма данных, которые я хотел бы использовать для внешнего соединения с merge(), но объединенная таблица слишком велика для оперативной памяти.Мой обходной путь - использовать пакет RSQLite для внешнего соединения и сохранять объединенную таблицу обратно в базу данных.

Я хотел бы использовать функцию R для столбцов в этой объединенной таблице, но не могу понять, как добавить столбец в объединенную таблицу.Я знаю, как это сделать с dbWriteTable() (показано ниже), но это не вариант, поскольку объединенная таблица больше ОЗУ.

library(RSQLite)
left <- data.frame(let = letters[rep(1:4, each = 5)], num = 1:20)
right <- data.frame(let = letters[rep(1:4, each = 5)], num = 21:40)
con <- dbConnect(dbDriver("SQLite"), dbname = tempfile())
dbWriteTable(con, "left_table", left, row.names = F)
dbWriteTable(con, "right_table", right, row.names = F)
dbGetQuery(con, "CREATE TABLE merged_table (letters TEXT, left_num INTEGER, right_num INTEGER)")
dbGetQuery(con, "INSERT INTO merged_table SELECT * FROM left_table LEFT OUTER JOIN right_table USING (let)")
fun <- function(x) rowSums(x)
temp <- dbReadTable(con, "merged_table")
dbWriteTable(con, "merged_table_new", cbind(temp, fun(temp[, 2:3])))
dbDisconnect(con)

Я слышал, что базы данных работают со строками, поэтому яподозреваю, что правильное решение может просто циклически проходить по строкам, добавляя запись в каждую строку, но я не уверен, как реализовать.Спасибо!

(И здесь нет ничего священного в SQLite, я просто подумал, что было бы лучше для этого ad hoc анализа.)


Редактировать: Iузнал о опции bind.data в dbGetPreparedQuery() и понял, что мне нужно соединение для чтения и записи в базу данных, но у меня все еще есть некоторые проблемы (т. е. данные не INSERT в базу данных).Скрипт работает без ошибок, но также и без желаемого результата.

library(RSQLite)
left <- data.frame(let = letters[rep(1:4, each = 5)], num = 1:20)
right <- data.frame(let = letters[rep(1:4, each = 5)], num = 21:40)
my.tempfile <- tempfile()
con.write <- dbConnect(dbDriver("SQLite"), dbname = my.tempfile)
con.read <- dbConnect(dbDriver("SQLite"), dbname = my.tempfile)
dbWriteTable(con.write, "left_table", left, row.names = F)
dbWriteTable(con.write, "right_table", right, row.names = F)
dbGetQuery(con.write, "CREATE TABLE merged_table (letters TEXT, left_num INTEGER, right_num INTEGER)")
dbGetQuery(con.write, "INSERT INTO merged_table SELECT * FROM left_table LEFT OUTER JOIN right_table USING (let)")
dbGetQuery(con.write, "ALTER TABLE merged_table ADD COLUMN sum INTEGER")
dbGetQuery(con.write, "ALTER TABLE merged_table ADD COLUMN mean INTEGER")

res <- dbSendQuery(con.read, "SELECT left_num, right_num FROM merged_table")
while (!dbHasCompleted(res)) {
    data.1 <- fetch(res)
    data.2 <- data.frame(rowSums(data.1), rowMeans(data.1))
    dbGetPreparedQuery(con.write, "INSERT INTO merged_table (sum, mean) VALUES (?, ?)", bind.data = data.2)
}
dbClearResult(res)

dbGetQuery(con.read, "SELECT * FROM merged_table LIMIT 5")

дает

  letters left_num right_num sum mean
1       a        1        21  NA   NA
2       a        1        22  NA   NA
3       a        1        23  NA   NA
4       a        1        24  NA   NA
5       a        1        25  NA   NA

, но я ожидал

  left_num right_num sum mean
1        1        21  22 11.0
2        1        22  23 11.5
3        1        23  24 12.0
4        1        24  25 12.5
5        1        25  26 13.0

1 Ответ

2 голосов
/ 08 июля 2011

Эксперт по SQLite может улучшить это решение, но вы можете сделать это с помощью одного запроса, выполнив его сразу после создания merged_table:

dbGetQuery(con, "INSERT INTO merged_table SELECT
                  letters,left_num,right_num,left_num+right_num row_sum FROM 
                    (SELECT let letters,left_table.num left_num, right_table.num right_num FROM 
                        left_table LEFT OUTER JOIN right_table USING (let))")

Это вроде уродливый SQL, я полагаю, но он работает. Если вам нужно добавить более двух столбцов, вы можете построить часть запроса на добавление столбцов, используя paste в R, если это необходимо.

Другие вещи, которые нужно исследовать, - это добавление дополнительного столбца с помощью ALTER TABLE, а затем массовое обновление пакетами. Я играл с этим в течение минуты или двух, но не мог заставить это работать, но это определенно не означает, что это невозможно.

EDIT

Следующий код создает желаемый результат. В данный момент я как бы тороплюсь (выходя из двери), поэтому цикл while выдает ошибку, потому что цикл достигает конца данных до того, как будет достигнуто условие выхода, поэтому в последний раз у вас есть пустой data.1 фрейм данных, который вызывает ошибку в bind.data. Но если вы выполните последний запрос, вы увидите, что все данные были вставлены.

library(RSQLite)
left <- data.frame(let = letters[rep(1:4, each = 5)], num = 1:20)
right <- data.frame(let = letters[rep(1:4, each = 5)], num = 21:40)
conn <- dbConnect(dbDriver("SQLite"), dbname = "sotemp.db")
conn.copy <- dbConnect(dbDriver("SQLite"), dbname = "sotempCopy.db")
dbWriteTable(conn, "left_table", left, row.names = F)
dbWriteTable(conn, "right_table", right, row.names = F)
dbGetQuery(conn, "CREATE TABLE merged_table1 (letters TEXT, left_num INTEGER, right_num INTEGER)")
dbGetQuery(conn.copy, "CREATE TABLE merged_table2 (letters TEXT, left_num INTEGER, right_num INTEGER, rowSum INTEGER,
                            rowMean REAL)")

dbGetQuery(conn, "INSERT INTO merged_table1 SELECT * FROM left_table LEFT OUTER JOIN right_table USING (let)")

res <- dbSendQuery(conn, "SELECT letters, left_num, right_num FROM merged_table1")
while (!dbHasCompleted(res)) {
    data.1 <- fetch(res,n=5)
    data.1 <- cbind(data.1,rowSums(data.1[,2:3]),rowMeans(data.1[,2:3]))
    colnames(data.1)[4:5] <- c("rowSum","rowMean")
    dbGetPreparedQuery(conn.copy, "INSERT INTO merged_table2 (letters, left_num, right_num,rowSum, rowMean) VALUES 
                        (?, ?, ?, ?, ?)", bind.data = data.1)
}
dbClearResult(res)

dbGetQuery(conn.copy, "SELECT * FROM merged_table2")

Это ни в коем случае не идеально. Я надеюсь, что другие будут проходить мимо и редактировать / добавлять к этому. Несколько замечаний о том, почему ваше решение не работает.

Сначала INSERT добавляет новые строки в таблицу. Вы, похоже, ожидали, что он изменит значения в столбце, что обычно делается с помощью UPDATE, что может стать громоздким.

Во-вторых, я не уверен, что отдельные подключения для чтения / записи вам что-нибудь покупают. SQLite не поддерживает полный параллелизм чтения / записи, даже при использовании отдельных соединений. Поэтому, пока вы не очистите результаты SELECT, вы будете получать ошибки блокировки при попытке записи.

В-третьих, стратегия, которую я использовал здесь для пакетной обработки, заключалась в создании двух отдельных БД, циклическом просмотре результатов SELECT в первом, создании новых столбцов в R и затем INSERT результирующих данных во втором дб.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...