/************************************************************************** /* 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);
}
}