/** * Copyright (C) 2001-2017 by RapidMiner and the contributors * * Complete list of developers available at our web site: * * http://rapidminer.com * * This program is free software: you can redistribute it and/or modify it under the terms of the * GNU Affero General Public License as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License along with this program. * If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.gui.new_plotter.configuration; import com.rapidminer.datatable.DataTable; import com.rapidminer.datatable.DataTableRow; import com.rapidminer.gui.new_plotter.ChartConfigurationException; import com.rapidminer.gui.new_plotter.listener.events.ValueGroupingChangeEvent; import com.rapidminer.gui.new_plotter.utility.NumericalValueRange; import com.rapidminer.gui.new_plotter.utility.ValueRange; import java.text.DateFormat; import java.util.LinkedList; import java.util.List; /** * Defines a binning with a fixed number of equal width bins between a min and max value. Can be * either categorical or numerical. If the binning is categorical, each created ValueRange delivers * an integer idx as identifier, otherwise the mean value of upper and lower bound. * * If this binning is categorical, overflow and underflow bins are created for values which are * greater/lesser than minValue/maxValue. * * @author Marius Helf, Nils Woehler * */ public class EquidistantFixedBinCountBinning extends AbstractValueGrouping { /** * Number of bins (excluding overflow bins) */ private int binCount; private double userDefinedMinValue; private double userDefinedMaxValue; private boolean autoRange = true; private final GroupingType type = GroupingType.EQUIDISTANT_FIXED_BIN_COUNT; /** * @param binCount * @param minValue * The left value of the left-most normal (non-overflow) bin. If NaN, the left-most * point in the data is chosen. Infinity is not allowed. * @param maxValue * The right value of the right-most normal (non-overflow) bin. If NaN, the * right-most point in the data is chosen. Infinity is not allowed. * @param dataTableColumn * @param categorical * @throws ChartConfigurationException * if dataTableColumn is nominal */ public EquidistantFixedBinCountBinning(int binCount, double minValue, double maxValue, DataTableColumn dataTableColumn, boolean categorical, DateFormat dateFormat) throws ChartConfigurationException { super(dataTableColumn, categorical, dateFormat); if (dataTableColumn.isNominal()) { throw new ChartConfigurationException("grouping.illegal_column_type", getGroupingType().getName(), dataTableColumn.getName(), dataTableColumn.getValueType(), "numerical or date."); } this.binCount = binCount; this.userDefinedMinValue = minValue; this.userDefinedMaxValue = maxValue; } /** * Copy ctor. */ public EquidistantFixedBinCountBinning(EquidistantFixedBinCountBinning other) { super(other.getDataTableColumn(), other.isCategorical(), other.getDateFormat()); this.binCount = other.binCount; this.userDefinedMinValue = other.userDefinedMinValue; this.userDefinedMaxValue = other.userDefinedMaxValue; this.autoRange = other.autoRange; this.forceDataTableColumn(other.getDataTableColumn()); } /** * @return the binCount */ public int getBinCount() { return binCount; } /** * @param binCount * the binCount to set */ public void setBinCount(int binCount) { if (binCount != this.binCount) { this.binCount = binCount; // invalidateCache(); fireGroupingChanged(new ValueGroupingChangeEvent(this, binCount)); } } /** * @return the minValue */ public double getMinValue() { return userDefinedMinValue; } /** * @param minValue * the minValue to set */ public void setMinValue(double minValue) { if (minValue != this.userDefinedMinValue) { this.userDefinedMinValue = minValue; fireGroupingChanged(new ValueGroupingChangeEvent(this)); } } /** * @return the maxValue */ public double getMaxValue() { return userDefinedMaxValue; } /** * @param maxValue * the maxValue to set */ public void setMaxValue(double maxValue) { if (maxValue != this.userDefinedMaxValue) { this.userDefinedMaxValue = maxValue; fireGroupingChanged(new ValueGroupingChangeEvent(this)); } } @Override protected List<ValueRange> createGroupingModel(DataTable data, double userDefinedUpperDimensionBound, double userDefinedLowerDimensionBound) { if (getDataTableColumn() == null) { return null; } List<ValueRange> groupingModel = new LinkedList<ValueRange>(); double currentMinValue = userDefinedMinValue; double currentMaxValue = userDefinedMaxValue; if (calculatesAutoRange()) { // determine min and max values from data double newMinValue = Double.POSITIVE_INFINITY; double newMaxValue = Double.NEGATIVE_INFINITY; int columnIdx = DataTableColumn.getColumnIndex(data, getDataTableColumn()); for (DataTableRow row : data) { double value = row.getValue(columnIdx); if (value >= userDefinedLowerDimensionBound && value <= userDefinedUpperDimensionBound) { if (newMinValue > value) { newMinValue = value; } if (newMaxValue < value) { newMaxValue = value; } } } if (!Double.isInfinite(userDefinedLowerDimensionBound) && newMinValue > userDefinedLowerDimensionBound) { newMinValue = userDefinedLowerDimensionBound; } if (!Double.isInfinite(userDefinedUpperDimensionBound) && newMaxValue < userDefinedUpperDimensionBound) { newMaxValue = userDefinedUpperDimensionBound; } currentMinValue = newMinValue; currentMaxValue = newMaxValue; } int columnIdx = DataTableColumn.getColumnIndex(data, getDataTableColumn()); boolean columnIsDate = getDataTableColumn().isDate(); groupingModel = new LinkedList<ValueRange>(); if (isCategorical() && (!isAutoRanging() || Double.isNaN(currentMinValue))) { // create underflow bin ValueRange group = new NumericalValueRange(Double.NEGATIVE_INFINITY, currentMinValue, columnIdx, null, true, false); groupingModel.add(group); } boolean includeLowerBound = true; boolean includeUpperBound = false; // create normal bins double stepWidth = (currentMaxValue - currentMinValue) / binCount; for (int i = 0; i < binCount; ++i) { userDefinedLowerDimensionBound = currentMinValue + (currentMaxValue - currentMinValue) / binCount * i; userDefinedUpperDimensionBound = userDefinedLowerDimensionBound + stepWidth; if (i == binCount - 1) { // last bin should include largest value includeUpperBound = true; groupingModel.add(new NumericalValueRange(userDefinedLowerDimensionBound, currentMaxValue, columnIdx, null, includeLowerBound, includeUpperBound)); continue; } groupingModel.add(new NumericalValueRange(userDefinedLowerDimensionBound, userDefinedUpperDimensionBound, columnIdx, null, includeLowerBound, includeUpperBound)); } if (isCategorical() && (!isAutoRanging() || Double.isNaN(currentMaxValue))) { // create overflow bin ValueRange group = new NumericalValueRange(currentMaxValue, Double.POSITIVE_INFINITY, columnIdx, null, false, true); groupingModel.add(group); } applyAdaptiveVisualRounding(groupingModel, columnIsDate); return groupingModel; } @Override public GroupingType getGroupingType() { return type; } @Override public ValueGrouping clone() { return new EquidistantFixedBinCountBinning(this); } // @Override // protected void invalidateCache() { // super.invalidateCache(); // } private boolean calculatesAutoRange() { return Double.isNaN(userDefinedMinValue) || Double.isNaN(userDefinedMaxValue) || autoRange; } /** * @return the autoRange */ public boolean isAutoRanging() { return autoRange; } /** * @param autoRange * the autoRange to set */ public void setAutoRange(boolean autoRange) { if (autoRange != this.autoRange) { this.autoRange = autoRange; fireGroupingChanged(new ValueGroupingChangeEvent(this)); } } @Override public boolean equals(Object obj) { if (obj == null || !(obj instanceof EquidistantFixedBinCountBinning)) { return false; } EquidistantFixedBinCountBinning other = (EquidistantFixedBinCountBinning) obj; if (other.isCategorical() != isCategorical()) { return false; } if (other.getBinCount() != getBinCount()) { return false; } if (other.isAutoRanging() != isAutoRanging()) { return false; } if (Double.isNaN(other.getMaxValue())) { if (!Double.isNaN(getMaxValue())) { return false; } } else if (other.getMaxValue() != getMaxValue()) { return false; } if (Double.isNaN(other.getMinValue())) { if (!Double.isNaN(getMinValue())) { return false; } } else if (other.getMinValue() != getMinValue()) { return false; } return true; } @Override public boolean definesUpperLowerBounds() { return true; } }