001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.ml.timeseries.collection;
031
032import java.util.ArrayList;
033import java.util.Collection;
034import java.util.HashMap;
035import java.util.HashSet;
036import java.util.Iterator;
037import java.util.Map;
038import java.util.Map.Entry;
039import java.util.Set;
040
041import org.openimaj.ml.timeseries.IncompatibleTimeSeriesException;
042import org.openimaj.ml.timeseries.TimeSeries;
043import org.openimaj.ml.timeseries.TimeSeriesSetException;
044import org.openimaj.ml.timeseries.converter.TimeSeriesConverter;
045import org.openimaj.ml.timeseries.processor.TimeSeriesProcessor;
046import org.openimaj.util.pair.IndependentPair;
047
048/**
049 * A collection of time series which share exactly the same time steps.
050 * Given that this is true, SynchronisedTimeSeriesProcessor instances can perform interesting analysis
051 *  
052 * @author Sina Samangooei (ss@ecs.soton.ac.uk)
053 * @param <ALLINPUT> The collection input type 
054 * @param <SINGLEINPUT> The type of a single data type 
055 * @param <TIMESERIES> the type of time series returned
056 * @param <INTERNALSERIES> the type of time series held
057 *
058 */
059public abstract class TimeSeriesCollection<
060        ALLINPUT, 
061        SINGLEINPUT, 
062        TIMESERIES extends TimeSeriesCollection<ALLINPUT, SINGLEINPUT,TIMESERIES,INTERNALSERIES>,
063        INTERNALSERIES extends TimeSeries<ALLINPUT,SINGLEINPUT,INTERNALSERIES>
064> extends TimeSeries<
065        Map<String,ALLINPUT>, 
066        Map<String,SINGLEINPUT>,
067        TIMESERIES
068> {
069        
070        protected HashMap<String, INTERNALSERIES> timeSeriesHolder;
071
072        /**
073         * initialise the underlying time series holder
074         */
075        public TimeSeriesCollection(){
076                this.timeSeriesHolder = new HashMap<String,INTERNALSERIES>();
077        }
078        
079        /**
080         * @param name
081         * @param series
082         * @throws IncompatibleTimeSeriesException
083         */
084        public void addTimeSeries(String name, INTERNALSERIES series) throws IncompatibleTimeSeriesException {
085                this.timeSeriesHolder.put(name, series);
086                
087        }
088        
089        /**
090         * @param name the name of the series
091         * @return the series held by the name
092         */
093        public INTERNALSERIES series(String name){
094                return this.timeSeriesHolder.get(name);
095        }
096        
097        /**
098         * @return all the time series held
099         */
100        public Collection<INTERNALSERIES> allseries(){
101                return this.timeSeriesHolder.values();
102        }
103        
104
105        @Override
106        public TIMESERIES get(long time, int nbefore, int nafter) {
107                TIMESERIES t = newInstance();
108                HashMap<String, ALLINPUT> inputs = new HashMap<String,ALLINPUT>();
109                long[] times = null;
110                for (Entry<String, INTERNALSERIES> a : this.timeSeriesHolder.entrySet()) {
111                        INTERNALSERIES sub = a.getValue().get(time, nbefore, nafter);
112                        times = sub.getTimes();
113                        inputs.put(a.getKey(), sub.getData());
114                }
115                try{
116                        t.set(times, inputs);
117                }
118                catch(Exception e){
119                        e.printStackTrace();
120                }
121                return t;
122        }
123        
124        @Override
125        public Map<String, ALLINPUT> getData() {
126                Map<String, ALLINPUT> ret = new HashMap<String, ALLINPUT>();
127                for (Entry<String, INTERNALSERIES> es : this.timeSeriesHolder.entrySet()) {
128                        ret.put(es.getKey(), es.getValue().getData());
129                }
130                
131                return ret;
132        }
133        
134        @Override
135        public TIMESERIES get(long start, long end) {
136                TIMESERIES t = newInstance();
137                HashMap<String, ALLINPUT> inputs = new HashMap<String,ALLINPUT>();
138                long[] times = null;
139                for (Entry<String, INTERNALSERIES> a : this.timeSeriesHolder.entrySet()) {
140                        INTERNALSERIES sub = a.getValue().get(start,end);
141                        times = sub.getTimes();
142                        inputs.put(a.getKey(), sub.getData());
143                }
144                try{
145                        t.set(times, inputs);
146                }
147                catch(Exception e){
148                        
149                }
150                return t;
151        }
152        @Override
153        public TIMESERIES get(long time, int nbefore, int nafter, TIMESERIES output) {
154                HashMap<String, ALLINPUT> inputs = new HashMap<String,ALLINPUT>();
155                long[] times = null;
156                for (Entry<String, INTERNALSERIES> a : this.timeSeriesHolder.entrySet()) {
157                        INTERNALSERIES sub = a.getValue().get(time,nbefore,nafter,output.timeSeriesHolder.get(a.getKey()));
158                        times = sub.getTimes();
159                        inputs.put(a.getKey(), sub.getData());
160                }
161                try{
162                        output.set(times, inputs);
163                }
164                catch(Exception e){
165                        
166                }
167                return output;
168        }
169        
170        /**
171         * @return an instance of the internal series held
172         */
173        public abstract INTERNALSERIES internalNewInstance();
174        
175        @Override
176        public TIMESERIES get(long time, long threshbefore, long threshafter) {
177                TIMESERIES t = newInstance();
178                HashMap<String, ALLINPUT> inputs = new HashMap<String,ALLINPUT>();
179                long[] times = null;
180                for (Entry<String, INTERNALSERIES> a : this.timeSeriesHolder.entrySet()) {
181                        INTERNALSERIES sub = a.getValue().get(time,threshbefore,threshafter);
182                        times = sub.getTimes();
183                        inputs.put(a.getKey(), sub.getData());
184                }
185                try{
186                        t.set(times, inputs);
187                }
188                catch(Exception e){
189                        
190                }
191                return t;
192        }
193        
194        /**
195         * @param names
196         * @return a new time series containing sub time series by name
197         */
198        public TIMESERIES collectionByNames(String... names) {
199                Map<String, ALLINPUT> ret = new HashMap<String, ALLINPUT>();
200                ArrayList<long[]> times = new ArrayList<long[]>();
201                for (String name: names) {
202                        INTERNALSERIES exists = this.timeSeriesHolder.get(name);
203                        if(exists != null) {
204                                ret.put(name, exists.getData());
205                                times.add(exists.getTimes());
206                        }
207                }
208                
209                TIMESERIES rets = newInstance();
210                Iterator<long[]> titer = times.iterator();
211                try {
212                        rets.set(titer.next(), ret);
213                } catch (TimeSeriesSetException e) {
214                }
215                return rets;
216        }
217        
218        /**
219         * @param names
220         * @return a new time series containing sub time series by name
221         */
222        public TIMESERIES collectionByNames(Collection<String> names){
223                return collectionByNames(names.toArray(new String[names.size()]));
224        }
225        
226        /**
227         * @return the set of names of the time series in this collection
228         */
229        public Set<String> getNames(){
230                Set<String> copy = new HashSet<String>();
231                for (String string : this.timeSeriesHolder.keySet()) {
232                        copy.add(string);
233                }
234                return copy;
235        }
236        
237        /**
238         * @return number of series held
239         */
240        public int nSeries() {
241                return this.timeSeriesHolder.size();
242        }
243        
244        /**
245         * process the internal series held by this collection with this processor
246         * @param tsp
247         * @return a new instance of each internal series held in the same type of collection
248         */
249        public TIMESERIES processInternal(TimeSeriesProcessor<ALLINPUT, SINGLEINPUT,INTERNALSERIES> tsp){
250                TIMESERIES inst = newInstance();
251                for (Entry<String, INTERNALSERIES> type: this.timeSeriesHolder.entrySet()) {
252                        try {
253                                inst.addTimeSeries(type.getKey(), type.getValue().process(tsp));
254                        } catch (IncompatibleTimeSeriesException e) {
255                        }
256                }
257                return inst;
258        }
259        
260        /**
261         * process the internal series held by this collection with this processor
262         * @param tsp
263         * @return each held instance processed
264         */
265        public TIMESERIES processInternalInplace(TimeSeriesProcessor<ALLINPUT, SINGLEINPUT,INTERNALSERIES> tsp){
266                TIMESERIES inst = newInstance();
267                for (Entry<String, INTERNALSERIES> type: this.timeSeriesHolder.entrySet()) {
268                        try {
269                                inst.addTimeSeries(type.getKey(), type.getValue().processInplace(tsp));
270                        } catch (IncompatibleTimeSeriesException e) {
271                        }
272                }
273                return inst;
274        }
275        
276        /**
277         * @param <OUTPUTALL>
278         * @param <OUTPUTSINGLE>
279         * @param <OUTPUTTS>
280         * @param <OUTPUTTSC> 
281         * @param tsc the method to convert each item
282         * @param inst the instance to be filled
283         * @return the inputed instance for convenience 
284         */
285        public <
286                OUTPUTALL, 
287                OUTPUTSINGLE, 
288                OUTPUTTS extends TimeSeries<OUTPUTALL, OUTPUTSINGLE, OUTPUTTS>,
289                OUTPUTTSC extends TimeSeriesCollection<OUTPUTALL, OUTPUTSINGLE, OUTPUTTSC, OUTPUTTS>> 
290                OUTPUTTSC
291                convertInternal(
292                        TimeSeriesConverter<ALLINPUT, SINGLEINPUT, INTERNALSERIES, OUTPUTALL, OUTPUTSINGLE, OUTPUTTS> tsc,
293                        OUTPUTTSC inst
294                )
295        {
296                for (Entry<String, INTERNALSERIES> type: this.timeSeriesHolder.entrySet()) {
297                        try {
298                                inst.addTimeSeries(type.getKey(), type.getValue().convert(tsc));
299                        } catch (IncompatibleTimeSeriesException e) {
300                        }
301                }
302                return inst;
303        }
304        
305        /**
306         * @param <OUTPUTALL>
307         * @param <OUTPUTSINGLE>
308         * @param <OUTPUTTS>
309         * @param <OUTPUTTSC> 
310         * @param tsc the method to convert each item
311         * @param tsp a processor to apply during conversion
312         * @param inst the instance to be filled
313         * @return the inputed instance for convenience 
314         */
315        public <
316                OUTPUTALL, 
317                OUTPUTSINGLE, 
318                OUTPUTTS extends TimeSeries<OUTPUTALL, OUTPUTSINGLE, OUTPUTTS>,
319                OUTPUTTSC extends TimeSeriesCollection<OUTPUTALL, OUTPUTSINGLE, OUTPUTTSC, OUTPUTTS>> 
320                OUTPUTTSC
321                convertInternal(
322                        TimeSeriesConverter<ALLINPUT, SINGLEINPUT, INTERNALSERIES, OUTPUTALL, OUTPUTSINGLE, OUTPUTTS> tsc,
323                        TimeSeriesProcessor<OUTPUTALL, OUTPUTSINGLE,OUTPUTTS> tsp,
324                        OUTPUTTSC inst
325                )
326        {
327                for (Entry<String, INTERNALSERIES> type: this.timeSeriesHolder.entrySet()) {
328                        try {
329                                inst.addTimeSeries(type.getKey(), type.getValue().convert(tsc,tsp));
330                        } catch (IncompatibleTimeSeriesException e) {
331                        }
332                }
333                return inst;
334        }
335        
336        
337        @Override
338        public Iterator<IndependentPair<Long, Map<String, SINGLEINPUT>>> iterator() {
339                throw new UnsupportedOperationException();
340        }
341        
342        @Override
343        public long[] getTimes() {
344                throw new UnsupportedOperationException();
345        }
346        
347        @Override
348        public int size() {
349                throw new UnsupportedOperationException();
350        }
351        
352        @Override
353        public void set(long[] time, Map<String, ALLINPUT> data)throws TimeSeriesSetException {
354                for (Entry<String, ALLINPUT> l : data.entrySet()) {
355                        INTERNALSERIES instance = internalNewInstance();
356                        instance.internalAssign(time,l.getValue());
357                        this.timeSeriesHolder.put(l.getKey(), instance);
358                }
359                
360        }
361}