/* * Apache License * Version 2.0, January 2004 * http://www.apache.org/licenses/ * * Copyright 2013 Aurelian Tutuianu * Copyright 2014 Aurelian Tutuianu * Copyright 2015 Aurelian Tutuianu * Copyright 2016 Aurelian Tutuianu * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package rapaio.data; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.BinaryOperator; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collector; /** * Builds a numeric variable. Numeric variables stores data as double values * and allows modelling of any type of continuous or discrete numeric variable. * <p> * The placeholder for missing value is Double.NaN. Any form of usage of Double.NaN * on set/add operation will result in a missing value. * <p> * User: Aurelian Tutuianu <padreati@yahoo.com> */ public final class Numeric extends AbstractVar { /** * @return new empty numeric variable of size 0 */ public static Numeric empty() { return new Numeric(0, 0, Double.NaN); } /** * Builds an empty numeric var wil all values set missing * * @param rows size of the variable * @return new instance of numeric var */ public static Numeric empty(int rows) { return new Numeric(rows, rows, Double.NaN); } /** * Builds a numeric variable with values copied from given collection * * @param values given values * @return new instance of numeric variable */ public static Numeric copy(Collection<? extends Number> values) { final Numeric numeric = new Numeric(0, 0, Double.NaN); values.forEach(n -> numeric.addValue(n.doubleValue())); return numeric; } /** * Builds a numeric variable with values copied from given array of integer values * * @param values given numeric values * @return new instance of numeric variable */ public static Numeric copy(int... values) { Numeric numeric = new Numeric(0, 0, 0); numeric.data = new double[values.length]; for (int i = 0; i < values.length; i++) { numeric.data[i] = values[i]; } numeric.rows = values.length; return numeric; } /** * Builds new instance of numeric var with values copied from given array of doubles * * @param values given numeric values * @return new instance of numeric variable */ public static Numeric copy(double... values) { Numeric numeric = new Numeric(values.length, values.length, 0); numeric.data = Arrays.copyOf(values, values.length); return numeric; } /** * Builds new numeric variable with values copied from another numeric variable * * @param source source numeric var * @return new instance of numeric variable */ public static Numeric copy(Var source) { Numeric numeric = new Numeric(source.rowCount(), source.rowCount(), 0).withName(source.name()); if (!(source instanceof Numeric)) { for (int i = 0; i < source.rowCount(); i++) { numeric.setValue(i, source.value(i)); } } else { numeric.data = Arrays.copyOf(((Numeric) source).data, source.rowCount()); } return numeric; } /** * Builds new numeric variable as a wrapper around an array of doubles * * @param values wrapped array of doubles * @return new instance of numeric variable */ public static Numeric wrap(double... values) { Numeric numeric = new Numeric(0, 0, 0); numeric.data = values; numeric.rows = values.length; return numeric; } /** * Builds new numeric variable filled with 0 * * @param rows size of the variable * @return new instance of numeric variable of given size and filled with 0 */ public static Numeric fill(int rows) { return new Numeric(rows, rows, 0); } /** * Builds new numeric variable filled with given fill value * * @param rows size of the variable * @param fill fill value used to set all the values * @return new instance of numeric variable of given size and filled with given value */ public static Numeric fill(int rows, double fill) { return new Numeric(rows, rows, fill); } /** * Builds a numeric variable of size 1 filled with given value * * @param value fill value * @return new instance of numeric variable of size 1 and filled with given fill value */ public static Numeric scalar(double value) { return new Numeric(1, 1, value); } public static Numeric seq(double end) { return seq(0, end); } public static Numeric seq(double start, double end) { return seq(start, end, 1.0); } public static Numeric seq(double start, double end, double step) { Numeric num = Numeric.empty(); int i = 0; while (start+i*step <= end) { num.addValue(start+i*step); i++; } return num; } public static Numeric from(int rows, Supplier<Double> supplier) { Numeric numeric = new Numeric(0, 0, 0); numeric.data = new double[rows]; numeric.rows = rows; for (int i = 0; i < rows; i++) { numeric.data[i] = supplier.get(); } return numeric; } private static final long serialVersionUID = -3167416341273129670L; private static final double missingValue = Double.NaN; private double[] data; private int rows; // public static builders private Numeric(int rows, int capacity, double fill) { if (rows < 0) { throw new IllegalArgumentException("Illegal row count: " + rows); } this.data = new double[capacity]; this.rows = rows; if (fill != 0) Arrays.fill(data, 0, rows, fill); } // stream collectors public static Numeric from(int rows, Function<Integer, Double> supplierFromRow) { Numeric numeric = new Numeric(0, 0, 0); numeric.data = new double[rows]; numeric.rows = rows; for (int i = 0; i < rows; i++) { numeric.data[i] = supplierFromRow.apply(i); } return numeric; } // private constructor public static Collector<Double, Numeric, Numeric> collector() { return new Collector<Double, Numeric, Numeric>() { @Override public Supplier<Numeric> supplier() { return Numeric::empty; } @Override public BiConsumer<Numeric, Double> accumulator() { return Numeric::addValue; } @Override public BinaryOperator<Numeric> combiner() { return (x, y) -> { y.stream().forEach(s -> x.addValue(s.value())); return x; }; } @Override public Function<Numeric, Numeric> finisher() { return Numeric::solidCopy; } @Override public Set<Characteristics> characteristics() { return new HashSet<>(); } }; } @Override public Numeric withName(String name) { return (Numeric) super.withName(name); } @Override public VarType type() { return VarType.NUMERIC; } private void ensureCapacity(int minCapacity) { minCapacity = Math.max(10, minCapacity); // overflow-conscious code if (minCapacity - data.length > 0) { // overflow-conscious code int oldCapacity = data.length; int newCapacity = oldCapacity > 0xFFFF ? oldCapacity << 1 : oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; data = Arrays.copyOf(data, newCapacity); } } @Override public int rowCount() { return rows; } @Override public void addRows(int rowCount) { ensureCapacity(this.rows + rowCount + 1); for (int i = 0; i < rowCount; i++) { data[rows + i] = Numeric.missingValue; } rows += rowCount; } @Override public double value(int row) { return data[row]; } @Override public void setValue(int row, double value) { data[row] = value; } @Override public void addValue(double value) { ensureCapacity(rows + 1); data[rows++] = value; } @Override public int index(int row) { return (int) Math.rint(value(row)); } @Override public void setIndex(int row, int value) { setValue(row, value); } @Override public void addIndex(int value) { ensureCapacity(rows + 1); data[rows++] = value; } @Override public String label(int row) { if (missing(row)) return "?"; return String.valueOf(value(row)); } @Override public void setLabel(int row, String value) { if ("?".equals(value)) { setMissing(row); return; } setValue(row, Double.parseDouble(value)); } @Override public void addLabel(String value) { if ("?".equals(value)) { addMissing(); return; } addValue(Double.parseDouble(value)); } @Override public String[] levels() { throw new RuntimeException("Operation not available for numeric vectors."); } @Override public void setLevels(String[] dict) { throw new RuntimeException("Operation not available for numeric vectors."); } @Override public boolean binary(int row) { return value(row) == 1.0; } @Override public void setBinary(int row, boolean value) { setValue(row, value ? 1 : 0); } @Override public void addBinary(boolean value) { addValue(value ? 1 : 0); } @Override public long stamp(int row) { return (long) Math.rint(value(row)); } @Override public void setStamp(int row, long value) { setValue(row, Double.valueOf(String.valueOf(value))); } @Override public void addStamp(long value) { addValue(Double.valueOf(String.valueOf(value))); } @Override public boolean missing(int row) { return value(row) != value(row); } @Override public void setMissing(int row) { setValue(row, missingValue); } @Override public void addMissing() { addValue(missingValue); } @Override public void remove(int index) { int numMoved = rows - index - 1; if (numMoved > 0) { System.arraycopy(data, index + 1, data, index, numMoved); rows--; } } @Override public void clear() { rows = 0; } @Override public Var newInstance(int rows) { return Numeric.empty(rows); } @Override public String toString() { return "Numeric[name:" + name() + ", rowCount:" + rowCount() + "]"; } @Override public Numeric solidCopy() { return (Numeric) super.solidCopy(); } private void writeObject(ObjectOutputStream out) throws IOException { out.writeInt(rowCount()); for (int i = 0; i < rowCount(); i++) { out.writeDouble(data[i]); } } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { rows = in.readInt(); data = new double[rows]; for (int i = 0; i < rows; i++) { data[i] = in.readDouble(); } } }