1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package p3j.experiment.results;
17
18 import java.io.BufferedInputStream;
19 import java.io.BufferedOutputStream;
20 import java.io.File;
21 import java.io.FileOutputStream;
22 import java.io.FileWriter;
23 import java.io.IOException;
24 import java.io.Writer;
25 import java.text.SimpleDateFormat;
26 import java.util.ArrayList;
27 import java.util.Calendar;
28 import java.util.Collections;
29 import java.util.Comparator;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.logging.Level;
34
35 import org.jamesii.SimSystem;
36 import org.jamesii.core.util.misc.Strings;
37 import org.jamesii.core.util.misc.Triple;
38
39 import p3j.database.DatabaseFactory;
40 import p3j.database.IProjectionResultsIterator;
41 import p3j.experiment.results.filters.IResultFilter;
42 import p3j.gui.P3J;
43 import p3j.misc.IProgressObserver;
44 import p3j.misc.gui.GUI;
45 import p3j.misc.math.Matrix2D;
46 import p3j.pppm.ProjectionModel;
47 import p3j.pppm.SubPopulation;
48 import freemarker.template.Configuration;
49 import freemarker.template.DefaultObjectWrapper;
50 import freemarker.template.Template;
51 import freemarker.template.TemplateException;
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 public class ResultExport {
69
70
71 public static final String R_LIBRARY = "plotting.R";
72
73
74 public static final String TEMPLATE_DIRECTORY = "report_template";
75
76
77 private static final int COPY_BUFFER_SIZE = 1024;
78
79
80 private final IResultFilter resultFilter;
81
82
83 private final int[] yearOffsetToConsider;
84
85
86 private final int numOfYears;
87
88
89 private final int numOfTrials;
90
91
92 private final File targetDir;
93
94
95 private final ProjectionModel projection;
96
97
98
99
100
101 static final double[] QUANTILES = new double[] { 0, .025, .05, .1, .5, .9,
102 0.95, 0.975, 1 };
103
104
105 static final int DENSITY_PLOT_NUM_SUBPLOTS = 9;
106
107
108 static final int DENSITY_PLOT_FIRST_OFFSET = 3;
109
110
111 static final int DENSITY_PLOT_REGULAR_OFFSET = 5;
112
113
114
115
116
117
118
119
120
121
122
123 public ResultExport(ProjectionModel projectionModel, File targetDirectory,
124 IResultFilter resultFilter) {
125
126 projection = projectionModel;
127 targetDir = targetDirectory;
128 this.resultFilter = resultFilter;
129
130
131 if (!targetDir.exists() && !targetDir.mkdir()) {
132 throw new IllegalArgumentException("Could not create directory: "
133 + targetDir.getAbsolutePath());
134 }
135
136
137 yearOffsetToConsider = new int[projectionModel.getYears() - 1];
138 for (int i = 0; i < yearOffsetToConsider.length; i++) {
139 yearOffsetToConsider[i] = 1;
140 }
141
142
143 numOfYears = projectionModel.getYears();
144 numOfTrials = DatabaseFactory.getDatabaseSingleton()
145 .getAllResults(projection).size();
146 }
147
148
149
150
151
152
153
154 public void exportAllResults() throws IOException {
155 File dataDirectory = initializeSubDirectory("data");
156 exportData(dataDirectory);
157 GUI.printMessage(P3J.getInstance(), "Results Export Finished",
158 "Results export is finished.");
159 }
160
161
162
163
164
165
166
167
168
169 public void exportAggregatedResults(IProgressObserver progress)
170 throws IOException {
171 File aggregatedDirectory = initializeSubDirectory("aggregated_data");
172 aggregateData(aggregatedDirectory,
173 (new ResultAggregation(projection.getGenerations(), numOfYears,
174 projection.getSubPopulationModel()))
175 .getSelectorsForAggregatedDataExport(), progress);
176 if (progress.isCancelled())
177 return;
178 GUI.printMessage(P3J.getInstance(), "Aggregated Results Export Finished",
179 "Aggregated results export is finished.");
180 }
181
182
183
184
185
186
187
188
189
190
191
192 public void createResultReport(IProgressObserver progress)
193 throws IOException, TemplateException {
194 progress.addWaypoints(5);
195 progress.incrementProgress("Initializing sub-directory...");
196 File aggregatedDirectory = initializeSubDirectory("aggregated");
197 progress.incrementProgress("Retrieving data selectors...");
198 IAggregationSelector[] selectors = (new ResultAggregation(
199 projection.getGenerations(), numOfYears,
200 projection.getSubPopulationModel())).getSelectorsForReport();
201 progress.incrementProgress("Data aggregation...");
202 Map<String, Object> aggregationInfoMap = aggregateData(aggregatedDirectory,
203 selectors, progress);
204 if (aggregationInfoMap.isEmpty()) {
205 GUI.printMessage(P3J.getInstance(), "No results to report",
206 "No results could be reported, as none matched you filter.");
207 return;
208 }
209 if (progress.isCancelled())
210 return;
211 progress.incrementProgress("Coppy plotting library...");
212 copyPlottingLib();
213 progress.incrementProgress("Creating Sweave file...");
214 createSweaveFile(aggregationInfoMap);
215 GUI.printMessage(
216 P3J.getInstance(),
217 "Report Generation Finished",
218 "Result report generation is finished. Now run \"Sweave('report.Rtex')\" in R, then use LaTeX to process the 'report.tex' file.'");
219 }
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234 private ParameterAssumptionEncoder storeAssumptionEncoding(
235 ProjectionModel projection, File targetDir) throws IOException {
236 ParameterAssumptionEncoder assumptionEncoder = new ParameterAssumptionEncoder(
237 projection);
238 assumptionEncoder.writeMappingSummary(targetDir);
239 return assumptionEncoder;
240 }
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255 private Map<String, Object> aggregateData(File destinationDir,
256 IAggregationSelector[] selectors, IProgressObserver progress)
257 throws IOException {
258
259
260 ParameterAssumptionEncoder assumptionEncoder = storeAssumptionEncoding(
261 projection, destinationDir);
262 Map<String, Object> results = new HashMap<String, Object>();
263
264
265
266
267 initializeSelectors(projection, selectors);
268 List<Triple<Integer, Double, int[]>> trialAssumptions = analyzeResults(
269 projection, selectors, assumptionEncoder, progress);
270 if (progress.isCancelled() || trialAssumptions.isEmpty())
271 return results;
272
273 storeData(destinationDir, selectors, trialAssumptions);
274
275 results.put(
276 "densityYears",
277 createDensityYearList(projection.getJumpOffYear(),
278 DENSITY_PLOT_FIRST_OFFSET, DENSITY_PLOT_REGULAR_OFFSET));
279 results.put("finalYearIndex", numOfYears - 1);
280 results.put("allYears",
281 createFullYearList(projection.getJumpOffYear(), numOfYears - 1));
282
283 return results;
284 }
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299 private void storeData(File destinationDir, IAggregationSelector[] selectors,
300 List<Triple<Integer, Double, int[]>> trialAssumptions) throws IOException {
301 List<Integer> indexOrder = sortAndStore(destinationDir, trialAssumptions);
302 for (IAggregationSelector selector : selectors) {
303 selector.finish(destinationDir, indexOrder, this);
304 }
305 }
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321 private List<Integer> sortAndStore(File destinationDir,
322 List<Triple<Integer, Double, int[]>> trialAssumptions) throws IOException {
323
324
325 Collections.sort(trialAssumptions,
326 new Comparator<Triple<Integer, Double, int[]>>() {
327 @Override
328 public int compare(Triple<Integer, Double, int[]> o1,
329 Triple<Integer, Double, int[]> o2) {
330 return -1 * o1.getB().compareTo(o2.getB());
331 }
332 });
333
334
335 FileWriter fw = new FileWriter(destinationDir.getAbsolutePath()
336 + File.separatorChar + "assumptions_sorted.txt");
337 List<Integer> resultingOrder = new ArrayList<Integer>();
338 for (Triple<Integer, Double, int[]> trialAssumption : trialAssumptions) {
339 resultingOrder.add(trialAssumption.getA());
340 fw.append(trialAssumption.getA() + "\t" + trialAssumption.getB() + "\t");
341 for (int elem : trialAssumption.getC()) {
342 fw.append(elem + "\t");
343 }
344 fw.append("\n");
345 }
346 fw.close();
347
348 return resultingOrder;
349 }
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366 private List<Triple<Integer, Double, int[]>> analyzeResults(
367 ProjectionModel projection, IAggregationSelector[] selectors,
368 ParameterAssumptionEncoder assumptionEncoder, IProgressObserver progress) {
369
370
371
372 progress.addWaypoints(DatabaseFactory.getDatabaseSingleton()
373 .getAllResults(projection).size());
374
375
376 IProjectionResultsIterator resultsIterator = DatabaseFactory
377 .getDatabaseSingleton().getResultIterator(projection);
378
379 ResultsOfTrial result = resultsIterator.getNextResult();
380 int trialCount = 0;
381 List<Triple<Integer, Double, int[]>> trialAssumptions = new ArrayList<Triple<Integer, Double, int[]>>();
382
383 while (result != null) {
384
385
386 if (progress.isCancelled()) {
387 progress.taskCanceled();
388 break;
389 }
390
391 if (!resultFilter.considerResult(result)) {
392 publishInfo(
393 progress,
394 "Results with ID " + result.getID()
395 + " are dismissed by the result filter '"
396 + Strings.dispClassName(resultFilter.getClass()));
397 result = resultsIterator.getNextResult();
398 continue;
399 }
400 publishInfo(progress, "Analyzing trial #" + (trialCount + 1));
401 try {
402 trialAssumptions.add(new Triple<Integer, Double, int[]>(trialCount,
403 result.getAssignmentProbability(), assumptionEncoder.encode(result
404 .getAssignment())));
405 } catch (RuntimeException ex) {
406 GUI.printErrorMessage("Encoding trial failed.", ex);
407 result = resultsIterator.getNextResult();
408 continue;
409 }
410
411 for (IAggregationSelector selector : selectors) {
412 selector.consider(trialCount, result);
413 }
414 trialCount++;
415
416 result = resultsIterator.getNextResult();
417 }
418 return trialAssumptions;
419 }
420
421
422
423
424
425
426
427
428
429 private void publishInfo(IProgressObserver progress, String msg) {
430 progress.incrementProgress(msg);
431 SimSystem.report(Level.INFO, msg);
432 }
433
434
435
436
437
438
439
440
441
442 private void initializeSelectors(ProjectionModel projection,
443 IAggregationSelector[] selectors) {
444 for (IAggregationSelector selector : selectors) {
445 selector
446 .init(numOfTrials, numOfYears, projection.getNumberOfAgeClasses());
447 }
448 }
449
450
451
452
453
454
455
456
457
458 protected double[][] calcQuantiles(double[][] inputMatrix) {
459 if (inputMatrix.length == 0) {
460 return new double[0][0];
461 }
462 int maxRowIndex = inputMatrix.length - 1;
463 int cols = inputMatrix[0].length;
464 double[][] result = new double[QUANTILES.length][cols];
465 for (int col = 0; col < cols; col++) {
466 List<Double> currentValues = new ArrayList<Double>();
467 for (int i = 0; i < inputMatrix.length; i++) {
468 currentValues.add(inputMatrix[i][col]);
469 }
470 Collections.sort(currentValues);
471 for (int i = 0; i < QUANTILES.length; i++) {
472 result[i][col] = currentValues.get((int) (QUANTILES[i] * maxRowIndex));
473 }
474 }
475 return result;
476 }
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495 private String createDensityYearList(int jumpOffYear, int firstOffset,
496 int regularOffset) {
497 StringBuffer yearList = new StringBuffer();
498 for (int i = 0; i < DENSITY_PLOT_NUM_SUBPLOTS; i++) {
499 yearList.append(jumpOffYear + firstOffset + i * regularOffset
500 + (i == DENSITY_PLOT_NUM_SUBPLOTS - 1 ? "" : ","));
501 }
502 return yearList.toString();
503 }
504
505
506
507
508
509
510
511 protected String createEquidistantDensityYearList(int jumpOffYear) {
512 StringBuffer yearList = new StringBuffer();
513 for (int i = 0; i < DENSITY_PLOT_NUM_SUBPLOTS; i++) {
514 yearList.append((jumpOffYear + (int) Math.round(i * numOfYears
515 / (DENSITY_PLOT_NUM_SUBPLOTS - 1.0)))
516 + (i == DENSITY_PLOT_NUM_SUBPLOTS - 1 ? "" : ","));
517 }
518 return yearList.toString();
519 }
520
521
522
523
524
525
526
527
528
529
530 private String createFullYearList(int jumpOffYear, int projectionYears) {
531 StringBuffer yearList = new StringBuffer(Integer.toString(jumpOffYear));
532 yearList.append(',');
533 int lastYear = jumpOffYear;
534 for (int i = 0; i < projectionYears - 1; i++) {
535 lastYear++;
536 yearList.append(lastYear);
537 yearList.append(',');
538 }
539 yearList.append("" + (lastYear + 1));
540 return yearList.toString();
541 }
542
543
544
545
546
547
548
549
550
551 protected double[][] filter(double[][] original) {
552 double[][] filteredResult = new double[original.length][yearOffsetToConsider.length + 1];
553 for (int i = 0; i < original.length; i++) {
554 filteredResult[i][0] = original[i][0];
555 int currentOffset = 0;
556 for (int j = 0; j < yearOffsetToConsider.length; j++) {
557 currentOffset += yearOffsetToConsider[j];
558 filteredResult[i][j + 1] = original[i][currentOffset];
559 }
560 }
561 return filteredResult;
562 }
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577 protected void writeResult(File destinationDir, double[][] result,
578 String fileName) throws IOException {
579 FileWriter fw = new FileWriter(destinationDir.getAbsolutePath()
580 + File.separatorChar + fileName);
581 fw.append(toCSV(result, ','));
582 fw.close();
583 }
584
585
586
587
588
589
590
591
592
593
594 private void exportData(File dataDirectory) throws IOException {
595 SimSystem.report(Level.INFO, "Exporting CSV data...");
596 IProjectionResultsIterator resultsSet = DatabaseFactory
597 .getDatabaseSingleton().getResultIterator(projection);
598
599 ParameterAssumptionEncoder assumptionEncoder = storeAssumptionEncoding(
600 projection, dataDirectory);
601
602 int resultCount = 1;
603 for (ResultsOfTrial results : resultsSet) {
604 SimSystem.report(Level.INFO, "Export Trial #" + resultCount + " (ID: "
605 + results.getID() + ")");
606 if (!resultFilter.considerResult(results)) {
607 SimSystem.report(
608 Level.INFO,
609 "Results with ID " + results.getID()
610 + " are dismissed by the result filter '"
611 + Strings.dispClassName(resultFilter.getClass()));
612 continue;
613 }
614
615 File trialDirectory = new File(dataDirectory.getAbsolutePath()
616 + File.separatorChar + "trial_" + resultCount);
617 if (!trialDirectory.mkdir()) {
618 throw new IllegalStateException("Could not create directory: "
619 + trialDirectory.getAbsolutePath());
620 }
621
622 for (SubPopulation subPop : projection.getSubPopulationModel()
623 .getSubPopulations()) {
624 if (subPop.isConsistingOfDescendantGenerations())
625 writeGenerationResults(trialDirectory, results.retrieveFor(subPop),
626 subPop.getSimplifiedName());
627 else
628 exportBasicResults(trialDirectory,
629 results.retrieveFor(subPop).get(0), subPop.getSimplifiedName());
630 }
631
632 writeAssumptionsUsed(trialDirectory, results, assumptionEncoder);
633 resultCount++;
634 }
635 }
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651 private void writeAssumptionsUsed(File trialDirectory,
652 ResultsOfTrial results, ParameterAssumptionEncoder assumptionEncoder)
653 throws IOException {
654
655 FileWriter fw = new FileWriter(trialDirectory.getAbsolutePath()
656 + File.separatorChar + "assumptions.txt");
657 fw.append("Assumption probability: \t" + results.getAssignmentProbability()
658 + "\n");
659 fw.append("Numerical encoding: \t"
660 + Strings.dispArray(assumptionEncoder.encode(results.getAssignment()))
661 + "\n");
662 fw.append("\n\nAssumptions: \n"
663 + assumptionEncoder.verboseEncoding(results.getAssignment()) + "\n");
664 fw.close();
665 }
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681 private void writeGenerationResults(File trialDirectory,
682 List<BasicResults> results, String populationName) throws IOException {
683 for (int i = 0; i < results.size(); i++) {
684 exportBasicResults(trialDirectory, results.get(i), populationName
685 + "_gen_" + i);
686 }
687 }
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702 private void exportBasicResults(File trialDirectory,
703 BasicResults basicResults, String exportPrefix) throws IOException {
704 exportMatrix(trialDirectory, basicResults.getEndXm(), exportPrefix
705 + "_end_x_m.csv");
706 exportMatrix(trialDirectory, basicResults.getEndXf(), exportPrefix
707 + "_end_x_f.csv");
708 exportMatrix(trialDirectory, basicResults.getMeanXm(), exportPrefix
709 + "_mean_x_m.csv");
710 exportMatrix(trialDirectory, basicResults.getMeanXf(), exportPrefix
711 + "_mean_x_f.csv");
712 }
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727 private static void exportMatrix(File trialDirectory, Matrix2D data,
728 String fileName) throws IOException {
729 if (data == null) {
730 SimSystem.report(Level.INFO, "WARNING: Cannot write '" + fileName
731 + "' - data is null.");
732 return;
733 }
734 FileWriter fw = new FileWriter(trialDirectory.getAbsolutePath()
735 + File.separatorChar + fileName);
736 fw.append(toCSV(data.toArray(), ','));
737 fw.close();
738 }
739
740
741
742
743
744
745
746
747
748 private File initializeSubDirectory(String subDirName) {
749 File subDirectory = new File(targetDir.getAbsolutePath()
750 + File.separatorChar + subDirName);
751
752 if (subDirectory.exists()
753 && (!deleteAllDirFiles(subDirectory) || !subDirectory.delete())) {
754 throw new IllegalArgumentException("Could not delete directory: "
755 + subDirectory.getAbsolutePath());
756 }
757
758 if (!subDirectory.mkdir()) {
759 throw new IllegalArgumentException("Could not create directory: "
760 + subDirectory.getAbsolutePath());
761 }
762 return subDirectory;
763 }
764
765
766
767
768
769
770
771
772
773 private boolean deleteAllDirFiles(File dir) {
774 for (File subFile : dir.listFiles()) {
775 if (!subFile.delete()) {
776 return false;
777 }
778 }
779 return true;
780 }
781
782
783
784
785
786
787
788 private void copyPlottingLib() throws IOException {
789
790
791
792 String sourcePath = '/' + TEMPLATE_DIRECTORY + '/' + R_LIBRARY;
793 String targetPath = targetDir.getAbsolutePath() + File.separatorChar
794 + R_LIBRARY;
795
796 BufferedInputStream source = null;
797 BufferedOutputStream target = null;
798 final String errorMessage = "Could not copy library file from '"
799 + sourcePath + "' to '" + targetPath
800 + "' (required to execute R code).";
801 try {
802 source = new BufferedInputStream(getClass().getResourceAsStream(
803 sourcePath));
804 target = new BufferedOutputStream(new FileOutputStream(targetPath));
805 byte[] buffer = new byte[COPY_BUFFER_SIZE];
806 int len = source.read(buffer);
807 while (len != -1) {
808 target.write(buffer, 0, len);
809 len = source.read(buffer);
810 }
811 } catch (IOException e) {
812 GUI.printErrorMessage(errorMessage, e);
813 } finally {
814 try {
815 if (target != null) {
816 target.close();
817 }
818 } catch (IOException e) {
819 GUI.printErrorMessage(errorMessage, e);
820 }
821 try {
822 if (source != null) {
823 source.close();
824 }
825 } catch (IOException e) {
826 GUI.printErrorMessage(errorMessage, e);
827 }
828 }
829 }
830
831
832
833
834
835
836
837
838
839
840
841 public static StringBuilder toCSV(double[][] matrix, char delim) {
842 if (matrix == null) {
843 return new StringBuilder("null");
844 }
845 StringBuilder matrixString = new StringBuilder();
846 for (int i = 0; i < matrix.length; i++) {
847 for (int j = 0; j < matrix[i].length; j++) {
848 matrixString.append(Double.toString(matrix[i][j]));
849 if (j < matrix[i].length - 1) {
850 matrixString.append(delim);
851 }
852 }
853 matrixString.append('\n');
854 }
855 return matrixString;
856 }
857
858
859
860
861
862
863
864
865
866
867
868 private void createSweaveFile(Map<String, Object> dataToBeInserted)
869 throws IOException, TemplateException {
870
871 SimSystem.report(Level.INFO, "Creating report template.");
872
873 Configuration cfg = new Configuration();
874 cfg.setClassForTemplateLoading(ResultExport.class, "/" + TEMPLATE_DIRECTORY);
875 cfg.setObjectWrapper(new DefaultObjectWrapper());
876
877 dataToBeInserted.put("projection", projection);
878 dataToBeInserted.put("ages", projection.getMaximumAge() + 1);
879 dataToBeInserted.put("years", projection.getYears() + 1);
880 dataToBeInserted.put("trials", numOfTrials);
881 dataToBeInserted.put("date", (new SimpleDateFormat(
882 "dd. MM. yyyy (HH:mm:ss)")).format(Calendar.getInstance().getTime()));
883 dataToBeInserted.put("numsettypes", projection.getAllSetTypes().size());
884
885
886 dataToBeInserted.put("subPopulations", projection.getSubPopulationModel()
887 .getSubPopulations());
888
889
890 List<Integer> generations = new ArrayList<Integer>();
891 for (int i = 0; i < projection.getGenerations(); i++) {
892 generations.add(i);
893 }
894 dataToBeInserted.put("generations", generations);
895
896
897 List<Integer> descendantGenerations = new ArrayList<Integer>();
898 for (int i = 1; i < projection.getGenerations(); i++) {
899 descendantGenerations.add(i);
900 }
901 dataToBeInserted.put("desc_generations", descendantGenerations);
902
903 Template basicTemplate = cfg.getTemplate("template.Rtfm");
904 Writer out = new FileWriter(targetDir.getAbsolutePath()
905 + File.separatorChar + "report.Rtex");
906 basicTemplate.process(dataToBeInserted, out);
907 out.close();
908 }
909
910
911
912
913
914
915 public IResultFilter getResultFilter() {
916 return resultFilter;
917 }
918
919 }