Skip to content

Commit dd19166

Browse files
committed
Merge branch 'master' into fwrite_forceDecimal
2 parents eb17a34 + 35b6299 commit dd19166

File tree

7 files changed

+75
-52
lines changed

7 files changed

+75
-52
lines changed

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@
105105
106106
17. `t1 - t2`, where one is an `IDate` and the other is a `Date`, are now consistent with the case where both are `IDate` or both are `Date`, [#4749](https://github.com/Rdatatable/data.table/issues/4749). Thanks @George9000 for the report and @MichaelChirico for the fix.
107107
108+
18. `fwrite` now allows `dec` to be the same as `sep` for edge cases where only one will be written, e.g. 0-row or 1-column tables. [#7227](https://github.com/Rdatatable/data.table/issues/7227). Thanks @MichaelChirico for the report and @venom1204 for the fix.
109+
108110
### NOTES
109111
110112
1. The following in-progress deprecations have proceeded:

R/fwrite.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ fwrite = function(x, file="", append=FALSE, quote="auto",
4545
is.character(sep) && length(sep)==1L && (nchar(sep) == 1L || identical(sep, "")),
4646
is.character(sep2) && length(sep2)==3L && nchar(sep2[2L])==1L,
4747
is.character(dec) && length(dec)==1L && nchar(dec) == 1L,
48-
dec != sep, # sep2!=dec and sep2!=sep checked at C level when we know if list columns are present
48+
`dec and sep must be distinct whenever both might be needed` = (!NROW(x) || NCOL(x) <= 1L || dec != sep), # sep2!=dec and sep2!=sep checked at C level when we know if list columns are present
4949
is.character(eol) && length(eol)==1L,
5050
length(qmethod) == 1L && qmethod %chin% c("double", "escape"),
5151
length(compress) == 1L && compress %chin% c("auto", "none", "gzip"),

inst/tests/tests.Rraw

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10977,9 +10977,9 @@ test(1732.7, fwrite(DT, quote='auto'), output='A,B\n,5\nNA,7\n"",0\nmonty,')
1097710977
test(1732.8, fwrite(DT, quote='auto', na="NA"), output='A,B\nNA,5\n"NA",7\n"",0\n"monty",NA')
1097810978

1097910979
# dec=","
10980-
test(1733.1, fwrite(data.table(pi),dec=","), error=base_messages$stopifnot("dec != sep"))
10980+
# Test 1733.1 removed, see #7227
1098110981
test(1733.2, fwrite(data.table(c(1.2,-8.0,pi,67.99),1:4),dec=",",sep=";"),
10982-
output="V1;V2\n1,2;1\n-8;2\n3,14159265358979;3\n67,99;4")
10982+
output="V1;V2\n1,2;1\n-8;2\n3,14159265358979;3\n67,99;4")
1098310983

1098410984
# fwrite implied and actual row.names
1098510985
DT = data.table(foo=1:3,bar=c(1.2,9.8,-6.0))
@@ -21594,6 +21594,15 @@ test(2336.4, all.equal(as.Date(t2) - t1, t2 - t1))
2159421594
test(2336.5, all.equal(t1 - as.Date(t2), t1 - t2))
2159521595
test(2336.6, all.equal(t2 - as.Date(t1), t2 - t1))
2159621596

21597+
# fwrite: allow dec=',' with single column, #7227
21598+
test(2337.1, fwrite(data.table(1), dec=","), NULL)
21599+
if (getRversion() >= "4.0.0") { # rely on stopifnot(named = ...) for correct message
21600+
test(2337.2, fwrite(data.table(0.1, 0.2), dec=",", sep=","), error = "dec and sep must be distinct")
21601+
}
21602+
test(2337.3, is.null(fwrite(data.table(c(0.1, 0.2)), dec=",", sep="\t")))
21603+
test(2337.4, is.null(fwrite(data.table(a=numeric(), b=numeric()), dec=",", sep=",")))
21604+
test(2337.5, is.null(fwrite(data.table(a=numeric()), dec=",", sep=",")))
21605+
2159721606
# 2864 force decimal points for whole numbers in numeric columns
2159821607
dd = data.table(x=c(1, 2, 3))
2159921608
di = data.table(x=c(1L, 2L, 3L))
@@ -21603,9 +21612,11 @@ test(2337.3, capture.output(fwrite(dd, forceDecimal=FALSE)), c("x", "1", "2", "3
2160321612
test(2337.4, capture.output(fwrite(di, forceDecimal=TRUE)), c("x", "1", "2", "3"))
2160421613
test(2337.5, capture.output(fwrite(data.table(x=c(0.)), forceDecimal=TRUE)), c("x", "0."))
2160521614
test(2337.6, capture.output(fwrite(data.table(x=c(-0.)), forceDecimal=TRUE)), c("x", "0."))
21615+
test(2337.7, capture.output(fwrite(data.table(x=c(0.00)), forceDecimal=TRUE)), c("x", "0."))
2160621616
# round trip
2160721617
local({
2160821618
f <- tempfile(); on.exit(unlink(f))
21609-
test(2337.7, {fwrite(dd, f, forceDecimal=TRUE); fread(f)}, dd)
21610-
test(2337.8, {fwrite(dd, f, forceDecimal=FALSE); fread(f)}, di)
21619+
test(2337.8, {fwrite(dd, f, forceDecimal=TRUE); fread(f)}, dd)
21620+
test(2337.9, {fwrite(dd, f, forceDecimal=FALSE); fread(f)}, di)
2161121621
})
21622+

src/fmelt.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,14 @@ static const char *concat(SEXP vec, SEXP idx) {
103103
// with missing inputs, and -1 in the positions with column names not
104104
// found. Column names not found will eventually cause error via
105105
// uniq_diff().
106-
SEXP chmatch_na(SEXP x, SEXP table){
107-
SEXP ans;
108-
PROTECT(ans = chmatch(x, table, -1));
109-
for(int i=0; i<length(ans); i++){
110-
if(STRING_ELT(x, i) == NA_STRING){
111-
INTEGER(ans)[i] = NA_INTEGER;
106+
SEXP chmatch_na(SEXP x, SEXP table)
107+
{
108+
SEXP ans = chmatch(x, table, -1);
109+
PROTECT(ans);
110+
int *restrict target = INTEGER(ans);
111+
for (int i = 0; i < length(ans); i++) {
112+
if (STRING_ELT(x, i) == NA_STRING) {
113+
target[i] = NA_INTEGER;
112114
}
113115
}
114116
UNPROTECT(1);

src/frank.c

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,26 @@
33
// #include <signal.h> // the debugging machinery + breakpoint aidee
44
// raise(SIGINT);
55

6-
SEXP dt_na(SEXP x, SEXP cols) {
7-
int n=0, elem;
8-
6+
SEXP dt_na(SEXP x, SEXP cols)
7+
{
98
if (!isNewList(x)) internal_error(__func__, "Argument '%s' to %s is type '%s' not '%s'", "x", "Cdt_na", type2char(TYPEOF(x)), "list"); // # nocov
109
if (!isInteger(cols)) internal_error(__func__, "Argument '%s' to %s is type '%s' not '%s'", "cols", "Cdt_na", type2char(TYPEOF(cols)), "integer"); // # nocov
11-
for (int i=0; i<LENGTH(cols); ++i) {
12-
elem = INTEGER(cols)[i];
13-
if (elem<1 || elem>LENGTH(x))
14-
error(_("Item %d of 'cols' is %d which is outside 1-based range [1,ncol(x)=%d]"), i+1, elem, LENGTH(x));
15-
if (!n) n = length(VECTOR_ELT(x, elem-1));
10+
11+
int n = 0;
12+
const int numCols = LENGTH(cols);
13+
const int* col_ints = INTEGER_RO(cols);
14+
for (int i = 0; i < numCols; i++) {
15+
const int elem = col_ints[i];
16+
if (elem < 1 || elem > LENGTH(x))
17+
error(_("Item %d of 'cols' is %d which is outside 1-based range [1,ncol(x)=%d]"), i + 1, elem, LENGTH(x));
18+
if (!n) n = length(VECTOR_ELT(x, elem - 1));
1619
}
1720
SEXP ans = PROTECT(allocVector(LGLSXP, n));
1821
int *ians = LOGICAL(ans);
19-
for (int i=0; i<n; ++i) ians[i]=0;
20-
for (int i=0; i<LENGTH(cols); ++i) {
21-
SEXP v = VECTOR_ELT(x, INTEGER(cols)[i]-1);
22+
memset(ians, 0, n * sizeof(int));
23+
24+
for (int i = 0; i < numCols; i++) {
25+
SEXP v = VECTOR_ELT(x, col_ints[i]-1);
2226
if (!length(v) || isList(v)) continue; // like stats:::na.omit.data.frame, skip pairlist columns
2327
if (n != length(v))
2428
error(_("Column %d of input list x is length %d, inconsistent with first column of that item which is length %d."), i+1,length(v),n);

src/idatetime.c

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#include "data.table.h"
22

3-
#define YEARS400 146097
4-
#define YEARS100 36524
5-
#define YEARS4 1461
6-
#define YEARS1 365
3+
static const int YEARS400 = 146097;
4+
static const int YEARS100 = 36524;
5+
static const int YEARS4 = 1461;
6+
static const int YEARS1 = 365;
77

88
typedef enum { YDAY, WDAY, MDAY, WEEK, MONTH, QUARTER, YEAR, YEARMON, YEARQTR} datetype;
99

@@ -124,37 +124,40 @@ void convertSingleDate(int x, datetype type, void *out)
124124
SEXP convertDate(SEXP x, SEXP type)
125125
{
126126
if (!isInteger(x)) error(_("x must be an integer vector"));
127-
const int *ix = INTEGER(x);
127+
const int *ix = INTEGER_RO(x);
128128
const int n = length(x);
129129
if (!isString(type) || length(type) != 1)
130130
internal_error(__func__, "invalid type for, should have been caught before"); // # nocov
131-
datetype ctype=0;
131+
datetype ctype = 0;
132132
bool ansint = true;
133-
if (!strcmp(CHAR(STRING_ELT(type, 0)), "yday")) ctype = YDAY;
134-
else if (!strcmp(CHAR(STRING_ELT(type, 0)), "wday")) ctype = WDAY;
135-
else if (!strcmp(CHAR(STRING_ELT(type, 0)), "mday")) ctype = MDAY;
136-
else if (!strcmp(CHAR(STRING_ELT(type, 0)), "week")) ctype = WEEK;
137-
else if (!strcmp(CHAR(STRING_ELT(type, 0)), "month")) ctype = MONTH;
138-
else if (!strcmp(CHAR(STRING_ELT(type, 0)), "quarter")) ctype = QUARTER;
139-
else if (!strcmp(CHAR(STRING_ELT(type, 0)), "year")) ctype = YEAR;
140-
else if (!strcmp(CHAR(STRING_ELT(type, 0)), "yearmon")) { ctype = YEARMON; ansint = false; }
141-
else if (!strcmp(CHAR(STRING_ELT(type, 0)), "yearqtr")) { ctype = YEARQTR; ansint = false; }
142-
else internal_error(__func__, "invalid type for, should have been caught before"); // # nocov
143-
144-
SEXP ans;
133+
134+
const char* ctype_str = CHAR(STRING_ELT(type, 0));
135+
if (!strcmp(ctype_str, "yday")) ctype = YDAY;
136+
else if (!strcmp(ctype_str, "wday")) ctype = WDAY;
137+
else if (!strcmp(ctype_str, "mday")) ctype = MDAY;
138+
else if (!strcmp(ctype_str, "week")) ctype = WEEK;
139+
else if (!strcmp(ctype_str, "month")) ctype = MONTH;
140+
else if (!strcmp(ctype_str, "quarter")) ctype = QUARTER;
141+
else if (!strcmp(ctype_str, "year")) ctype = YEAR;
142+
else if (!strcmp(ctype_str, "yearmon")) { ctype = YEARMON; ansint = false; }
143+
else if (!strcmp(ctype_str, "yearqtr")) { ctype = YEARQTR; ansint = false; }
144+
else internal_error(__func__, "invalid type, should have been caught before"); // # nocov
145+
145146
if (ansint) {
146-
ans = PROTECT(allocVector(INTSXP, n));
147+
SEXP ans = PROTECT(allocVector(INTSXP, n));
147148
int *ansp = INTEGER(ans);
148-
for (int i=0; i < n; ++i) {
149+
for (int i = 0; i < n; i++) {
149150
convertSingleDate(ix[i], ctype, &ansp[i]);
150151
}
152+
UNPROTECT(1);
153+
return ans;
151154
} else {
152-
ans = PROTECT(allocVector(REALSXP, n));
155+
SEXP ans = PROTECT(allocVector(REALSXP, n));
153156
double *ansp = REAL(ans);
154-
for (int i=0; i < n; ++i) {
157+
for (int i = 0; i < n; i++) {
155158
convertSingleDate(ix[i], ctype, &ansp[i]);
156159
}
160+
UNPROTECT(1);
161+
return ans;
157162
}
158-
UNPROTECT(1);
159-
return ans;
160163
}

src/nqrecreateindices.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
11
#include "data.table.h"
2+
#include <string.h>
23

34
// TODO: Add oxygen style comments and cleanup var names.
45
// See other TODOs inside the function.
56
SEXP nqRecreateIndices(SEXP xo, SEXP len, SEXP indices, SEXP nArg, SEXP nomatch)
67
{
7-
R_len_t n = INTEGER(nArg)[0], xn = length(xo);
8+
const R_len_t n = INTEGER_RO(nArg)[0], xn = length(xo);
89
SEXP ans, newstarts, newlen;
910
ans = PROTECT(allocVector(VECSXP, 2));
1011
SET_VECTOR_ELT(ans, 0, (newstarts = allocVector(INTSXP, n)));
1112
SET_VECTOR_ELT(ans, 1, (newlen = allocVector(INTSXP, n)));
1213

1314
int *inewlen = INTEGER(newlen);
14-
const int *iindices = INTEGER(indices);
15-
const int *ilen = INTEGER(len);
16-
const int *ixo = INTEGER(xo);
17-
const int inomatch = isNull(nomatch) ? 0 : INTEGER(nomatch)[0];
15+
const int *iindices = INTEGER_RO(indices);
16+
const int *ilen = INTEGER_RO(len);
17+
const int *ixo = INTEGER_RO(xo);
18+
const int inomatch = isNull(nomatch) ? 0 : INTEGER_RO(nomatch)[0];
1819
int *inewstarts = INTEGER(newstarts);
1920

20-
for (int i = 0; i < n; i++) inewlen[i] = 0;
21+
memset(inewlen, 0, n * sizeof(int));
2122

2223
// simplifying logic ... also fixes #2275
2324
for (int i = 0; i < length(indices); i++) {

0 commit comments

Comments
 (0)