#region License Information
/* HeuristicLab
* Copyright (C) 2002-2011 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using HeuristicLab.Common;
using HeuristicLab.Data;
using HeuristicLab.MainForm;
using HeuristicLab.MainForm.WindowsForms;
using HeuristicLab.Optimization;
namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Views {
[Content(typeof(RunCollection), false)]
[View("Variable Impacts")]
public sealed partial class RunCollectionVariableImpactView : AsynchronousContentView {
private const string variableImpactResultName = "Variable impacts";
public RunCollectionVariableImpactView() {
InitializeComponent();
}
public new RunCollection Content {
get { return (RunCollection)base.Content; }
set { base.Content = value; }
}
#region events
protected override void RegisterContentEvents() {
base.RegisterContentEvents();
Content.UpdateOfRunsInProgressChanged += new EventHandler(Content_UpdateOfRunsInProgressChanged);
Content.ItemsAdded += new HeuristicLab.Collections.CollectionItemsChangedEventHandler(Content_ItemsAdded);
Content.ItemsRemoved += new HeuristicLab.Collections.CollectionItemsChangedEventHandler(Content_ItemsRemoved);
Content.CollectionReset += new HeuristicLab.Collections.CollectionItemsChangedEventHandler(Content_CollectionReset);
RegisterRunEvents(Content);
}
protected override void DeregisterContentEvents() {
base.RegisterContentEvents();
Content.UpdateOfRunsInProgressChanged -= new EventHandler(Content_UpdateOfRunsInProgressChanged);
Content.ItemsAdded -= new HeuristicLab.Collections.CollectionItemsChangedEventHandler(Content_ItemsAdded);
Content.ItemsRemoved -= new HeuristicLab.Collections.CollectionItemsChangedEventHandler(Content_ItemsRemoved);
Content.CollectionReset -= new HeuristicLab.Collections.CollectionItemsChangedEventHandler(Content_CollectionReset);
DeregisterRunEvents(Content);
}
private void RegisterRunEvents(IEnumerable runs) {
foreach (IRun run in runs)
run.Changed += new EventHandler(Run_Changed);
}
private void DeregisterRunEvents(IEnumerable runs) {
foreach (IRun run in runs)
run.Changed -= new EventHandler(Run_Changed);
}
private void Content_ItemsAdded(object sender, HeuristicLab.Collections.CollectionItemsChangedEventArgs e) {
RegisterRunEvents(e.Items);
UpdateData();
}
private void Content_ItemsRemoved(object sender, HeuristicLab.Collections.CollectionItemsChangedEventArgs e) {
DeregisterRunEvents(e.Items);
UpdateData();
}
private void Content_CollectionReset(object sender, HeuristicLab.Collections.CollectionItemsChangedEventArgs e) {
DeregisterRunEvents(e.OldItems);
RegisterRunEvents(e.Items);
UpdateData();
}
private void Content_UpdateOfRunsInProgressChanged(object sender, EventArgs e) {
if (!Content.UpdateOfRunsInProgress) UpdateData();
}
private void Run_Changed(object sender, EventArgs e) {
if (!Content.UpdateOfRunsInProgress) UpdateData();
}
#endregion
protected override void OnContentChanged() {
base.OnContentChanged();
this.UpdateData();
}
private void UpdateData() {
matrixView.Content = CalculateVariableImpactMatrix();
}
private DoubleMatrix CalculateVariableImpactMatrix() {
DoubleMatrix matrix = null;
if (Content != null) {
List runsWithVariables = Content.Where(r => r.Visible && r.Results.ContainsKey(variableImpactResultName)).ToList();
IEnumerable allVariableImpacts = (from run in runsWithVariables
select run.Results[variableImpactResultName]).Cast();
IEnumerable variableNames = (from variableImpact in allVariableImpacts
from variableName in variableImpact.RowNames
select variableName)
.Distinct();
// filter variableNames: only include names that have at least one non-zero value in a run
List variableNamesList = (from variableName in variableNames
where GetVariableImpacts(variableName, allVariableImpacts).Any(x => !x.IsAlmost(0.0))
select variableName)
.ToList();
List statictics = new List { "Median Rank", "Mean", "StdDev", "pValue" };
List columnNames = runsWithVariables.Select(r => r.Name).ToList();
columnNames.AddRange(statictics);
int runs = runsWithVariables.Count();
matrix = new DoubleMatrix(variableNamesList.Count, runs + statictics.Count);
matrix.SortableView = true;
matrix.RowNames = variableNamesList;
matrix.ColumnNames = columnNames;
for (int i = 0; i < runsWithVariables.Count; i++) {
IRun run = runsWithVariables[i];
DoubleMatrix runVariableImpacts = (DoubleMatrix)run.Results[variableImpactResultName];
for (int j = 0; j < runVariableImpacts.Rows; j++) {
int rowIndex = variableNamesList.FindIndex(s => s == runVariableImpacts.RowNames.ElementAt(j));
if (rowIndex > -1) {
matrix[rowIndex, i] = runVariableImpacts[j, 0];
}
}
}
List> variableImpactsOverRuns = (from variableName in variableNamesList
select GetVariableImpacts(variableName, allVariableImpacts).ToList())
.ToList();
List> variableRanks = (from variableName in variableNamesList
select GetVariableImpactRanks(variableName, allVariableImpacts).ToList())
.ToList();
if (variableImpactsOverRuns.Count() > 0) {
// the variable with the worst median impact value is chosen as the reference variable
// this is problematic if all variables are relevant, however works often in practice
List referenceImpacts = (from impacts in variableImpactsOverRuns
let avg = impacts.Median()
orderby avg
select impacts)
.First();
// for all variables
for (int row = 0; row < variableImpactsOverRuns.Count; row++) {
// median rank
matrix[row, runs] = variableRanks[row].Median();
// also show mean and std.dev. of relative variable impacts to indicate the relative difference in impacts of variables
matrix[row, runs + 1] = variableImpactsOverRuns[row].Average();
matrix[row, runs + 2] = variableImpactsOverRuns[row].StandardDeviation();
double leftTail = 0; double rightTail = 0; double bothTails = 0;
// calc differences of impacts for current variable and reference variable
double[] z = new double[referenceImpacts.Count];
for (int i = 0; i < z.Length; i++) {
z[i] = variableImpactsOverRuns[row][i] - referenceImpacts[i];
}
// wilcoxon signed rank test is used because the impact values of two variables in a single run are not independent
alglib.wsr.wilcoxonsignedranktest(z, z.Length, 0, ref bothTails, ref leftTail, ref rightTail);
matrix[row, runs + 3] = bothTails;
}
}
}
return matrix;
}
private IEnumerable GetVariableImpactRanks(string variableName, IEnumerable allVariableImpacts) {
foreach (DoubleMatrix runVariableImpacts in allVariableImpacts) {
// certainly not yet very efficient because ranks are computed multiple times for the same run
string[] variableNames = runVariableImpacts.RowNames.ToArray();
double[] values = (from row in Enumerable.Range(0, runVariableImpacts.Rows)
select runVariableImpacts[row, 0] * -1)
.ToArray();
Array.Sort(values, variableNames);
// calculate ranks
double[] ranks = new double[values.Length];
// check for tied ranks
int i = 0;
while (i < values.Length) {
ranks[i] = i + 1;
int j = i + 1;
while (j < values.Length && values[i].IsAlmost(values[j])) {
ranks[j] = ranks[i];
j++;
}
i = j;
}
int rankIndex = 0;
foreach (string rowVariableName in variableNames) {
if (rowVariableName == variableName)
yield return ranks[rankIndex];
rankIndex++;
}
}
}
private IEnumerable GetVariableImpacts(string variableName, IEnumerable allVariableImpacts) {
foreach (DoubleMatrix runVariableImpacts in allVariableImpacts) {
int row = 0;
foreach (string rowName in runVariableImpacts.RowNames) {
if (rowName == variableName)
yield return runVariableImpacts[row, 0];
row++;
}
}
}
}
}