Передача кадра данных из-в R и C с использованием .call()

Есть ли общий способ передачи кадра данных с произвольными столбцами (integer/factor, numeric, character data) от r до c и обратно? Было бы очень полезно оценить указатели на достаточно близкие примеры.

Спасибо.

РТ

Ответ 1

Data.frame - это список, поэтому по линиям

#include <Rdefines.h>

SEXP df_fun(SEXP df)
{
    int i, len = Rf_length(df);
    SEXP result;
    PROTECT(result = NEW_CHARACTER(len));
    for (i = 0; i < len; ++i)
        switch(TYPEOF(VECTOR_ELT(df, i))) {
        case INTSXP:
            SET_STRING_ELT(result, i, mkChar("integer"));
            break;
        case REALSXP:
            SET_STRING_ELT(result, i, mkChar("numeric"));
            break;
        default:
            SET_STRING_ELT(result, i, mkChar("other"));
            break;
        };
        UNPROTECT(1);
    return result;
}

а затем после R CMD SHLIB df_fun.c

> dyn.load("df_fun.so")
> df=data.frame(x=1:5, y=letters[1:5], z=pi, stringsAsFactors=FALSE)
> .Call("df_fun", df)
[1] "integer" "other"   "numeric"

Используйте GET_CLASS, GET_ATTR и другие макросы в Rdefines.h(или их эквивалентные функции, например getAttrib), чтобы обнаружить другую информацию о кадре данных. Обратите внимание, что data.frame имеет API, который может отличаться от его структуры. Так, например, функция R row.names может возвращать нечто отличное от значения, хранящегося в атрибуте row.names. Я думаю, что большинство функций .Call работают на атомных векторах, сохраняя манипуляции с более сложными объектами на уровне R.

Ответ 3

Тип data.frame - это список с атрибутом data.frame.

Это пример создания data.frame в источнике R (src/library/stats/src/model.c):

/* Turn the data "list" into a "data.frame" */
/* so that subsetting methods will work. */

PROTECT(tmp = mkString("data.frame"));
setAttrib(data, R_ClassSymbol, tmp);
UNPROTECT(1);

Вы можете имитировать data.frame вручную следующим образом:

l <- list(1:5)
attr(l, "class") <- "data.frame"
attr(l, "names") <- "Column 1"
attr(l, "row.names") <- paste("Row ", 1:5)