DataFormatters.psm1
using namespace Microsoft.Office.Interop $TextInfo = (Get-Culture).TextInfo $WorksheetMaxLen = 31 $HeaderRows = 4 $EPS = 0.0001 # Excel uses BGR color values $ColorPalette = @{ "LightGreen" = 0x9EF0A1 "Green" = 0x135C1E "LightRed" = 0x9EA1FF "Red" = 0x202A80 "Blue" = @(0x633f16, 0x9C6527, 0xD68546, 0xFFB894) # Dark -> Light "Orange" = @(0x005b97, 0x047CCC, 0x19A9FC, 0x5BC6FC) "LightGray" = @(0xf5f5f5, 0xd9d9d9) } $ABBREVIATIONS = @{ "sessions" = "sess." "bufferLen" = "bufLen." "bufferCount" = "bufCt." "protocol" = "" "sendMethod" = "sndMthd" } <# .SYNOPSIS Converts an index to an Excel column name. .NOTES Valid for A to ZZ #> function Get-ColName($n) { if ($n -ge 26) { $a = [Int][Math]::floor($n / 26) $c1 = [Char]($a + 64) } $c2 = [Char](($n % 26) + 65) return "$c1$c2" } ## # Format-RawData # -------------- # This function formats raw data into tables, one for each dataEntry property. Data samples are # organized by their sortProp and labeled with the name of the file from which the data sample was extracted. # # Parameters # ---------- # DataObj (HashTable) - Object containing processed data, raw data, and meta data # TableTitle (String) - Title to be displayed at the top of each table # # Return # ------ # HashTable[] - Array of HashTable objects which each store a table of formatted raw data # ## function Format-RawData { param ( [Parameter(Mandatory=$true)] [PSobject[]] $DataObj, [Parameter(Mandatory=$true)] $OPivotKey, [Parameter()] [String] $Tool = "", [Parameter()] [switch] $NoNewWorksheets ) $legend = @{ "meta" = @{ "colLabelDepth" = 1 "rowLabelDepth" = 1 "dataWidth" = 2 "dataHeight" = 3 "name" = "legend" "numWrites" = 3 + 3 + 5 } "rows" = @{ " " = 0 " " = 1 " " = 2 } "cols" = @{ "legend" = @{ " " = 0 " " = 1 } } "data" = @{ "legend" = @{ " " = @{ " " = @{ "value" = "Test values are compared against the mean basline value." } " " = @{ "value" = "Test values which show improvement are colored green:" } " " = @{ "value" = "Test values which show regression are colored red:" } } " " = @{ " " = @{ "value" = "Improvement" "fontColor" = $ColorPalette.Green "cellColor" = $ColorPalette.LightGreen } " " = @{ "value" = "Regression" "fontColor" = $ColorPalette.Red "cellColor" = $ColorPalette.LightRed } } } } } $meta = $DataObj.meta $innerPivot = $meta.InnerPivot $outerPivot = $meta.OuterPivot $tables = @() if (-not $NoNewWorksheets) { $tables += Get-WorksheetTitle -BaseName "Raw Data" -OuterPivot $outerPivot -OPivotKey $OPivotKey } if ($meta.comparison) { $tables += $legend } $numBaseline = $DataObj.rawData.baseline.Count $numTest = if ($meta.comparison) {$DataObj.rawData.test.Count} else {0} $numProps = $dataObj.data.$OPivotKey.Keys.Count $numIters = ($numBaseline + $numTest + $meta.outerPivotKeys.Count + ($numProps * ($numBaseline + $numTest))) $j = 0 # Fill single array with all data and sort, label data as baseline/test if necessary [Array] $data = @() foreach ($entry in $DataObj.rawData.baseline) { if ($meta.comparison) { $entry.baseline = $true } if ($OPivotKey -in @("", $entry.$outerPivot)) { $data += $entry } Write-Progress -Activity "Formatting Tables" -Status "Raw Data Table" -Id 3 -PercentComplete (100 * (($j++) / $numIters)) } if ($meta.comparison) { foreach ($entry in $DataObj.rawData.test) { if ($OPivotKey -in @("", $entry.$outerPivot)) { $data += $entry } Write-Progress -Activity "Formatting Tables" -Status "Raw Data Table" -Id 3 -PercentComplete (100 * (($j++) / $numIters)) } } if ($innerPivot) { $data = Sort-ByProp -Objs $data -Prop $innerPivot -Int $true } foreach ($prop in $dataObj.data.$OPivotKey.Keys) { $tableTitle = Get-TableTitle -Tool $Tool -OuterPivot $outerPivot -OPivotKey $OPivotKey $table = @{ "rows" = @{ $prop = @{} } "cols" = @{ $tableTitle = @{ $innerPivot = @{} } } "meta" = @{ "columnFormats" = @() "leftAlign" = [Array] @(2) "name" = "Raw Data" "numWrites" = 1 + 2 } "data" = @{ $tableTitle = @{ $innerPivot = @{} } } } $col = 0 $row = 0 foreach ($entry in $data) { $iPivotKey = if ($innerPivot) {$entry.$innerPivot} else {""} # Add column labels to table if (-not ($table.cols.$tableTitle.$innerPivot.Keys -contains $iPivotKey)) { if ($meta.comparison) { $table.cols.$tableTitle.$innerPivot.$iPivotKey = @{ "baseline" = $col "test" = $col + 1 } $table.meta.columnFormats += @($meta.format.$prop, $meta.format.$prop) $table.meta.numWrites += 3 $col += 2 $table.data.$tableTitle.$innerPivot.$iPivotKey = @{ "baseline" = @{ $prop = @{} } "test" = @{ $prop = @{} } } } else { $table.meta.numWrites += 1 $table.meta.columnFormats += $meta.format.$prop $table.cols.$tableTitle.$innerPivot.$iPivotKey = $col $table.data.$tableTitle.$innerPivot.$iPivotKey = @{ $prop = @{} } $col += 1 } } # Add row labels and fill data in table $filename = $entry.fileName.Split('\')[-2] + "\" + $entry.fileName.Split('\')[-1] while ($table.rows.$prop.keys -contains $filename) { $filename += "*" } $table.rows.$prop.$filename = $row $table.meta.numWrites += 1 $row += 1 if ($meta.comparison) { if ($entry.baseline) { $table.data.$tableTitle.$innerPivot.$iPivotKey.baseline.$prop.$filename = @{ "value" = $entry.$prop } } else { $table.data.$tableTitle.$innerPivot.$iPivotKey.test.$prop.$filename = @{ "value" = $entry.$prop } $params = @{ "Cell" = $table.data.$tableTitle.$innerPivot.$iPivotKey.test.$prop.$filename "TestVal" = $entry.$prop "BaseVal" = $DataObj.data.$OPivotKey.$prop.$iPivotKey.baseline.stats.mean "Goal" = $meta.goal.$prop } $table.data.$tableTitle.$innerPivot.$iPivotKey.test.$prop.$filename = Set-CellColor @params } } else { $table.data.$tableTitle.$innerPivot.$iPivotKey.$prop.$filename = @{ "value" = $entry.$prop } } Write-Progress -Activity "Formatting Tables" -Status "Raw Data Table" -Id 3 -PercentComplete (100 * (($j++) / $numIters)) } $table.meta.numWrites += $data.Count $table.meta.dataWidth = Get-TreeWidth $table.cols $table.meta.colLabelDepth = Get-TreeDepth $table.cols $table.meta.dataHeight = Get-TreeWidth $table.rows $table.meta.rowLabelDepth = Get-TreeDepth $table.rows $tables = $tables + $table } foreach ($entry in $data) { if ($entry.baseline) { $entry.Remove("baseline") } } Write-Progress -Activity "Formatting Tables" -Status "Raw Data Table" -Id 3 -PercentComplete 100 return $tables } function Sort-ByProp ($Objs, $Prop, $Int) { $arrs = [Array] @() foreach ($obj in $Objs) { $arrs += [Array]@($obj) } while ($arrs.Count > 1) { $newArr = @() for ($i = 0; $i -lt $arrs.Count - 1; $i += 2) { $merged = Merge-Arrays($arrs[$i], $arrs[$i + 1], $Prop, $Int) $newArr.Add($merged) } $arrs = $newArr } return $arrs } function Merge-Arrays ($Arr1, $Arr2, $Prop, $Int) { $i = 0 $j = 0 $newArr = @() while (($i -lt $Arr1.Count) -and ($j -lt $Arr2.Count)) { if ($true) { if ([Int]($Arr1[$i].$Prop) -le [Int]($Arr2[$j].$Prop)) { $newArr += $Arr1[$i] $i++ } else { $newArr += $Arr2[$j] $j++ } } else { if ($Arr1[$i].$Prop -le $Arr2[$j].$Prop) { $newArr += $Arr1[$i] $i++ } else { $newArr += $Arr2[$j] $j++ } } } while ($i -lt $Arr1.Count) { $newArr += $Arr1[$i] $i++ } while ($j -lt $arr2.Count) { $newArr += $Arr2[$j] $j++ } return $newArr } function Get-WorksheetTitle ($BaseName, $OuterPivot, $OPivotKey, $InnerPivot, $IPivotKey, $Prop="") { if ($OuterPivot -and $InnerPivot) { $OAbv = $ABBREVIATIONS[$OuterPivot] $IAbv = $ABBREVIATIONS[$InnerPivot] $name = "$BaseName - $OPivotKey $OAbv - $IPivotKey $IAbv" if ($name.Length -gt $WorksheetMaxLen) { $name = "$BaseName - $OPivotKey - $IPivotKey" } return $name } elseif ($OuterPivot) { $OAbv = $ABBREVIATIONS[$OuterPivot] $name = "$BaseName - $OPivotKey $OAbv" if ($name.Length -gt $WorksheetMaxLen) { $name = "$BaseName - $OPivotKey" } return $name } elseif ($InnerPivot) { $IAbv = $ABBREVIATIONS[$InnerPivot] $name = "$BaseName - $IPivotKey $IAbv" if ($name.Length -gt $WorksheetMaxLen) { $name = "$BaseName - $IPivotKey" } return $name } else { if ($Prop) { return "$BaseName $($Prop.Replace("/", " per "))" } return "$BaseName" } } function Get-TableTitle ($Tool, $OuterPivot, $OPivotKey, $InnerPivot, $IPivotKey) { if ($OuterPivot -and $InnerPivot) { $OAbv = $ABBREVIATIONS[$OuterPivot] $IAbv = $ABBREVIATIONS[$InnerPivot] return "$Tool - $OPivotKey $OAbv - $IPivotKey $IAbv" } elseif ($OuterPivot) { $OAbv = $ABBREVIATIONS[$OuterPivot] return "$Tool - $OPivotKey $OAbv" } elseif ($InnerPivot) { $IAbv = $ABBREVIATIONS[$InnerPivot] return "$Tool - $IPivotKey $IAbv" } else { return "$Tool" } } ## # Format-Stats # ------------------- # This function formats statistical metrics (min, mean, max, etc) into a table, one per property. # When run in comparison mode, the table also displays % change and is color-coded to indicate # improvement/regression. # # Parameters # ---------- # DataObj (HashTable) - Object containing processed data, raw data, and meta data # TableTitle (String) - Title to be displayed at the top of each table # Metrics (String[]) - Array containing statistical metrics that should be displayed on generated # tables. All metrics are displayed if this parameter is null. # # Return # ------ # HashTable[] - Array of HashTable objects which each store a table of formatted statistical data # ## function Format-Stats { Param ( [Parameter(Mandatory=$true)] [PSObject[]] $DataObj, [Parameter(Mandatory=$true)] $OPivotKey, [Parameter()] [String] $Tool = "", [Parameter()] [Switch] $NoNewWorksheets ) $tables = @() $data = $DataObj.data $meta = $DataObj.meta $innerPivot = $meta.InnerPivot $outerPivot = $meta.OuterPivot $nextRow = $HeaderRows + 1 $numProps = $data.$OPivotKey.keys.Count $propEnum = $data.$OPivotKey.keys.GetEnumerator() $propEnum.MoveNext() $prop = $propEnum.Current $iKeyEnum = $data.$OPivotKey.$prop.keys.GetEnumerator() $iKeyEnum.MoveNext() $iKey = $iKeyEnum.Current $numMetrics = $data.$OPivotKey.$prop.$iKey.baseline.stats.keys.Count $numIters = ($numProps * $meta.innerPivotKeys.Count * $numMetrics) $j = 0 foreach ($prop in $data.$OPivotKey.keys) { $tableTitle = Get-TableTitle -Tool $Tool -OuterPivot $outerPivot -OPivotKey $OPivotKey $table = @{ "rows" = @{ $prop = @{} } "cols" = @{ $tableTitle = @{ $innerPivot = @{} } } "meta" = @{ "columnFormats" = @() "name" = "Stats" "numWrites" = 1 + 2 } "data" = @{ $tableTitle = @{ $innerPivot = @{} } } } $col = 0 $row = 0 foreach ($IPivotKey in $data.$OPivotKey.$prop.Keys | Sort) { # Add column labels to table if (-not $meta.comparison) { $table.cols.$tableTitle.$innerPivot.$IPivotKey = $col $table.data.$tableTitle.$innerPivot.$IPivotKey = @{ $prop = @{} } $col += 1 $table.meta.columnFormats += $meta.format.$prop $table.meta.numWrites += 1 } else { $table.cols.$tableTitle.$innerPivot.$IPivotKey = @{ "baseline" = $col "% Change" = $col + 1 "test" = $col + 2 } $table.meta.numWrites += 4 $table.meta.columnFormats += $meta.format.$prop $table.meta.columnFormats += "0.0%" $table.meta.columnFormats += $meta.format.$prop $col += 3 $table.data.$tableTitle.$innerPivot.$IPivotKey = @{ "baseline" = @{ $prop = @{} } "% Change" = @{ $prop = @{} } "test" = @{ $prop = @{} } } } # Add row labels and fill data in table #$cellRow = $nextRow if ($data.$OPivotKey.$prop.$IPivotKey.baseline.stats) {$metrics = $data.$OPivotKey.$prop.$IPivotKey.baseline.stats.Keys} else {$metrics = $data.$OPivotKey.$prop.$IPivotKey.test.stats.keys} foreach ($metric in $Metrics) { if ($table.rows.$prop.Keys -notcontains $metric) { $table.rows.$prop.$metric = $row $row += 1 $table.meta.numWrites += 1 } if (-not $meta.comparison) { $table.data.$tableTitle.$innerPivot.$IPivotKey.$prop.$metric = @{"value" = $data.$OPivotKey.$prop.$IPivotKey.baseline.stats.$metric} } else { if ($data.$OPivotKey.$prop.$IPivotKey.baseline.stats) { $table.data.$tableTitle.$innerPivot.$IPivotKey.baseline.$prop.$metric = @{"value" = $data.$OPivotKey.$prop.$IPivotKey.baseline.stats.$metric} } if ($data.$OPivotKey.$prop.$IPivotKey.test.stats) { $table.data.$tableTitle.$innerPivot.$IPivotKey.test.$prop.$metric = @{"value" = $data.$OPivotKey.$prop.$IPivotKey.test.stats.$metric} } if ($data.$OPivotKey.$prop.$IPivotKey.baseline.stats -and $data.$OPivotKey.$prop.$IPivotKey.test.stats) { $baseCell = "$(Get-ColName ($col - 1))$nextRow" $testCell = "$(Get-ColName ($col + 1))$nextRow" $table.data.$tableTitle.$innerPivot.$IPivotKey."% change".$prop.$metric = @{"value" = "=IF($baseCell=0, ""--"", ($testCell-$baseCell)/ABS($baseCell))"} $params = @{ "Cell" = $table.data.$tableTitle.$innerPivot.$IPivotKey."% change".$prop.$metric "TestVal" = $data.$OPivotKey.$prop.$IPivotKey.test.stats.$metric "BaseVal" = $data.$OPivotKey.$prop.$IPivotKey.baseline.stats.$metric "Goal" = $meta.goal.$prop } # Certain statistics always have the same goal. if ($metric -eq "n") { $params.goal = "increase" } elseif ($metric -in @("range", "variance", "std dev", "std err")) { $params.goal = "decrease" } elseif ($metric -in @("skewness", "kurtosis")) { $params.goal = "none" } $table.data.$tableTitle.$innerPivot.$IPivotKey."% change".$prop.$metric = Set-CellColor @params } Write-Progress -Activity "Formatting Tables" -Status "Stats Table" -Id 3 -PercentComplete (100 * (($j++) / $numIters)) } $nextRow += 1 #$cellRow += 1 } # foreach $metric } $nextRow += $HeaderRows + 1 $table.meta.dataWidth = Get-TreeWidth $table.cols $table.meta.colLabelDepth = Get-TreeDepth $table.cols $table.meta.dataHeight = Get-TreeWidth $table.rows $table.meta.rowLabelDepth = Get-TreeDepth $table.rows $table.meta.numWrites += $table.meta.dataHeight * $table.meta.dataWidth $tables += $table } if (($tables.Count -gt 0) -and (-not $NoNewWorksheets)) { $sheetTitle = Get-WorksheetTitle -BaseName "Stats" -OuterPivot $outerPivot -OPivotKey $OPivotKey $tables = [Array]@($sheetTitle) + $tables } Write-Progress -Activity "Formatting Tables" -Status "Stats Table" -Id 3 -PercentComplete 100 return $tables } ## # Format-Quartiles # ---------------- # This function formats a table in order to create a chart that displays the quartiles # of each data subcategory (organized by sortProp), one chart per property. # # Parameters # ---------- # DataObj (HashTable) - Object containing processed data, raw data, and meta data # TableTitle (String) - Title to be displayed at the top of each table # # Return # ------ # HashTable[] - Array of HashTable objects which each store a table of formatted quartile data # ## function Format-Quartiles { param ( [Parameter(Mandatory=$true)] [PSobject[]] $DataObj, [Parameter(Mandatory=$true)] $OPivotKey, [Parameter()] [String] $Tool = "", [Parameter()] [switch] $NoNewWorksheets ) $tables = @() $data = $DataObj.data $meta = $DataObj.meta $innerPivot = $meta.InnerPivot $outerPivot = $meta.OuterPivot $numProps = $data.$OPivotKey.keys.Count $numIters = ($numProps * $meta.innerPivotKeys.Count) $j = 0 foreach ($prop in $data.$OPivotKey.Keys) { $format = $meta.format.$prop $tableTitle = Get-TableTitle -Tool $Tool -OuterPivot $outerPivot -OPivotKey $OPivotKey $cappedProp = (Get-Culture).TextInfo.ToTitleCase($prop) $table = @{ "rows" = @{ $prop = @{ $innerPivot = @{} } } "cols" = @{ $tableTitle = @{ "min" = 0 "Q1" = 1 "Q2" = 2 "Q3" = 3 "Q4" = 4 } } "meta" = @{ "dataWidth" = 5 "name" = "Quartiles" "numWrites" = 2 + 6 } "data" = @{ $tableTitle = @{ "min" = @{ $prop = @{ $innerPivot = @{} } } "Q1" = @{ $prop = @{ $innerPivot = @{} } } "Q2" = @{ $prop = @{ $innerPivot = @{} } } "Q3" = @{ $prop = @{ $innerPivot = @{} } } "Q4" = @{ $prop = @{ $innerPivot = @{} } } } } "chartSettings" = @{ "chartType"= [Excel.XlChartType]::xlColumnStacked "plotBy" = [Excel.XlRowCol]::xlColumns "xOffset" = 1 "YOffset" = 1 "title" = "$cappedProp Quartiles" "seriesSettings"= @{ 1 = @{ "hide" = $true "name" = " " } 2 = @{ "color" = $ColorPalette.blue[1] } 3 = @{ "color" = $ColorPalette.blue[3] } 4 = @{ "color" = $ColorPalette.blue[0] } 5 = @{ "color" = $ColorPalette.blue[2] } } "axisSettings" = @{ 1 = @{ "majorGridlines" = $true } 2 = @{ "minorGridlines" = $true "minorGridlinesColor" = $ColorPalette.LightGray[0] "majorGridlinesColor" = $ColorPalette.LightGray[1] "title" = $meta.units[$prop] } } } } if ($meta.comparison) { $table.cols = @{ $tableTitle = @{ "<baseline>min" = 0 "<baseline>Q1" = 1 "<baseline>Q2" = 2 "<baseline>Q3" = 3 "<baseline>Q4" = 4 "<test>min" = 5 "<test>Q1" = 6 "<test>Q2" = 7 "<test>Q3" = 8 "<test>Q4" = 9 } } $table.data = @{ $tableTitle = @{ "<baseline>min" = @{ $prop = @{ $innerPivot = @{} } } "<baseline>Q1" = @{ $prop = @{ $innerPivot = @{} } } "<baseline>Q2" = @{ $prop = @{ $innerPivot = @{} } } "<baseline>Q3" = @{ $prop = @{ $innerPivot = @{} } } "<baseline>Q4" = @{ $prop = @{ $innerPivot = @{} } } "<test>min" = @{ $prop = @{ $innerPivot = @{} } } "<test>Q1" = @{ $prop = @{ $innerPivot = @{} } } "<test>Q2" = @{ $prop = @{ $innerPivot = @{} } } "<test>Q3" = @{ $prop = @{ $innerPivot = @{} } } "<test>Q4" = @{ $prop = @{ $innerPivot = @{} } } } } $table.chartSettings.seriesSettings[6] = @{ "hide" = $true "name" = " " } $table.chartSettings.seriesSettings[7] = @{ "color" = $ColorPalette.orange[1] } $table.chartSettings.seriesSettings[8] = @{ "color" = $ColorPalette.orange[3] } $table.chartSettings.seriesSettings[9] = @{ "color" = $ColorPalette.orange[0] } $table.chartSettings.seriesSettings[10] = @{ "color" = $ColorPalette.orange[2] } $table.meta.columnFormats = @($format) * $table.cols.$tableTitle.Count; } # Add row labels and fill data in table $row = 0 foreach ($IPivotKey in $data.$OPivotKey.$prop.Keys | Sort) { if (-not $meta.comparison) { $table.meta.numWrites += 1 $table.rows.$prop.$innerPivot.$IPivotKey = $row $row += 1 $table.data.$TableTitle.min.$prop.$innerPivot.$IPivotKey = @{ "value" = $data.$OPivotKey.$prop.$IPivotKey.baseline.stats.min } $table.data.$TableTitle.Q1.$prop.$innerPivot.$IPivotKey = @{ "value" = $data.$OPivotKey.$prop.$IPivotKey.baseline.percentiles[25] - $data.$OPivotKey.$prop.$IPivotKey.baseline.stats.min } $table.data.$TableTitle.Q2.$prop.$innerPivot.$IPivotKey = @{ "value" = $data.$OPivotKey.$prop.$IPivotKey.baseline.percentiles[50] - $data.$OPivotKey.$prop.$IPivotKey.baseline.percentiles[25] } $table.data.$TableTitle.Q3.$prop.$innerPivot.$IPivotKey = @{ "value" = $data.$OPivotKey.$prop.$IPivotKey.baseline.percentiles[75] - $data.$OPivotKey.$prop.$IPivotKey.baseline.percentiles[50]} $table.data.$TableTitle.Q4.$prop.$innerPivot.$IPivotKey = @{ "value" = $data.$OPivotKey.$prop.$IPivotKey.baseline.stats.max - $data.$OPivotKey.$prop.$IPivotKey.baseline.percentiles[75] } } else { $table.meta.numWrites += 3 $table.rows.$prop.$innerPivot.$IPivotKey = @{ "baseline" = $row "test" = $row + 1 } $row += 2 $table.data.$TableTitle."<baseline>min".$prop.$innerPivot.$IPivotKey = @{ "baseline" = @{ "value" = $data.$OPivotKey.$prop.$IPivotKey.baseline.stats.min } } $table.data.$TableTitle."<test>min".$prop.$innerPivot.$IPivotKey = @{ "test" = @{ "value" = $data.$OPivotKey.$prop.$IPivotKey.test.stats.min} } $table.data.$TableTitle."<baseline>Q1".$prop.$innerPivot.$IPivotKey = @{ "baseline" = @{ "value" = $data.$OPivotKey.$prop.$IPivotKey.baseline.percentiles[25] - $data.$OPivotKey.$prop.$IPivotKey.baseline.stats.min } } $table.data.$TableTitle."<test>Q1".$prop.$innerPivot.$IPivotKey = @{ "test" = @{ "value" = $data.$OPivotKey.$prop.$IPivotKey.test.percentiles[25] - $data.$OPivotKey.$prop.$IPivotKey.test.stats.min } } $table.data.$TableTitle."<baseline>Q2".$prop.$innerPivot.$IPivotKey = @{ "baseline" = @{ "value" = $data.$OPivotKey.$prop.$IPivotKey.baseline.percentiles[50] - $data.$OPivotKey.$prop.$IPivotKey.baseline.percentiles[25] } } $table.data.$TableTitle."<test>Q2".$prop.$innerPivot.$IPivotKey = @{ "test" = @{ "value" = $data.$OPivotKey.$prop.$IPivotKey.test.percentiles[50] - $data.$OPivotKey.$prop.$IPivotKey.test.percentiles[25] } } $table.data.$TableTitle."<baseline>Q3".$prop.$innerPivot.$IPivotKey = @{ "baseline" = @{ "value" = $data.$OPivotKey.$prop.$IPivotKey.baseline.percentiles[75] - $data.$OPivotKey.$prop.$IPivotKey.baseline.percentiles[50] } } $table.data.$TableTitle."<test>Q3".$prop.$innerPivot.$IPivotKey = @{ "test" = @{ "value" = $data.$OPivotKey.$prop.$IPivotKey.test.percentiles[75] - $data.$OPivotKey.$prop.$IPivotKey.test.percentiles[50] } } $table.data.$TableTitle."<baseline>Q4".$prop.$innerPivot.$IPivotKey = @{ "baseline" = @{ "value" = $data.$OPivotKey.$prop.$IPivotKey.baseline.stats.max - $data.$OPivotKey.$prop.$IPivotKey.baseline.percentiles[75] } } $table.data.$TableTitle."<test>Q4".$prop.$innerPivot.$IPivotKey = @{ "test" = @{ "value" = $data.$OPivotKey.$prop.$IPivotKey.test.stats.max - $data.$OPivotKey.$prop.$IPivotKey.test.percentiles[75] } } } Write-Progress -Activity "Formatting Tables" -Status "Quartiles Table" -Id 3 -PercentComplete (100 * (($j++) / $numIters)) } $table.meta.dataWidth = Get-TreeWidth $table.cols $table.meta.colLabelDepth = Get-TreeDepth $table.cols $table.meta.dataHeight = Get-TreeWidth $table.rows $table.meta.rowLabelDepth = Get-TreeDepth $table.rows $table.meta.numWrites += $table.meta.dataHeight * $table.meta.dataWidth $tables = $tables + $table } if (($tables.Count -gt 0) -and (-not $NoNewWorksheets)) { $sheetTitle = Get-WorksheetTitle -BaseName "Quartiles" -OuterPivot $outerPivot -OPivotKey $OPivotKey $tables = @($sheetTitle) + $tables } Write-Progress -Activity "Formatting Tables" -Status "Quartiles Table" -Id 3 -PercentComplete 100 return $tables } ## # Format-MinMaxChart # ---------------- # This function formats a table that displays min, mean, and max of each data subcategory, # one table per property. This table primarily serves to generate a line chart for the # visualization of this data. # # Parameters # ---------- # DataObj (HashTable) - Object containing processed data, raw data, and meta data # TableTitle (String) - Title to be displayed at the top of each table # # Return # ------ # HashTable[] - Array of HashTable objects which each store a table of formatted data # ## function Format-MinMaxChart { Param ( [Parameter(Mandatory=$true)] [PSobject[]] $DataObj, [Parameter(Mandatory=$true)] $OPivotKey, [Parameter()] [String] $Tool = "", [Parameter()] [switch] $NoNewWorksheets ) $tables = @() $data = $DataObj.data $meta = $DataObj.meta $innerPivot = $meta.InnerPivot $outerPivot = $meta.OuterPivot $metrics = @("min", "mean", "max") $numProps = $data.$OPivotKey.keys.Count $numIters = ($numProps * $meta.innerPivotKeys.Count * $metrics.Count) $j = 0 foreach ($prop in $data.$OPivotKey.keys) { $cappedProp = (Get-Culture).TextInfo.ToTitleCase($prop) $tableTitle = Get-TableTitle -Tool $Tool -OuterPivot $outerPivot -OPivotKey $OPivotKey $table = @{ "rows" = @{ $prop = @{} } "cols" = @{ $tableTitle = @{ $innerPivot = @{} } } "meta" = @{ "columnFormats" = @() "name" = "MinMaxCharts" "numWrites" = 1 + 2 } "data" = @{ $tableTitle = @{ $innerPivot = @{} } } "chartSettings" = @{ "chartType" = [Excel.XlChartType]::xlLineMarkers "plotBy" = [Excel.XlRowCol]::xlRows "title" = $cappedProp "xOffset" = 1 "yOffset" = 2 "dataTable" = $true "hideLegend" = $true "axisSettings" = @{ 1 = @{ "majorGridlines" = $true } 2 = @{ "minorGridlines" = $true "minorGridlinesColor" = $ColorPalette.LightGray[0] "majorGridlinesColor" = $ColorPalette.LightGray[1] "title" = $meta.units.$prop } } } } if ($meta.comparison) { $table.chartSettings.seriesSettings = @{ 1 = @{ "color" = $ColorPalette.blue[3] "markerColor" = $ColorPalette.blue[3] "markerStyle" = [Excel.XlMarkerStyle]::xlMarkerStyleCircle "lineWeight" = 3 "markerSize" = 5 } 2 = @{ "color" = $ColorPalette.orange[3] "markerColor" = $ColorPalette.orange[3] "markerStyle" = [Excel.XlMarkerStyle]::xlMarkerStyleCircle "lineWeight" = 3 "markerSize" = 5 } 3 = @{ "color" = $ColorPalette.blue[2] "markerColor" = $ColorPalette.blue[2] "markerStyle" = [Excel.XlMarkerStyle]::xlMarkerStyleCircle "lineWeight" = 3 "markerSize" = 5 } 4 = @{ "color" = $ColorPalette.orange[2] "markerColor" = $ColorPalette.orange[2] "markerStyle" = [Excel.XlMarkerStyle]::xlMarkerStyleCircle "lineWeight" = 3 "markerSize" = 5 } 5 = @{ "color" = $ColorPalette.blue[1] "markerColor" = $ColorPalette.blue[1] "markerStyle" = [Excel.XlMarkerStyle]::xlMarkerStyleCircle "lineWeight" = 3 "markerSize" = 5 } 6 = @{ "color" = $ColorPalette.orange[1] "markerColor" = $ColorPalette.orange[1] "markerStyle" = [Excel.XlMarkerStyle]::xlMarkerStyleCircle "lineWeight" = 3 "markerSize" = 5 } } } else { $table.chartSettings.seriesSettings = @{ 1 = @{ "color" = $ColorPalette.blue[3] "markerColor" = $ColorPalette.blue[3] "markerStyle" = [Excel.XlMarkerStyle]::xlMarkerStyleCircle "lineWeight" = 3 "markerSize" = 5 } 2 = @{ "color" = $ColorPalette.blue[2] "markerColor" = $ColorPalette.blue[2] "markerStyle" = [Excel.XlMarkerStyle]::xlMarkerStyleCircle "lineWeight" = 3 "markerSize" = 5 } 3 = @{ "color" = $ColorPalette.blue[1] "markerColor" = $ColorPalette.blue[1] "markerStyle" = [Excel.XlMarkerStyle]::xlMarkerStyleCircle "lineWeight" = 3 "markerSize" = 5 } } } if (-not $innerPivot) { $table.chartSettings.yOffset = 3 } $col = 0 $row = 0 foreach ($IPivotKey in $data.$OPivotKey.$prop.Keys | Sort) { # Add column labels to table $table.cols.$tableTitle.$innerPivot.$IPivotKey = $col $table.meta.numWrites += 1 $table.data.$tableTitle.$innerPivot.$IPivotKey = @{ $prop = @{} } $table.meta.columnFormats += $meta.format.$prop $col += 1 # Add row labels and fill data in table foreach ($metric in $metrics) { if (-not ($table.rows.$prop.Keys -contains $metric)) { if (-not $meta.comparison) { $table.rows.$prop.$metric = $row $row += 1 $table.meta.numWrites += 1 } else { $table.meta.numWrites += 3 $table.rows.$prop.$metric = @{ "baseline" = $row "test" = $row + 1 } $row += 2 } } if (-not ($table.data.$tableTitle.$innerPivot.$IPivotKey.$prop.Keys -contains $metric)) { $table.data.$tableTitle.$innerPivot.$IPivotKey.$prop.$metric = @{} } if (-not $meta.comparison) { $table.data.$tableTitle.$innerPivot.$IPivotKey.$prop.$metric = @{"value" = $data.$OPivotKey.$prop.$IPivotKey.baseline.stats.$metric} } else { $table.data.$tableTitle.$innerPivot.$IPivotKey.$prop.$metric.baseline = @{"value" = $data.$OPivotKey.$prop.$IPivotKey.baseline.stats.$metric} $table.data.$tableTitle.$innerPivot.$IPivotKey.$prop.$metric.test = @{"value" = $data.$OPivotKey.$prop.$IPivotKey.test.stats.$metric} } Write-Progress -Activity "Formatting Tables" -Status "MinMeanMax Table" -Id 3 -PercentComplete (100 * (($j++) / $numIters)) } } $table.meta.dataWidth = Get-TreeWidth $table.cols $table.meta.colLabelDepth = Get-TreeDepth $table.cols $table.meta.dataHeight = Get-TreeWidth $table.rows $table.meta.rowLabelDepth = Get-TreeDepth $table.rows $table.meta.numWrites += $table.meta.dataHeight * $table.meta.dataWidth $tables = $tables + $table } if (($tables.Count -gt 0) -and (-not $NoNewWorksheets)) { $sheetTitle = Get-WorksheetTitle -BaseName "MinMeanMax" -OuterPivot $outerPivot -OPivotKey $OPivotKey $tables = @($sheetTitle) + $tables } Write-Progress -Activity "Formatting Tables" -Status "MinMeanMax Table" -Id 3 -PercentComplete 100 return $tables } ## # Format-Percentiles # ---------------- # This function formats a table displaying percentiles of each data subcategory, one # table per property + sortProp combo. When in comparison mode, percent change is also # plotted and is color-coded to indicate improvement/regression. A chart is also formatted # with each table. # # Parameters # ---------- # DataObj (HashTable) - Object containing processed data, raw data, and meta data # TableTitle (String) - Title to be displayed at the top of each table # # Return # ------ # HashTable[] - Array of HashTable objects which each store a table of formatted percentile data # ## function Format-Percentiles { Param ( [Parameter(Mandatory=$true)] [PSobject[]] $DataObj, [Parameter(Mandatory=$true)] $OPivotKey, [Parameter()] [String] $Tool = "", [Parameter()] [switch] $NoNewWorksheets ) $tables = @() $data = $DataObj.data $meta = $DataObj.meta $innerPivot = $meta.InnerPivot $outerPivot = $meta.OuterPivot $nextRow = $HeaderRows $numIters = 0 $propEnum = $data.$OPivotKey.keys.GetEnumerator() while ($propEnum.MoveNext()) { $prop = $propEnum.Current $iKeyEnum = $data.$OPivotKey.$prop.keys.GetEnumerator() while ($iKeyEnum.MoveNext()) { $iKey = $iKeyEnum.Current if ($data.$OPivotKey.$prop.$iKey.baseline.percentiles) { $numIters += $data.$OPivotKey.$prop.$iKey.baseline.percentiles.keys.Count } elseif ($data.$OPivotKey.$prop.$iKey.test.percentiles) { $numIters += $data.$OPivotKey.$prop.$iKey.test.percentiles.keys.Count } } } $j = 0 foreach ($prop in $data.$OPivotKey.Keys) { foreach ($IPivotKey in $data.$OPivotKey.$prop.Keys | Sort) { if ($innerPivot) { $chartTitle = (Get-Culture).TextInfo.ToTitleCase("$prop Percentiles - $IPivotKey $innerPivot") $tableTitle = Get-TableTitle -Tool $Tool -OuterPivot $outerPivot -OPivotKey $OPivotKey -InnerPivot $innerPivot -IPivotKey $IPivotKey } else { $chartTitle = (Get-Culture).TextInfo.ToTitleCase("$prop Percentiles") $tableTitle = Get-TableTitle -Tool $Tool -OuterPivot $outerPivot -OPivotKey $OPivotKey -InnerPivot $innerPivot -IPivotKey $IPivotKey } $table = @{ "rows" = @{ "percentiles" = @{} } "cols" = @{ $tableTitle = @{ $prop = 0 } } "meta" = @{ "columnFormats" = @($meta.format.$prop) "rightAlign" = [Array] @(2) "name" = "Percentiles" "numWrites" = 1 + 2 } "data" = @{ $tableTitle = @{ $prop = @{ "percentiles" = @{} } } } "chartSettings" = @{ "title" = $chartTitle "yOffset" = 1 "xOffset" = 1 "chartType" = [Excel.XlChartType]::xlXYScatterLinesNoMarkers "seriesSettings" = @{ 1 = @{ "color" = $ColorPalette.blue[2] "lineWeight" = 3 } } "axisSettings" = @{ 1 = @{ "max" = 100 "title" = "Percentiles" "minorGridlines" = $true } 2 = @{ "title" = $meta.units[$prop] } } } } $table.chartSettings.axisSettings[2].logarithmic = Set-Logarithmic -Data $data -OPivotKey $OPivotKey -Prop $prop -IPivotKey $IPivotKey -Meta $meta if ($meta.comparison) { $table.meta.numWrites += 3 $table.cols.$tableTitle.$prop = @{ "baseline" = 0 "% change" = 1 "test" = 2 } $table.data.$tableTitle.$prop = @{ "baseline" = @{ "percentiles" = @{} } "% change" = @{ "percentiles" = @{} } "test" = @{ "percentiles" = @{} } } $table.chartSettings.seriesSettings[2] = @{ "delete" = $true } $table.chartSettings.seriesSettings[3] = @{ "color" = $ColorPalette.orange[2] "lineWeight" = 3 } $table.meta.columnFormats = @($meta.format.$prop, "0.0%", $meta.format.$prop) } $row = 0 $keys = @() if ($data.$OPivotKey.$prop.$IPivotKey.baseline.percentiles.Keys.Count -gt 0) { $keys = $data.$OPivotKey.$prop.$IPivotKey.baseline.percentiles.Keys } else { $keys = $data.$OPivotKey.$prop.$IPivotKey.test.percentiles.Keys } # Add row labels and fill data in table foreach ($percentile in $keys | Sort) { $table.rows.percentiles.$percentile = $row $table.meta.numWrites += 1 if ($meta.comparison) { $baseCell = "C$nextRow" $testCell = "E$nextRow" if ($data.$OPivotKey.$prop.$IPivotKey.ContainsKey("baseline")) { $table.data.$tableTitle.$prop.baseline.percentiles[$percentile] = @{"value" = $data.$OPivotKey.$prop.$IPivotKey.baseline.percentiles.$percentile} } if ($data.$OPivotKey.$prop.$IPivotKey.ContainsKey("test")) { $table.data.$tableTitle.$prop.test.percentiles[$percentile] = @{"value" = $data.$OPivotKey.$prop.$IPivotKey.test.percentiles.$percentile} } if ($data.$OPivotKey.$prop.$IPivotKey.ContainsKey("baseline") -and $data.$OPivotKey.$prop.$IPivotKey.ContainsKey("test")) { $table.data.$tableTitle.$prop."% change".percentiles[$percentile] = @{"value" = "=IF($baseCell=0, ""--"", ($testCell-$baseCell)/ABS($baseCell))"} $params = @{ "Cell" = $table.data.$tableTitle.$prop."% change".percentiles[$percentile] "TestVal" = $data.$OPivotKey.$prop.$IPivotKey.test.percentiles[$percentile] "BaseVal" = $data.$OPivotKey.$prop.$IPivotKey.baseline.percentiles[$percentile] "Goal" = $meta.goal.$prop } $table.data.$tableTitle.$prop."% change".percentiles[$percentile] = Set-CellColor @params } } else { $table.data.$tableTitle.$prop.percentiles[$percentile] = @{"value" = $data.$OPivotKey.$prop.$IPivotKey.baseline.percentiles.$percentile} } $row += 1 $nextRow += 1 Write-Progress -Activity "Formatting Tables" -Status "Percentiles Table" -Id 3 -PercentComplete (100 * (($j++) / $numIters)) } $nextRow += $HeaderRows $table.meta.dataWidth = Get-TreeWidth $table.cols $table.meta.colLabelDepth = Get-TreeDepth $table.cols $table.meta.dataHeight = Get-TreeWidth $table.rows $table.meta.rowLabelDepth = Get-TreeDepth $table.rows $table.meta.numWrites += $table.meta.dataHeight * $table.meta.dataWidth $tables = $tables + $table } } if (($tables.Count -gt 0) -and (-not $NoNewWorksheets)) { $sheetTitle = Get-WorksheetTitle -BaseName "Percentiles" -OuterPivot $outerPivot -OPivotKey $OPivotKey $tables = @($sheetTitle) + $tables } Write-Progress -Activity "Formatting Tables" -Status "Done" -Id 3 -PercentComplete 100 return $tables } function Set-Logarithmic ($Data, $OPivotKey, $Prop, $IPivotKey, $Meta) { if ($data.$OPivotKey.$Prop.$IPivotKey.baseline.stats) { if ($data.$OPivotKey.$Prop.$IPivotKey.baseline.stats.min -le 0) { return $false } if ($Meta.comparison) { if ($data.$OPivotKey.$Prop.$IPivotKey.test.stats.min -le 0) { return $false } } if (($data.$OPivotKey.$Prop.$IPivotKey.baseline.stats.max / ($data.$OPivotKey.$Prop.$IPivotKey.baseline.stats.min + $EPS)) -gt 10) { return $true } if ($Meta.comparison) { if (($data.$OPivotKey.$Prop.$IPivotKey.test.stats.max / ($data.$OPivotKey.$Prop.$IPivotKey.test.stats.min + $EPS)) -gt 10) { return $true } if (($data.$OPivotKey.$Prop.$IPivotKey.test.stats.max / ($data.$OPivotKey.$Prop.$IPivotKey.baseline.stats.min + $EPS)) -gt 10) { return $true } if (($data.$OPivotKey.$Prop.$IPivotKey.baseline.stats.max / ($data.$OPivotKey.$Prop.$IPivotKey.test.stats.min + $EPS)) -gt 10) { return $true } } } return $false } <# .SYNOPSIS Returns a template for Format-Histogram #> function Get-HistogramTemplate { param( [PSObject[]] $DataObj, [String] $TableTitle, [String] $Property, [String] $IPivotKey ) $meta = $DataObj.meta $chartTitle = if ($IPivotKey) { "$Property Histogram - $IPivotKey $($meta.InnerPivot)" } else { "$Property Histogram" } $table = @{ "rows" = @{ "histogram buckets" = @{} } "cols" = @{ $TableTitle = @{ $Property = 0 } } "meta" = @{ "rightAlign" = [Array] @(2) "columnFormats" = @("0.0%") "name" = "Histogram" "numWrites" = 1 + 2 } "data" = @{ $TableTitle = @{ $Property = @{ "histogram buckets" = @{} } } } "chartSettings"= @{ "title" = $TextInfo.ToTitleCase($chartTitle) "yOffset" = 1 "xOffset" = 1 "seriesSettings" = @{ 1 = @{ "color" = $ColorPalette.blue[2] "lineWeight" = 1 "name" = "Frequency" } } "axisSettings" = @{ 1 = @{ "title" = "$Property ($($meta.units[$Property]))" "tickLabelSpacing" = 5 } 2 = @{ "title" = "Frequency" } } } # chartSettings } # Support base/test comparison mode if ($meta.comparison) { $table.cols.$TableTitle.$Property = @{ "baseline" = 0 "% change" = 1 "test" = 2 } $table.meta.numWrites += 3 $table.data.$TableTitle.$Property = @{ "baseline" = @{ "histogram buckets" = @{} } "% change" = @{ "histogram buckets" = @{} } "test" = @{ "histogram buckets" = @{} } } $table.chartSettings.seriesSettings[1].name = "Baseline" $table.chartSettings.seriesSettings[2] = @{ "delete" = $true # don't plot % change } $table.chartSettings.seriesSettings[3] = @{ "color" = $ColorPalette.orange[2] "name" = "Test" "lineWeight" = 1 } $table.meta.columnFormats = @("0.0%", "0.0%", "0.0%") } return $table } # Get-HistogramTemplate <# .SYNOPSIS Outputs a table with a histogram and chart. #> function Format-Histogram { param( [Parameter(Mandatory=$true)] [PSObject[]] $DataObj, [Parameter(Mandatory=$true)] $OPivotKey, [Parameter(Mandatory=$true)] [String] $Tool ) $tables = @() $meta = $DataObj.meta foreach ($prop in $DataObj.data.$OPivotKey.Keys) { foreach ($iPivotKey in $DataObj.data.$OPivotKey.$prop.Keys | sort) { $data = $DataObj.data.$OPivotKey.$prop.$iPivotKey if (-not $data.baseline.histogram -and -not $data.test.histogram) { continue } $tableTitle = Get-TableTitle -Tool $Tool -OuterPivot $meta.OuterPivot -OPivotKey $OPivotKey -InnerPivot $meta.InnerPivot -IPivotKey $iPivotKey $table = Get-HistogramTemplate -DataObj $DataObj -TableTitle $tableTitle -Property $prop -IPivotKey $iPivotKey if ($data.baseline.histogram) { $baseSum = ($data.baseline.histogram.Values | measure -Sum).Sum } if ($data.test.histogram) { $testSum = ($data.test.histogram.Values | measure -Sum).Sum } # Add row labels and fill data in table $row = 0 $buckets = if ($data.baseline.histogram.Keys.Count -gt 0) {$data.baseline.histogram.Keys} else {$data.test.histogram.Keys} foreach ($bucket in ($buckets | sort)) { $table.rows."histogram buckets".$bucket = $row $table.meta.numWrites += 1 if (-not $meta.comparison) { $baseVal = $data.baseline.histogram.$bucket / $baseSum $table.data.$tableTitle.$prop."histogram buckets"[$bucket] = @{"value" = $baseVal} } else { if ($data.baseline.histogram) { $baseVal = $data.baseline.histogram.$bucket / $baseSum $table.data.$tableTitle.$prop.baseline."histogram buckets"[$bucket] = @{"value" = $baseVal} } if ($data.test.histogram) { $testVal = $data.test.histogram.$bucket / $testSum $table.data.$tableTitle.$prop.test."histogram buckets"[$bucket] = @{"value" = $testVal} } if ($data.baseline.histogram -and $data.test.histogram) { $baseCell = "C$($row + $HeaderRows)" $testCell = "E$($row + $HeaderRows)" $table.data.$tableTitle.$prop."% change"."histogram buckets"[$bucket] = @{"value" = "=IF($baseCell=0, ""--"", ($testCell-$baseCell)/ABS($baseCell))"} $table.data.$tableTitle.$prop."% change"."histogram buckets"[$bucket] = Set-CellColor -Cell $table.data.$tableTitle.$prop."% change"."histogram buckets"[$bucket] -BaseVal $baseVal -TestVal $testVal -Goal "increase" } } $row += 1 } $table.meta.dataWidth = Get-TreeWidth $table.cols $table.meta.colLabelDepth = Get-TreeDepth $table.cols $table.meta.dataHeight = Get-TreeWidth $table.rows $table.meta.rowLabelDepth = Get-TreeDepth $table.rows $table.meta.numWrites += $table.meta.dataHeight * $table.meta.dataWidth $tables = $tables + $table } } if ($table.Count -gt 0) { $worksheetTitle = Get-WorksheetTitle -BaseName "Histogram" -OuterPivot $meta.OuterPivot -OPivotKey $OPivotKey $tables = @($worksheetTitle) + $tables } return $tables } ## # Format-Distribution # ------------------- # This function formats a table in order to create a chart that displays the the # distribution of data over time. # # Parameters # ---------- # DataObj (HashTable) - Object containing processed data, raw data, and meta data # TableTitle (String) - Title to be displayed at the top of each table # Prop (String) - The name of the property for which a table should be created (raw data must be in array form) # SubSampleRate (int) - How many time samples should be grouped together for a single data point on the chart # # Return # ------ # HashTable[] - Array of HashTable objects which each store a table of formatted distribution data # ## function Format-Distribution { Param ( [Parameter(Mandatory=$true)] [PSobject[]] $DataObj, [Parameter()] [string] $OPivotKey, [Parameter()] [String] $Tool = "", [Parameter()] [String] $Prop, [Parameter()] [Int] $SubSampleRate = -1, [Parameter()] [switch] $NoNewWorksheets ) $DEFALT_SEGMENTS_TARGET = 200 $meta = $DataObj.meta $modes = if ($meta.comparison) { @("baseline", "test") } else { @(,"baseline") } $tables = @() $innerPivot = $meta.InnerPivot $outerPivot = $meta.OuterPivot $NumSamples = Calculate-MaxNumSamples -RawData $DataObj.rawData -Modes $modes -Prop $Prop if ($SubSampleRate -eq -1) { $SubSampleRate = [Int] ($NumSamples/$DEFALT_SEGMENTS_TARGET) } $numIters = Calculate-NumIterations -Distribution -DataObj $dataObj -Prop $Prop -SubSampleRate $SubSampleRate $j = 0 foreach ($IPivotKey in $DataObj.data.$OPivotKey.$Prop.Keys) { foreach ($mode in $modes) { if (-Not $DataObj.data.$OPivotKey.$Prop.$IPivotKey.$mode.stats) { continue } $logarithmic = Set-Logarithmic -Data $dataObj.data -OPivotKey $OPivotKey -Prop $Prop -IPivotKey $IPivotKey ` -Meta $meta $tableTitle = Get-TableTitle -Tool $Tool -OuterPivot $outerPivot -OPivotKey $OPivotKey -InnerPivot $innerPivot -IPivotKey $IPivotKey $data = $dataObj.rawData.$mode $table = @{ "meta" = @{ "name" = "Distribution" "numWrites" = 1 + 3 } "rows" = @{ "Data Point" = @{} } "cols" = @{ $tableTitle = @{ "Time Segment" = 0 $Prop = 1 } } "data" = @{ $tableTitle = @{ "Time Segment" = @{ "Data Point" = @{} } $Prop = @{ "Data Point" = @{} } } } "chartSettings" = @{ "chartType" = [Excel.XlChartType]::xlXYScatter "yOffset" = 2 "xOffset" = 2 "title" = "Temporal $prop Distribution" "axisSettings" = @{ 1 = @{ "title" = "Time Series" "minorGridlines" = $true "majorGridlines" = $true "max" = $NumSamples } 2 = @{ "title" = $meta.units.$Prop "logarithmic" = $logarithmic "min" = 10 } } } } if ($mode -eq "baseline") { $table.chartSettings.seriesSettings = @{ 1 = @{ "markerStyle" = [Excel.XlMarkerStyle]::xlMarkerStyleCircle "markerBackgroundColor" = $ColorPalette.blue[3] "markerForegroundColor" = $ColorPalette.blue[2] "name" = "$Prop Sample" } } } else { $table.chartSettings.seriesSettings = @{ 1 = @{ "markerStyle" = [Excel.XlMarkerStyle]::xlMarkerStyleCircle "markerBackgroundColor" = $ColorPalette.blue[3] "markerForegroundColor" = $ColorPalette.blue[2] "name" = "$Prop Sample" } } } # Add row labels and fill data in table $i = 0 $row = 0 if ($SubSampleRate -gt 0) { $finished = $false while (-Not $finished) { [Array]$segmentData = @() foreach ($entry in $data) { if ($entry.$Prop.GetType().Name -ne "Object[]") { continue } if (((-not $innerPivot) -or ($entry.$innerPivot -eq $IPivotKey)) -and ` ((-not $outerPivot) -or ($entry.$outerPivot -eq $OPivotKey)) -and ` ($i * $SubSampleRate -lt $entry.$Prop.Count)) { $finalIdx = (($i + 1) * $SubSampleRate) - 1 if (((($i + 1) * $SubSampleRate) - 1) -ge $entry.$Prop.Count) { $finalIdx = $entry.$Prop.Count - 1 } $segmentData += $entry.$Prop[($i * $SubSampleRate) .. $finalIdx] } } $segmentData = $segmentData | Sort $time = $i * $subSampleRate if ($segmentData.Count -ge 5) { $table.rows."Data Point".$row = $row $table.rows."Data Point".($row + 1) = $row + 1 $table.rows."Data Point".($row + 2) = $row + 2 $table.rows."Data Point".($row + 3) = $row + 3 $table.rows."Data Point".($row + 4) = $row + 4 $table.data.$tableTitle."Time Segment"."Data Point".$row = @{"value" = $time} $table.data.$tableTitle."Time Segment"."Data Point".($row + 1) = @{"value" = $time} $table.data.$tableTitle."Time Segment"."Data Point".($row + 2) = @{"value" = $time} $table.data.$tableTitle."Time Segment"."Data Point".($row + 3) = @{"value" = $time} $table.data.$tableTitle."Time Segment"."Data Point".($row + 4) = @{"value" = $time} $table.data.$tableTitle.$Prop."Data Point".$row = @{"value" = $segmentData[0]} $table.data.$tableTitle.$Prop."Data Point".($row + 1) = @{"value" = $segmentData[[int]($segmentData.Count / 4)]} $table.data.$tableTitle.$Prop."Data Point".($row + 2) = @{"value" = $segmentData[[int]($segmentData.Count / 2)]} $table.data.$tableTitle.$Prop."Data Point".($row + 3) = @{"value" = $segmentData[[int]((3 * $segmentData.Count) / 4)]} $table.data.$tableTitle.$Prop."Data Point".($row + 4) = @{"value" = $segmentData[-1]} $row += 5 $table.meta.numWrites += 5 } elseif ($segmentData.Count -ge 1){ foreach ($sample in $segmentData) { $table.rows."Data Point".$row = $row $table.data.$tableTitle."Time Segment"."Data Point".$row = @{"value" = $time} $table.data.$tableTitle.$Prop."Data Point".$row = @{"value" = $sample} $row++ $table.meta.numWrites += 1 } } else { $finished = $true } $i++ Write-Progress -Activity "Formatting Tables" -Status "Distribution Table" -Id 3 -PercentComplete (100 * (($j++) / $numIters)) } } else { $finished = $false while (-not $finished) { [Array]$segmentData = @() foreach ($entry in $data) { if ($entry.$prop.GetType().Name -ne "Object[]") { continue } if (((-not $innerPivot) -or ($entry.$innerPivot -eq $IPivotKey)) -and ((-not $outerPivot) -or ($entry.$outerPivot -eq $OPivotKey))) { if ($null -eq $entry[$Prop][$i]) { continue } $segmentData += $entry[$Prop][$i] } } $finished = ($segmentData.Count -eq 0) foreach ($sample in $segmentData) { $table.rows."Data Point".$row = $row $table.data.$tableTitle."Time Segment"."Data Point".$row = @{"value" = $i} $table.data.$tableTitle.$Prop."Data Point".$row = @{"value" = $sample} $row++ $table.meta.numWrites += 1 } $i++ Write-Progress -Activity "Formatting Tables" -Status "Distribution Table" -Id 3 -PercentComplete (100 * (($j++) / $numIters)) } } $table.meta.dataWidth = Get-TreeWidth $table.cols $table.meta.colLabelDepth = Get-TreeDepth $table.cols $table.meta.dataHeight = Get-TreeWidth $table.rows $table.meta.rowLabelDepth = Get-TreeDepth $table.rows $table.meta.numWrites += $table.meta.dataHeight * $table.meta.dataWidth if (-not $NoNewWorksheets) { if ($modes.Count -gt 1) { if ($mode -eq "baseline") { $worksheetName = Get-WorksheetTitle -BaseName "Base Distr." -OuterPivot $outerPivot -OPivotKey $OPivotKey -InnerPivot $innerPivot -IPivotKey $IPivotKey -Prop $Prop } else { $worksheetName = Get-WorksheetTitle -BaseName "Test Distr." -OuterPivot $outerPivot -OPivotKey $OPivotKey -InnerPivot $innerPivot -IPivotKey $IPivotKey -Prop $Prop } } else { $worksheetName = Get-WorksheetTitle -BaseName "Distr." -OuterPivot $outerPivot -OPivotKey $OPivotKey -InnerPivot $innerPivot -IPivotKey $IPivotKey -Prop $Prop } $tables += $worksheetName } $tables += $table } } Write-Progress -Activity "Formatting Tables" -Status "Distribution Table" -Id 3 -PercentComplete 100 return $tables } <# .SYNOPSIS Calculates the maximum numer of samples of a given property provided by a single data file #> function Calculate-MaxNumSamples ($RawData, $Modes, $Prop) { $max = 0 foreach ($mode in $Modes) { foreach ($fileEntry in $RawData.$mode) { if ($fileEntry.$Prop.Count -gt $max) { $max = $fileEntry.$Prop.Count } } } $max } function Calculate-NumIterations { param ( [Parameter(Mandatory=$true, ParameterSetName="distribution")] [Switch] $Distribution, [Parameter(Mandatory=$true)] $DataObj, [Parameter(Mandatory=$true, ParameterSetName = "distribution")] [String] $Prop, [Parameter(Mandatory=$true, ParameterSetName = "distribution")] [Int] $SubSampleRate ) if ($Distribution) { $innerLoopIters = 0 $maxSamples = Calculate-MaxNumSamples -RawData $DataObj.rawData -Modes @("baseline") -Prop $Prop if ($SubsampleRate -gt 0) { $innerLoopIters += 1 + [Int]( ($maxSamples / $SubsampleRate) + 0.5) } else { $innerLoopIters += $maxSamples } if ($dataObj.meta.comparison) { $maxSamples = Calculate-MaxNumSamples -RawData $DataObj.rawData -Modes @("test") -Prop $Prop if ($SubsampleRate -gt 0) { $innerLoopIters += 1 + [Int](($maxSamples/ $SubSampleRate) + 0.5) } else { $innerLoopIters += $maxSamples + 1 } } return $DataObj.meta.innerPivotKeys.Count * $innerLoopIters } } <# .SYNOPSIS Sets the colors of a cell, indicating whether a test value shows an improvement when compared to a baseline value. Improvement is defined by the goal (increase/decrease) for the given value. .PARAMETER Cell Object containg a cell's value and other settings. .PARAMETER TestVal Test metric value. .PARAMETER BaseVal Baseline metric value. .PARAMETER Goal Defines metric improvement direction. "increase", "decrease", or "none". #> function Set-CellColor ($Cell, [Decimal] $TestVal, [Decimal] $BaseVal, $Goal) { if (($Goal -ne "none") -and ($TestVal -ne $BaseVal)) { if (($Goal -eq "increase") -eq ($TestVal -gt $BaseVal)) { $Cell["fontColor"] = $ColorPalette.Green $Cell["cellColor"] = $ColorPalette.LightGreen } else { $Cell["fontColor"] = $ColorPalette.Red $Cell["cellColor"] = $ColorPalette.LightRed } } return $Cell } ## # Get-TreeWidth # ------------- # Calculates the width of a tree structure # # Parameters # ---------- # Tree (HashTable) - Object with a heirarchical tree structure # # Return # ------ # int - Width of Tree # ## function Get-TreeWidth ($Tree) { if ($Tree.GetType().Name -eq "Int32") { return 1 } $width = 0 foreach ($key in $Tree.Keys) { $width += [int](Get-TreeWidth -Tree $Tree[$key]) } return $width } ## # Get-TreeWidth # ------------- # Calculates the depth of a tree structure # # Parameters # ---------- # Tree (HashTable) - Object with a heirarchical tree structure # # Return # ------ # int - Depth of Tree # ## function Get-TreeDepth ($Tree) { if ($Tree.GetType().Name -eq "Int32") { return 0 } $depths = @() foreach ($key in $Tree.Keys) { $depths = $depths + [int](Get-TreeDepth -Tree $Tree[$key]) } return ($depths | Measure -Maximum).Maximum + 1 } |