-
Notifications
You must be signed in to change notification settings - Fork 991
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New function psum/pprod #4188
New function psum/pprod #4188
Changes from 4 commits
4109bbe
7ebe3a2
cfed1c8
0d92018
93743c3
77cb265
7e360a2
abbbfed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
\name{psum} | ||
\alias{psum} | ||
\alias{pprod} | ||
\title{ Sum and Product} | ||
\description{ | ||
Similar to \code{pmin} and \code{pmax} but for sum and product. Only works for integer and double. These functions do not recycle vectors. | ||
} | ||
\usage{ | ||
psum(..., na.rm=FALSE) | ||
pprod(..., na.rm=FALSE) | ||
} | ||
\arguments{ | ||
\item{...}{ Numeric arguments of type integer or double.} | ||
\item{na.rm}{ A logical indicating whether missing values should be removed. Default value is \code{FALSE}.} | ||
} | ||
\value{ | ||
Return the sum or product of all numeric argument. The value returned will be of the type of the highest argument types (integer < double) | ||
} | ||
\examples{ | ||
x = c(1, 3, NA, 5) | ||
y = c(2, NA, 4, 1) | ||
z = c(3, 4, 4, 1) | ||
|
||
# Example 1: psum | ||
psum(x, y, z, na.rm = FALSE) | ||
psum(x, y, z, na.rm = TRUE) | ||
|
||
# Example 2: pprod | ||
pprod(x, y, z, na.rm = FALSE) | ||
pprod(x, y, z, na.rm = TRUE) | ||
|
||
} | ||
\keyword{ data } |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -344,3 +344,169 @@ SEXP fcaseR(SEXP na, SEXP rho, SEXP args) { | |
UNPROTECT(nprotect); | ||
return ans; | ||
} | ||
|
||
SEXP psumR(SEXP na, SEXP args) { | ||
if (!IS_TRUE_OR_FALSE(na)) { | ||
error(_("Argument 'na.rm' must be TRUE or FALSE and length 1.")); | ||
} | ||
const int n=length(args); | ||
if (n <= 1) { | ||
error(_("Please supply at least 2 arguments. (%d argument supplied)"), n); | ||
} | ||
SEXPTYPE anstype = TYPEOF(SEXPPTR_RO(args)[0]); | ||
const int64_t len0 = xlength(SEXPPTR_RO(args)[0]); | ||
if (anstype != INTSXP && anstype != REALSXP) { | ||
error(_("Argument 1 is of type %s. Only integer and double types are supported."), type2char(anstype)); | ||
} | ||
for (int i = 1; i < n; ++i) { | ||
SEXPTYPE type = TYPEOF(SEXPPTR_RO(args)[i]); | ||
int64_t len1 = xlength(SEXPPTR_RO(args)[i]); | ||
if (type != INTSXP && type != REALSXP) { | ||
error(_("Argument %d is of type %s. Only integer and double types are supported."), | ||
i+1, type2char(type)); | ||
} | ||
if (len1 != len0) { | ||
error(_("Argument %d is of length %"PRId64" but argument 1 is of length %"PRId64". " | ||
"If you wish to 'recycle' your argument, please use rep() to make this intent " | ||
"clear to the readers of your code."), i+1, len1, len0); | ||
} | ||
if (type > anstype) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should use typeorder for this, the ordering of the SEXPTYPEs doesn't necessarily reflect the coercion order. I guess it's fine for just int/double but later we may add other types |
||
anstype = type; | ||
} | ||
} | ||
int nprotect = 0; | ||
SEXP ans = PROTECT(duplicate(SEXPPTR_RO(args)[0])); nprotect++; | ||
if (anstype != TYPEOF(ans)) { | ||
ans = coerceVector(ans, anstype); | ||
} | ||
const bool narm = LOGICAL(na)[0]; | ||
switch(anstype) { | ||
case INTSXP: { | ||
int *restrict pans =INTEGER(ans); | ||
SEXP int_a = R_NilValue; | ||
PROTECT_INDEX Iaint; | ||
PROTECT_WITH_INDEX(int_a, &Iaint); nprotect++; | ||
for (int i = 1; i < n; ++i) { | ||
REPROTECT(int_a = duplicate(SEXPPTR_RO(args)[i]), Iaint); | ||
int *pa = INTEGER(int_a); | ||
if (narm) { | ||
for (int64_t j = 0; j < len0; ++j) { | ||
pans[j] = pans[j] == NA_INTEGER ? pa[j] : (pa[j]==NA_INTEGER ? pans[j] : (pans[j] + pa[j])); | ||
} | ||
} else { | ||
for (int64_t j = 0; j < len0; ++j) { | ||
pans[j] = (pans[j] == NA_INTEGER || pa[j] == NA_INTEGER) ? NA_INTEGER : (pans[j] + pa[j]); | ||
} | ||
} | ||
} | ||
} break; | ||
case REALSXP: { | ||
double *restrict pans = REAL(ans); | ||
SEXP dbl_a = R_NilValue; | ||
PROTECT_INDEX Iadbl; | ||
PROTECT_WITH_INDEX(dbl_a, &Iadbl); nprotect++; | ||
for (int i = 1; i < n; ++i) { | ||
if (TYPEOF(SEXPPTR_RO(args)[i]) != anstype) { | ||
REPROTECT(dbl_a = coerceVector(duplicate(SEXPPTR_RO(args)[i]), anstype), Iadbl); | ||
} else { | ||
REPROTECT(dbl_a = duplicate(SEXPPTR_RO(args)[i]), Iadbl); | ||
} | ||
double *pa = REAL(dbl_a); | ||
if (narm) { | ||
for (int64_t j = 0; j < len0; ++j) { | ||
pans[j] = ISNAN(pans[j]) ? pa[j] : (ISNAN(pa[j]) ? pans[j] : (pans[j] + pa[j])); | ||
} | ||
} else { | ||
for (int64_t j = 0; j < len0; ++j) { | ||
pans[j] = (ISNAN(pans[j]) || ISNAN(pa[j])) ? NA_REAL : (pans[j] + pa[j]); | ||
} | ||
} | ||
} | ||
} break; | ||
} | ||
UNPROTECT(nprotect); | ||
return ans; | ||
} | ||
|
||
SEXP pprodR(SEXP na, SEXP args) { | ||
if (!IS_TRUE_OR_FALSE(na)) { | ||
error(_("Argument 'na.rm' must be TRUE or FALSE and length 1.")); | ||
} | ||
const int n=length(args); | ||
if (n <= 1) { | ||
error(_("Please supply at least 2 arguments. %d argument supplied."), n); | ||
} | ||
SEXPTYPE anstype = TYPEOF(SEXPPTR_RO(args)[0]); | ||
const int64_t len0 = xlength(SEXPPTR_RO(args)[0]); | ||
if (anstype != INTSXP && anstype != REALSXP) { | ||
error(_("Argument 1 is of type %s. Only integer and double types are supported."), type2char(anstype)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for translation, better to use %d here as well, then only one message to translate instead of 2 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry Michael, I did not understand what you mean. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. each unique item wrapped in _() will generate a message to be translated one with Argument 1, another with Argument %d -- twice as much work for our translation team vs using Argument %d for both, and hard-coding 1 to fill that in the former case; looks same to user, very similar in code, and easier for translators |
||
} | ||
for (int i = 1; i < n; ++i) { | ||
SEXPTYPE type = TYPEOF(SEXPPTR_RO(args)[i]); | ||
int64_t len1 = xlength(SEXPPTR_RO(args)[i]); | ||
if (type != INTSXP && type != REALSXP) { | ||
error(_("Argument %d is of type %s. Only integer and double types are supported."), | ||
i+1, type2char(type)); | ||
} | ||
if (len1 != len0) { | ||
error(_("Argument %d is of length %"PRId64" but argument 1 is of length %"PRId64". " | ||
"If you wish to 'recycle' your argument, please use rep() to make this intent " | ||
"clear to the readers of your code."), i+1, len1, len0); | ||
} | ||
if (type > anstype) { | ||
anstype = type; | ||
} | ||
} | ||
int nprotect = 0; | ||
SEXP ans = PROTECT(duplicate(SEXPPTR_RO(args)[0])); nprotect++; | ||
if (anstype != TYPEOF(ans)) { | ||
ans = coerceVector(ans, anstype); | ||
} | ||
const bool narm = LOGICAL(na)[0]; | ||
switch(anstype) { | ||
case INTSXP: { | ||
int *restrict pans =INTEGER(ans); | ||
SEXP int_a = R_NilValue; | ||
PROTECT_INDEX Iaint; | ||
PROTECT_WITH_INDEX(int_a, &Iaint); nprotect++; | ||
for (int i = 1; i < n; ++i) { | ||
REPROTECT(int_a = duplicate(SEXPPTR_RO(args)[i]), Iaint); | ||
int *pa = INTEGER(int_a); | ||
if (narm) { | ||
for (int64_t j = 0; j < len0; ++j) { | ||
pans[j] = pans[j] == NA_INTEGER ? pa[j] : (pa[j]==NA_INTEGER ? pans[j] : (pans[j] * pa[j])); | ||
} | ||
} else { | ||
for (int64_t j = 0; j < len0; ++j) { | ||
pans[j] = (pans[j] == NA_INTEGER || pa[j] == NA_INTEGER) ? NA_INTEGER : (pans[j] * pa[j]); | ||
} | ||
} | ||
} | ||
} break; | ||
case REALSXP: { | ||
double *restrict pans = REAL(ans); | ||
SEXP dbl_a = R_NilValue; | ||
PROTECT_INDEX Iadbl; | ||
PROTECT_WITH_INDEX(dbl_a, &Iadbl); nprotect++; | ||
for (int i = 1; i < n; ++i) { | ||
if (TYPEOF(SEXPPTR_RO(args)[i]) != anstype) { | ||
REPROTECT(dbl_a = coerceVector(duplicate(SEXPPTR_RO(args)[i]), anstype), Iadbl); | ||
} else { | ||
REPROTECT(dbl_a = duplicate(SEXPPTR_RO(args)[i]), Iadbl); | ||
} | ||
double *pa = REAL(dbl_a); | ||
if (narm) { | ||
for (int64_t j = 0; j < len0; ++j) { | ||
pans[j] = ISNAN(pans[j]) ? pa[j] : (ISNAN(pa[j]) ? pans[j] : (pans[j] * pa[j])); | ||
} | ||
} else { | ||
for (int64_t j = 0; j < len0; ++j) { | ||
pans[j] = (ISNAN(pans[j]) || ISNAN(pa[j])) ? NA_REAL : (pans[j] * pa[j]); | ||
} | ||
} | ||
} | ||
} break; | ||
} | ||
UNPROTECT(nprotect); | ||
return ans; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why error, instead of just returning x itself?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find it odd to provide 1 argument. A sum operation needs at least 2 arguments. Does it make sense to you to only provide 1 argument?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, it's odd, but edge case like this can come up in dynamic scripting, where e.g. user is running
and
DT
has a variable number of numeric columns depending on some other conditions in the script.This way, the user can use the same code to cover the normal case when there are several normal columns, as well as the case when there only ends up being one such column.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok will change that later. Tks