Last active
October 13, 2025 12:25
-
-
Save eyalk11/7052231e839d218495f86488512584f6 to your computer and use it in GitHub Desktop.
Powershell function to get histogram of data
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #based on https://www.powershellgallery.com/packages/Statistics/1.1.53/Content/Get-Histogram.ps1 | |
| #improved. by Eyal Karni | |
| function Get-Histogram { | |
| [CmdletBinding(DefaultParameterSetName='BucketCount')] | |
| Param( | |
| [Parameter(Mandatory, ValueFromPipeline, Position=1)] | |
| [ValidateNotNullOrEmpty()] | |
| [array] | |
| $InputObject | |
| , | |
| [Parameter(Position=2)] | |
| [ValidateNotNullOrEmpty()] | |
| [string] | |
| $Property | |
| , | |
| [Parameter()] | |
| [ValidateNotNullOrEmpty()] | |
| [float] | |
| $Minimum | |
| , | |
| [Parameter()] | |
| [ValidateNotNullOrEmpty()] | |
| [float] | |
| $Maximum | |
| , | |
| [Parameter()] | |
| [ValidateNotNullOrEmpty()] | |
| [Alias('Width')] | |
| [float] | |
| $BucketWidth = 1 | |
| , | |
| [Parameter()] | |
| [ValidateNotNullOrEmpty()] | |
| [Alias('Count')] | |
| [float] | |
| $BucketCount | |
| , | |
| [Parameter()] | |
| [switch] | |
| $Visualize | |
| , | |
| [Parameter()] | |
| [ValidateRange(1, 200)] | |
| [int] | |
| $BarWidth = 73 | |
| , | |
| [Parameter()] | |
| [switch] | |
| $Weighted | |
| ) | |
| Begin { | |
| Write-Verbose ('[{0}] Initializing' -f $MyInvocation.MyCommand) | |
| $Buckets = @{} | |
| $Data = @() | |
| } | |
| Process { | |
| Write-Verbose ('[{0}] Processing {1} items' -f $MyInvocation.MyCommand, $InputObject.Length) | |
| $InputObject | ForEach-Object { | |
| if ($Weighted) { | |
| # Expect data in format (probability, value) or [probability, value] | |
| if ($_ -is [array] -and $_.Count -eq 2) { | |
| $Data += [PSCustomObject]@{ | |
| Weight = $_[0] | |
| Value = $_[1] | |
| } | |
| } elseif ($_.GetType().ToString() -like 'System.Tuple*') { | |
| $Data += [PSCustomObject]@{ | |
| Weight = $_.Item1 | |
| Value = $_.Item2 | |
| } | |
| } else { | |
| Write-Host $_ | |
| throw ('Weighted data must be in format (probability, value) or [probability, value]') | |
| } | |
| } else { | |
| if ($Property) { | |
| if (-Not ($_ | Select-Object -ExpandProperty $Property -ErrorAction SilentlyContinue)) { | |
| throw ('Input object does not contain a property called <{0}>.' -f $Property) | |
| } | |
| } | |
| $Data += $_ | |
| } | |
| } | |
| } | |
| End { | |
| Write-Verbose ('[{0}] Building histogram' -f $MyInvocation.MyCommand) | |
| Write-Debug ('[{0}] Retrieving measurements from upstream cmdlet.' -f $MyInvocation.MyCommand) | |
| if ($Weighted) { | |
| $Stats = $Data | Microsoft.PowerShell.Utility\Measure-Object -Minimum -Maximum -Property Value | |
| } elseif ($Property) { | |
| $Stats = $Data | Microsoft.PowerShell.Utility\Measure-Object -Minimum -Maximum -Property $Property | |
| } else { | |
| $Stats = $Data | Microsoft.PowerShell.Utility\Measure-Object -Minimum -Maximum | |
| } | |
| if (-Not $PSBoundParameters.ContainsKey('Minimum')) { | |
| $Minimum = $Stats.Minimum | |
| Write-Debug ('[{0}] Minimum value not specified. Using smallest value ({1}) from input data.' -f $MyInvocation.MyCommand, $Minimum) | |
| } | |
| if (-Not $PSBoundParameters.ContainsKey('Maximum')) { | |
| $Maximum = $Stats.Maximum | |
| Write-Debug ('[{0}] Maximum value not specified. Using largest value ({1}) from input data.' -f $MyInvocation.MyCommand, $Maximum) | |
| } | |
| if (-Not $PSBoundParameters.ContainsKey('BucketCount')) { | |
| $BucketCount = [math]::Ceiling(($Maximum - $Minimum) / $BucketWidth) | |
| Write-Debug ('[{0}] Bucket count not specified. Calculated {1} buckets from width of {2}.' -f $MyInvocation.MyCommand, $BucketCount, $BucketWidth) | |
| } | |
| if ($BucketCount -gt 100) { | |
| Write-Warning ('[{0}] Generating {1} buckets' -f $MyInvocation.MyCommand, $BucketCount) | |
| } | |
| Write-Debug ('[{0}] Building buckets using: Minimum=<{1}> Maximum=<{2}> BucketWidth=<{3}> BucketCount=<{4}>' -f $MyInvocation.MyCommand, $Minimum, $Maximum, $BucketWidth, $BucketCount) | |
| $OverallCount = 0 | |
| $Buckets = 1..$BucketCount | ForEach-Object { | |
| [pscustomobject]@{ | |
| Index = $_ | |
| lowerBound = $Minimum + ($_ - 1) * $BucketWidth | |
| upperBound = $Minimum + $_ * $BucketWidth | |
| Count = 0 | |
| RelativeCount = 0 | |
| Group = @() | |
| PSTypeName = 'HistogramBucket' | |
| } | |
| } | |
| Write-Debug ('[{0}] Building histogram' -f $MyInvocation.MyCommand) | |
| $Data | ForEach-Object { | |
| if ($Weighted) { | |
| $Value = $_.Value | |
| $Weight = $_.Weight | |
| } elseif ($Property) { | |
| $Value = $_.$Property | |
| $Weight = 1 | |
| } else { | |
| $Value = $_ | |
| $Weight = 1 | |
| } | |
| if ($Value -ge $Minimum -and $Value -le $Maximum) { | |
| $BucketIndex = [math]::Floor(($Value - $Minimum) / $BucketWidth) | |
| if ($BucketIndex -lt $Buckets.Length) { | |
| $Buckets[$BucketIndex].Count += $Weight | |
| $Buckets[$BucketIndex].Group += $_ | |
| $OverallCount += $Weight | |
| } | |
| } | |
| } | |
| Write-Debug ('[{0}] Adding relative count' -f $MyInvocation.MyCommand) | |
| $Buckets | ForEach-Object { | |
| if ($OverallCount -gt 0) { | |
| $_.RelativeCount = $_.Count / $OverallCount | |
| } else { | |
| $_.RelativeCount = 0 | |
| } | |
| } | |
| if ($Visualize) { | |
| Write-Debug ('[{0}] Generating visualization' -f $MyInvocation.MyCommand) | |
| $MaxCount = ($Buckets | Measure-Object -Property Count -Maximum).Maximum | |
| $Buckets | Where-Object { $_.Count -gt 0 } | ForEach-Object { | |
| # Format the bucket range/label | |
| $Label = if ($Property) { | |
| "[{0:N1}-{1:N1}]" -f $_.lowerBound, $_.upperBound | |
| } else { | |
| "[{0:N1}-{1:N1}]" -f $_.lowerBound, $_.upperBound | |
| } | |
| # Calculate percentage | |
| $Percentage = if ($OverallCount -gt 0) { | |
| [int](100 * $_.Count / $OverallCount) | |
| } else { | |
| 0 | |
| } | |
| # Calculate bar length based on proportion to max count | |
| $BarLength = if ($MaxCount -gt 0) { | |
| [int]($BarWidth * $_.Count / $MaxCount) | |
| } else { | |
| 0 | |
| } | |
| # Create the bar | |
| $Bar = ("*" * $BarLength).PadRight($BarWidth) | |
| # Format and display the line | |
| $Line = "{0} {1}% {2} [{3}]" -f ` | |
| $Label.PadLeft(20), ` | |
| $Percentage.ToString().PadLeft(3), ` | |
| $Bar, ` | |
| $_.Count | |
| Write-Host $Line -ForegroundColor Green | |
| } | |
| } | |
| Write-Debug ('[{0}] Returning histogram' -f $MyInvocation.MyCommand) | |
| $Buckets | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment