org.jfree.data.general.DatasetUtils Maven / Gradle / Ivy
Show all versions of jfreechart Show documentation
/* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* -----------------
* DatasetUtils.java
* -----------------
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Original Author: David Gilbert;
* Contributor(s): Andrzej Porebski (bug fix);
* Jonathan Nash (bug fix);
* Richard Atkinson;
* Andreas Schroeder;
* Rafal Skalny (patch 1925366);
* Jerome David (patch 2131001);
* Peter Kolb (patch 2791407);
* Martin Hoeller (patch 2952086);
*
*/
package org.jfree.data.general;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.jfree.chart.util.ArrayUtils;
import org.jfree.chart.util.Args;
import org.jfree.data.DomainInfo;
import org.jfree.data.DomainOrder;
import org.jfree.data.KeyToGroupMap;
import org.jfree.data.KeyedValues;
import org.jfree.data.Range;
import org.jfree.data.RangeInfo;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.CategoryRangeInfo;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.category.IntervalCategoryDataset;
import org.jfree.data.function.Function2D;
import org.jfree.data.statistics.BoxAndWhiskerCategoryDataset;
import org.jfree.data.statistics.BoxAndWhiskerXYDataset;
import org.jfree.data.statistics.MultiValueCategoryDataset;
import org.jfree.data.statistics.StatisticalCategoryDataset;
import org.jfree.data.xy.IntervalXYDataset;
import org.jfree.data.xy.OHLCDataset;
import org.jfree.data.xy.TableXYDataset;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYDomainInfo;
import org.jfree.data.xy.XYRangeInfo;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.data.xy.XYZDataset;
/**
* A collection of useful static methods relating to datasets.
*/
public final class DatasetUtils {
/**
* Private constructor for non-instanceability.
*/
private DatasetUtils() {
// now try to instantiate this ;-)
}
/**
* Calculates the total of all the values in a {@link PieDataset}. If
* the dataset contains negative or {@code null} values, they are
* ignored.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The total.
*/
public static double calculatePieDatasetTotal(PieDataset dataset) {
Args.nullNotPermitted(dataset, "dataset");
List keys = dataset.getKeys();
double totalValue = 0;
Iterator iterator = keys.iterator();
while (iterator.hasNext()) {
Comparable current = (Comparable) iterator.next();
if (current != null) {
Number value = dataset.getValue(current);
double v = 0.0;
if (value != null) {
v = value.doubleValue();
}
if (v > 0) {
totalValue = totalValue + v;
}
}
}
return totalValue;
}
/**
* Creates a pie dataset from a table dataset by taking all the values
* for a single row.
*
* @param dataset the dataset ({@code null} not permitted).
* @param rowKey the row key.
*
* @return A pie dataset.
*/
public static PieDataset createPieDatasetForRow(CategoryDataset dataset,
Comparable rowKey) {
int row = dataset.getRowIndex(rowKey);
return createPieDatasetForRow(dataset, row);
}
/**
* Creates a pie dataset from a table dataset by taking all the values
* for a single row.
*
* @param dataset the dataset ({@code null} not permitted).
* @param row the row (zero-based index).
*
* @return A pie dataset.
*/
public static PieDataset createPieDatasetForRow(CategoryDataset dataset,
int row) {
DefaultPieDataset result = new DefaultPieDataset();
int columnCount = dataset.getColumnCount();
for (int current = 0; current < columnCount; current++) {
Comparable columnKey = dataset.getColumnKey(current);
result.setValue(columnKey, dataset.getValue(row, current));
}
return result;
}
/**
* Creates a pie dataset from a table dataset by taking all the values
* for a single column.
*
* @param dataset the dataset ({@code null} not permitted).
* @param columnKey the column key.
*
* @return A pie dataset.
*/
public static PieDataset createPieDatasetForColumn(CategoryDataset dataset,
Comparable columnKey) {
int column = dataset.getColumnIndex(columnKey);
return createPieDatasetForColumn(dataset, column);
}
/**
* Creates a pie dataset from a {@link CategoryDataset} by taking all the
* values for a single column.
*
* @param dataset the dataset ({@code null} not permitted).
* @param column the column (zero-based index).
*
* @return A pie dataset.
*/
public static PieDataset createPieDatasetForColumn(CategoryDataset dataset,
int column) {
DefaultPieDataset result = new DefaultPieDataset();
int rowCount = dataset.getRowCount();
for (int i = 0; i < rowCount; i++) {
Comparable rowKey = dataset.getRowKey(i);
result.setValue(rowKey, dataset.getValue(i, column));
}
return result;
}
/**
* Creates a new pie dataset based on the supplied dataset, but modified
* by aggregating all the low value items (those whose value is lower
* than the {@code percentThreshold}) into a single item with the
* key "Other".
*
* @param source the source dataset ({@code null} not permitted).
* @param key a new key for the aggregated items ({@code null} not
* permitted).
* @param minimumPercent the percent threshold.
*
* @return The pie dataset with (possibly) aggregated items.
*/
public static PieDataset createConsolidatedPieDataset(PieDataset source,
Comparable key, double minimumPercent) {
return DatasetUtils.createConsolidatedPieDataset(source, key,
minimumPercent, 2);
}
/**
* Creates a new pie dataset based on the supplied dataset, but modified
* by aggregating all the low value items (those whose value is lower
* than the {@code percentThreshold}) into a single item. The
* aggregated items are assigned the specified key. Aggregation only
* occurs if there are at least {@code minItems} items to aggregate.
*
* @param source the source dataset ({@code null} not permitted).
* @param key the key to represent the aggregated items.
* @param minimumPercent the percent threshold (ten percent is 0.10).
* @param minItems only aggregate low values if there are at least this
* many.
*
* @return The pie dataset with (possibly) aggregated items.
*/
public static PieDataset createConsolidatedPieDataset(PieDataset source,
Comparable key, double minimumPercent, int minItems) {
DefaultPieDataset result = new DefaultPieDataset();
double total = DatasetUtils.calculatePieDatasetTotal(source);
// Iterate and find all keys below threshold percentThreshold
List keys = source.getKeys();
ArrayList otherKeys = new ArrayList();
Iterator iterator = keys.iterator();
while (iterator.hasNext()) {
Comparable currentKey = (Comparable) iterator.next();
Number dataValue = source.getValue(currentKey);
if (dataValue != null) {
double value = dataValue.doubleValue();
if (value / total < minimumPercent) {
otherKeys.add(currentKey);
}
}
}
// Create new dataset with keys above threshold percentThreshold
iterator = keys.iterator();
double otherValue = 0;
while (iterator.hasNext()) {
Comparable currentKey = (Comparable) iterator.next();
Number dataValue = source.getValue(currentKey);
if (dataValue != null) {
if (otherKeys.contains(currentKey)
&& otherKeys.size() >= minItems) {
// Do not add key to dataset
otherValue += dataValue.doubleValue();
}
else {
// Add key to dataset
result.setValue(currentKey, dataValue);
}
}
}
// Add other category if applicable
if (otherKeys.size() >= minItems) {
result.setValue(key, otherValue);
}
return result;
}
/**
* Creates a {@link CategoryDataset} that contains a copy of the data in an
* array (instances of {@code double} are created to represent the
* data items).
*
* Row and column keys are created by appending 0, 1, 2, ... to the
* supplied prefixes.
*
* @param rowKeyPrefix the row key prefix.
* @param columnKeyPrefix the column key prefix.
* @param data the data.
*
* @return The dataset.
*/
public static CategoryDataset createCategoryDataset(String rowKeyPrefix,
String columnKeyPrefix, double[][] data) {
DefaultCategoryDataset result = new DefaultCategoryDataset();
for (int r = 0; r < data.length; r++) {
String rowKey = rowKeyPrefix + (r + 1);
for (int c = 0; c < data[r].length; c++) {
String columnKey = columnKeyPrefix + (c + 1);
result.addValue(data[r][c], rowKey, columnKey);
}
}
return result;
}
/**
* Creates a {@link CategoryDataset} that contains a copy of the data in
* an array.
*
* Row and column keys are created by appending 0, 1, 2, ... to the
* supplied prefixes.
*
* @param rowKeyPrefix the row key prefix.
* @param columnKeyPrefix the column key prefix.
* @param data the data.
*
* @return The dataset.
*/
public static CategoryDataset createCategoryDataset(String rowKeyPrefix,
String columnKeyPrefix, Number[][] data) {
DefaultCategoryDataset result = new DefaultCategoryDataset();
for (int r = 0; r < data.length; r++) {
String rowKey = rowKeyPrefix + (r + 1);
for (int c = 0; c < data[r].length; c++) {
String columnKey = columnKeyPrefix + (c + 1);
result.addValue(data[r][c], rowKey, columnKey);
}
}
return result;
}
/**
* Creates a {@link CategoryDataset} that contains a copy of the data in
* an array (instances of {@code double} are created to represent the
* data items).
*
* Row and column keys are taken from the supplied arrays.
*
* @param rowKeys the row keys ({@code null} not permitted).
* @param columnKeys the column keys ({@code null} not permitted).
* @param data the data.
*
* @return The dataset.
*/
public static CategoryDataset createCategoryDataset(Comparable[] rowKeys,
Comparable[] columnKeys, double[][] data) {
Args.nullNotPermitted(rowKeys, "rowKeys");
Args.nullNotPermitted(columnKeys, "columnKeys");
if (ArrayUtils.hasDuplicateItems(rowKeys)) {
throw new IllegalArgumentException("Duplicate items in 'rowKeys'.");
}
if (ArrayUtils.hasDuplicateItems(columnKeys)) {
throw new IllegalArgumentException(
"Duplicate items in 'columnKeys'.");
}
if (rowKeys.length != data.length) {
throw new IllegalArgumentException(
"The number of row keys does not match the number of rows in "
+ "the data array.");
}
int columnCount = 0;
for (int r = 0; r < data.length; r++) {
columnCount = Math.max(columnCount, data[r].length);
}
if (columnKeys.length != columnCount) {
throw new IllegalArgumentException(
"The number of column keys does not match the number of "
+ "columns in the data array.");
}
// now do the work...
DefaultCategoryDataset result = new DefaultCategoryDataset();
for (int r = 0; r < data.length; r++) {
Comparable rowKey = rowKeys[r];
for (int c = 0; c < data[r].length; c++) {
Comparable columnKey = columnKeys[c];
result.addValue(data[r][c], rowKey, columnKey);
}
}
return result;
}
/**
* Creates a {@link CategoryDataset} by copying the data from the supplied
* {@link KeyedValues} instance.
*
* @param rowKey the row key ({@code null} not permitted).
* @param rowData the row data ({@code null} not permitted).
*
* @return A dataset.
*/
public static CategoryDataset createCategoryDataset(Comparable rowKey,
KeyedValues rowData) {
Args.nullNotPermitted(rowKey, "rowKey");
Args.nullNotPermitted(rowData, "rowData");
DefaultCategoryDataset result = new DefaultCategoryDataset();
for (int i = 0; i < rowData.getItemCount(); i++) {
result.addValue(rowData.getValue(i), rowKey, rowData.getKey(i));
}
return result;
}
/**
* Creates an {@link XYDataset} by sampling the specified function over a
* fixed range.
*
* @param f the function ({@code null} not permitted).
* @param start the start value for the range.
* @param end the end value for the range.
* @param samples the number of sample points (must be > 1).
* @param seriesKey the key to give the resulting series ({@code null} not
* permitted).
*
* @return A dataset.
*/
public static XYDataset sampleFunction2D(Function2D f, double start,
double end, int samples, Comparable seriesKey) {
// defer argument checking
XYSeries series = sampleFunction2DToSeries(f, start, end, samples,
seriesKey);
XYSeriesCollection collection = new XYSeriesCollection(series);
return collection;
}
/**
* Creates an {@link XYSeries} by sampling the specified function over a
* fixed range.
*
* @param f the function ({@code null} not permitted).
* @param start the start value for the range.
* @param end the end value for the range.
* @param samples the number of sample points (must be > 1).
* @param seriesKey the key to give the resulting series
* ({@code null} not permitted).
*
* @return A series.
*/
public static XYSeries sampleFunction2DToSeries(Function2D f,
double start, double end, int samples, Comparable seriesKey) {
Args.nullNotPermitted(f, "f");
Args.nullNotPermitted(seriesKey, "seriesKey");
if (start >= end) {
throw new IllegalArgumentException("Requires 'start' < 'end'.");
}
if (samples < 2) {
throw new IllegalArgumentException("Requires 'samples' > 1");
}
XYSeries series = new XYSeries(seriesKey);
double step = (end - start) / (samples - 1);
for (int i = 0; i < samples; i++) {
double x = start + (step * i);
series.add(x, f.getValue(x));
}
return series;
}
/**
* Returns {@code true} if the dataset is empty (or {@code null}),
* and {@code false} otherwise.
*
* @param dataset the dataset ({@code null} permitted).
*
* @return A boolean.
*/
public static boolean isEmptyOrNull(PieDataset dataset) {
if (dataset == null) {
return true;
}
int itemCount = dataset.getItemCount();
if (itemCount == 0) {
return true;
}
for (int item = 0; item < itemCount; item++) {
Number y = dataset.getValue(item);
if (y != null) {
double yy = y.doubleValue();
if (yy > 0.0) {
return false;
}
}
}
return true;
}
/**
* Returns {@code true} if the dataset is empty (or {@code null}),
* and {@code false} otherwise.
*
* @param dataset the dataset ({@code null} permitted).
*
* @return A boolean.
*/
public static boolean isEmptyOrNull(CategoryDataset dataset) {
if (dataset == null) {
return true;
}
int rowCount = dataset.getRowCount();
int columnCount = dataset.getColumnCount();
if (rowCount == 0 || columnCount == 0) {
return true;
}
for (int r = 0; r < rowCount; r++) {
for (int c = 0; c < columnCount; c++) {
if (dataset.getValue(r, c) != null) {
return false;
}
}
}
return true;
}
/**
* Returns {@code true} if the dataset is empty (or {@code null}),
* and {@code false} otherwise.
*
* @param dataset the dataset ({@code null} permitted).
*
* @return A boolean.
*/
public static boolean isEmptyOrNull(XYDataset dataset) {
if (dataset != null) {
for (int s = 0; s < dataset.getSeriesCount(); s++) {
if (dataset.getItemCount(s) > 0) {
return false;
}
}
}
return true;
}
/**
* Returns the range of values in the domain (x-values) of a dataset.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The range of values (possibly {@code null}).
*/
public static Range findDomainBounds(XYDataset dataset) {
return findDomainBounds(dataset, true);
}
/**
* Returns the range of values in the domain (x-values) of a dataset.
*
* @param dataset the dataset ({@code null} not permitted).
* @param includeInterval determines whether or not the x-interval is taken
* into account (only applies if the dataset is an
* {@link IntervalXYDataset}).
*
* @return The range of values (possibly {@code null}).
*/
public static Range findDomainBounds(XYDataset dataset,
boolean includeInterval) {
Args.nullNotPermitted(dataset, "dataset");
Range result;
// if the dataset implements DomainInfo, life is easier
if (dataset instanceof DomainInfo) {
DomainInfo info = (DomainInfo) dataset;
result = info.getDomainBounds(includeInterval);
}
else {
result = iterateDomainBounds(dataset, includeInterval);
}
return result;
}
/**
* Returns the bounds of the x-values in the specified {@code dataset}
* taking into account only the visible series and including any x-interval
* if requested.
*
* @param dataset the dataset ({@code null} not permitted).
* @param visibleSeriesKeys the visible series keys ({@code null}
* not permitted).
* @param includeInterval include the x-interval (if any)?
*
* @return The bounds (or {@code null} if the dataset contains no values.
*/
public static Range findDomainBounds(XYDataset dataset,
List visibleSeriesKeys, boolean includeInterval) {
Args.nullNotPermitted(dataset, "dataset");
Range result;
if (dataset instanceof XYDomainInfo) {
XYDomainInfo info = (XYDomainInfo) dataset;
result = info.getDomainBounds(visibleSeriesKeys, includeInterval);
}
else {
result = iterateToFindDomainBounds(dataset, visibleSeriesKeys,
includeInterval);
}
return result;
}
/**
* Iterates over the items in an {@link XYDataset} to find
* the range of x-values. If the dataset is an instance of
* {@link IntervalXYDataset}, the starting and ending x-values
* will be used for the bounds calculation.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The range (possibly {@code null}).
*/
public static Range iterateDomainBounds(XYDataset dataset) {
return iterateDomainBounds(dataset, true);
}
/**
* Iterates over the items in an {@link XYDataset} to find
* the range of x-values.
*
* @param dataset the dataset ({@code null} not permitted).
* @param includeInterval a flag that determines, for an
* {@link IntervalXYDataset}, whether the x-interval or just the
* x-value is used to determine the overall range.
*
* @return The range (possibly {@code null}).
*/
public static Range iterateDomainBounds(XYDataset dataset,
boolean includeInterval) {
Args.nullNotPermitted(dataset, "dataset");
double minimum = Double.POSITIVE_INFINITY;
double maximum = Double.NEGATIVE_INFINITY;
int seriesCount = dataset.getSeriesCount();
double lvalue, uvalue;
if (includeInterval && dataset instanceof IntervalXYDataset) {
IntervalXYDataset intervalXYData = (IntervalXYDataset) dataset;
for (int series = 0; series < seriesCount; series++) {
int itemCount = dataset.getItemCount(series);
for (int item = 0; item < itemCount; item++) {
double value = intervalXYData.getXValue(series, item);
lvalue = intervalXYData.getStartXValue(series, item);
uvalue = intervalXYData.getEndXValue(series, item);
if (!Double.isNaN(value)) {
minimum = Math.min(minimum, value);
maximum = Math.max(maximum, value);
}
if (!Double.isNaN(lvalue)) {
minimum = Math.min(minimum, lvalue);
maximum = Math.max(maximum, lvalue);
}
if (!Double.isNaN(uvalue)) {
minimum = Math.min(minimum, uvalue);
maximum = Math.max(maximum, uvalue);
}
}
}
}
else {
for (int series = 0; series < seriesCount; series++) {
int itemCount = dataset.getItemCount(series);
for (int item = 0; item < itemCount; item++) {
lvalue = dataset.getXValue(series, item);
uvalue = lvalue;
if (!Double.isNaN(lvalue)) {
minimum = Math.min(minimum, lvalue);
maximum = Math.max(maximum, uvalue);
}
}
}
}
if (minimum > maximum) {
return null;
}
else {
return new Range(minimum, maximum);
}
}
/**
* Returns the range of values in the range for the dataset.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The range (possibly {@code null}).
*/
public static Range findRangeBounds(CategoryDataset dataset) {
return findRangeBounds(dataset, true);
}
/**
* Returns the range of values in the range for the dataset.
*
* @param dataset the dataset ({@code null} not permitted).
* @param includeInterval a flag that determines whether or not the
* y-interval is taken into account.
*
* @return The range (possibly {@code null}).
*/
public static Range findRangeBounds(CategoryDataset dataset,
boolean includeInterval) {
Args.nullNotPermitted(dataset, "dataset");
Range result;
if (dataset instanceof RangeInfo) {
RangeInfo info = (RangeInfo) dataset;
result = info.getRangeBounds(includeInterval);
}
else {
result = iterateRangeBounds(dataset, includeInterval);
}
return result;
}
/**
* Finds the bounds of the y-values in the specified dataset, including
* only those series that are listed in visibleSeriesKeys.
*
* @param dataset the dataset ({@code null} not permitted).
* @param visibleSeriesKeys the keys for the visible series
* ({@code null} not permitted).
* @param includeInterval include the y-interval (if the dataset has a
* y-interval).
*
* @return The data bounds.
*/
public static Range findRangeBounds(CategoryDataset dataset,
List visibleSeriesKeys, boolean includeInterval) {
Args.nullNotPermitted(dataset, "dataset");
Range result;
if (dataset instanceof CategoryRangeInfo) {
CategoryRangeInfo info = (CategoryRangeInfo) dataset;
result = info.getRangeBounds(visibleSeriesKeys, includeInterval);
}
else {
result = iterateToFindRangeBounds(dataset, visibleSeriesKeys,
includeInterval);
}
return result;
}
/**
* Returns the range of values in the range for the dataset. This method
* is the partner for the {@link #findDomainBounds(XYDataset)} method.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The range (possibly {@code null}).
*/
public static Range findRangeBounds(XYDataset dataset) {
return findRangeBounds(dataset, true);
}
/**
* Returns the range of values in the range for the dataset. This method
* is the partner for the {@link #findDomainBounds(XYDataset, boolean)}
* method.
*
* @param dataset the dataset ({@code null} not permitted).
* @param includeInterval a flag that determines whether or not the
* y-interval is taken into account.
*
* @return The range (possibly {@code null}).
*/
public static Range findRangeBounds(XYDataset dataset,
boolean includeInterval) {
Args.nullNotPermitted(dataset, "dataset");
Range result;
if (dataset instanceof RangeInfo) {
RangeInfo info = (RangeInfo) dataset;
result = info.getRangeBounds(includeInterval);
}
else {
result = iterateRangeBounds(dataset, includeInterval);
}
return result;
}
/**
* Finds the bounds of the y-values in the specified dataset, including
* only those series that are listed in visibleSeriesKeys, and those items
* whose x-values fall within the specified range.
*
* @param dataset the dataset ({@code null} not permitted).
* @param visibleSeriesKeys the keys for the visible series
* ({@code null} not permitted).
* @param xRange the x-range ({@code null} not permitted).
* @param includeInterval include the y-interval (if the dataset has a
* y-interval).
*
* @return The data bounds.
*/
public static Range findRangeBounds(XYDataset dataset,
List visibleSeriesKeys, Range xRange, boolean includeInterval) {
Args.nullNotPermitted(dataset, "dataset");
Range result;
if (dataset instanceof XYRangeInfo) {
XYRangeInfo info = (XYRangeInfo) dataset;
result = info.getRangeBounds(visibleSeriesKeys, xRange,
includeInterval);
}
else {
result = iterateToFindRangeBounds(dataset, visibleSeriesKeys,
xRange, includeInterval);
}
return result;
}
/**
* Iterates over the data item of the category dataset to find
* the range bounds.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The range (possibly {@code null}).
*/
public static Range iterateRangeBounds(CategoryDataset dataset) {
return iterateRangeBounds(dataset, true);
}
/**
* Iterates over the data item of the category dataset to find
* the range bounds.
*
* @param dataset the dataset ({@code null} not permitted).
* @param includeInterval a flag that determines whether or not the
* y-interval is taken into account.
*
* @return The range (possibly {@code null}).
*/
public static Range iterateRangeBounds(CategoryDataset dataset,
boolean includeInterval) {
double minimum = Double.POSITIVE_INFINITY;
double maximum = Double.NEGATIVE_INFINITY;
int rowCount = dataset.getRowCount();
int columnCount = dataset.getColumnCount();
if (includeInterval && dataset instanceof IntervalCategoryDataset) {
// handle the special case where the dataset has y-intervals that
// we want to measure
IntervalCategoryDataset icd = (IntervalCategoryDataset) dataset;
Number value, lvalue, uvalue;
for (int row = 0; row < rowCount; row++) {
for (int column = 0; column < columnCount; column++) {
value = icd.getValue(row, column);
double v;
if ((value != null)
&& !Double.isNaN(v = value.doubleValue())) {
minimum = Math.min(v, minimum);
maximum = Math.max(v, maximum);
}
lvalue = icd.getStartValue(row, column);
if (lvalue != null
&& !Double.isNaN(v = lvalue.doubleValue())) {
minimum = Math.min(v, minimum);
maximum = Math.max(v, maximum);
}
uvalue = icd.getEndValue(row, column);
if (uvalue != null
&& !Double.isNaN(v = uvalue.doubleValue())) {
minimum = Math.min(v, minimum);
maximum = Math.max(v, maximum);
}
}
}
}
else {
// handle the standard case (plain CategoryDataset)
for (int row = 0; row < rowCount; row++) {
for (int column = 0; column < columnCount; column++) {
Number value = dataset.getValue(row, column);
if (value != null) {
double v = value.doubleValue();
if (!Double.isNaN(v)) {
minimum = Math.min(minimum, v);
maximum = Math.max(maximum, v);
}
}
}
}
}
if (minimum == Double.POSITIVE_INFINITY) {
return null;
}
else {
return new Range(minimum, maximum);
}
}
/**
* Iterates over the data item of the category dataset to find
* the range bounds.
*
* @param dataset the dataset ({@code null} not permitted).
* @param includeInterval a flag that determines whether or not the
* y-interval is taken into account.
* @param visibleSeriesKeys the visible series keys.
*
* @return The range (possibly {@code null}).
*/
public static Range iterateToFindRangeBounds(CategoryDataset dataset,
List visibleSeriesKeys, boolean includeInterval) {
Args.nullNotPermitted(dataset, "dataset");
Args.nullNotPermitted(visibleSeriesKeys, "visibleSeriesKeys");
double minimum = Double.POSITIVE_INFINITY;
double maximum = Double.NEGATIVE_INFINITY;
int columnCount = dataset.getColumnCount();
if (includeInterval
&& dataset instanceof BoxAndWhiskerCategoryDataset) {
// handle special case of BoxAndWhiskerDataset
BoxAndWhiskerCategoryDataset bx
= (BoxAndWhiskerCategoryDataset) dataset;
Iterator iterator = visibleSeriesKeys.iterator();
while (iterator.hasNext()) {
Comparable seriesKey = (Comparable) iterator.next();
int series = dataset.getRowIndex(seriesKey);
int itemCount = dataset.getColumnCount();
for (int item = 0; item < itemCount; item++) {
Number lvalue = bx.getMinRegularValue(series, item);
if (lvalue == null) {
lvalue = bx.getValue(series, item);
}
Number uvalue = bx.getMaxRegularValue(series, item);
if (uvalue == null) {
uvalue = bx.getValue(series, item);
}
if (lvalue != null) {
minimum = Math.min(minimum, lvalue.doubleValue());
}
if (uvalue != null) {
maximum = Math.max(maximum, uvalue.doubleValue());
}
}
}
}
else if (includeInterval
&& dataset instanceof IntervalCategoryDataset) {
// handle the special case where the dataset has y-intervals that
// we want to measure
IntervalCategoryDataset icd = (IntervalCategoryDataset) dataset;
Number lvalue, uvalue;
Iterator iterator = visibleSeriesKeys.iterator();
while (iterator.hasNext()) {
Comparable seriesKey = (Comparable) iterator.next();
int series = dataset.getRowIndex(seriesKey);
for (int column = 0; column < columnCount; column++) {
lvalue = icd.getStartValue(series, column);
uvalue = icd.getEndValue(series, column);
if (lvalue != null && !Double.isNaN(lvalue.doubleValue())) {
minimum = Math.min(minimum, lvalue.doubleValue());
}
if (uvalue != null && !Double.isNaN(uvalue.doubleValue())) {
maximum = Math.max(maximum, uvalue.doubleValue());
}
}
}
}
else if (includeInterval
&& dataset instanceof MultiValueCategoryDataset) {
// handle the special case where the dataset has y-intervals that
// we want to measure
MultiValueCategoryDataset mvcd
= (MultiValueCategoryDataset) dataset;
Iterator iterator = visibleSeriesKeys.iterator();
while (iterator.hasNext()) {
Comparable seriesKey = (Comparable) iterator.next();
int series = dataset.getRowIndex(seriesKey);
for (int column = 0; column < columnCount; column++) {
List values = mvcd.getValues(series, column);
Iterator valueIterator = values.iterator();
while (valueIterator.hasNext()) {
Object o = valueIterator.next();
if (o instanceof Number){
double v = ((Number) o).doubleValue();
if (!Double.isNaN(v)){
minimum = Math.min(minimum, v);
maximum = Math.max(maximum, v);
}
}
}
}
}
}
else if (includeInterval
&& dataset instanceof StatisticalCategoryDataset) {
// handle the special case where the dataset has y-intervals that
// we want to measure
StatisticalCategoryDataset scd
= (StatisticalCategoryDataset) dataset;
Iterator iterator = visibleSeriesKeys.iterator();
while (iterator.hasNext()) {
Comparable seriesKey = (Comparable) iterator.next();
int series = dataset.getRowIndex(seriesKey);
for (int column = 0; column < columnCount; column++) {
Number meanN = scd.getMeanValue(series, column);
if (meanN != null) {
double std = 0.0;
Number stdN = scd.getStdDevValue(series, column);
if (stdN != null) {
std = stdN.doubleValue();
if (Double.isNaN(std)) {
std = 0.0;
}
}
double mean = meanN.doubleValue();
if (!Double.isNaN(mean)) {
minimum = Math.min(minimum, mean - std);
maximum = Math.max(maximum, mean + std);
}
}
}
}
}
else {
// handle the standard case (plain CategoryDataset)
Iterator iterator = visibleSeriesKeys.iterator();
while (iterator.hasNext()) {
Comparable seriesKey = (Comparable) iterator.next();
int series = dataset.getRowIndex(seriesKey);
for (int column = 0; column < columnCount; column++) {
Number value = dataset.getValue(series, column);
if (value != null) {
double v = value.doubleValue();
if (!Double.isNaN(v)) {
minimum = Math.min(minimum, v);
maximum = Math.max(maximum, v);
}
}
}
}
}
if (minimum == Double.POSITIVE_INFINITY) {
return null;
}
else {
return new Range(minimum, maximum);
}
}
/**
* Iterates over the data item of the xy dataset to find
* the range bounds.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The range (possibly {@code null}).
*/
public static Range iterateRangeBounds(XYDataset dataset) {
return iterateRangeBounds(dataset, true);
}
/**
* Iterates over the data items of the xy dataset to find
* the range bounds.
*
* @param dataset the dataset ({@code null} not permitted).
* @param includeInterval a flag that determines, for an
* {@link IntervalXYDataset}, whether the y-interval or just the
* y-value is used to determine the overall range.
*
* @return The range (possibly {@code null}).
*/
public static Range iterateRangeBounds(XYDataset dataset,
boolean includeInterval) {
double minimum = Double.POSITIVE_INFINITY;
double maximum = Double.NEGATIVE_INFINITY;
int seriesCount = dataset.getSeriesCount();
// handle three cases by dataset type
if (includeInterval && dataset instanceof IntervalXYDataset) {
// handle special case of IntervalXYDataset
IntervalXYDataset ixyd = (IntervalXYDataset) dataset;
for (int series = 0; series < seriesCount; series++) {
int itemCount = dataset.getItemCount(series);
for (int item = 0; item < itemCount; item++) {
double value = ixyd.getYValue(series, item);
double lvalue = ixyd.getStartYValue(series, item);
double uvalue = ixyd.getEndYValue(series, item);
if (!Double.isNaN(value)) {
minimum = Math.min(minimum, value);
maximum = Math.max(maximum, value);
}
if (!Double.isNaN(lvalue)) {
minimum = Math.min(minimum, lvalue);
maximum = Math.max(maximum, lvalue);
}
if (!Double.isNaN(uvalue)) {
minimum = Math.min(minimum, uvalue);
maximum = Math.max(maximum, uvalue);
}
}
}
}
else if (includeInterval && dataset instanceof OHLCDataset) {
// handle special case of OHLCDataset
OHLCDataset ohlc = (OHLCDataset) dataset;
for (int series = 0; series < seriesCount; series++) {
int itemCount = dataset.getItemCount(series);
for (int item = 0; item < itemCount; item++) {
double lvalue = ohlc.getLowValue(series, item);
double uvalue = ohlc.getHighValue(series, item);
if (!Double.isNaN(lvalue)) {
minimum = Math.min(minimum, lvalue);
}
if (!Double.isNaN(uvalue)) {
maximum = Math.max(maximum, uvalue);
}
}
}
}
else {
// standard case - plain XYDataset
for (int series = 0; series < seriesCount; series++) {
int itemCount = dataset.getItemCount(series);
for (int item = 0; item < itemCount; item++) {
double value = dataset.getYValue(series, item);
if (!Double.isNaN(value)) {
minimum = Math.min(minimum, value);
maximum = Math.max(maximum, value);
}
}
}
}
if (minimum == Double.POSITIVE_INFINITY) {
return null;
}
else {
return new Range(minimum, maximum);
}
}
/**
* Returns the range of values in the z-dimension for the dataset. This
* method is the partner for the {@link #findRangeBounds(XYDataset)}
* and {@link #findDomainBounds(XYDataset)} methods.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The range (possibly {@code null}).
*/
public static Range findZBounds(XYZDataset dataset) {
return findZBounds(dataset, true);
}
/**
* Returns the range of values in the z-dimension for the dataset. This
* method is the partner for the
* {@link #findRangeBounds(XYDataset, boolean)} and
* {@link #findDomainBounds(XYDataset, boolean)} methods.
*
* @param dataset the dataset ({@code null} not permitted).
* @param includeInterval a flag that determines whether or not the
* z-interval is taken into account.
*
* @return The range (possibly {@code null}).
*/
public static Range findZBounds(XYZDataset dataset,
boolean includeInterval) {
Args.nullNotPermitted(dataset, "dataset");
Range result = iterateZBounds(dataset, includeInterval);
return result;
}
/**
* Finds the bounds of the z-values in the specified dataset, including
* only those series that are listed in visibleSeriesKeys, and those items
* whose x-values fall within the specified range.
*
* @param dataset the dataset ({@code null} not permitted).
* @param visibleSeriesKeys the keys for the visible series
* ({@code null} not permitted).
* @param xRange the x-range ({@code null} not permitted).
* @param includeInterval include the z-interval (if the dataset has a
* z-interval).
*
* @return The data bounds.
*/
public static Range findZBounds(XYZDataset dataset,
List visibleSeriesKeys, Range xRange, boolean includeInterval) {
Args.nullNotPermitted(dataset, "dataset");
Range result = iterateToFindZBounds(dataset, visibleSeriesKeys,
xRange, includeInterval);
return result;
}
/**
* Iterates over the data item of the xyz dataset to find
* the z-dimension bounds.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The range (possibly {@code null}).
*/
public static Range iterateZBounds(XYZDataset dataset) {
return iterateZBounds(dataset, true);
}
/**
* Iterates over the data items of the xyz dataset to find
* the z-dimension bounds.
*
* @param dataset the dataset ({@code null} not permitted).
* @param includeInterval include the z-interval (if the dataset has a
* z-interval.
*
* @return The range (possibly {@code null}).
*/
public static Range iterateZBounds(XYZDataset dataset,
boolean includeInterval) {
double minimum = Double.POSITIVE_INFINITY;
double maximum = Double.NEGATIVE_INFINITY;
int seriesCount = dataset.getSeriesCount();
for (int series = 0; series < seriesCount; series++) {
int itemCount = dataset.getItemCount(series);
for (int item = 0; item < itemCount; item++) {
double value = dataset.getZValue(series, item);
if (!Double.isNaN(value)) {
minimum = Math.min(minimum, value);
maximum = Math.max(maximum, value);
}
}
}
if (minimum == Double.POSITIVE_INFINITY) {
return null;
}
else {
return new Range(minimum, maximum);
}
}
/**
* Returns the range of x-values in the specified dataset for the
* data items belonging to the visible series.
*
* @param dataset the dataset ({@code null} not permitted).
* @param visibleSeriesKeys the visible series keys ({@code null} not
* permitted).
* @param includeInterval a flag that determines whether or not the
* y-interval for the dataset is included (this only applies if the
* dataset is an instance of IntervalXYDataset).
*
* @return The x-range (possibly {@code null}).
*/
public static Range iterateToFindDomainBounds(XYDataset dataset,
List visibleSeriesKeys, boolean includeInterval) {
Args.nullNotPermitted(dataset, "dataset");
Args.nullNotPermitted(visibleSeriesKeys, "visibleSeriesKeys");
double minimum = Double.POSITIVE_INFINITY;
double maximum = Double.NEGATIVE_INFINITY;
if (includeInterval && dataset instanceof IntervalXYDataset) {
// handle special case of IntervalXYDataset
IntervalXYDataset ixyd = (IntervalXYDataset) dataset;
Iterator iterator = visibleSeriesKeys.iterator();
while (iterator.hasNext()) {
Comparable seriesKey = (Comparable) iterator.next();
int series = dataset.indexOf(seriesKey);
int itemCount = dataset.getItemCount(series);
for (int item = 0; item < itemCount; item++) {
double xvalue = ixyd.getXValue(series, item);
double lvalue = ixyd.getStartXValue(series, item);
double uvalue = ixyd.getEndXValue(series, item);
if (!Double.isNaN(xvalue)) {
minimum = Math.min(minimum, xvalue);
maximum = Math.max(maximum, xvalue);
}
if (!Double.isNaN(lvalue)) {
minimum = Math.min(minimum, lvalue);
}
if (!Double.isNaN(uvalue)) {
maximum = Math.max(maximum, uvalue);
}
}
}
} else {
// standard case - plain XYDataset
Iterator iterator = visibleSeriesKeys.iterator();
while (iterator.hasNext()) {
Comparable seriesKey = (Comparable) iterator.next();
int series = dataset.indexOf(seriesKey);
int itemCount = dataset.getItemCount(series);
for (int item = 0; item < itemCount; item++) {
double x = dataset.getXValue(series, item);
if (!Double.isNaN(x)) {
minimum = Math.min(minimum, x);
maximum = Math.max(maximum, x);
}
}
}
}
if (minimum == Double.POSITIVE_INFINITY) {
return null;
} else {
return new Range(minimum, maximum);
}
}
/**
* Returns the range of y-values in the specified dataset for the
* data items belonging to the visible series and with x-values in the
* given range.
*
* @param dataset the dataset ({@code null} not permitted).
* @param visibleSeriesKeys the visible series keys ({@code null} not
* permitted).
* @param xRange the x-range ({@code null} not permitted).
* @param includeInterval a flag that determines whether or not the
* y-interval for the dataset is included (this only applies if the
* dataset is an instance of IntervalXYDataset).
*
* @return The y-range (possibly {@code null}).
*/
public static Range iterateToFindRangeBounds(XYDataset dataset,
List visibleSeriesKeys, Range xRange, boolean includeInterval) {
Args.nullNotPermitted(dataset, "dataset");
Args.nullNotPermitted(visibleSeriesKeys, "visibleSeriesKeys");
Args.nullNotPermitted(xRange, "xRange");
double minimum = Double.POSITIVE_INFINITY;
double maximum = Double.NEGATIVE_INFINITY;
// handle three cases by dataset type
if (includeInterval && dataset instanceof OHLCDataset) {
// handle special case of OHLCDataset
OHLCDataset ohlc = (OHLCDataset) dataset;
Iterator iterator = visibleSeriesKeys.iterator();
while (iterator.hasNext()) {
Comparable seriesKey = (Comparable) iterator.next();
int series = dataset.indexOf(seriesKey);
int itemCount = dataset.getItemCount(series);
for (int item = 0; item < itemCount; item++) {
double x = ohlc.getXValue(series, item);
if (xRange.contains(x)) {
double lvalue = ohlc.getLowValue(series, item);
double uvalue = ohlc.getHighValue(series, item);
if (!Double.isNaN(lvalue)) {
minimum = Math.min(minimum, lvalue);
}
if (!Double.isNaN(uvalue)) {
maximum = Math.max(maximum, uvalue);
}
}
}
}
}
else if (includeInterval && dataset instanceof BoxAndWhiskerXYDataset) {
// handle special case of BoxAndWhiskerXYDataset
BoxAndWhiskerXYDataset bx = (BoxAndWhiskerXYDataset) dataset;
Iterator iterator = visibleSeriesKeys.iterator();
while (iterator.hasNext()) {
Comparable seriesKey = (Comparable) iterator.next();
int series = dataset.indexOf(seriesKey);
int itemCount = dataset.getItemCount(series);
for (int item = 0; item < itemCount; item++) {
double x = bx.getXValue(series, item);
if (xRange.contains(x)) {
Number lvalue = bx.getMinRegularValue(series, item);
Number uvalue = bx.getMaxRegularValue(series, item);
if (lvalue != null) {
minimum = Math.min(minimum, lvalue.doubleValue());
}
if (uvalue != null) {
maximum = Math.max(maximum, uvalue.doubleValue());
}
}
}
}
}
else if (includeInterval && dataset instanceof IntervalXYDataset) {
// handle special case of IntervalXYDataset
IntervalXYDataset ixyd = (IntervalXYDataset) dataset;
Iterator iterator = visibleSeriesKeys.iterator();
while (iterator.hasNext()) {
Comparable seriesKey = (Comparable) iterator.next();
int series = dataset.indexOf(seriesKey);
int itemCount = dataset.getItemCount(series);
for (int item = 0; item < itemCount; item++) {
double x = ixyd.getXValue(series, item);
if (xRange.contains(x)) {
double yvalue = ixyd.getYValue(series, item);
double lvalue = ixyd.getStartYValue(series, item);
double uvalue = ixyd.getEndYValue(series, item);
if (!Double.isNaN(yvalue)) {
minimum = Math.min(minimum, yvalue);
maximum = Math.max(maximum, yvalue);
}
if (!Double.isNaN(lvalue)) {
minimum = Math.min(minimum, lvalue);
}
if (!Double.isNaN(uvalue)) {
maximum = Math.max(maximum, uvalue);
}
}
}
}
} else {
// standard case - plain XYDataset
Iterator iterator = visibleSeriesKeys.iterator();
while (iterator.hasNext()) {
Comparable seriesKey = (Comparable) iterator.next();
int series = dataset.indexOf(seriesKey);
int itemCount = dataset.getItemCount(series);
for (int item = 0; item < itemCount; item++) {
double x = dataset.getXValue(series, item);
double y = dataset.getYValue(series, item);
if (xRange.contains(x)) {
if (!Double.isNaN(y)) {
minimum = Math.min(minimum, y);
maximum = Math.max(maximum, y);
}
}
}
}
}
if (minimum == Double.POSITIVE_INFINITY) {
return null;
} else {
return new Range(minimum, maximum);
}
}
/**
* Returns the range of z-values in the specified dataset for the
* data items belonging to the visible series and with x-values in the
* given range.
*
* @param dataset the dataset ({@code null} not permitted).
* @param visibleSeriesKeys the visible series keys ({@code null} not
* permitted).
* @param xRange the x-range ({@code null} not permitted).
* @param includeInterval a flag that determines whether or not the
* z-interval for the dataset is included (this only applies if the
* dataset has an interval, which is currently not supported).
*
* @return The y-range (possibly {@code null}).
*/
public static Range iterateToFindZBounds(XYZDataset dataset,
List visibleSeriesKeys, Range xRange, boolean includeInterval) {
Args.nullNotPermitted(dataset, "dataset");
Args.nullNotPermitted(visibleSeriesKeys, "visibleSeriesKeys");
Args.nullNotPermitted(xRange, "xRange");
double minimum = Double.POSITIVE_INFINITY;
double maximum = Double.NEGATIVE_INFINITY;
Iterator iterator = visibleSeriesKeys.iterator();
while (iterator.hasNext()) {
Comparable seriesKey = (Comparable) iterator.next();
int series = dataset.indexOf(seriesKey);
int itemCount = dataset.getItemCount(series);
for (int item = 0; item < itemCount; item++) {
double x = dataset.getXValue(series, item);
double z = dataset.getZValue(series, item);
if (xRange.contains(x)) {
if (!Double.isNaN(z)) {
minimum = Math.min(minimum, z);
maximum = Math.max(maximum, z);
}
}
}
}
if (minimum == Double.POSITIVE_INFINITY) {
return null;
} else {
return new Range(minimum, maximum);
}
}
/**
* Finds the minimum domain (or X) value for the specified dataset. This
* is easy if the dataset implements the {@link DomainInfo} interface (a
* good idea if there is an efficient way to determine the minimum value).
* Otherwise, it involves iterating over the entire data-set.
*
* Returns {@code null} if all the data values in the dataset are
* {@code null}.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The minimum value (possibly {@code null}).
*/
public static Number findMinimumDomainValue(XYDataset dataset) {
Args.nullNotPermitted(dataset, "dataset");
Number result;
// if the dataset implements DomainInfo, life is easy
if (dataset instanceof DomainInfo) {
DomainInfo info = (DomainInfo) dataset;
return info.getDomainLowerBound(true);
}
else {
double minimum = Double.POSITIVE_INFINITY;
int seriesCount = dataset.getSeriesCount();
for (int series = 0; series < seriesCount; series++) {
int itemCount = dataset.getItemCount(series);
for (int item = 0; item < itemCount; item++) {
double value;
if (dataset instanceof IntervalXYDataset) {
IntervalXYDataset intervalXYData
= (IntervalXYDataset) dataset;
value = intervalXYData.getStartXValue(series, item);
}
else {
value = dataset.getXValue(series, item);
}
if (!Double.isNaN(value)) {
minimum = Math.min(minimum, value);
}
}
}
if (minimum == Double.POSITIVE_INFINITY) {
result = null;
}
else {
result = minimum;
}
}
return result;
}
/**
* Returns the maximum domain value for the specified dataset. This is
* easy if the dataset implements the {@link DomainInfo} interface (a good
* idea if there is an efficient way to determine the maximum value).
* Otherwise, it involves iterating over the entire data-set. Returns
* {@code null} if all the data values in the dataset are
* {@code null}.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The maximum value (possibly {@code null}).
*/
public static Number findMaximumDomainValue(XYDataset dataset) {
Args.nullNotPermitted(dataset, "dataset");
Number result;
// if the dataset implements DomainInfo, life is easy
if (dataset instanceof DomainInfo) {
DomainInfo info = (DomainInfo) dataset;
return info.getDomainUpperBound(true);
}
// hasn't implemented DomainInfo, so iterate...
else {
double maximum = Double.NEGATIVE_INFINITY;
int seriesCount = dataset.getSeriesCount();
for (int series = 0; series < seriesCount; series++) {
int itemCount = dataset.getItemCount(series);
for (int item = 0; item < itemCount; item++) {
double value;
if (dataset instanceof IntervalXYDataset) {
IntervalXYDataset intervalXYData
= (IntervalXYDataset) dataset;
value = intervalXYData.getEndXValue(series, item);
}
else {
value = dataset.getXValue(series, item);
}
if (!Double.isNaN(value)) {
maximum = Math.max(maximum, value);
}
}
}
if (maximum == Double.NEGATIVE_INFINITY) {
result = null;
}
else {
result = maximum;
}
}
return result;
}
/**
* Returns the minimum range value for the specified dataset. This is
* easy if the dataset implements the {@link RangeInfo} interface (a good
* idea if there is an efficient way to determine the minimum value).
* Otherwise, it involves iterating over the entire data-set. Returns
* {@code null} if all the data values in the dataset are
* {@code null}.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The minimum value (possibly {@code null}).
*/
public static Number findMinimumRangeValue(CategoryDataset dataset) {
Args.nullNotPermitted(dataset, "dataset");
if (dataset instanceof RangeInfo) {
RangeInfo info = (RangeInfo) dataset;
return info.getRangeLowerBound(true);
}
// hasn't implemented RangeInfo, so we'll have to iterate...
else {
double minimum = Double.POSITIVE_INFINITY;
int seriesCount = dataset.getRowCount();
int itemCount = dataset.getColumnCount();
for (int series = 0; series < seriesCount; series++) {
for (int item = 0; item < itemCount; item++) {
Number value;
if (dataset instanceof IntervalCategoryDataset) {
IntervalCategoryDataset icd
= (IntervalCategoryDataset) dataset;
value = icd.getStartValue(series, item);
}
else {
value = dataset.getValue(series, item);
}
if (value != null) {
minimum = Math.min(minimum, value.doubleValue());
}
}
}
if (minimum == Double.POSITIVE_INFINITY) {
return null;
}
else {
return minimum;
}
}
}
/**
* Returns the minimum range value for the specified dataset. This is
* easy if the dataset implements the {@link RangeInfo} interface (a good
* idea if there is an efficient way to determine the minimum value).
* Otherwise, it involves iterating over the entire data-set. Returns
* {@code null} if all the data values in the dataset are
* {@code null}.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The minimum value (possibly {@code null}).
*/
public static Number findMinimumRangeValue(XYDataset dataset) {
Args.nullNotPermitted(dataset, "dataset");
// work out the minimum value...
if (dataset instanceof RangeInfo) {
RangeInfo info = (RangeInfo) dataset;
return info.getRangeLowerBound(true);
}
// hasn't implemented RangeInfo, so we'll have to iterate...
else {
double minimum = Double.POSITIVE_INFINITY;
int seriesCount = dataset.getSeriesCount();
for (int series = 0; series < seriesCount; series++) {
int itemCount = dataset.getItemCount(series);
for (int item = 0; item < itemCount; item++) {
double value;
if (dataset instanceof IntervalXYDataset) {
IntervalXYDataset intervalXYData
= (IntervalXYDataset) dataset;
value = intervalXYData.getStartYValue(series, item);
}
else if (dataset instanceof OHLCDataset) {
OHLCDataset highLowData = (OHLCDataset) dataset;
value = highLowData.getLowValue(series, item);
}
else {
value = dataset.getYValue(series, item);
}
if (!Double.isNaN(value)) {
minimum = Math.min(minimum, value);
}
}
}
if (minimum == Double.POSITIVE_INFINITY) {
return null;
}
else {
return minimum;
}
}
}
/**
* Returns the maximum range value for the specified dataset. This is easy
* if the dataset implements the {@link RangeInfo} interface (a good idea
* if there is an efficient way to determine the maximum value).
* Otherwise, it involves iterating over the entire data-set. Returns
* {@code null} if all the data values are {@code null}.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The maximum value (possibly {@code null}).
*/
public static Number findMaximumRangeValue(CategoryDataset dataset) {
Args.nullNotPermitted(dataset, "dataset");
// work out the minimum value...
if (dataset instanceof RangeInfo) {
RangeInfo info = (RangeInfo) dataset;
return info.getRangeUpperBound(true);
}
// hasn't implemented RangeInfo, so we'll have to iterate...
else {
double maximum = Double.NEGATIVE_INFINITY;
int seriesCount = dataset.getRowCount();
int itemCount = dataset.getColumnCount();
for (int series = 0; series < seriesCount; series++) {
for (int item = 0; item < itemCount; item++) {
Number value;
if (dataset instanceof IntervalCategoryDataset) {
IntervalCategoryDataset icd
= (IntervalCategoryDataset) dataset;
value = icd.getEndValue(series, item);
}
else {
value = dataset.getValue(series, item);
}
if (value != null) {
maximum = Math.max(maximum, value.doubleValue());
}
}
}
if (maximum == Double.NEGATIVE_INFINITY) {
return null;
}
else {
return maximum;
}
}
}
/**
* Returns the maximum range value for the specified dataset. This is
* easy if the dataset implements the {@link RangeInfo} interface (a good
* idea if there is an efficient way to determine the maximum value).
* Otherwise, it involves iterating over the entire data-set. Returns
* {@code null} if all the data values are {@code null}.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The maximum value (possibly {@code null}).
*/
public static Number findMaximumRangeValue(XYDataset dataset) {
Args.nullNotPermitted(dataset, "dataset");
// work out the minimum value...
if (dataset instanceof RangeInfo) {
RangeInfo info = (RangeInfo) dataset;
return info.getRangeUpperBound(true);
}
// hasn't implemented RangeInfo, so we'll have to iterate...
else {
double maximum = Double.NEGATIVE_INFINITY;
int seriesCount = dataset.getSeriesCount();
for (int series = 0; series < seriesCount; series++) {
int itemCount = dataset.getItemCount(series);
for (int item = 0; item < itemCount; item++) {
double value;
if (dataset instanceof IntervalXYDataset) {
IntervalXYDataset intervalXYData
= (IntervalXYDataset) dataset;
value = intervalXYData.getEndYValue(series, item);
}
else if (dataset instanceof OHLCDataset) {
OHLCDataset highLowData = (OHLCDataset) dataset;
value = highLowData.getHighValue(series, item);
}
else {
value = dataset.getYValue(series, item);
}
if (!Double.isNaN(value)) {
maximum = Math.max(maximum, value);
}
}
}
if (maximum == Double.NEGATIVE_INFINITY) {
return null;
}
else {
return maximum;
}
}
}
/**
* Returns the minimum and maximum values for the dataset's range
* (y-values), assuming that the series in one category are stacked.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The range ({@code null} if the dataset contains no values).
*/
public static Range findStackedRangeBounds(CategoryDataset dataset) {
return findStackedRangeBounds(dataset, 0.0);
}
/**
* Returns the minimum and maximum values for the dataset's range
* (y-values), assuming that the series in one category are stacked.
*
* @param dataset the dataset ({@code null} not permitted).
* @param base the base value for the bars.
*
* @return The range ({@code null} if the dataset contains no values).
*/
public static Range findStackedRangeBounds(CategoryDataset dataset,
double base) {
Args.nullNotPermitted(dataset, "dataset");
Range result = null;
double minimum = Double.POSITIVE_INFINITY;
double maximum = Double.NEGATIVE_INFINITY;
int categoryCount = dataset.getColumnCount();
for (int item = 0; item < categoryCount; item++) {
double positive = base;
double negative = base;
int seriesCount = dataset.getRowCount();
for (int series = 0; series < seriesCount; series++) {
Number number = dataset.getValue(series, item);
if (number != null) {
double value = number.doubleValue();
if (value > 0.0) {
positive = positive + value;
}
if (value < 0.0) {
negative = negative + value;
// '+', remember value is negative
}
}
}
minimum = Math.min(minimum, negative);
maximum = Math.max(maximum, positive);
}
if (minimum <= maximum) {
result = new Range(minimum, maximum);
}
return result;
}
/**
* Returns the minimum and maximum values for the dataset's range
* (y-values), assuming that the series in one category are stacked.
*
* @param dataset the dataset.
* @param map a structure that maps series to groups.
*
* @return The value range ({@code null} if the dataset contains no
* values).
*/
public static Range findStackedRangeBounds(CategoryDataset dataset,
KeyToGroupMap map) {
Args.nullNotPermitted(dataset, "dataset");
boolean hasValidData = false;
Range result = null;
// create an array holding the group indices for each series...
int[] groupIndex = new int[dataset.getRowCount()];
for (int i = 0; i < dataset.getRowCount(); i++) {
groupIndex[i] = map.getGroupIndex(map.getGroup(
dataset.getRowKey(i)));
}
// minimum and maximum for each group...
int groupCount = map.getGroupCount();
double[] minimum = new double[groupCount];
double[] maximum = new double[groupCount];
int categoryCount = dataset.getColumnCount();
for (int item = 0; item < categoryCount; item++) {
double[] positive = new double[groupCount];
double[] negative = new double[groupCount];
int seriesCount = dataset.getRowCount();
for (int series = 0; series < seriesCount; series++) {
Number number = dataset.getValue(series, item);
if (number != null) {
hasValidData = true;
double value = number.doubleValue();
if (value > 0.0) {
positive[groupIndex[series]]
= positive[groupIndex[series]] + value;
}
if (value < 0.0) {
negative[groupIndex[series]]
= negative[groupIndex[series]] + value;
// '+', remember value is negative
}
}
}
for (int g = 0; g < groupCount; g++) {
minimum[g] = Math.min(minimum[g], negative[g]);
maximum[g] = Math.max(maximum[g], positive[g]);
}
}
if (hasValidData) {
for (int j = 0; j < groupCount; j++) {
result = Range.combine(result, new Range(minimum[j],
maximum[j]));
}
}
return result;
}
/**
* Returns the minimum value in the dataset range, assuming that values in
* each category are "stacked".
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The minimum value.
*
* @see #findMaximumStackedRangeValue(CategoryDataset)
*/
public static Number findMinimumStackedRangeValue(CategoryDataset dataset) {
Args.nullNotPermitted(dataset, "dataset");
Number result = null;
boolean hasValidData = false;
double minimum = 0.0;
int categoryCount = dataset.getColumnCount();
for (int item = 0; item < categoryCount; item++) {
double total = 0.0;
int seriesCount = dataset.getRowCount();
for (int series = 0; series < seriesCount; series++) {
Number number = dataset.getValue(series, item);
if (number != null) {
hasValidData = true;
double value = number.doubleValue();
if (value < 0.0) {
total = total + value;
// '+', remember value is negative
}
}
}
minimum = Math.min(minimum, total);
}
if (hasValidData) {
result = minimum;
}
return result;
}
/**
* Returns the maximum value in the dataset range, assuming that values in
* each category are "stacked".
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The maximum value (possibly {@code null}).
*
* @see #findMinimumStackedRangeValue(CategoryDataset)
*/
public static Number findMaximumStackedRangeValue(CategoryDataset dataset) {
Args.nullNotPermitted(dataset, "dataset");
Number result = null;
boolean hasValidData = false;
double maximum = 0.0;
int categoryCount = dataset.getColumnCount();
for (int item = 0; item < categoryCount; item++) {
double total = 0.0;
int seriesCount = dataset.getRowCount();
for (int series = 0; series < seriesCount; series++) {
Number number = dataset.getValue(series, item);
if (number != null) {
hasValidData = true;
double value = number.doubleValue();
if (value > 0.0) {
total = total + value;
}
}
}
maximum = Math.max(maximum, total);
}
if (hasValidData) {
result = maximum;
}
return result;
}
/**
* Returns the minimum and maximum values for the dataset's range,
* assuming that the series are stacked.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The range ([0.0, 0.0] if the dataset contains no values).
*/
public static Range findStackedRangeBounds(TableXYDataset dataset) {
return findStackedRangeBounds(dataset, 0.0);
}
/**
* Returns the minimum and maximum values for the dataset's range,
* assuming that the series are stacked, using the specified base value.
*
* @param dataset the dataset ({@code null} not permitted).
* @param base the base value.
*
* @return The range ({@code null} if the dataset contains no values).
*/
public static Range findStackedRangeBounds(TableXYDataset dataset,
double base) {
Args.nullNotPermitted(dataset, "dataset");
double minimum = base;
double maximum = base;
for (int itemNo = 0; itemNo < dataset.getItemCount(); itemNo++) {
double positive = base;
double negative = base;
int seriesCount = dataset.getSeriesCount();
for (int seriesNo = 0; seriesNo < seriesCount; seriesNo++) {
double y = dataset.getYValue(seriesNo, itemNo);
if (!Double.isNaN(y)) {
if (y > 0.0) {
positive += y;
}
else {
negative += y;
}
}
}
if (positive > maximum) {
maximum = positive;
}
if (negative < minimum) {
minimum = negative;
}
}
if (minimum <= maximum) {
return new Range(minimum, maximum);
}
else {
return null;
}
}
/**
* Calculates the total for the y-values in all series for a given item
* index.
*
* @param dataset the dataset.
* @param item the item index.
*
* @return The total.
*/
public static double calculateStackTotal(TableXYDataset dataset, int item) {
double total = 0.0;
int seriesCount = dataset.getSeriesCount();
for (int s = 0; s < seriesCount; s++) {
double value = dataset.getYValue(s, item);
if (!Double.isNaN(value)) {
total = total + value;
}
}
return total;
}
/**
* Calculates the range of values for a dataset where each item is the
* running total of the items for the current series.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The range.
*
* @see #findRangeBounds(CategoryDataset)
*/
public static Range findCumulativeRangeBounds(CategoryDataset dataset) {
Args.nullNotPermitted(dataset, "dataset");
boolean allItemsNull = true; // we'll set this to false if there is at
// least one non-null data item...
double minimum = 0.0;
double maximum = 0.0;
for (int row = 0; row < dataset.getRowCount(); row++) {
double runningTotal = 0.0;
for (int column = 0; column <= dataset.getColumnCount() - 1;
column++) {
Number n = dataset.getValue(row, column);
if (n != null) {
allItemsNull = false;
double value = n.doubleValue();
if (!Double.isNaN(value)) {
runningTotal = runningTotal + value;
minimum = Math.min(minimum, runningTotal);
maximum = Math.max(maximum, runningTotal);
}
}
}
}
if (!allItemsNull) {
return new Range(minimum, maximum);
}
else {
return null;
}
}
/**
* Returns the interpolated value of y that corresponds to the specified
* x-value in the given series. If the x-value falls outside the range of
* x-values for the dataset, this method returns {@code Double.NaN}.
*
* @param dataset the dataset ({@code null} not permitted).
* @param series the series index.
* @param x the x-value.
*
* @return The y value.
*/
public static double findYValue(XYDataset dataset, int series, double x) {
// delegate null check on dataset
int[] indices = findItemIndicesForX(dataset, series, x);
if (indices[0] == -1) {
return Double.NaN;
}
if (indices[0] == indices[1]) {
return dataset.getYValue(series, indices[0]);
}
double x0 = dataset.getXValue(series, indices[0]);
double x1 = dataset.getXValue(series, indices[1]);
double y0 = dataset.getYValue(series, indices[0]);
double y1 = dataset.getYValue(series, indices[1]);
return y0 + (y1 - y0) * (x - x0) / (x1 - x0);
}
/**
* Finds the indices of the the items in the dataset that span the
* specified x-value. There are three cases for the return value:
*
* - there is an exact match for the x-value at index i
* (returns {@code int[] {i, i}});
* - the x-value falls between two (adjacent) items at index i and i+1
* (returns {@code int[] {i, i+1}});
* - the x-value falls outside the domain bounds, in which case the
* method returns {@code int[] {-1, -1}}.
*
* @param dataset the dataset ({@code null} not permitted).
* @param series the series index.
* @param x the x-value.
*
* @return The indices of the two items that span the x-value.
*
* @see #findYValue(org.jfree.data.xy.XYDataset, int, double)
*/
public static int[] findItemIndicesForX(XYDataset dataset, int series,
double x) {
Args.nullNotPermitted(dataset, "dataset");
int itemCount = dataset.getItemCount(series);
if (itemCount == 0) {
return new int[] {-1, -1};
}
if (itemCount == 1) {
if (x == dataset.getXValue(series, 0)) {
return new int[] {0, 0};
} else {
return new int[] {-1, -1};
}
}
if (dataset.getDomainOrder() == DomainOrder.ASCENDING) {
int low = 0;
int high = itemCount - 1;
double lowValue = dataset.getXValue(series, low);
if (lowValue > x) {
return new int[] {-1, -1};
}
if (lowValue == x) {
return new int[] {low, low};
}
double highValue = dataset.getXValue(series, high);
if (highValue < x) {
return new int[] {-1, -1};
}
if (highValue == x) {
return new int[] {high, high};
}
int mid = (low + high) / 2;
while (high - low > 1) {
double midV = dataset.getXValue(series, mid);
if (x == midV) {
return new int[] {mid, mid};
}
if (midV < x) {
low = mid;
}
else {
high = mid;
}
mid = (low + high) / 2;
}
return new int[] {low, high};
}
else if (dataset.getDomainOrder() == DomainOrder.DESCENDING) {
int high = 0;
int low = itemCount - 1;
double lowValue = dataset.getXValue(series, low);
if (lowValue > x) {
return new int[] {-1, -1};
}
double highValue = dataset.getXValue(series, high);
if (highValue < x) {
return new int[] {-1, -1};
}
int mid = (low + high) / 2;
while (high - low > 1) {
double midV = dataset.getXValue(series, mid);
if (x == midV) {
return new int[] {mid, mid};
}
if (midV < x) {
low = mid;
}
else {
high = mid;
}
mid = (low + high) / 2;
}
return new int[] {low, high};
}
else {
// we don't know anything about the ordering of the x-values,
// so we iterate until we find the first crossing of x (if any)
// we know there are at least 2 items in the series at this point
double prev = dataset.getXValue(series, 0);
if (x == prev) {
return new int[] {0, 0}; // exact match on first item
}
for (int i = 1; i < itemCount; i++) {
double next = dataset.getXValue(series, i);
if (x == next) {
return new int[] {i, i}; // exact match
}
if ((x > prev && x < next) || (x < prev && x > next)) {
return new int[] {i - 1, i}; // spanning match
}
}
return new int[] {-1, -1}; // no crossing of x
}
}
}