В Rcpp, как получить пользовательскую структуру из C в R - PullRequest
0 голосов
/ 30 июня 2018

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

    typedef struct {
        char*   firstname[128];
        char*   lastname[128];
        int      nbrOfSamples;
    } HEADER_INFO;

    // [[Rcpp::export]]
    HEADER_INFO* read_header(Rcpp::StringVector strings) {
        FILE *fp;
        MEF_HEADER_INFO *header;

        char * filename = (char*)(strings(0));
        char * password = (char*)(strings(1));

        header = (HEADER_INFO*)malloc(sizeof(HEADER_INFO));
        memset(header, 0, sizeof(HEADER_INFO));

        fp = fopen(filename, "r");
        (void)read_header(header, password);
        return header;
    }

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

Спасибо!

1 Ответ

0 голосов
/ 01 июля 2018

Правильная структура в R зависит от того, как выглядит ваш struct. Именованный список является наиболее общим. Вот простой пример реализации функции wrap, как указано в комментариях:

#include <RcppCommon.h>

typedef struct {
  char*   firstname[128];
  char*   lastname[128];
  int      nbrOfSamples;
} HEADER_INFO;

namespace Rcpp {
  template <>
  SEXP wrap(const HEADER_INFO& x);
}

#include <Rcpp.h>

namespace Rcpp {
  template <>
  SEXP wrap(const HEADER_INFO& x) {
    Rcpp::CharacterVector firstname(x.firstname, x.firstname + x.nbrOfSamples);
    Rcpp::CharacterVector lastname(x.lastname, x.lastname + x.nbrOfSamples);
    return Rcpp::wrap(Rcpp::List::create(Rcpp::Named("firstname") = firstname,
                      Rcpp::Named("lastname") = lastname,
                      Rcpp::Named("nbrOfSamples") = Rcpp::wrap(x.nbrOfSamples)));
  };
}

//  [[Rcpp::export]]
HEADER_INFO getHeaderInfo() {
  HEADER_INFO header;
  header.firstname[0] = (char*)"Albert";
  header.lastname[0] = (char*)"Einstein";
  header.firstname[1] = (char*)"Niels";
  header.lastname[1] = (char*)"Bohr";
  header.firstname[2] = (char*)"Werner";
  header.lastname[2] = (char*)"Heisenberg";
  header.nbrOfSamples = 3;
  return header;
}

/*** R
getHeaderInfo()
 */

Выход:

> getHeaderInfo()
$firstname
[1] "Albert" "Niels"   "Werner"

$lastname
[1] "Einstein"   "Bohr"       "Heisenberg"

$nbrOfSamples
[1] 3

Однако для этого конкретного случая data.frame будет более естественным для использования, чего можно достичь, заменив выше wrap на:

  template <>
  SEXP wrap(const HEADER_INFO& x) {
    Rcpp::CharacterVector firstname(x.firstname, x.firstname + x.nbrOfSamples);
    Rcpp::CharacterVector lastname(x.lastname, x.lastname + x.nbrOfSamples);
    return Rcpp::wrap(Rcpp::DataFrame::create(Rcpp::Named("firstname") = firstname,
                                              Rcpp::Named("lastname") = lastname));
  };

Выход:

> getHeaderInfo()
  firstname   lastname
1    Albert   Einstein
2     Niels       Bohr
3    Werner Heisenberg
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...