001/* *********************************************************************** *
002 * project: org.matsim.*
003 *                                                                         *
004 * *********************************************************************** *
005 *                                                                         *
006 * copyright       : (C) 2012 by the members listed in the COPYING,        *
007 *                   LICENSE and WARRANTY file.                            *
008 * email           : info at matsim dot org                                *
009 *                                                                         *
010 * *********************************************************************** *
011 *                                                                         *
012 *   This program is free software; you can redistribute it and/or modify  *
013 *   it under the terms of the GNU General Public License as published by  *
014 *   the Free Software Foundation; either version 2 of the License, or     *
015 *   (at your option) any later version.                                   *
016 *   See also COPYING, LICENSE and WARRANTY file                           *
017 *                                                                         *
018 * *********************************************************************** */
019
020package org.matsim.analysis;
021
022import java.io.BufferedWriter;
023import java.io.IOException;
024import java.text.DecimalFormat;
025import java.util.Collections;
026import java.util.List;
027import java.util.Vector;
028
029import org.apache.log4j.Logger;
030import org.matsim.core.utils.charts.BarChart;
031import org.matsim.core.utils.io.IOUtils;
032
033public class Bins {
034
035        protected double interval;
036        protected int numberOfBins;
037        protected double maxVal;
038        protected String desc;
039        protected List<BinEntry> entries = new Vector<BinEntry>();
040        protected double [] bins;
041
042        private final static Logger log = Logger.getLogger(Bins.class);
043
044        public Bins(double interval, double maxVal, String desc) {
045                this.interval = interval;
046                this.maxVal = maxVal;
047                this.numberOfBins = (int)Math.ceil(maxVal / interval);
048                this.desc = desc;
049                this.bins = new double[this.numberOfBins];
050        }
051        public void addValues(double[] values, double[] weights) {
052                for (int index = 0; index < values.length; index++) {
053                        this.addVal(values[index], weights[index]);
054                }
055        }
056
057        public void addVal(double value, double weight) {
058                int index = (int)Math.floor(value / interval);
059                // values > maximum value are assigned to the last bin
060                if (value >= maxVal) {
061                        index = this.numberOfBins -1; 
062                }
063
064                // values < 0.0 value are assigned to the first bin
065                if (value < 0.0) {
066                        log.error("Value < 0.0 received");
067                        index = 0;
068                }
069                
070                this.bins[index] += weight;
071                this.entries.add(new BinEntry(value, weight));
072        }
073
074        public void clear() {
075                this.entries.clear();
076                this.bins = new double[this.numberOfBins];
077        }
078
079        public void plotBinnedDistribution(String path, String xLabel, String xUnit) {
080                String [] categories  = new String[this.numberOfBins];
081                for (int i = 0; i < this.numberOfBins; i++) {
082                        categories[i] = Integer.toString(i);
083                }
084                Double[] values = new Double[this.entries.size()];
085                Double[] weights = new Double[this.entries.size()];
086
087                for (int index = 0; index < this.entries.size(); index++) {
088                        values[index] = this.entries.get(index).getValue();
089                        weights[index] = this.entries.get(index).getWeight();
090                }
091
092                DecimalFormat formatter = new DecimalFormat("0.0000");
093                String s = xLabel + " " +
094                "[interval = " + formatter.format(this.interval) + xUnit + "]" +
095                "[number of entries = " + this.entries.size() + "]" +
096                "[mean = " + formatter.format(this.weightedMean(values, weights)) + xUnit + "]" +
097                "[median = " + formatter.format(this.median(values)) + xUnit + "]" +
098                "[max = " + formatter.format(this.getMax(values)) + xUnit + "]";
099
100                BarChart chart =
101                        new BarChart(desc, s , "#", categories);
102                chart.addSeries("Bin size", this.bins);
103                chart.saveAsPng(path + desc + ".png", 1600, 800);
104
105                try {
106                        BufferedWriter out = IOUtils.getBufferedWriter(path + desc + ".txt");
107                        out.write("Bin [interval = " + this.interval + " " + xUnit  + "]\t" + "#" + "\n");
108                        for (int j = 0; j < bins.length;  j++) {
109                                out.write(j + "\t" + bins[j] + "\n");
110                        }
111                        out.flush();
112                        out.close();
113                } catch (IOException e) {
114                        e.printStackTrace();
115                }
116        }
117        public double[] getBins() {
118                return bins;
119        }
120        public void setBins(double[] bins) {
121                this.bins = bins;
122        }
123                
124        public double getInterval() {
125                return interval;
126        }
127        public void setInterval(double interval) {
128                this.interval = interval;
129        }
130        // ----------------------------------------------------------------------------
131        private double median(Double [] values) {
132                List<Double> list = new Vector<Double>();
133
134        Collections.addAll(list, values);
135                return median(list);
136        }
137        
138        public double median(List<Double> values) {
139
140                if (values.size() == 0) return 0.0;
141
142            Collections.sort(values);
143            if (values.size() % 2 != 0) {
144                return values.get((values.size()+1)/2-1);
145            }
146            else {
147                double lower = values.get(values.size()/2-1);
148                double upper = values.get(values.size()/2);
149                return (lower + upper) / 2.0;
150            }
151        }
152        
153        public double mean(List<Double> values) {
154                double sum = 0.0;
155                int cnt = 0;
156                if (values.size() == 0) return 0.0;
157                for (Double value : values) {
158                        sum += value;
159                        cnt++;
160                }
161                return sum / cnt;
162        }
163        
164        public double weightedMean(List<Double> values, List<Double> weights) {
165                return weightedMean(values.toArray(new Double[values.size()]), weights.toArray(new Double[weights.size()]));
166        }
167        
168        public double weightedMean(Double[] values, Double[] weights) {
169                double sumValues = 0.0;
170                double sumWeights = 0.0;
171                
172                if (values.length == 0) return 0.0;
173                
174                if (values.length  != weights.length ) {
175                        log.info("size of weights and values not identical");
176                        return -1;
177                }
178                for (int index = 0; index < values.length; index++) {
179                        sumValues += (values[index] * weights[index]);
180                        sumWeights += weights[index];
181                }
182                return sumValues / sumWeights;
183        }
184        
185        public double getMax(List<Double> values) {
186                return getMax(values.toArray(new Double[values.size()]));
187        }
188        
189        public double getMax(Double[] values) {
190                double maxVal = Double.MIN_VALUE;
191                for (Double v : values) {
192                        if (v > maxVal) {
193                                maxVal = v;
194                        }
195                }
196                return maxVal;
197        }
198}