На мой взгляд, это наиболее целесообразно предварительно выделить df_final
НСБУ, а затем индекс-назначения столбцы из df_data
. Единственный трюк - это определение того, какие столбцы нужно назначить.
Я вижу, что вы хотите правильно-обосновать (так сказать) столбцы в наборах столбцов. Таким образом, требование сводится к тому, что я бы назвал «кумулятивным соответствием» типов обращенных столбцов df_data
в типах обращенных столбцов df_final
.Другими словами, вам необходимо перейти от права налево через типы столбцов df_data
и df_final
и найти следующее (с правой стороны) совпадение.
Я отдаю себе отчет в различных некумулятивных/кумулятивных парах функций в R, такие как sum()
/cumsum()
, prod()
/cumprod()
, min()
/cummin()
и max()
/cummax()
(на самом деле я думаю, что те являются единственными), однако похоже, не существует какой-либо функции «кумулятивного соответствия». Так что я писал мой собственный:
cummatch <- function(small,big) {
cur <- 1L;
res <- integer();
biglen <- length(big);
for (s in small) {
if (cur > biglen) break;
rescur <- match(s,big[cur:biglen])+cur-1L;
if (is.na(rescur)) break;
res[length(res)+1L] <- rescur;
cur <- rescur+1L;
};
length(res) <- length(small);
return(res);
};
И теперь мы можем использовать его, чтобы получить индексы столбцов для назначения:
cis <- ncol(df_final)+1L-rev(cummatch(rev(sapply(df_data,typeof)),rev(sapply(df_final,typeof))));
cis;
## [1] 2 3 6 9
df_final[nrow(df_data),1] <- NA; ## preallocate rows of NA
df_final;
## a b c l m n x y z
## 1 NA NA NA NA NA NA NA NA NA
## 2 NA NA NA NA NA NA NA NA NA
## 3 NA NA NA NA NA NA NA NA NA
df_final[cis] <- df_data;
df_final;
## a b c l m n x y z
## 1 NA 21.3 23.22 NA NA NA NA NA 23.2
## 2 NA 23.1 64.20 NA NA 2 NA NA 90.2
## 3 NA 22.2 46.20 NA NA 3 NA NA 9.1
С точки зрения производительности, моя cummatch()
функция, вероятно сосет, учитывая все вызов цикла R-уровня и вызов функции (например, повторные вызовы match()
на подвекторах big
). В последнее время я играл с Rcpp, поэтому решил попробовать записать более эффективную версию в Rcpp. Я ссылался на how can I handle vectors without knowing the type in Rcpp, чтобы попытаться выяснить, как написать вектор-тип-агностическую функцию, а решение немного хаки, с использованием функции шаблона C++ с функцией обертки, которая switch
es на векторе, и, следовательно, должна в основном создайте отдельный вызов функции для каждого case
в пределах switch
. Моя функция принимает два векторных аргумента, поэтому макроса RCPP_RETURN_VECTOR()
на самом деле недостаточно для него, но поскольку оба вектора должны быть одного типа (для сопоставления), я смог массировать макросы для работы с двумя аргументами, а не с одним. Это связано с применением правил продвижения типа R вручную в одном из макросов, и я уверен, что я прав. Излишне говорить, что это, вероятно, достигает (или превосходит) пределов того, что разумно делать с Rcpp. Во всяком случае, вот он:
cppFunction('
using namespace Rcpp;
#define ___RCPP_HANDLE_CASE___2(___RTYPE___ , ___FUN___ , ___OBJECT___1 , ___OBJECT___2 , ___RCPPTYPE___) \\
case ___RTYPE___ : \\
return ___FUN___(::Rcpp::___RCPPTYPE___<___RTYPE___>(___OBJECT___1), ::Rcpp::___RCPPTYPE___<___RTYPE___>(___OBJECT___2)) ;
#define ___RCPP_RETURN___2(__FUN__, __SEXP__1 , __SEXP__2, __RCPPTYPE__) \\
SEXP __TMP__1 = __SEXP__1 ; \\
SEXP __TMP__2 = __SEXP__2 ; \\
unsigned int __TMP__1_TYPE = TYPEOF(__TMP__1); \\
unsigned int __TMP__2_TYPE = TYPEOF(__TMP__2); \\
unsigned int __TMP__TYPE = __TMP__1_TYPE == RAWSXP ? __TMP__2_TYPE : __TMP__2_TYPE == RAWSXP ? __TMP__1_TYPE : std::max(__TMP__1_TYPE,__TMP__2_TYPE); /* note: the SEXPTYPE enumeration order *almost* aligns with the R type promotion rules; only raw is out-of-order, so we can test for that first, then use std::max() */ \\
if (__TMP__1_TYPE < LGLSXP || __TMP__2_TYPE < LGLSXP) __TMP__TYPE = 0; \\
switch(__TMP__TYPE) { \\
___RCPP_HANDLE_CASE___2(INTSXP , __FUN__ , __TMP__1 , __TMP__2 , __RCPPTYPE__) \\
___RCPP_HANDLE_CASE___2(REALSXP , __FUN__ , __TMP__1 , __TMP__2 , __RCPPTYPE__) \\
___RCPP_HANDLE_CASE___2(RAWSXP , __FUN__ , __TMP__1 , __TMP__2 , __RCPPTYPE__) \\
___RCPP_HANDLE_CASE___2(LGLSXP , __FUN__ , __TMP__1 , __TMP__2 , __RCPPTYPE__) \\
___RCPP_HANDLE_CASE___2(CPLXSXP , __FUN__ , __TMP__1 , __TMP__2 , __RCPPTYPE__) \\
___RCPP_HANDLE_CASE___2(STRSXP , __FUN__ , __TMP__1 , __TMP__2 , __RCPPTYPE__) \\
/* no == for generic ___RCPP_HANDLE_CASE___2(VECSXP , __FUN__ , __TMP__1 , __TMP__2 , __RCPPTYPE__) */ \\
/* no == for expression ___RCPP_HANDLE_CASE___2(EXPRSXP , __FUN__ , __TMP__1 , __TMP__2 , __RCPPTYPE__) */ \\
default: \\
throw std::range_error("not a vector") ; \\
}
#define RCPP_RETURN_VECTOR2(_FUN_, _SEXP_1, _SEXP_2) ___RCPP_RETURN___2(_FUN_, _SEXP_1, _SEXP_2, Vector)
#define RCPP_RETURN_MATRIX2(_FUN_, _SEXP_1, _SEXP_2) ___RCPP_RETURN___2(_FUN_, _SEXP_1, _SEXP_2, Matrix)
template<typename T> IntegerVector cummatch_impl(T small, T big) {
int smalllen = LENGTH(small);
IntegerVector res(smalllen,NA_INTEGER);
int cur = 0;
int biglen = LENGTH(big);
for (int si = 0; si < smalllen; ++si) {
int rescur = NA_INTEGER;
for (int bi = cur; bi < biglen; ++bi) {
if (small(si) == big(bi)) {
rescur = bi;
break;
}
}
if (rescur == NA_INTEGER) break;
res(si) = rescur+1;
cur = rescur+1;
}
return res;
}
// [[Rcpp::export]]
IntegerVector cummatch(SEXP small, SEXP big) { RCPP_RETURN_VECTOR2(cummatch_impl,small,big); }
');
Откуда вы знаете, какие столбцы отсутствуют? Например, если между столбцом не существует целочисленного столбца, как вы их назначаете? Кроме того, вы не задавали 'l',' m', 'n' как целые столбцы. Ни 'col3', как целочисленный столбец. Это должно быть 'col3 = c (NA_integer_, 2L, 3L)' –
@DavidArenburg фиксировал целые числа. Между числовыми десятичными столбцами всегда будет хотя бы один целочисленный столбец (но, возможно, с 'NA'). – tospig