PowerShell в CSV-файле - поиск строки в зависимости от строки - PullRequest
0 голосов
/ 27 октября 2018

Мне нужна ваша помощь в программировании PowerShell для файла CSV.

Я провел несколько поисков, но не могу найти то, что ищу (или, возможно, я не знаю технических терминов).По сути, у меня есть книга Excel с большим объемом данных (более или менее 38 столбцов х 350 000 строк), и есть несколько формул, для расчета которых требуются часы.

Сначала мне было интересно, может ли PowerShell ускорить работунемного вычисления по сравнению с Excel.Расчеты, занимающие большую часть моего времени, на самом деле не так сложны (по крайней мере, на первый взгляд).Мои данные более или менее построены так:

Ref      Title
-----    --------------------------
A/001    "free_text"
A/002    "free_text A/001 free_text"
...      ...
A/005    "free_text A/004 free_text"
A/006    "free_text"
B/001    "free_text" 
B/002    "free_text"
C/001    "free_text"
C/002    "free_text"
...
C/050    "free_text C/047 free_text"
...      ...
C/103    "free_text"
D/001    "free_text"
D/002    "free_text D/001 free_text"
...      ....

В основном данные выглядят следующим образом:

  1. поле Ref содержит уникальные значения, в {letter}/{incremental value} формат.
  2. В некоторых строках поле Title может вызывать одно из данных Ref .Например, в строке 2 Title вызывает A / 001 Ref .В последнем ряду Title вызывает D / 001 Ref и т. Д.
  3. Нет логического шаблона, определяющего, когда эта ссылка может быть вызвана взаглавие.Это случайно.

Тем не менее, я уверен на 100% в следующем:

  1. Ref , вызываемый в Заголовок всегда принадлежит одному и тому же блоку {letter}.Например: строка «C / 047» в поле Title может быть найдена только в блоке, где Ref {letter} равно C.
  2. Ref , вызываемый в Title , всегда будет находиться «после» (или в нижнем ряду), чем Ref , к которому он относится.Другими словами, у меня не может быть строки со следующим шаблоном:

    Ref             Title
    ------------    -----------------------------------------
    {letter/i}      {free_text {letter/j} free_text} with j<i
    

    → Это невозможно.
    → j всегда> i

IЯ использовал эти характеристики в Excel, чтобы свести к минимуму мои массивы поиска.Но все еще требуется час, чтобы вычислить все.

Поэтому я заглянул в PowerShell и начал немного «играть» с CSV и повторять цикл с ForEach-Object, надеясь, что у меня будут более быстрые результаты.До сих пор я в основном дважды зацикливался на своем CSV-файле.

$CSV1 = myfile.csv
$CSV2 = myfile.csv

$CSV1 | ForEach-Object {
    # find Title
    $TitSearch = $_.$Ref
    $CSV2 | ForEach-Object {
        if ($_.$Title -eq $TitSearch) {
            myinstructions
        }
    }
}

Это работает, но действительно очень долго.Тогда я попробовал следующее вместо использования $CSV2 | ForEach...:

$CSV | where {$_.$Title -eq $TitleSearch} | % $Ref

В любом случае это слишком долго и совсем не эффективно.Кроме того, с этими двумя решениями я не использую вышеупомянутые характеристики, которые могут уменьшить массив поиска, и, как уже говорилось, кажется, что я заканчиваю циклом два раза по файлу CSV от его начала до конца.

Вопросы:

  1. Есть ли более простой способ сделать это?
  2. Я трачу свое время на PowerShell?
  3. Я думаю о создании 1 файла на Ссылка {letter} block (1 файл для блока A, 1 для B и т. Д.).Однако мне нужно создать около 50 000 блоков.Или создайте их один за другим, проведите анализ, поместите результаты в новый файл и удалите их.Это будет быстрее?

Примечание: это для работы, которая будет использоваться другими коллегами, и Excel и PowerShell - действительно единственное программное обеспечение, которое мы можем использовать.Я знаю VBA, но хорошо ... В конце мне интересно, как и если это можно решить простым способом с помощью PowerShell.

1 Ответ

0 голосов
/ 29 октября 2018

Насколько я вижу, ваш базовый алгоритм выполняет N ^ 2 итерации (~ 120 миллиардов). Существует стандартный способ сделать его эффективным - сначала нужно создать хеш-таблицу. Hashtable - это хранилище ключей / значений, и поиск происходит практически мгновенно, поэтому временная сложность алгоритма станет ~ N. Powershell имеет встроенный тип данных для этого. В вашем случае ключом будет ref, а значением будет массив данных ячейки (при условии, что ваша таблица имеет вид что-то вроде: ref, title, col1, ..., colN)

$hash = @{}
foreach($row in $table} {$hash.Add($row.ref, @($row.title, $row.col1, ...)}
#it will take 350K steps to generate it
#then you can iterate over it again
foreach($key in $hash.Keys) { 
 $key # access current ref
 $rowData = $hash.$key # access to current row elements (by index)
 $refRowData = $hash[$rowData[$j]] # lookup from other rows, assuming lookup reference is in some column
}

Так что это общая идея, как решить проблему времени. Если честно, я не верю, что вам нужно воссоздавать колесо и кодировать его самостоятельно. Что вам нужно, это реляционная база данных. Поскольку у вас есть Excel, вы должны иметь MS ACCESS тоже Просто импортируйте свои данные туда, сделайте ref и title индексом, и все, что вам нужно сделать, это самостоятельно присоединиться. MS Access отстой, но я уверен, что он справится с 350K строкой просто отлично. В идеале вам нужно получить базу данных на каком-нибудь корпоративном сервере MSSQL (открыть тикет, поговорить с вашим менеджером и т. Д.). Он вычислит все это за считанные секунды, а затем вы также можете связать вывод с электронной таблицей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...