/************************************************************************** /* This class is part of the Java Plotlib Toolkit. /* /* Copyright (c) 1999-2004 by Bernhard Bablok (bablokb@gmx.de) /* /* This program is free software; you can redistribute it and/or modify /* it under the terms of the GNU General Public License as published /* by the Free Software Foundation; either version 2 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 Library General Public License for more details. /* /* You should have received a copy of the GNU General Public License /* along with this program; see the file COPYING. If not, write to /* the Free Software Foundation Inc., 59 Temple Place - Suite 330, /* Boston, MA 02111-1307 USA /**************************************************************************/ package de.bablokb.plotlib.data; import java.util.*; import java.awt.geom.*; import javax.swing.event.*; /** Encapsulate a set of datapoints. This class stores objects of type Point2D in a collection and additionally provides a number of simple statistical functions on both x- and y-coordinates.

Dataset is a bean signaling an event when changed. Dataset could also implement Collection directly (to enforce manipulation only via methods of this class), but this would limit collection manipulation methods too much (e.g. a SplinePlot might need to insert additional points into the dataset). Therefore, the method invalidate() must be used to signal a change of the Dataset if the collection is manipulated directly.

FIXME: Statistical methods could calculate results incrementally. This also needs the invalidate() method. @version $Revision: 1.3 $ @author $Author: bablokb $ */ public class Dataset { /** Collection holding the datapoints. */ private Collection iData = null; /** List of DatasetListeners to notify of changes in this Dataset. */ private EventListenerList iListener = new EventListenerList(); /** Event sent to all listeners. This event is created dynamically. */ private DatasetChangedEvent iEvent = null; ////////////////////////////////////////////////////////////////////////////// /** Construct a Dataset with no collection specified. */ public Dataset() { } ////////////////////////////////////////////////////////////////////////////// /** Construct a Dataset from the specified collection. */ public Dataset(Collection c) { iData = c; } ///////////////////////////////////////////////////////////////////////////// /** Set the data to the given collection. */ public void setData(Collection c) { iData = c; } ///////////////////////////////////////////////////////////////////////////// /** Get the implementing collection. */ public Collection getData() { return iData; } ///////////////////////////////////////////////////////////////////////////// /** Add a DatasetListener to the list of listeners. */ public void addDatasetListener(DatasetListener l) { iListener.add(DatasetListener.class, l); } ///////////////////////////////////////////////////////////////////////////// /** Remove a DatasetListener from the list of listeners. */ public void removeDatasetListener(DatasetListener l) { iListener.remove(DatasetListener.class, l); } ///////////////////////////////////////////////////////////////////////////// /** Notify all listeners of a change in this dataset. */ protected void notifyListeners() { Object[] listeners = iListener.getListenerList(); for (int i = listeners.length-2; i>=0; i-=2) { if (iEvent == null) iEvent = new DatasetChangedEvent(this); ((DatasetListener)listeners[i+1]).datasetChanged(iEvent); } } ///////////////////////////////////////////////////////////////////////////// /** Invalidate the current state of the Dataset. This will notify all listeners and recalculate the statistics.

FIXME: Incremental calculation of statistics is not yet implemented. Therefore this method only notifies listeners. */ public void invalidate() { notifyListeners(); } ///////////////////////////////////////////////////////////////////////////// /** Add a point to the Dataset. This is a convinience wrapper to the add()-method of java.util.Collection. If no collection exists, an ArrayList is created. Note that this method does not notify listeners. This is done for performance reasons. Use the method addAndNotify(Point2D p) instead if notification should be performed automatically. After extensive changes of the Dataset using add(), the method invalidate() should be called to recalculate statistics and to signal the change to all listeners. */ public void add(Point2D p) { if (iData == null) iData = new ArrayList(); iData.add(p); } ///////////////////////////////////////////////////////////////////////////// /** Add a point to the Dataset and notify all listeners of the change. */ public void addAndNotify(Point2D p) { add(p); notifyListeners(); } ///////////////////////////////////////////////////////////////////////////// /** Return an iterator of the Dataset. This is a convinience wrapper to the iterator()-method of java.util.Collection. */ public Iterator iterator() { if (iData == null) return null; else return iData.iterator(); } ///////////////////////////////////////////////////////////////////////////// /** Number of elements in the dataset. */ public int n() { if (iData == null) return 0; else return iData.size(); } ///////////////////////////////////////////////////////////////////////////// /** Minimum of x-coordinates. */ public double xmin() { if (iData == null) throw new MissingDatasetException(); else if (iData.size() == 0) throw new NoDataException(); double min = Double.POSITIVE_INFINITY; Iterator it = iData.iterator(); while (it.hasNext()) { Point2D p = (Point2D) it.next(); if (p.getX() < min) min = p.getX(); } return min; } ///////////////////////////////////////////////////////////////////////////// /** Maximum of x-coordinates. */ public double xmax() { if (iData == null) throw new MissingDatasetException(); else if (iData.size() == 0) throw new NoDataException(); double max = Double.NEGATIVE_INFINITY; Iterator it = iData.iterator(); while (it.hasNext()) { Point2D p = (Point2D) it.next(); if (p.getX() > max) max = p.getX(); } return max; } ///////////////////////////////////////////////////////////////////////////// /** Mean of x-coordinates. */ public double xmean() { if (iData == null) throw new MissingDatasetException(); else if (iData.size() == 0) throw new NoDataException(); double mean = 0; Iterator it = iData.iterator(); while (it.hasNext()) { Point2D p = (Point2D) it.next(); mean += p.getX(); } return mean/iData.size(); } ///////////////////////////////////////////////////////////////////////////// /** Variance of x-coordinates. */ public double xvar() { if (iData == null) throw new MissingDatasetException(); else if (iData.size() < 2) throw new NoDataException(); double var = 0, mean = xmean(); Iterator it = iData.iterator(); while (it.hasNext()) { Point2D p = (Point2D) it.next(); var += (p.getX()-mean)*(p.getX()-mean); } return var/(iData.size()-1); } ///////////////////////////////////////////////////////////////////////////// /** Minimum of y-coordinates. */ public double ymin() { if (iData == null) throw new MissingDatasetException(); else if (iData.size() == 0) throw new NoDataException(); double min = Double.POSITIVE_INFINITY; Iterator it = iData.iterator(); while (it.hasNext()) { Point2D p = (Point2D) it.next(); if (p.getY() < min) min = p.getY(); } return min; } ///////////////////////////////////////////////////////////////////////////// /** Maximum of y-coordinates. */ public double ymax() { if (iData == null) throw new MissingDatasetException(); else if (iData.size() == 0) throw new NoDataException(); double max = Double.NEGATIVE_INFINITY; Iterator it = iData.iterator(); while (it.hasNext()) { Point2D p = (Point2D) it.next(); if (p.getY() > max) max = p.getY(); } return max; } ///////////////////////////////////////////////////////////////////////////// /** Mean of y-coordinates. */ public double ymean() { if (iData == null) throw new MissingDatasetException(); else if (iData.size() == 0) throw new NoDataException(); double mean = 0; Iterator it = iData.iterator(); while (it.hasNext()) { Point2D p = (Point2D) it.next(); mean += p.getY(); } return mean/iData.size(); } ///////////////////////////////////////////////////////////////////////////// /** Variance of y-coordinates. */ public double yvar() { if (iData == null) throw new MissingDatasetException(); else if (iData.size() < 2) throw new NoDataException(); double var = 0, mean = ymean(); Iterator it = iData.iterator(); while (it.hasNext()) { Point2D p = (Point2D) it.next(); var += (p.getY()-mean)*(p.getY()-mean); } return var/(iData.size()-1); } ///////////////////////////////////////////////////////////////////////////// /** Covariance of (x,y)-coordinates. */ public double cov() { if (iData == null) throw new MissingDatasetException(); else if (iData.size() < 2) throw new NoDataException(); double cov = 0, xMean = xmean(), yMean = ymean(); Iterator it = iData.iterator(); while (it.hasNext()) { Point2D p = (Point2D) it.next(); cov += (p.getX()-xMean)*(p.getY()-yMean); } return cov/(iData.size()-1); } }