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