此问题是this question的后续问题:
如何确定给定数据集(数组)statistical mode,即最常出现的一个值或一组值?
例如,在数组1, 2, 2, 3, 4, 4, 5
中有两种模式,2
和4
,因为它们是最频繁出现的值。
[使用Group-Object
,Sort-Object
和ForEach-Object
的组合:
# Sample dataset.
$dataset = 1, 2, 2, 3, 4, 4, 5
do { # dummy loop to allow efficient termination of the pipeline
$dataset | Group-Object | Sort-Object Count -Descending |
ForEach-Object -Begin { $topCount = 0 } -Process {
if ($_.Count -lt $topCount) { break } # No longer top occurrence count, exit
$topCount = $_.Count # Store the occurrence count.
$_.Group[0] # Output the input value represented by the group.
}
} while ($false)
以上产生2
和4
,这是两种模式(值最频繁出现,在这种情况下,每次出现两次);模式按输入顺序返回。
注意:虽然此解决方案在概念上很简单,但是可能需要考虑大型数据集的性能;有关某些输入可能的优化,请参见底部。
说明:
Group-Object
按相等性对所有输入进行分组。
[Group-Object
按成员计数以降序对结果组进行排序(首先出现最频繁的输入)。
Sort-Object -Descending
命令循环遍历已排序的组,并为出现次数(频率)最高的/所有组输出每个代表的输入。
产生虚拟Sort-Object -Descending
循环的原因是,从PowerShell Core 7.0.0-preview.5开始,如果不再需要处理其他输入,则没有direct方式退出管道。
有一个ForEach-Object
添加对此的支持。
解决方法是使用一个封闭的loop并以ForEach-Object
代替它。注意:not在没有封闭循环的管道中使用do
或longstanding feature request on GitHub,因为它会在封闭循环中查找调用堆栈并退出脚本(如果没有)。
相反,虽然break
可以在管道的break
块内使用,但它仅跳到next输入项-它不会停止处理其他输入。] >
更好的解决方案:
如果输入元素统一为简单数字或字符串
(相对于复杂对象),则可以进行优化:[continue
的return
禁止收集每个组中的各个输入。
每个组的ForEach-Object
属性反映了分组值,但是作为string
Group-Object