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