Как отправить data.frame из R в Q/KDB?
У меня большой data.frame
(15 столбцов и 100000 строк) в существующем сеансе R, который я хочу отправить в экземпляр Q/KDB. Из кулинарной книги KDB возможны следующие решения:
RServer for Q: используйте KDB для создания нового экземпляра R, который разделяет пространство памяти. Это не работает, потому что мои данные находятся в существующем экземпляре R.
RServe: запустить сервер R и использовать TCP/IP для связи с клиентом Q/KDB. Это не работает, потому что согласно документации RServe, "каждое соединение имеет отдельную рабочую область и рабочий каталог", и поэтому я предполагаю, что не видит мои существующие данные.
R Math Library: доступ к функциональности R через математическую библиотеку без необходимости экземпляра R. Это не работает, потому что мои данные уже находятся в экземпляре R.
Итак, есть ли другие идеи о том, как отправить данные из R в Q/KDB?
1 ответ
Открыть порт в Q. Я запускаю Q с командным файлом:
@echo off
c:\q\w32\q -p 5001
загрузить qserver.dll
tryCatch({
dyn.load("c:/q/qserver.dll")}
,error = function(f){
print("can't load qserver.dll")
})
Тогда используйте эти
open_connection <- function(host="localhost", port=5001, user=NULL) {
parameters <- list(host, as.integer(port), user)
h <- .Call("kx_r_open_connection", parameters)
assign(".k.h", h, envir = .GlobalEnv)
return(h)
}
close_connection <- function(connection) {
.Call("kx_r_close_connection", as.integer(connection))
}
execute <- function(connection, query) {
.Call("kx_r_execute", as.integer(connection), query)
}
d<<-open_connection(host="localhost",port=thePort)
ex2 <- function(...)
{
query <- list(...)
theResult <- NULL
for(i in query) theResult <- paste0(theResult,i)
return(execute(d,paste0(theResult)))
}
тогда ex2 может принимать несколько аргументов, чтобы вы могли строить запросы с R переменными и строками
Изменить: это для R из Q, вот R к Q
2nd Edit: улучшенный алгоритм:
library(stringr)
RToQTable <- function(Rtable,Qname,withColNames=TRUE,withRowNames=TRUE,colSuffix = NULL)
{
theColnames <- if(!withColNames || length(colnames(Rtable))==0) paste0("col",as.character(1:length(Rtable[1,])),colSuffix) else colnames(Rtable)
if(!withRowNames || length(rownames(Rtable))==0) withRowNames <- FALSE
Rtable <- rbind(Rtable,"linesep")
charnum <- as.integer(nchar(thestr <- paste(paste0(theColnames,':("',str_split(paste(Rtable,collapse='";"'),';\"linesep\";\"')[[1]],');'),collapse="")) - 11)
if(withRowNames)
ex2(Qname,":([]",Qname,str_replace_all(paste0("`",paste(rownames(Rtable),collapse="`"))," ","_"),";",.Internal(substr(thestr,1L,charnum)),"))") else
ex2(Qname,":([]",.Internal(substr(thestr,1L,charnum)),"))")
}
> bigMat <- matrix(runif(1500000),nrow=100000,ncol=15)
> microbenchmark(RToQTable(bigMat,"Qmat"),times=3)
Unit: seconds
expr min lq mean median uq max neval
RToQTable(bigMat, "Qmat") 10.29171 10.315 10.32766 10.33829 10.34563 10.35298 3
Это будет работать для матрицы, поэтому для фрейма данных просто сохраните вектор, содержащий типы каждого столбца, затем преобразуйте фрейм данных в матрицу, импортируйте матрицу в Q и приведите типы
Обратите внимание, что этот алгоритм равен приблизительно O(строки * столбцы ^1.1), поэтому вам нужно будет разбить столбцы на несколько матриц, если у вас есть больше 20, чтобы получить O(строки * столбцы)
но для вашего примера 150000 строк и 15 столбцов занимают 10 секунд, поэтому дальнейшая оптимизация может не потребоваться.