Как отправить 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 секунд, поэтому дальнейшая оптимизация может не потребоваться.

Другие вопросы по тегам