Найти статистические режимы набора данных в PowerShell - PullRequest
3 голосов
/ 07 ноября 2019

Этот вопрос с автоответчиком является продолжением этого вопроса :

Как определить статистический режим данного массива данных (массива) , т. Е. одно значение или набор значений, которые встречаются чаще всего?

Например, в массиве 1, 2, 2, 3, 4, 4, 5 есть два режима, 2 и 4, поскольку они являются значениями, встречающимися наиболее часто.

1 Ответ

3 голосов
/ 07 ноября 2019

Используйте комбинацию 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 группывсе входы по равенству.

  • Sort-Object -Descending сортирует результирующие группы по количеству элементов в порядке убывания (сначала чаще всего встречаются входы).

  • Команда ForEach-Object проходит по отсортированным группам и выводит вход, представленный каждым, для групп / all с наибольшим количеством вхождений (частотой).

Причина фиктивного цикла do заключается в том, что в PowerShell Core 7.0.0-preview.5 отсутствует прямой способ выхода из конвейера при обработке дальнейших входных данных. больше не требуется.

Для добавления поддержки в GitHub существует давний запрос на добавление поддержки.

Обходной путь - использовать включающий loop и вырваться из него с помощью break.
Примечание: не используйте break или continue в конвейере без замкнутого цикла, так как он ищет стек вызовов для замкнутого цикла и завершает работу сценария, если его нет.

В отличие от этого, тогда как return может используется внутри блока ForEach-Object в конвейере, он только пропускает к элементу ввода next - он не останавливает обработку дальнейших вводов.


Более эффективное решение:

Если входные элементы равномерно простые числа или строки (в отличие от сложных объектов), возможна оптимизация:

  • Group-Object -NoElement подавляет сбор отдельных входных данных в каждой группе.

  • Свойство .Name каждой группы отражает значение группировки, но делает это так: строка , поэтому его необходимо преобразовать обратно в исходный тип данных.

# Sample dataset.
# Must be composed of all numbers or strings.
$dataset = 1, 2, 2, 3, 4, 4, 5

# Determine the data type of the elements in the set
# (assumed to be homogeneous).
$dataType = $dataset[0].GetType()

do {
  # Note the use of -NoElement
  $dataset | Group-Object -NoElement | Sort-Object Count -Descending | 
    ForEach-Object -Begin { $topCount = 0 } -Process { 
      if ($_.Count -lt $topCount) { break }
      $topCount = $_.Count # Store the occurrence count.
      # Convert the string-valued .Name property
      # back to the original type.
      if ($dataType -eq [string]) {
        $_.Name
      } else {
        $dataType::Parse($_.Name)
      }
    }
} while ($false)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...