package water.rapids.ast.prims.mungers; import water.fvec.Frame; import water.fvec.Vec; import water.rapids.Env; import water.rapids.Val; import water.rapids.ast.AstParameter; import water.rapids.ast.AstRoot; import water.rapids.vals.ValFrame; import water.rapids.vals.ValRow; import water.rapids.ast.AstPrimitive; import java.util.Arrays; /** * Column slice; allows R-like syntax. * Numbers past the largest column are an error. * Negative numbers and number lists are allowed, and represent an *exclusion* list */ public class AstColSlice extends AstPrimitive { @Override public String[] args() { return new String[]{"ary", "cols"}; } @Override public int nargs() { return 1 + 2; } // (cols src [col_list]) @Override public String str() { return "cols"; } @Override public Val apply(Env env, Env.StackHelp stk, AstRoot asts[]) { Val v = stk.track(asts[1].exec(env)); AstParameter col_list = (AstParameter) asts[2]; if (v instanceof ValRow) { ValRow vv = (ValRow) v; return vv.slice(col_list.columns(vv.getNames())); } Frame src = v.getFrame(); int[] cols = col_select(src.names(), col_list); Frame dst = new Frame(); Vec[] vecs = src.vecs(); for (int col : cols) dst.add(src._names[col], vecs[col]); return new ValFrame(dst); } // Complex column selector; by list of names or list of numbers or single // name or number. Numbers can be ranges or negative. public static int[] col_select(String[] names, AstParameter col_selector) { int[] cols = col_selector.columns(names); if (cols.length == 0) return cols; // Empty inclusion list? if (cols[0] >= 0) { // Positive (inclusion) list if (cols[cols.length - 1] >= names.length) throw new IllegalArgumentException("Column must be an integer from 0 to " + (names.length - 1)); return cols; } // Negative (exclusion) list; convert to positive inclusion list int[] pos = new int[names.length]; for (int col : cols) // more or less a radix sort, filtering down to cols to ignore if (0 <= -col - 1 && -col - 1 < names.length) pos[-col - 1] = -1; int j = 0; for (int i = 0; i < names.length; i++) if (pos[i] == 0) pos[j++] = i; return Arrays.copyOfRange(pos, 0, j); } }