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