Сколько векторов можно добавить в DataFrame::create( vec1, vec2 ...)?
Я создаю DataFrame для хранения разобранных файлов журнала http haproxy, который имеет довольно много полей (25+).
Если я добавлю более 20 векторов (по одному на каждое поле), я получу ошибку компиляции:
no matching function call to 'create'
Метод создания:
return DataFrame::create(
_["clientIp"] = clientIp,
_["clientPort"] = clientPort,
_["acceptDate"] = acceptDate,
_["frontendName"] = frontendName,
_["backendName"] = backendName,
_["serverName"] = serverName,
_["tq"] = tq,
_["tw"] = tw,
_["tc"] = tc,
_["tr"] = tr,
_["tt"] = tt,
_["status_code"] = statusCode,
_["bytes_read"] = bytesRead,
#if CAPTURED_REQUEST_COOKIE_FIELD == 1
_["capturedRequestCookie"] = capturedRequestCookie,
#endif
#if CAPTURED_REQUEST_COOKIE_FIELD == 1
_["capturedResponseCookie"] = capturedResponseCookie,
#endif
_["terminationState"] = terminationState,
_["actconn"] = actconn,
_["feconn"] = feconn,
_["beconn"] = beconn,
_["srv_conn"] = srvConn,
_["retries"] = retries,
_["serverQueue"] = serverQueue,
_["backendQueue"] = backendQueue
);
Вопросы:
- Я достиг жесткого предела?
- Есть ли обходной путь, позволяющий мне добавить более 20 векторов во фрейм данных?
2 ответа
Да, вы достигли жесткого предела - Rcpp
ограничено стандартом C++98, который требует явного раздувания кода для поддержки аргументов 'variadic'. По сути, новая перегрузка должна быть сгенерирована для каждого create
Используемая функция, и чтобы не задушить компилятор Rcpp
просто обеспечивает до 20.
Обходным путем будет использование класса "строитель", где вы последовательно добавляете элементы, а затем конвертируете в DataFrame
в конце. Простой пример такого класса - мы создаем ListBuilder
объект, для которого мы последовательно add
новые столбцы. Попробуйте запустить Rcpp::sourceCpp()
с этим файлом, чтобы увидеть вывод.
#include <Rcpp.h>
using namespace Rcpp;
class ListBuilder {
public:
ListBuilder() {};
~ListBuilder() {};
inline ListBuilder& add(std::string const& name, SEXP x) {
names.push_back(name);
// NOTE: we need to protect the SEXPs we pass in; there is
// probably a nicer way to handle this but ...
elements.push_back(PROTECT(x));
return *this;
}
inline operator List() const {
List result(elements.size());
for (size_t i = 0; i < elements.size(); ++i) {
result[i] = elements[i];
}
result.attr("names") = wrap(names);
UNPROTECT(elements.size());
return result;
}
inline operator DataFrame() const {
List result = static_cast<List>(*this);
result.attr("class") = "data.frame";
result.attr("row.names") = IntegerVector::create(NA_INTEGER, XLENGTH(elements[0]));
return result;
}
private:
std::vector<std::string> names;
std::vector<SEXP> elements;
ListBuilder(ListBuilder const&) {}; // not safe to copy
};
// [[Rcpp::export]]
DataFrame test_builder(SEXP x, SEXP y, SEXP z) {
return ListBuilder()
.add("foo", x)
.add("bar", y)
.add("baz", z);
}
/*** R
test_builder(1:5, letters[1:5], rnorm(5))
*/
PS: с Rcpp11
, у нас есть переменные функции и, следовательно, ограничения сняты.
Другой распространенный подход с Rcpp - это просто использовать внешний список, содержащий столько объектов DataFrame (каждый из которых ограничен количеством элементов, предоставляемых посредством расширения / повторения макросов старой школы) в соответствующем заголовке), сколько вам нужно.
В (не проверенном) коде:
Rcpp::DataFrame a = Rcpp::DateFrame::create(/* ... */);
Rcpp::DataFrame b = Rcpp::DateFrame::create(/* ... */);
Rcpp::DataFrame c = Rcpp::DateFrame::create(/* ... */);
return Rcpp::List::create(Rcpp::Named("a") = a,
Rcpp::Named("b") = b,
Rcpp::Named("c") = c);