/* * Encog(tm) Core v3.4 - Java Version * http://www.heatonresearch.com/encog/ * https://github.com/encog/encog-java-core * Copyright 2008-2016 Heaton Research, Inc. * * 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. * * For more information on Heaton Research copyrights, licenses * and trademarks visit: * http://www.heatonresearch.com/copyright */ package org.encog.ml.data.versatile; import java.util.Iterator; import org.encog.EncogError; import org.encog.ml.data.MLData; import org.encog.ml.data.MLDataPair; import org.encog.ml.data.MLDataSet; import org.encog.ml.data.basic.BasicMLData; import org.encog.ml.data.basic.BasicMLDataPair; import org.encog.util.EngineArray; /** * The MatrixMLDataSet can use a large 2D matrix of doubles to internally hold * data. It supports several advanced features such as the ability to mask and * time-box. Masking allows several datasets to use the same backing array, * however use different parts. * * Time boxing allows time-series data to be represented for prediction. The * following shows how data is laid out for different lag and lead settings. * * Lag 0; Lead 0 [10 rows] 1→1 2→2 3→3 4→4 5→5 6→6 7→7 8→8 9→9 10→10 * * Lag 0; Lead 1 [9 rows] 1→2 2→3 3→4 4→5 5→6 6→7 7→8 8→9 9→10 * * Lag 1; Lead 0 [9 rows, not useful] 1,2→1 2,3→2 3,4→3 4,5→4 5,6→5 6,7→6 * 7,8→7 8,9→8 9,10→9 * * Lag 1; Lead 1 [8 rows] 1,2→3 2,3→4 3,4→5 4,5→6 5,6→7 6,7→8 7,8→9 * 8,9→10 * * Lag 1; Lead 2 [7 rows] 1,2→3,4 2,3→4,5 3,4→5,6 4,5→6,7 5,6→7,8 6,7→8,9 * 7,8→9,10 * * Lag 2; Lead 1 [7 rows] 1,2,3→4 2,3,4→5 3,4,5→6 4,5,6→7 5,6,7→8 6,7,8→9 * 7,8,9→10 */ public class MatrixMLDataSet implements MLDataSet { /** * An iterator to be used with the MatrixMLDataSet. This iterator does not * support removes. * * @author jheaton */ public class MatrixMLDataSetIterator implements Iterator<MLDataPair> { /** * The index that the iterator is currently at. */ private int currentIndex = 0; /** * {@inheritDoc} */ @Override public final boolean hasNext() { return this.currentIndex < MatrixMLDataSet.this.size(); } /** * {@inheritDoc} */ @Override public final MLDataPair next() { if (!hasNext()) { return null; } return MatrixMLDataSet.this.get(this.currentIndex++); } /** * {@inheritDoc} */ @Override public final void remove() { throw new EncogError("Called remove, unsupported operation."); } } /** * The number of inputs. */ private int calculatedInputSize = -1; /** * The number of ideal values. */ private int calculatedIdealSize = -1; /** * The backing data. */ private double[][] data; /** * The mask to the data. */ private int[] mask; /** * The lag window size. */ private int lagWindowSize = 0; /** * The lead window size. */ private int leadWindowSize = 0; /** * The default constructor. */ public MatrixMLDataSet() { } /** * Construct the dataset with no mask. * @param theData The backing array. * @param theCalculatedInputSize The input size. * @param theCalculatedIdealSize The ideal size. */ public MatrixMLDataSet(double[][] theData, int theCalculatedInputSize, int theCalculatedIdealSize) { this.data = theData; this.calculatedInputSize = theCalculatedInputSize; this.calculatedIdealSize = theCalculatedIdealSize; } /** * Construct the dataset from a 2D double array.. * @param theData The data. * @param inputCount The input count. * @param idealCount The ideal count. * @param theMask The mask. */ public MatrixMLDataSet(double[][] theData, int inputCount, int idealCount, int[] theMask) { this.data = theData; this.calculatedInputSize = inputCount; this.calculatedIdealSize = idealCount; this.mask = theMask; } /** * Construct the dataset from another matrix dataset. * @param data The data. * @param mask The mask. */ public MatrixMLDataSet(MatrixMLDataSet data, int[] mask) { this.data = data.getData(); this.calculatedInputSize = data.getCalculatedInputSize(); this.calculatedIdealSize = data.getCalculatedIdealSize(); this.mask = mask; } /** * @return The mask. */ public int[] getMask() { return this.mask; } /** * {@inheritDoc} */ @Override public Iterator<MLDataPair> iterator() { return new MatrixMLDataSetIterator(); } /** * {@inheritDoc} */ @Override public int getIdealSize() { return this.calculatedIdealSize * Math.min(this.leadWindowSize, 1); } /** * {@inheritDoc} */ @Override public int getInputSize() { return this.calculatedInputSize * this.lagWindowSize; } /** * {@inheritDoc} */ @Override public boolean isSupervised() { return getIdealSize() == 0; } /** * {@inheritDoc} */ @Override public long getRecordCount() { if (this.data == null) { throw new EncogError( "You must normalize the dataset before using it."); } if (this.mask == null) { return this.data.length - (this.lagWindowSize + this.leadWindowSize); } return this.mask.length - (this.lagWindowSize + this.leadWindowSize); } private int calculateLagCount() { return (MatrixMLDataSet.this.lagWindowSize <= 0) ? 1 : (this.lagWindowSize + 1); } private int calculateLeadCount() { return (this.leadWindowSize <= 1) ? 1 : this.leadWindowSize; } /** * {@inheritDoc} */ @Override public void getRecord(long index, MLDataPair pair) { if (this.data == null) { throw new EncogError( "You must normalize the dataset before using it."); } // Copy the input, account for time windows. int inputSize = calculateLagCount(); for (int i = 0; i < inputSize; i++) { double[] dataRow = lookupDataRow((int) (index + i)); EngineArray.arrayCopy(dataRow, 0, pair.getInput().getData(), i * MatrixMLDataSet.this.calculatedInputSize, MatrixMLDataSet.this.calculatedInputSize); } // Copy the output, account for time windows. int outputStart = (this.leadWindowSize > 0) ? 1 : 0; int outputSize = calculateLeadCount(); for (int i = 0; i < outputSize; i++) { double[] dataRow = lookupDataRow((int) (index + i + outputStart)); EngineArray.arrayCopy(dataRow, this.calculatedInputSize, pair.getIdealArray(), i * MatrixMLDataSet.this.calculatedIdealSize, MatrixMLDataSet.this.calculatedIdealSize); } } /** * Find a row, using the mask. * @param index The index we seek. * @return The row. */ private double[] lookupDataRow(int index) { if (this.mask != null) { return this.data[this.mask[index]]; } else { return this.data[index]; } } /** * {@inheritDoc} */ @Override public MLDataSet openAdditional() { MatrixMLDataSet result = new MatrixMLDataSet(this.data, this.calculatedInputSize, this.calculatedIdealSize, this.mask); result.setLagWindowSize(getLagWindowSize()); result.setLeadWindowSize(getLeadWindowSize()); return result; } /** * {@inheritDoc} */ @Override public void add(MLData data1) { // TODO Auto-generated method stub } /** * {@inheritDoc} */ @Override public void add(MLData inputData, MLData idealData) { // TODO Auto-generated method stub } /** * {@inheritDoc} */ @Override public void add(MLDataPair inputData) { // TODO Auto-generated method stub } /** * {@inheritDoc} */ @Override public void close() { // TODO Auto-generated method stub } /** * {@inheritDoc} */ @Override public int size() { return (int) getRecordCount(); } /** * {@inheritDoc} */ @Override public MLDataPair get(int index) { if (index > size()) { return null; } BasicMLData input = new BasicMLData( MatrixMLDataSet.this.calculatedInputSize * calculateLagCount()); BasicMLData ideal = new BasicMLData( MatrixMLDataSet.this.calculatedIdealSize * calculateLeadCount()); MLDataPair pair = new BasicMLDataPair(input, ideal); MatrixMLDataSet.this.getRecord(index, pair); return pair; } /** * @return the calculatedInputSize */ public int getCalculatedInputSize() { return calculatedInputSize; } /** * @param calculatedInputSize * the calculatedInputSize to set */ public void setCalculatedInputSize(int calculatedInputSize) { this.calculatedInputSize = calculatedInputSize; } /** * @return the calculatedIdealSize */ public int getCalculatedIdealSize() { return calculatedIdealSize; } /** * @param calculatedIdealSize * the calculatedIdealSize to set */ public void setCalculatedIdealSize(int calculatedIdealSize) { this.calculatedIdealSize = calculatedIdealSize; } /** * @return the data */ public double[][] getData() { return data; } /** * @param data * the data to set */ public void setData(double[][] data) { this.data = data; } /** * @return the lagWindowSize */ public int getLagWindowSize() { return lagWindowSize; } /** * @param lagWindowSize * the lagWindowSize to set */ public void setLagWindowSize(int lagWindowSize) { this.lagWindowSize = lagWindowSize; } /** * @return the leadWindowSize */ public int getLeadWindowSize() { return leadWindowSize; } /** * @param leadWindowSize * the leadWindowSize to set */ public void setLeadWindowSize(int leadWindowSize) { this.leadWindowSize = leadWindowSize; } }