forked from tidyverse/dplyr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcombine_variables.cpp
148 lines (125 loc) · 3.39 KB
/
combine_variables.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#include <Rcpp.h>
using namespace Rcpp;
int vector_sign(IntegerVector x) {
bool pos = false, neg = false;
int n = x.size();
for (int i = 0; i < n; ++i) {
if (x[i] < 0) neg = true;
if (x[i] > 0) pos = true;
if (neg && pos) break;
}
if (neg == pos) {
// Either mixed, or all zeros
return 0;
} else if (neg) {
return -1;
} else {
return 1;
}
}
class VarList {
std::vector<int> out_indx;
std::vector<String> out_name;
int find(int i) {
std::vector<int>::iterator pos = std::find(out_indx.begin(), out_indx.end(), i);
if (pos == out_indx.end()) {
return -1;
} else {
return pos - out_indx.begin();
}
}
public:
VarList(int n) : out_indx(), out_name() {
out_indx.reserve(n);
out_name.reserve(n);
}
bool has(int i) {
return find(i) != -1;
}
void add(int i, String name) {
out_indx.push_back(i);
out_name.push_back(name);
}
void remove(int i) {
int pos = find(i);
if (pos == -1) return;
out_indx.erase(out_indx.begin() + pos);
out_name.erase(out_name.begin() + pos);
}
void update(int i, String name) {
int pos = find(i);
if (pos == -1) {
add(i, name);
} else {
out_name[pos] = name;
}
}
operator SEXP() {
IntegerVector out(out_indx.begin(), out_indx.end());
CharacterVector out_names(out_name.begin(), out_name.end());
out.names() = out_names;
return out;
}
};
// [[Rcpp::export]]
SEXP combine_vars(CharacterVector vars, ListOf<IntegerVector> xs) {
VarList selected(vars.size());
if (xs.size() == 0)
return IntegerVector::create();
// Workaround bug in ListOf<>; can't access attributes
SEXP raw_names = Rf_getAttrib(xs, Rf_mkString("names"));
CharacterVector xs_names;
if (raw_names == R_NilValue) {
xs_names = CharacterVector(xs.size(), "" );
} else {
xs_names = raw_names ;
}
// If first component is negative, pre-fill with existing vars
if (vector_sign(xs[0]) == -1) {
for (int j = 0; j < vars.size(); ++j) {
selected.add(j + 1, vars[j]);
}
}
for (int i = 0; i < xs.size(); ++i) {
IntegerVector x = xs[i];
if (x.size() == 0) continue;
int sign = vector_sign(x);
if (sign == 0)
stop("Each argument must yield either positive or negative integers");
if (sign == 1) {
bool group_named = xs_names[i] != "";
bool has_names = x.attr("names") != R_NilValue;
if (group_named) {
if (x.size() == 1) {
selected.update(x[0], xs_names[i]);
} else {
// If the group is named, children are numbered sequentially
for (int j = 0; j < x.size(); ++j) {
std::stringstream out;
out << xs_names[i] << j + 1;
selected.update(x[j], out.str());
}
}
} else if (has_names) {
CharacterVector names = x.names() ;
for (int j = 0; j < x.size(); ++j) {
selected.update(x[j], names[j]);
}
} else {
for (int j = 0; j < x.size(); ++j) {
int pos = x[j];
if (pos < 1 || pos > vars.size())
stop("Position must be between 0 and n");
// Add default name, if not all ready present
if (!selected.has(pos))
selected.update(pos, vars[pos - 1]);
}
}
} else {
for (int j = 0; j < x.size(); ++j) {
selected.remove(-x[j]);
}
}
}
return selected;
}