/**
* Copyright (C) DataMelt project. The jHPLot package by S.Chekanov and Work.ORG
* All rights reserved.
*
* 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 3 of the License, or 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, see .
*
* Additional permission under GNU GPL version 3 section 7:
* If you have received this program as a library with written permission from the DataMelt team,
* you can link or combine this library with your non-GPL project to convey the resulting work.
* In this case, this library should be considered as released under the terms of
* GNU Lesser public license (see ),
* provided you include this license notice and a URL through which recipients can access the
* Corresponding Source.
**/
package jhplot;
import hep.aida.IAnalysisFactory;
import hep.aida.IDataPointSet;
import hep.aida.IDataPointSetFactory;
import hep.aida.ITree;
import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.Date;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import jhplot.gui.HelpBrowser;
import jhplot.io.PReader;
/**
* Data holder in 2D for integer number. It is similar to matrix in 2D. It has
* columns and rows. The number of columns may not be the same. For example,
* data can be presented as:
*
*
*
* 1 2 3 4 5
* 12 3 4
* 1 3 4 555 5 66 77
* 1 2 2 33 434 4
*
*
* All numbers are expected to be integers..
*
* It extends ArrayList and adds many new features for data manipulation. The
* class does not have graphical option (use H1D to show the data or methods of
* this class which transform P0D to a H1D histogram).
*
* @author S.Chekanov
*
*/
public class PNI implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private ArrayList data;
private String title;
private int dimension;
private double min = 0;
private double max = 0;
/**
* Construct an empty container with a title
*
* @param title
* A title
*
*/
public PNI(String title) {
data = new ArrayList();
this.title = title;
this.dimension = 0;
}
/**
* Construct a copy from a PNI. If the last argument is true, a shallow copy
* of a collection. In this case a new collection contains references to
* same objects as the source collection. Data are not cloned
*
* @param title
* new title
* @param shallow
* if true, a shallow copy of a collection.
* @param PNI
* inpit data
*/
public PNI(String title, boolean shallow, PNI pni) {
this.title = title;
this.data = new ArrayList();
this.dimension = pni.getDimension();
if (shallow) {
// ArrayList jplot3d=p0d.getArrayList();
data = (ArrayList) (pni.getArrayList().clone());
} else {
for (int i = 0; i < pni.size(); i++)
data.add(pni.get(i));
}
}
/**
* Construct a container with a title from external file (see the method
* toFile() how to write such ASCII file)
*
* @param title
* A title
*
* @param file
* input file name. It can be either a file on a file system or
* URL location (must start from http or ftp)
*/
public PNI(String title, String file) {
this.dimension = 0;
data = new ArrayList();
this.title = title;
read(file);
}
/**
* Construct an empty container with no title
*
*/
public PNI() {
this("NOT SET");
}
/**
* Set a new title
*
* @param title
* New Title
*/
public void setTitle(String title) {
this.title = title;
}
/**
* Get a new title
*
* @return Title
*/
public String getTitle() {
return this.title;
}
/**
* Clear the container
*/
public void clear() {
data.clear();
}
/**
* Return a specific row as array
*
* @param row
* index of the row
*
* @return array of values
*/
public int[] get(int row) {
return (int[]) data.get(row);
}
/**
* Return a specific row as P0D
*
* @param row
* index of the row
*
* @return array of values
*/
public P0I getRowP0D(int row) {
P0I p = new P0I(title + ":row:" + Integer.toString(row));
p.fill((int[]) data.get(row));
return p;
}
/**
* Return a specific value.
*
* @param row
* row index
* @param column
* column index
*
* @return value
*/
public int get(int row, int column) {
int[] tmp = (int[]) data.get(row);
return tmp[column];
}
/**
* Get a string representing PNI
*
*
* @return String with all values
*/
public String toString() {
String tmp = "\nPNI: " + title + "\n";
for (int i = 0; i < data.size(); i++) {
int[] tt = (int[]) data.get(i);
for (int j = 0; j < tt.length; j++) {
tmp += Double.toString(tt[j]);
tmp += " ";
}
tmp += "\n";
}
return tmp;
}
/**
* Print PNI to System.out.
*/
public void print() {
System.out.println(this.toString());
}
/**
* Return a specific column as an array
*
* @param column
* index of the column
*
* @return array with values.
*
*/
public int[] getColumn(int column) {
int[] tmp = new int[data.size()];
if (column >= dimension) {
ErrorMessage("Index is too large");
return tmp;
}
for (int j = 0; j < data.size(); j++) {
int[] tt = (int[]) data.get(j);
tmp[j] = tt[column];
}
return tmp;
}
/**
* Return 2 columns as P1D to show as a X-Y plot
*
* @param c1
* index of the first column
* @param c2
* index of the second column
*
* @return P1D array with X-Y values.
*
*/
public P1D getP1D(int c1, int c2) {
P1D tmp = new P1D(title + ":" + Integer.toString(c1) + "-"
+ Integer.toString(c2));
tmp.setDimension(2);
if (c1 >= dimension || c2 >= dimension) {
ErrorMessage("Index is too large");
return tmp;
}
for (int j = 0; j < data.size(); j++) {
int[] tt = (int[]) data.get(j);
tmp.add(tt[c1], tt[c2]);
}
return tmp;
}
/**
* Return a specific column as an array
*
* @param column
* index of the column
*
* @return P0D array with values.
*
*/
public P0I getP0I(int column) {
P0I tmp = new P0I(title + " :" + Integer.toString(column));
if (column >= dimension) {
ErrorMessage("Index is too large");
return tmp;
}
for (int j = 0; j < data.size(); j++) {
int[] tt = (int[]) data.get(j);
tmp.add(tt[column]);
}
return tmp;
}
/**
* Return 3 columns as P2D to show as a X-Y-Z plot
*
* @param c1
* index of the first column
* @param c2
* index of the second column
* @param c3
* index of the third column
*
* @return P3D array with X-Y-Z values.
*
*/
public P2D getP2D(int c1, int c2, int c3) {
P2D tmp = new P2D(title + ":" + Integer.toString(c1) + "-"
+ Integer.toString(c2) + "-" + Integer.toString(c3));
if (c1 >= dimension || c2 >= dimension || c3 >= dimension) {
ErrorMessage("Index is too large");
return tmp;
}
for (int j = 0; j < data.size(); j++) {
int[] tt = (int[]) data.get(j);
tmp.add(tt[c1], tt[c2], tt[c3]);
}
return tmp;
}
/**
* Return a specific row as array
*
* @param row
* index of the row
*
* @return array of values
*/
public P0D getRow(int row) {
P0D tmp = new P0D(title + " row=" + Integer.toString(row));
if (row > data.size()) {
ErrorMessage("Index is too large");
return tmp;
}
int[] tt = (int[]) data.get(row);
tmp.setArray(tt);
return tmp;
}
/**
* Get data in form of ArrayList
*
* @return data in form of ArrayList
*/
public ArrayList getArrayList() {
return this.data;
}
/**
* Get the data point set for JAIDA
*
* @return
*/
public IDataPointSet getIDataPointSet() {
IAnalysisFactory af = IAnalysisFactory.create();
ITree tree = af.createTreeFactory().create();
IDataPointSetFactory dpsf = af.createDataPointSetFactory(tree);
IDataPointSet fDps2D = dpsf.create(getTitle(), getTitle(), dimension);
for (int i = 0; i < data.size(); i++) {
fDps2D.addPoint();
int[] tt = (int[]) data.get(i);
for (int j = 0; j < dimension; j++)
fDps2D.point(i).coordinate(j).setValue(tt[j]);
}
return fDps2D;
}
/**
* Adds (appends) the specified element to the end of this list.
*
* @param values
* array of values to be added.
*
*/
public void add(int[] values) {
dimension = values.length;
data.add(values);
}
/**
* Adds (appends) P0D to the end of this list.
*
* @param values
* array of values to be added.
*
*/
public void add(P0I values) {
dimension = values.size();
data.add(values.getArray());
}
/**
* Sets (replace) the specified row.
*
* @param index
* position index
* @param values
* array of values to be added.
*
*/
public void set(int index, int[] values) {
if (index >= data.size()) {
ErrorMessage("Index is too large");
}
dimension = values.length;
data.set(index, values);
}
/**
* Sets (replace) the specified element with P0D.
*
* @param index
* position index
* @param values
* array of values to be added.
*
*/
public void set(int index, P0I values) {
if (index >= data.size()) {
ErrorMessage("Index is too large");
}
dimension = values.size();
data.set(index, values.getArray());
}
/**
*
* Read the data from external file. Old data will be lost. Use "#" or "*"
* for comments.
*
* @param sfile
* File name with input
* @return zero if success.
*/
public int read(BufferedReader br) {
data.clear();
try {
String line;
// dis.available() returns 0 if the file does not have more lines.
while ((line = br.readLine()) != null) {
line = line.trim();
if (!line.startsWith("#") && !line.startsWith("*")) {
StringTokenizer st = new StringTokenizer(line);
dimension = st.countTokens(); // number of words
int[] snum = new int[dimension];
int mm = 0;
while (st.hasMoreTokens()) { // make sure there is stuff
// to get
String tmp = st.nextToken();
// read
int dd = 0;
try {
dd = Integer.parseInt(tmp.trim());
} catch (NumberFormatException e) {
ErrorMessage("Error in reading the line "
+ Integer.toString(mm + 1));
}
snum[mm] = dd;
mm++;
} // end loop over each line
data.add(snum);
} // skip #
// this statement reads the line from the file and print it to
// System.out.println(line);
}
// dispose all the resources after using them.
br.close();
} catch (FileNotFoundException e) {
ErrorMessage("File not found!");
e.printStackTrace();
return 2;
} catch (IOException e) {
e.printStackTrace();
return 1;
}
return 0;
}
/**
*
* Read the data from ZIPed external file. Old data will be lost. Use "#" or
* "*" for comments.
*
* @param sfile
* File name with the input (extension .zip)
* @return zero if success.
*/
public int readZip(String sfile) {
data.clear();
try {
ZipFile zf = new ZipFile(sfile);
Enumeration entries = zf.entries();
BufferedReader input = new BufferedReader(new InputStreamReader(
System.in));
while (entries.hasMoreElements()) {
ZipEntry ze = (ZipEntry) entries.nextElement();
// System.out.println("Read " + ze.getName() + "?");
String inputLine = input.readLine();
if (inputLine.equalsIgnoreCase("yes")) {
long size = ze.getSize();
if (size > 0) {
// System.out.println("Length is " + size);
BufferedReader br = new BufferedReader(
new InputStreamReader(zf.getInputStream(ze)));
String line;
while ((line = br.readLine()) != null) {
line = line.trim();
if (!line.startsWith("#") && !line.startsWith("*")) {
StringTokenizer st = new StringTokenizer(line);
dimension = st.countTokens(); // number of words
int[] snum = new int[dimension];
int mm = 0;
while (st.hasMoreTokens()) { // make sure there
// is stuff
// to get
String tmp = st.nextToken();
// read double
int dd = 0;
try {
dd = Integer.parseInt(tmp.trim());
} catch (NumberFormatException e) {
ErrorMessage("Error in reading the line "
+ Integer.toString(mm + 1));
}
snum[mm] = dd;
mm++;
} // end loop over each line
data.add(snum);
} // skip #
}
br.close();
}
}
}
// this statement reads the line from the file and print it to
} catch (FileNotFoundException e) {
ErrorMessage("File not found!");
e.printStackTrace();
return 2;
} catch (IOException e) {
e.printStackTrace();
return 1;
}
return 0;
}
/**
* Read PNI from a file.
*
* The old content will be lost. Use a space to separate values in columns
* and tab to put new row. Comment lines starting with "#" and "*" are
* ignored.
*
*
* @param sfile
* input file
* @return zero if success
*/
public int read(File sfile) {
BufferedReader is = PReader.read(sfile);
if (is == null)
return 1;
return read(is);
}
/**
* Read data from URL. Use a space to separate values in columns and tab to
* put new row.
*
* @param url
* URL location of input file
*/
public int read(URL url) {
BufferedReader is = PReader.read(url);
if (is == null)
return 1;
return read(is);
}
/**
* Read PNI from a GZiped file. It can read URL if the string starts from
* http or ftp, otherwise a file on the file system is assumed.
*
* Use a space to separate values in columns and tab to put new row.
*
* @param sfile
* File name with input (extension .gz)
* @return zero if success
*/
public int readGZip(String sfile) {
BufferedReader is = PReader.readGZip(sfile);
if (is == null)
return 1;
return read(is);
}
/**
* Read PNI from a file. It can read URL if the string starts from http or
* ftp, otherwise a file on the file system is assumed.
*
* The old content will be lost. Use a space to separate values in columns
* and tab to put new row. Comment lines starting with "#" and "*" are
* ignored.
*
* @param sfile
* File name with input
* @return zero if success
*/
public int read(String sfile) {
BufferedReader is = PReader.read(sfile);
if (is == null)
return 1;
return read(is);
}
/**
* Read PNI from a GZiped file. The old content will be lost. Use a space to
* separate values in columns and tab to put new row. Comment lines start
* from "#" and "*" are ignored.
*
* @param sfile
* File name with input (extension .gz)
* @return zero if success
*/
public int readGZip(File sfile) {
BufferedReader is = PReader.readGZip(sfile);
if (is == null)
return 1;
return read(is);
}
/**
* Write a PNI to an external file. Same method as toFile()
*
* @param name
* File name with output
*/
public void write(String name) {
toFile(name);
}
/**
* Write a P0D object to a serialized file
*
* @param name
* serialized file name for output.
*
* @return zero if no errors
*/
public int writeSerialized(String name) {
return jhplot.io.Serialized.write(this, name);
}
/**
* Read a PNI object from a serialized file
*
* @param name
* serialized file name for input.
*
* @return new PNI object
*/
public PNI readSerialized(String name) {
return (PNI) jhplot.io.Serialized.read(name);
}
/**
* Write a PNI to an external file.
*
* @param name
* File name with output
*/
public void toFile(String name) {
Date dat = new Date();
String today = String.valueOf(dat);
try {
FileOutputStream f1 = new FileOutputStream(new File(name));
PrintStream tx = new PrintStream(f1);
tx.println("# DataMelt: output from PNI " + this.title);
tx.println("# DataMelt: created at " + today);
tx.println("# values:");
tx.println("#");
for (int i = 0; i < data.size(); i++) {
int[] tt = (int[]) get(i);
dimension = tt.length;
for (int j = 0; j < dimension; j++)
tx.print(tt[j]);
tx.print("\n");
}
f1.close();
} catch (IOException e) {
ErrorMessage("Error in the output file");
e.printStackTrace();
}
}
/**
* Remove a row
*
* @param index
* row index to be removed
*/
public PNI remove(int index) {
data.remove(index);
return this;
}
/**
* Operations on PNI containers: add, subtract, multiply, divide. Keep the
* same graphical attributes and title.
*
* @param PNI
* Input PNI container for operation
* @param what
* String representing the operation: "+" add a P0D container to
* the original; "-" subtract a P0D from the original; "*"
* multiply; "/" divide by P0D
* @return original PNI after the operation.
*/
public PNI oper(PNI PNI, String what) {
return oper(PNI, getTitle(), what);
}
/**
* Operations on PNI containers: add, subtract, multiply, divide. Keep the
* same graphical attributes
*
* @param PNI
* Input PNI container for operation
* @param title
* New title
* @param what
* String representing the operation: "+" add a P0D container to
* the original; "-" subtract a P0D from the original; "*"
* multiply; "/" divide by P0D
* @return original PNI after the operation.
*/
public PNI oper(PNI PNI, String title, String what) {
what = what.trim();
// first check them
if (data.size() != PNI.size()) {
ErrorMessage("Sizes of the PNIs are different!");
return this;
}
// first check them
if (dimension != PNI.getDimension()) {
ErrorMessage("Dimensions of the PNIs are different!");
return this;
}
if (what.equals("+")) {
for (int i = 0; i < data.size(); i++) {
int[] tt1 = (int[]) get(i);
int[] tt2 = (int[]) PNI.get(i);
for (int j = 0; j < tt1.length; j++)
tt1[j] = tt1[j] + tt2[j];
data.set(i, tt1);
}
return this;
}
if (what.equals("-")) {
for (int i = 0; i < data.size(); i++) {
int[] tt1 = (int[]) get(i);
int[] tt2 = (int[]) PNI.get(i);
for (int j = 0; j < tt1.length; j++)
tt1[j] = tt1[j] - tt2[j];
data.set(i, tt1);
}
return this;
}
if (what.equals("*")) {
for (int i = 0; i < data.size(); i++) {
int[] tt1 = (int[]) get(i);
int[] tt2 = (int[]) PNI.get(i);
for (int j = 0; j < tt1.length; j++)
tt1[j] = tt1[j] * tt2[j];
data.set(i, tt1);
}
return this;
}
if (what.equals("/")) {
for (int i = 0; i < data.size(); i++) {
int[] tt1 = (int[]) get(i);
int[] tt2 = (int[]) PNI.get(i);
for (int j = 0; j < tt1.length; j++)
tt1[j] = tt1[j] / tt2[j];
data.set(i, tt1);
}
return this;
}
return this;
}
/**
* Show container to a Table in a separate Frame. The numbers are formatted
* to scientific format. One can sort and search the data in this table (but
* not modify)
*/
public void toTable() {
new HTable(this);
}
/**
* Get last dimension of the data (or number of columns). The stored
* dimention is the one set after the last call "add", or if it set
* manually.
*
* @return dimension (number of elements in a row)
*/
public int getDimension() {
return dimension;
}
/**
* Get the numbers of columns. The stored dimention is the one set after the
* last call "add", or if it set manually.
*
* @return dimension (number of elements in a row)
*/
public int[] getDimensions() {
int[] ii = new int[data.size()];
for (int j = 0; j < data.size(); j++) {
int[] tt = (int[]) data.get(j);
ii[j] = tt.length;
}
return ii;
}
/**
* Scale each element of data
*
* @param scale
* Scale factor
*
*/
public void operScale(double scale) {
for (int i = 0; i < data.size(); i++) {
int[] tt1 = (int[]) get(i);
for (int j = 0; j < tt1.length; j++)
tt1[j] = (int) (tt1[j] * scale);
data.set(i, tt1);
}
return;
}
/**
* Data size (number of rows)
*
* @return number of rows
*/
public int size() {
return data.size();
}
/**
* Create an exact copy of the current P0D. New object is created.
*
* @param newtitle
* new title
*/
public PNI copy(String newtitle) {
PNI tmp = new PNI(newtitle);
for (int i = 0; i < data.size(); i++) {
int[] tt = (int[]) data.get(i);
int[] clone = (int[]) tt.clone();
tmp.add(clone);
}
return tmp;
}
/**
* Get rows : min is inxluded, max is not;
*
* @param title
* New title
* @param indexMin
* min index of row
* @param indexMax
* max index of row
* @return new PNI with rows indexMin-indexMax
**/
public PNI getRows(String newtitle, int indexMin, int indexMax) {
PNI tmp = new PNI(newtitle);
if (indexMin < 0) {
ErrorMessage("Wrong min index");
return null;
}
if (indexMax > data.size()) {
ErrorMessage("Wrong max index");
return null;
}
;
for (int i = indexMin; i < indexMax; i++)
tmp.add(get(i));
return tmp;
}
/**
* Set the data in form of ArrayList
*
* @param array
* ArrayList to be set.
*/
public void setArrayList(ArrayList array) {
this.data = array;
}
/*
* int[][] a = new int[2][4];
*
* This two-dimensional array will have two rows and four columns.
*
*
* In Java two-dimensional arrays are implemented is a one-dimensional array
* of one-dimensional arrays -- like this.
*/
/**
* Get a double array with values.
*
* @return double array with values
*/
public int[][] getArray() {
int[][] tmp = new int[data.size()][dimension];
for (int i = 0; i < data.size(); i++) {
int[] tt = (int[]) data.get(i);
for (int j = 0; j < tt.length; j++)
tmp[i][j] = tt[j];
}
return tmp;
}
/**
* Set values from double array Old content will be lost.
*
* @param values
* array to be set: [ROWS][dimension];
*
* @return PNI with new values
*/
public PNI setArray(int[][] values) {
if (values[0].length != dimension) {
ErrorMessage("Input array has too large dimension");
return this;
}
data.clear();
for (int i = 0; i < values.length; i++)
data.add(values[i]);
return this;
}
/**
* Generate error message
*
* @param a
* Message
*/
private void ErrorMessage(String a) {
jhplot.utils.Util.ErrorMessage(a);
}
/**
* Test
*
* @param args
*/
public static void main(String[] args) {
HPlot c1 = new HPlot("Canvas", 600, 400);
c1.visible(true);
c1.setAutoRange();
PNI p0 = new PNI("Example");
p0.add(new int[] { 1, 2, 3 });
p0.add(new int[] { 2, 5, 1 });
System.out.println(p0.toString());
c1.setNameX("X");
c1.setNameY("Y");
p0.print();
P0I ppp = p0.getP0I(1);
// drow as histogram
H1D h1 = ppp.getH1D(100);
c1.draw(h1);
}
/**
* Return H1D histogram with PND content. All values are added. Histogram
* range is defined by Min and Max values.
*
* @param bins
* Number of bins for the histogram.
* @param min
* Min value of histogram
* @param max
* Max value of histogram
* @return H2D histogram filled with P0D.
*/
public H1D getH1D(int bins, double min, double max) {
H1D h1d = new H1D(this.title, bins, min, max);
for (int n = 0; n < getDimension(); n++) {
for (int j = 0; j < size(); j++)
h1d.fill((double) get(j, n));
}
return h1d;
}
/**
* Create histogram. Min and Max are determined authomatically.
*
* @param bins
* Number of bins
* @return
*/
public H1D getH1D(int bins) {
getMinMax();
return getH1D(bins, min, max);
}
/**
* Make a histogram from a column of PND.
*
* @param bins
* Number of bins
* @param min
* Min value
* @param max
* Max value
* @param column
* column of PND ( maxValue) {
maxValue = get(j, n);
}
}
}
return maxValue;
}
/**
* Get max value
*
* @return
*/
public double getMin() {
double minValue = get(0, 0);
for (int n = 0; n < getDimension(); n++) {
for (int j = 0; j < size(); j++) {
if (get(j, n) < minValue) {
minValue = get(j, n);
}
}
}
return minValue;
}
/**
* Get min and max at the same time.
*/
private void getMinMax() {
min = get(0, 0);
min = max;
for (int n = 0; n < getDimension(); n++) {
for (int j = 0; j < size(); j++) {
if (get(j, n) < min) {
min = get(j, n);
}
if (get(j, n) > max) {
max = get(j, n);
}
}
}
}
/**
* Show online documentation.
*/
public void doc() {
String a = this.getClass().getName();
a = a.replace(".", "/") + ".html";
new HelpBrowser(HelpBrowser.JHPLOT_HTTP + a);
}
}