[14636] | 1 | #region License Information
|
---|
| 2 | /* HeuristicLab
|
---|
| 3 | * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
|
---|
| 4 | *
|
---|
| 5 | * This file is part of HeuristicLab.
|
---|
| 6 | *
|
---|
| 7 | * HeuristicLab is free software: you can redistribute it and/or modify
|
---|
| 8 | * it under the terms of the GNU General Public License as published by
|
---|
| 9 | * the Free Software Foundation, either version 3 of the License, or
|
---|
| 10 | * (at your option) any later version.
|
---|
| 11 | *
|
---|
| 12 | * HeuristicLab is distributed in the hope that it will be useful,
|
---|
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
| 15 | * GNU General Public License for more details.
|
---|
| 16 | *
|
---|
| 17 | * You should have received a copy of the GNU General Public License
|
---|
| 18 | * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
|
---|
| 19 | */
|
---|
| 20 | #endregion
|
---|
| 21 |
|
---|
| 22 | using System;
|
---|
| 23 | using System.Collections.Generic;
|
---|
| 24 | using System.ComponentModel;
|
---|
| 25 | using System.Data;
|
---|
| 26 | using System.Linq;
|
---|
| 27 | using System.Windows.Forms;
|
---|
| 28 | using HeuristicLab.Analysis;
|
---|
| 29 | using HeuristicLab.Collections;
|
---|
| 30 | using HeuristicLab.Common;
|
---|
| 31 | using HeuristicLab.Core.Views;
|
---|
| 32 | using HeuristicLab.MainForm;
|
---|
| 33 | using System.Windows.Forms.DataVisualization.Charting;
|
---|
| 34 | using System.Text;
|
---|
| 35 | using HeuristicLab.Data;
|
---|
| 36 | using System.Drawing;
|
---|
| 37 | using HeuristicLab.Core;
|
---|
| 38 |
|
---|
| 39 | namespace HeuristicLab.Optimization.Views {
|
---|
| 40 | [View("Parameter Analysis")]
|
---|
| 41 | [Content(typeof(RunCollection), false)]
|
---|
| 42 | public sealed partial class RunCollectionParameterAnalysisView : ItemView {
|
---|
| 43 | #region Colors
|
---|
| 44 | private static readonly Color[] colors = new[] {
|
---|
| 45 | Color.FromArgb(0x40, 0x6A, 0xB7),
|
---|
| 46 | Color.FromArgb(0xB1, 0x6D, 0x01),
|
---|
| 47 | Color.FromArgb(0x4E, 0x8A, 0x06),
|
---|
| 48 | Color.FromArgb(0x75, 0x50, 0x7B),
|
---|
| 49 | Color.FromArgb(0x72, 0x9F, 0xCF),
|
---|
| 50 | Color.FromArgb(0xA4, 0x00, 0x00),
|
---|
| 51 | Color.FromArgb(0xAD, 0x7F, 0xA8),
|
---|
| 52 | Color.FromArgb(0x29, 0x50, 0xCF),
|
---|
| 53 | Color.FromArgb(0x90, 0xB0, 0x60),
|
---|
| 54 | Color.FromArgb(0xF5, 0x89, 0x30),
|
---|
| 55 | Color.FromArgb(0x55, 0x57, 0x53),
|
---|
| 56 | Color.FromArgb(0xEF, 0x59, 0x59),
|
---|
| 57 | Color.FromArgb(0xED, 0xD4, 0x30),
|
---|
| 58 | Color.FromArgb(0x63, 0xC2, 0x16),
|
---|
| 59 | };
|
---|
| 60 | #endregion
|
---|
| 61 |
|
---|
| 62 | private bool suppressUpdates = false;
|
---|
| 63 | private int stepSize = 1000;
|
---|
| 64 | private Dictionary<IRun, List<Tuple<int, double>>> runData;
|
---|
| 65 | private Dictionary<string, ParameterInfo> paramInfos;
|
---|
| 66 |
|
---|
| 67 | public new RunCollection Content {
|
---|
| 68 | get { return (RunCollection)base.Content; }
|
---|
| 69 | set { base.Content = value; }
|
---|
| 70 | }
|
---|
| 71 |
|
---|
| 72 | public RunCollectionParameterAnalysisView() {
|
---|
| 73 | InitializeComponent();
|
---|
| 74 | chart.CustomizeAllChartAreas();
|
---|
| 75 | stepSizeTextBox.Text = stepSize.ToString();
|
---|
| 76 | errorProvider.SetIconAlignment(stepSizeTextBox, ErrorIconAlignment.MiddleLeft);
|
---|
| 77 | errorProvider.SetIconPadding(stepSizeTextBox, 2);
|
---|
| 78 | }
|
---|
| 79 |
|
---|
| 80 | #region Content Events
|
---|
| 81 | protected override void DeregisterContentEvents() {
|
---|
| 82 | Content.ItemsAdded -= Content_ItemsAdded;
|
---|
| 83 | Content.ItemsRemoved -= Content_ItemsRemoved;
|
---|
| 84 | Content.CollectionReset -= Content_CollectionReset;
|
---|
| 85 | Content.UpdateOfRunsInProgressChanged -= Content_UpdateOfRunsInProgressChanged;
|
---|
| 86 | DeregisterRunEvents(Content);
|
---|
| 87 | base.DeregisterContentEvents();
|
---|
| 88 | }
|
---|
| 89 | protected override void RegisterContentEvents() {
|
---|
| 90 | base.RegisterContentEvents();
|
---|
| 91 | Content.ItemsAdded += Content_ItemsAdded;
|
---|
| 92 | Content.ItemsRemoved += Content_ItemsRemoved;
|
---|
| 93 | Content.CollectionReset += Content_CollectionReset;
|
---|
| 94 | Content.UpdateOfRunsInProgressChanged += Content_UpdateOfRunsInProgressChanged;
|
---|
| 95 | RegisterRunEvents(Content);
|
---|
| 96 | }
|
---|
| 97 | private void DeregisterRunEvents(IEnumerable<IRun> runs) {
|
---|
| 98 | foreach (var run in runs)
|
---|
| 99 | run.PropertyChanged -= Run_PropertyChanged;
|
---|
| 100 | }
|
---|
| 101 | private void RegisterRunEvents(IEnumerable<IRun> runs) {
|
---|
| 102 | foreach (var run in runs)
|
---|
| 103 | run.PropertyChanged += Run_PropertyChanged;
|
---|
| 104 | }
|
---|
| 105 |
|
---|
| 106 | private void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IRun> e) {
|
---|
| 107 | RegisterRunEvents(e.Items);
|
---|
| 108 | }
|
---|
| 109 | private void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
|
---|
| 110 | DeregisterRunEvents(e.Items);
|
---|
| 111 | }
|
---|
| 112 | private void Content_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
|
---|
| 113 | DeregisterRunEvents(e.OldItems);
|
---|
| 114 | RegisterRunEvents(e.Items);
|
---|
| 115 | }
|
---|
| 116 | private void Content_UpdateOfRunsInProgressChanged(object sender, EventArgs e) {
|
---|
| 117 | if (InvokeRequired)
|
---|
| 118 | Invoke((Action<object, EventArgs>)Content_UpdateOfRunsInProgressChanged, sender, e);
|
---|
| 119 | else {
|
---|
| 120 | suppressUpdates = Content.UpdateOfRunsInProgress;
|
---|
| 121 | if (suppressUpdates) return;
|
---|
| 122 | CollectRunData();
|
---|
| 123 | AnalyzeParameters();
|
---|
| 124 | UpdateChart();
|
---|
| 125 | }
|
---|
| 126 | }
|
---|
| 127 | private void Run_PropertyChanged(object sender, PropertyChangedEventArgs e) {
|
---|
| 128 | if (suppressUpdates) return;
|
---|
| 129 | if (InvokeRequired)
|
---|
| 130 | Invoke((Action<object, PropertyChangedEventArgs>)Run_PropertyChanged, sender, e);
|
---|
| 131 | else {
|
---|
| 132 | CollectRunData();
|
---|
| 133 | AnalyzeParameters();
|
---|
| 134 | UpdateChart();
|
---|
| 135 | }
|
---|
| 136 | }
|
---|
| 137 | #endregion
|
---|
| 138 |
|
---|
| 139 | protected override void OnContentChanged() {
|
---|
| 140 | base.OnContentChanged();
|
---|
| 141 | CollectRunData();
|
---|
| 142 | AnalyzeParameters();
|
---|
| 143 | UpdateChart();
|
---|
| 144 | }
|
---|
| 145 | protected override void SetEnabledStateOfControls() {
|
---|
| 146 | base.SetEnabledStateOfControls();
|
---|
| 147 | parametersGroupBox.Enabled = Content != null;
|
---|
| 148 | groupsGroupBox.Enabled = Content != null;
|
---|
| 149 | stepSizeTextBox.Enabled = Content != null;
|
---|
| 150 | logScalingCheckBox.Enabled = Content != null;
|
---|
| 151 | dataRowsGroupBox.Enabled = Content != null;
|
---|
| 152 | }
|
---|
| 153 |
|
---|
| 154 | private void AnalyzeParameters() {
|
---|
| 155 | paramInfos = new Dictionary<string, ParameterInfo>();
|
---|
| 156 | if (Content == null) return;
|
---|
| 157 |
|
---|
| 158 | foreach (var run in runData.Keys) {
|
---|
| 159 | foreach (var param in run.Parameters) {
|
---|
| 160 | ParameterInfo info;
|
---|
| 161 | if (!paramInfos.TryGetValue(param.Key, out info))
|
---|
| 162 | paramInfos.Add(param.Key, new ParameterInfo(param.Key, param.Value.ToString(), run));
|
---|
| 163 | else
|
---|
| 164 | info.AddValue(param.Value.ToString(), run);
|
---|
| 165 | }
|
---|
| 166 | }
|
---|
| 167 |
|
---|
| 168 | // remove irrelevant parameters
|
---|
| 169 | if (paramInfos.ContainsKey("Seed"))
|
---|
| 170 | paramInfos.Remove("Seed");
|
---|
| 171 |
|
---|
| 172 | // remove all parameters which only have a single value
|
---|
| 173 | var singles = new List<string>();
|
---|
| 174 | foreach (var paramInfo in paramInfos.Values) {
|
---|
| 175 | if (paramInfo.Values.Count == 1)
|
---|
| 176 | singles.Add(paramInfo.Name);
|
---|
| 177 | }
|
---|
| 178 | foreach (var single in singles)
|
---|
| 179 | paramInfos.Remove(single);
|
---|
| 180 |
|
---|
| 181 | // set color of parameter values
|
---|
| 182 | int i = 0;
|
---|
| 183 | foreach (var valueInfo in paramInfos.Values.SelectMany(x => x.Values.Values)) {
|
---|
| 184 | valueInfo.Color = colors[i];
|
---|
| 185 | i = (i + 1) % colors.Length;
|
---|
| 186 | }
|
---|
| 187 |
|
---|
| 188 | // populate parametersTreeView
|
---|
| 189 | parametersTreeView.Nodes.Clear();
|
---|
| 190 | var paramsRoot = new TreeNode("All Runs (" + runData.Keys.Count + ")");
|
---|
| 191 | paramsRoot.Tag = runData.Keys.ToList();
|
---|
| 192 | foreach (var paramInfo in paramInfos.Values.OrderBy(x => x.Name)) {
|
---|
| 193 | var node = new TreeNode(paramInfo.Name + " (" + paramInfo.RunCount + ")");
|
---|
| 194 | foreach (var value in paramInfo.Values) {
|
---|
| 195 | var child = new TreeNode(value.Key + " (" + value.Value.RunCount + ")");
|
---|
| 196 | child.Tag = value.Value;
|
---|
| 197 | node.Nodes.Add(child);
|
---|
| 198 | }
|
---|
| 199 | node.Tag = paramInfo;
|
---|
| 200 | paramsRoot.Nodes.Add(node);
|
---|
| 201 | }
|
---|
| 202 | parametersTreeView.Nodes.Add(paramsRoot);
|
---|
| 203 | paramsRoot.Expand();
|
---|
[14641] | 204 | paramsRoot.Checked = true;
|
---|
[14636] | 205 | parametersTreeView.SelectedNode = paramsRoot;
|
---|
| 206 |
|
---|
| 207 | // populate groupsTreeView
|
---|
| 208 | groupsTreeView.Nodes.Clear();
|
---|
| 209 | var groupsRoot = new TreeNode("All Runs (" + runData.Keys.Count + ")");
|
---|
| 210 | groupsRoot.Tag = runData.Keys.ToList();
|
---|
| 211 | groupsTreeView.Nodes.Add(groupsRoot);
|
---|
| 212 | groupsRoot.Expand();
|
---|
| 213 | groupsTreeView.SelectedNode = groupsRoot;
|
---|
| 214 | }
|
---|
| 215 | private void CollectRunData() {
|
---|
| 216 | runData = new Dictionary<IRun, List<Tuple<int, double>>>();
|
---|
| 217 | if (Content == null) return;
|
---|
| 218 |
|
---|
| 219 | foreach (var run in Content) {
|
---|
| 220 | try {
|
---|
| 221 | IList<Tuple<double, double>> values = (run.Results["QualityPerEvaluations"] as IndexedDataTable<double>).Rows["First-hit Graph"].Values;
|
---|
| 222 | var bestKnown = (run.Results["BestKnownQuality"] as DoubleValue).Value;
|
---|
| 223 |
|
---|
| 224 | var data = new List<Tuple<int, double>>();
|
---|
| 225 | int i = 0;
|
---|
| 226 | double qual = double.NaN;
|
---|
| 227 | foreach (var val in values) {
|
---|
| 228 | while (i * stepSize < val.Item1) {
|
---|
| 229 | if (!double.IsNaN(qual)) data.Add(Tuple.Create(i * stepSize, qual));
|
---|
| 230 | i++;
|
---|
| 231 | }
|
---|
| 232 | var diff = Math.Abs(bestKnown - val.Item2);
|
---|
| 233 | qual = bestKnown == 0 ? diff : diff / bestKnown;
|
---|
| 234 | }
|
---|
| 235 | runData.Add(run, data);
|
---|
| 236 | }
|
---|
| 237 | catch {
|
---|
| 238 | }
|
---|
| 239 | }
|
---|
| 240 | }
|
---|
| 241 |
|
---|
| 242 | private void UpdateChart() {
|
---|
| 243 | if (suppressUpdates) return;
|
---|
| 244 |
|
---|
| 245 | chart.Series.Clear();
|
---|
| 246 | chart.Legends[0].CustomItems.Clear();
|
---|
[14641] | 247 | chart.Titles[0].Text = string.Empty;
|
---|
| 248 |
|
---|
| 249 | var checkedNodes = TraverseTreeNodes(parametersTreeView.Nodes)
|
---|
| 250 | .Where(x => x.Checked)
|
---|
| 251 | .Concat(
|
---|
| 252 | TraverseTreeNodes(groupsTreeView.Nodes)
|
---|
| 253 | .Where(x => x.Checked)
|
---|
| 254 | );
|
---|
| 255 |
|
---|
| 256 | foreach (var node in checkedNodes) {
|
---|
| 257 | if (node.Parent == null) {
|
---|
| 258 | var series = BuildSeries(node.Tag as IEnumerable<IRun>, colors[0]);
|
---|
| 259 | chart.Titles[0].Text = node.Text;
|
---|
[14636] | 260 | foreach (var s in series)
|
---|
| 261 | chart.Series.Add(s);
|
---|
| 262 | var legendItem = new LegendItem();
|
---|
[14641] | 263 | var legendItemInfo = new LegendItemInfo(colors[0], series);
|
---|
[14636] | 264 | legendItem.Color = legendItemInfo.Color;
|
---|
| 265 | legendItem.BorderColor = Color.Transparent;
|
---|
[14641] | 266 | legendItem.Name = node.Text;
|
---|
[14636] | 267 | legendItem.Tag = legendItemInfo;
|
---|
| 268 | chart.Legends[0].CustomItems.Add(legendItem);
|
---|
[14641] | 269 | } else if (node.Tag is ParameterInfo) {
|
---|
| 270 | var paramInfo = node.Tag as ParameterInfo;
|
---|
| 271 | chart.Titles[0].Text = paramInfo.Name + " (" + paramInfo.RunCount + ")";
|
---|
| 272 | foreach (var value in paramInfo.Values) {
|
---|
| 273 | var series = BuildSeries(value.Value.Runs, value.Value.Color);
|
---|
[14636] | 274 | foreach (var s in series)
|
---|
| 275 | chart.Series.Add(s);
|
---|
| 276 | var legendItem = new LegendItem();
|
---|
[14641] | 277 | var legendItemInfo = new LegendItemInfo(value.Value.Color, series);
|
---|
[14636] | 278 | legendItem.Color = legendItemInfo.Color;
|
---|
| 279 | legendItem.BorderColor = Color.Transparent;
|
---|
[14641] | 280 | legendItem.Name = value.Key + " (" + value.Value.RunCount + ")";
|
---|
[14636] | 281 | legendItem.Tag = legendItemInfo;
|
---|
| 282 | chart.Legends[0].CustomItems.Add(legendItem);
|
---|
| 283 | }
|
---|
[14641] | 284 | } else if (node.Tag is ParameterValueInfo) {
|
---|
| 285 | var valueInfo = node.Tag as ParameterValueInfo;
|
---|
| 286 | var series = BuildSeries(valueInfo.Runs, valueInfo.Color);
|
---|
| 287 | chart.Titles[0].Text = node.Parent.Text;
|
---|
[14636] | 288 | foreach (var s in series)
|
---|
| 289 | chart.Series.Add(s);
|
---|
| 290 | var legendItem = new LegendItem();
|
---|
[14641] | 291 | var legendItemInfo = new LegendItemInfo(valueInfo.Color, series);
|
---|
[14636] | 292 | legendItem.Color = legendItemInfo.Color;
|
---|
| 293 | legendItem.BorderColor = Color.Transparent;
|
---|
[14641] | 294 | legendItem.Name = node.Text;
|
---|
[14636] | 295 | legendItem.Tag = legendItemInfo;
|
---|
| 296 | chart.Legends[0].CustomItems.Add(legendItem);
|
---|
[14641] | 297 | } else if (node.Tag is GroupInfo) {
|
---|
| 298 | var groupInfo = node.Tag as GroupInfo;
|
---|
| 299 | if (groupInfo.IsParameter) {
|
---|
| 300 | chart.Titles[0].Text = groupInfo.Text + " (" + groupInfo.Runs.Count + ")";
|
---|
| 301 | foreach (TreeNode child in node.Nodes) {
|
---|
| 302 | var childInfo = child.Tag as GroupInfo;
|
---|
| 303 | var series = BuildSeries(childInfo.Runs, childInfo.Color);
|
---|
| 304 | foreach (var s in series)
|
---|
| 305 | chart.Series.Add(s);
|
---|
| 306 | var legendItem = new LegendItem();
|
---|
| 307 | var legendItemInfo = new LegendItemInfo(childInfo.Color, series);
|
---|
| 308 | legendItem.Color = legendItemInfo.Color;
|
---|
| 309 | legendItem.BorderColor = Color.Transparent;
|
---|
| 310 | legendItem.Name = childInfo.Text + " (" + childInfo.Runs.Count + ")";
|
---|
| 311 | legendItem.Tag = legendItemInfo;
|
---|
| 312 | chart.Legends[0].CustomItems.Add(legendItem);
|
---|
| 313 | }
|
---|
| 314 | } else {
|
---|
| 315 | var parentInfo = node.Parent.Tag as GroupInfo;
|
---|
| 316 | chart.Titles[0].Text = parentInfo.Text + " (" + parentInfo.Runs.Count + ")";
|
---|
| 317 | var series = BuildSeries(groupInfo.Runs, groupInfo.Color);
|
---|
| 318 | foreach (var s in series)
|
---|
| 319 | chart.Series.Add(s);
|
---|
| 320 | var legendItem = new LegendItem();
|
---|
| 321 | var legendItemInfo = new LegendItemInfo(groupInfo.Color, series);
|
---|
| 322 | legendItem.Color = legendItemInfo.Color;
|
---|
| 323 | legendItem.BorderColor = Color.Transparent;
|
---|
| 324 | legendItem.Name = groupInfo.Text + " (" + groupInfo.Runs.Count + ")";
|
---|
| 325 | legendItem.Tag = legendItemInfo;
|
---|
| 326 | chart.Legends[0].CustomItems.Add(legendItem);
|
---|
| 327 | }
|
---|
[14636] | 328 | }
|
---|
| 329 | }
|
---|
| 330 | }
|
---|
| 331 | private void UpdateSeriesVisibility() {
|
---|
| 332 | foreach (var legendItem in chart.Legends[0].CustomItems) {
|
---|
| 333 | var legendItemInfo = legendItem.Tag as LegendItemInfo;
|
---|
| 334 | foreach (var s in legendItemInfo.Series) {
|
---|
| 335 | if (legendItemInfo.SeriesVisible) {
|
---|
| 336 | var seriesInfo = s.Tag as SeriesInfo;
|
---|
| 337 | switch (seriesInfo.Type) {
|
---|
| 338 | case SeriesTypes.MinMax:
|
---|
| 339 | s.Color = minMaxCheckBox.Checked ? seriesInfo.Color : Color.Transparent;
|
---|
| 340 | break;
|
---|
| 341 | case SeriesTypes.Quartiles:
|
---|
| 342 | s.Color = quartilesCheckBox.Checked ? seriesInfo.Color : Color.Transparent;
|
---|
| 343 | break;
|
---|
| 344 | case SeriesTypes.Median:
|
---|
| 345 | s.Color = medianCheckBox.Checked ? seriesInfo.Color : Color.Transparent;
|
---|
| 346 | break;
|
---|
| 347 | case SeriesTypes.Average:
|
---|
| 348 | s.Color = averageCheckBox.Checked ? seriesInfo.Color : Color.Transparent;
|
---|
| 349 | break;
|
---|
| 350 | }
|
---|
| 351 | } else {
|
---|
| 352 | s.Color = Color.Transparent;
|
---|
| 353 | }
|
---|
| 354 | }
|
---|
| 355 | }
|
---|
| 356 | }
|
---|
| 357 |
|
---|
| 358 | private IEnumerable<Series> BuildSeries(IEnumerable<IRun> runs, Color color) {
|
---|
| 359 | var values = new Dictionary<int, List<double>>();
|
---|
| 360 | foreach (var run in runs) {
|
---|
| 361 | foreach (var step in runData[run]) {
|
---|
| 362 | List<double> vals;
|
---|
| 363 | if (!values.TryGetValue(step.Item1, out vals)) {
|
---|
| 364 | vals = new List<double>();
|
---|
| 365 | values.Add(step.Item1, vals);
|
---|
| 366 | }
|
---|
| 367 | vals.Add(step.Item2);
|
---|
| 368 | }
|
---|
| 369 | }
|
---|
| 370 |
|
---|
| 371 | List<Series> series = new List<Series>();
|
---|
| 372 | var minMaxSeries = new Series();
|
---|
| 373 | var minMaxSeriesInfo = new SeriesInfo(Color.FromArgb(25, color), SeriesTypes.MinMax);
|
---|
| 374 | minMaxSeries.ChartType = SeriesChartType.Range;
|
---|
| 375 | minMaxSeries.Color = minMaxCheckBox.Checked ? minMaxSeriesInfo.Color : Color.Transparent;
|
---|
| 376 | minMaxSeries.IsVisibleInLegend = false;
|
---|
| 377 | minMaxSeries.Tag = minMaxSeriesInfo;
|
---|
| 378 | series.Add(minMaxSeries);
|
---|
| 379 |
|
---|
| 380 | var quartilesSeries = new Series();
|
---|
| 381 | var quartilesSeriesInfo = new SeriesInfo(Color.FromArgb(50, color), SeriesTypes.Quartiles);
|
---|
| 382 | quartilesSeries.ChartType = SeriesChartType.Range;
|
---|
| 383 | quartilesSeries.Color = quartilesCheckBox.Checked ? quartilesSeriesInfo.Color : Color.Transparent;
|
---|
| 384 | quartilesSeries.IsVisibleInLegend = false;
|
---|
| 385 | quartilesSeries.Tag = quartilesSeriesInfo;
|
---|
| 386 | series.Add(quartilesSeries);
|
---|
| 387 |
|
---|
| 388 | var medianSeries = new Series();
|
---|
| 389 | var medianSeriesInfo = new SeriesInfo(color, SeriesTypes.Median);
|
---|
| 390 | medianSeries.ChartType = SeriesChartType.FastLine;
|
---|
| 391 | medianSeries.Color = medianCheckBox.Checked ? medianSeriesInfo.Color : Color.Transparent;
|
---|
| 392 | medianSeries.BorderWidth = 3;
|
---|
| 393 | medianSeries.BorderDashStyle = ChartDashStyle.Solid;
|
---|
| 394 | medianSeries.IsVisibleInLegend = false;
|
---|
| 395 | medianSeries.Tag = medianSeriesInfo;
|
---|
| 396 | series.Add(medianSeries);
|
---|
| 397 |
|
---|
| 398 | var averageSeries = new Series();
|
---|
| 399 | var averageSeriesInfo = new SeriesInfo(color, SeriesTypes.Average);
|
---|
| 400 | averageSeries.ChartType = SeriesChartType.FastLine;
|
---|
| 401 | averageSeries.Color = averageCheckBox.Checked ? averageSeriesInfo.Color : Color.Transparent;
|
---|
| 402 | averageSeries.BorderWidth = 3;
|
---|
| 403 | averageSeries.BorderDashStyle = ChartDashStyle.Dash;
|
---|
| 404 | averageSeries.IsVisibleInLegend = false;
|
---|
| 405 | averageSeries.Tag = averageSeriesInfo;
|
---|
| 406 | series.Add(averageSeries);
|
---|
| 407 |
|
---|
| 408 | foreach (var point in values.OrderBy(x => x.Key)) {
|
---|
| 409 | if (point.Value.Count > 0) {
|
---|
| 410 | minMaxSeries.Points.Add(new DataPoint(point.Key, new double[] { point.Value.Min(), point.Value.Max() }));
|
---|
| 411 | quartilesSeries.Points.Add(new DataPoint(point.Key, new double[] { point.Value.Quantile(0.25), point.Value.Quantile(0.75) }));
|
---|
| 412 | medianSeries.Points.Add(new DataPoint(point.Key, point.Value.Median()));
|
---|
| 413 | averageSeries.Points.Add(new DataPoint(point.Key, point.Value.Average()));
|
---|
| 414 | }
|
---|
| 415 | }
|
---|
| 416 | return series;
|
---|
| 417 | }
|
---|
| 418 |
|
---|
| 419 | #region Control Events
|
---|
| 420 | #region stepSizeTextBox
|
---|
| 421 | private void stepSizeTextBox_KeyDown(object sender, KeyEventArgs e) {
|
---|
| 422 | if ((e.KeyCode == Keys.Enter) || (e.KeyCode == Keys.Return))
|
---|
| 423 | stepSizeLabel.Select(); // select label to validate data
|
---|
| 424 |
|
---|
| 425 | if (e.KeyCode == Keys.Escape) {
|
---|
| 426 | stepSizeTextBox.Text = stepSize.ToString();
|
---|
| 427 | stepSizeLabel.Select(); // select label to validate data
|
---|
| 428 | }
|
---|
| 429 | }
|
---|
| 430 | private void stepSizeTextBox_Validating(object sender, CancelEventArgs e) {
|
---|
| 431 | int val;
|
---|
| 432 | if (!int.TryParse(stepSizeTextBox.Text, out val) || val <= 0) {
|
---|
| 433 | e.Cancel = true;
|
---|
| 434 | errorProvider.SetError(stepSizeTextBox, "Invalid Value (Valid Value Format: \"[+]digits\")");
|
---|
| 435 | stepSizeTextBox.SelectAll();
|
---|
| 436 | }
|
---|
| 437 | }
|
---|
| 438 | private void stepSizeTextBox_Validated(object sender, EventArgs e) {
|
---|
| 439 | int val = int.Parse(stepSizeTextBox.Text);
|
---|
| 440 | errorProvider.SetError(stepSizeTextBox, string.Empty);
|
---|
| 441 | stepSizeTextBox.Text = val.ToString();
|
---|
| 442 | if (stepSize != val) {
|
---|
| 443 | stepSize = val;
|
---|
| 444 | CollectRunData();
|
---|
| 445 | UpdateChart();
|
---|
| 446 | }
|
---|
| 447 | }
|
---|
| 448 | #endregion
|
---|
| 449 | #region logScalingCheckBox
|
---|
| 450 | private void logScalingCheckBox_CheckedChanged(object sender, EventArgs e) {
|
---|
| 451 | chart.ChartAreas[0].AxisX.IsLogarithmic = logScalingCheckBox.Checked;
|
---|
| 452 | }
|
---|
| 453 | #endregion
|
---|
| 454 | #region chart
|
---|
| 455 | private void chart_MouseDown(object sender, MouseEventArgs e) {
|
---|
| 456 | HitTestResult result = chart.HitTest(e.X, e.Y);
|
---|
| 457 | if (result.ChartElementType == ChartElementType.LegendItem) {
|
---|
| 458 | var legendItemInfo = (result.Object as LegendItem).Tag as LegendItemInfo;
|
---|
| 459 | legendItemInfo.SeriesVisible = !legendItemInfo.SeriesVisible;
|
---|
| 460 | UpdateSeriesVisibility();
|
---|
| 461 | }
|
---|
| 462 | }
|
---|
| 463 | private void chart_MouseMove(object sender, MouseEventArgs e) {
|
---|
| 464 | HitTestResult result = chart.HitTest(e.X, e.Y);
|
---|
| 465 | switch (result.ChartElementType) {
|
---|
| 466 | case ChartElementType.LegendItem:
|
---|
| 467 | Cursor = Cursors.Hand;
|
---|
| 468 | break;
|
---|
| 469 | default:
|
---|
| 470 | Cursor = Cursors.Default;
|
---|
| 471 | break;
|
---|
| 472 | }
|
---|
| 473 | }
|
---|
| 474 | private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {
|
---|
| 475 | foreach (LegendItem legendItem in e.LegendItems) {
|
---|
| 476 | var legendItemInfo = legendItem.Tag as LegendItemInfo;
|
---|
| 477 | legendItem.Color = legendItemInfo.SeriesVisible ? legendItemInfo.Color : Color.Transparent;
|
---|
| 478 | foreach (LegendCell cell in legendItem.Cells) {
|
---|
| 479 | cell.ForeColor = legendItemInfo.SeriesVisible ? Color.Black : Color.Gray;
|
---|
| 480 | }
|
---|
| 481 | }
|
---|
| 482 | }
|
---|
| 483 | #endregion
|
---|
| 484 | #region parametersTreeView
|
---|
| 485 | private void parametersTreeView_AfterSelect(object sender, TreeViewEventArgs e) {
|
---|
| 486 | addGroupButton.Enabled = (parametersTreeView.SelectedNode != null) && (parametersTreeView.SelectedNode.Parent != null);
|
---|
| 487 | }
|
---|
[14641] | 488 | private void parametersTreeView_AfterCheck(object sender, TreeViewEventArgs e) {
|
---|
| 489 | UpdateChart();
|
---|
| 490 | }
|
---|
[14636] | 491 | #endregion
|
---|
| 492 | #region groupsTreeView
|
---|
| 493 | private void groupsTreeView_AfterSelect(object sender, TreeViewEventArgs e) {
|
---|
| 494 | removeGroupButton.Enabled = (groupsTreeView.SelectedNode != null) && (groupsTreeView.SelectedNode.Parent != null);
|
---|
| 495 | }
|
---|
[14641] | 496 | private void groupsTreeView_AfterCheck(object sender, TreeViewEventArgs e) {
|
---|
| 497 | UpdateChart();
|
---|
| 498 | }
|
---|
[14636] | 499 | #endregion
|
---|
| 500 | #region addGroupButton, removeGroupButton
|
---|
| 501 | private void addGroupButton_Click(object sender, EventArgs e) {
|
---|
| 502 | var group = groupsTreeView.SelectedNode;
|
---|
| 503 | var param = parametersTreeView.SelectedNode;
|
---|
| 504 | var groupRuns = group.Parent == null ? group.Tag as IEnumerable<IRun> : (group.Tag as GroupInfo).Runs;
|
---|
| 505 |
|
---|
| 506 | if (param.Tag is ParameterInfo) {
|
---|
| 507 | var paramInfo = param.Tag as ParameterInfo;
|
---|
| 508 | var paramNode = new TreeNode();
|
---|
| 509 | foreach (var valueInfo in paramInfo.Values.Values) {
|
---|
[14641] | 510 | var valueRuns = groupRuns.Intersect(valueInfo.Runs);
|
---|
| 511 | var valueNode = new TreeNode(valueInfo.Value + " (" + valueRuns.Count() + ")");
|
---|
| 512 | valueNode.Tag = new GroupInfo(valueInfo.Value, valueInfo.Color, valueRuns, false);
|
---|
[14636] | 513 | paramNode.Nodes.Add(valueNode);
|
---|
| 514 | }
|
---|
[14641] | 515 | var paramRuns = groupRuns.Intersect(paramInfo.Runs);
|
---|
| 516 | paramNode.Text = paramInfo.Name + " (" + paramRuns.Count() + ")";
|
---|
| 517 | paramNode.Tag = new GroupInfo(paramInfo.Name, Color.Empty, paramRuns, true);
|
---|
[14636] | 518 | group.Nodes.Add(paramNode);
|
---|
| 519 | } else if (param.Tag is ParameterValueInfo) {
|
---|
| 520 | var paramInfo = param.Parent.Tag as ParameterInfo;
|
---|
| 521 | var valueInfo = param.Tag as ParameterValueInfo;
|
---|
[14641] | 522 | var valueRuns = groupRuns.Intersect(valueInfo.Runs);
|
---|
| 523 | var paramRuns = groupRuns.Intersect(paramInfo.Runs);
|
---|
| 524 | var paramNode = new TreeNode(paramInfo.Name + " (" + paramRuns.Count() + ")");
|
---|
| 525 | var valueNode = new TreeNode(valueInfo.Value + " (" + valueRuns.Count() + ")");
|
---|
| 526 | valueNode.Tag = new GroupInfo(valueInfo.Value, valueInfo.Color, valueRuns, false);
|
---|
| 527 | paramNode.Tag = new GroupInfo(paramInfo.Name, Color.Empty, paramRuns, true);
|
---|
[14636] | 528 | paramNode.Nodes.Add(valueNode);
|
---|
| 529 | group.Nodes.Add(paramNode);
|
---|
| 530 | }
|
---|
| 531 | groupsTreeView.Nodes[0].Expand();
|
---|
| 532 | }
|
---|
| 533 | private void removeGroupButton_Click(object sender, EventArgs e) {
|
---|
| 534 | if (groupsTreeView.SelectedNode != null)
|
---|
| 535 | groupsTreeView.SelectedNode.Remove();
|
---|
| 536 | UpdateChart();
|
---|
| 537 | }
|
---|
| 538 | #endregion
|
---|
| 539 | #region minMaxCheckBox, quartilesCheckBox, averageCheckBox, medianCheckBox
|
---|
| 540 | private void dataRowCheckBox_CheckedChanged(object sender, EventArgs e) {
|
---|
| 541 | UpdateSeriesVisibility();
|
---|
| 542 | }
|
---|
| 543 | #endregion
|
---|
| 544 | #endregion
|
---|
| 545 |
|
---|
[14641] | 546 | #region Helpers
|
---|
| 547 | private IEnumerable<TreeNode> TraverseTreeNodes(TreeNodeCollection nodes) {
|
---|
| 548 | foreach (var node in nodes.OfType<TreeNode>()) {
|
---|
| 549 | yield return node;
|
---|
| 550 | foreach (var child in TraverseTreeNodes(node.Nodes))
|
---|
| 551 | yield return child;
|
---|
| 552 | }
|
---|
| 553 | }
|
---|
| 554 | #endregion
|
---|
| 555 |
|
---|
[14636] | 556 | #region Inner Types
|
---|
| 557 | class ParameterInfo {
|
---|
[14638] | 558 | public string Name { get; private set; }
|
---|
| 559 | public Dictionary<string, ParameterValueInfo> Values { get; private set; }
|
---|
[14636] | 560 | public IEnumerable<IRun> Runs {
|
---|
| 561 | get { return Values.Values.SelectMany(x => x.Runs); }
|
---|
| 562 | }
|
---|
| 563 | public int RunCount {
|
---|
| 564 | get { return Values.Values.Select(x => x.RunCount).Sum(); }
|
---|
| 565 | }
|
---|
| 566 |
|
---|
| 567 | public ParameterInfo(string name, string value, IRun run) {
|
---|
| 568 | Name = name;
|
---|
[14637] | 569 | Values = new Dictionary<string, ParameterValueInfo>();
|
---|
[14636] | 570 | AddValue(value, run);
|
---|
| 571 | }
|
---|
| 572 | public void AddValue(string value, IRun run) {
|
---|
| 573 | ParameterValueInfo valueInfo;
|
---|
| 574 | if (!Values.TryGetValue(value, out valueInfo)) {
|
---|
| 575 | Values.Add(value, new ParameterValueInfo(value, run));
|
---|
| 576 | } else {
|
---|
| 577 | valueInfo.Runs.Add(run);
|
---|
| 578 | }
|
---|
| 579 | }
|
---|
| 580 | }
|
---|
| 581 | class ParameterValueInfo {
|
---|
[14638] | 582 | public string Value { get; private set; }
|
---|
[14636] | 583 | public Color Color { get; set; }
|
---|
[14638] | 584 | public IList<IRun> Runs { get; private set; }
|
---|
[14636] | 585 | public int RunCount {
|
---|
| 586 | get { return Runs.Count; }
|
---|
| 587 | }
|
---|
| 588 |
|
---|
| 589 | public ParameterValueInfo(string value, IRun run) {
|
---|
| 590 | Value = value;
|
---|
[14637] | 591 | Runs = new List<IRun>();
|
---|
[14636] | 592 | Runs.Add(run);
|
---|
| 593 | }
|
---|
| 594 | public ParameterValueInfo(string value, Color color, IRun run) : this(value, run) {
|
---|
| 595 | Color = color;
|
---|
| 596 | }
|
---|
| 597 | }
|
---|
| 598 | class GroupInfo {
|
---|
[14638] | 599 | public string Text { get; private set; }
|
---|
| 600 | public Color Color { get; private set; }
|
---|
| 601 | public IList<IRun> Runs { get; private set; }
|
---|
| 602 | public bool IsParameter { get; private set; }
|
---|
[14636] | 603 | public GroupInfo(string text, Color color, IEnumerable<IRun> runs, bool isParamter) {
|
---|
| 604 | Text = text;
|
---|
| 605 | Color = color;
|
---|
| 606 | Runs = runs.ToList();
|
---|
| 607 | IsParameter = isParamter;
|
---|
| 608 | }
|
---|
| 609 | }
|
---|
| 610 | enum SeriesTypes {
|
---|
| 611 | MinMax,
|
---|
| 612 | Quartiles,
|
---|
| 613 | Average,
|
---|
| 614 | Median
|
---|
| 615 | }
|
---|
| 616 | class SeriesInfo {
|
---|
[14638] | 617 | public Color Color { get; private set; }
|
---|
| 618 | public SeriesTypes Type { get; private set; }
|
---|
[14636] | 619 | public SeriesInfo(Color color, SeriesTypes type) {
|
---|
| 620 | Color = color;
|
---|
| 621 | Type = type;
|
---|
| 622 | }
|
---|
| 623 | }
|
---|
| 624 | class LegendItemInfo {
|
---|
[14638] | 625 | public Color Color { get; private set; }
|
---|
[14636] | 626 | public bool SeriesVisible { get; set; }
|
---|
[14638] | 627 | public IEnumerable<Series> Series { get; private set; }
|
---|
[14636] | 628 | public LegendItemInfo(Color color, IEnumerable<Series> series) {
|
---|
| 629 | Color = color;
|
---|
| 630 | SeriesVisible = true;
|
---|
| 631 | Series = series;
|
---|
| 632 | }
|
---|
| 633 | }
|
---|
| 634 | #endregion
|
---|
| 635 | }
|
---|
| 636 | }
|
---|