заполнить промежутки времени с силовым запросом - PullRequest
0 голосов
/ 22 января 2019

У меня есть следующие данные

   start        stop       status
+-----------+-----------+-----------+
| 09:01:10  | 09:01:40  |  active   |
| 09:02:30  | 09:04:50  |  active   |
| 09:10:01  | 09:11:50  |  active   |
+-----------+-----------+-----------+

Я хочу заполнить пробелы "пассивным"

   start        stop       status
+-----------+-----------+-----------+
| 09:01:10  | 09:01:40  |  active   |
| 09:01:40  | 09:02:30  |  passive  |
| 09:02:30  | 09:04:50  |  active   |
| 09:04:50  | 09:10:01  |  passive  |
| 09:10:01  | 09:11:50  |  active   |
+-----------+-----------+-----------+

Как я могу сделать это на языке M Query?

Ответы [ 3 ]

0 голосов
/ 22 января 2019

Я бы подошел к этому следующим образом:

  1. Дублируйте первую таблицу.
  2. Заменить «активный» на «пассивный».
  3. Удалить столбец start.
  4. Переименуйте stop в start.
  5. Создайте новый столбец stop, посмотрев самое раннее start время из исходной таблицы, которое происходит после текущего stop времени.
  6. Отфильтровать нули в этом новом столбце.
  7. Добавить эту таблицу к исходной таблице.

М-код будет выглядеть примерно так:

let
    Source = <...your starting table...>
    PassiveStatus = Table.ReplaceValue(Source,"active","passive",Replacer.ReplaceText,{"status"}),
    RemoveStart = Table.RemoveColumns(PassiveStatus,{"start"}),
    RenameStart = Table.RenameColumns(RemoveStart,{{"stop", "start"}}),
    AddStop = Table.AddColumn(RenameStart, "stop", (C) => List.Min(List.Select(Source[start], each _ > C[start])), type time),
    RemoveNulls = Table.SelectRows(AddStop, each ([stop] <> null)),
    CombineTables = Table.Combine({Source, RemoveNulls}),
    #"Sorted Rows" = Table.Sort(CombineTables,{{"start", Order.Ascending}})
in
    #"Sorted Rows"

Единственный хитрый бит выше - это пользовательская часть столбца, где я определяю новый столбец следующим образом:

(C) => List.Min(List.Select(Source[start], each _ > C[start]))

Принимает каждый элемент в столбце / списке Source[start] и сравнивает его со временем в текущей строке. Он выбирает только те из них, которые появляются после времени в текущей строке, а затем берет минимальное значение по этому списку, чтобы найти самый ранний.

Result

0 голосов
/ 23 января 2019

Думаю, у меня может быть более эффективное решение.

Из исходной таблицы (при условии, что она отсортирована) добавьте столбец индекса, начинающийся с 0, и столбец индекса, начинающийся с 1, а затем объединитетаблица сама выполняет левое внешнее соединение со столбцами индекса и расширяет столбец start.

Self Merge

Удаление столбцов, кроме stop,status и start.1 и отфильтровывать пустые значения.

Переименовать столбцы в start, status и stop и заменить "active" на "passive".

Наконец, добавьте эту таблицу к исходной таблице.

let
    Source = Table.RenameColumns(#"Removed Columns",{{"Column1.2", "start"}, {"Column1.3", "stop"}, {"Column1.4", "status"}}),
    Add1Index = Table.AddIndexColumn(Source, "Index", 1, 1),
    Add0Index = Table.AddIndexColumn(Add1Index, "Index.1", 0, 1),
    SelfMerge = Table.NestedJoin(Add0Index,{"Index"},Add0Index,{"Index.1"},"Added Index1",JoinKind.LeftOuter),
    ExpandStart1 = Table.ExpandTableColumn(SelfMerge, "Added Index1", {"start"}, {"start.1"}),
    RemoveCols = Table.RemoveColumns(ExpandStart1,{"start", "Index", "Index.1"}),
    FilterNulls = Table.SelectRows(RemoveCols, each ([start.1] <> null)),
    RenameCols = Table.RenameColumns(FilterNulls,{{"stop", "start"}, {"start.1", "stop"}}),
    ActiveToPassive = Table.ReplaceValue(RenameCols,"active","passive",Replacer.ReplaceText,{"status"}),
    AppendQuery = Table.Combine({Source, ActiveToPassive}),
    #"Sorted Rows" = Table.Sort(AppendQuery,{{"start", Order.Ascending}})
in
    #"Sorted Rows"

Это должна быть сложность O ( n ) с логикой, аналогичной @chillin, ноЯ думаю, что это должно быть быстрее, чем использование пользовательской функции, поскольку она будет использовать встроенное слияние, которое, вероятно, будет сильно оптимизировано.

0 голосов
/ 22 января 2019

Вы можете попробовать что-то похожее на приведенное ниже (мои первые два шага someTable и changedTypes просто для воссоздания образца данных на моем конце):

let
    someTable = Table.FromColumns({{"09:01:10", "09:02:30", "09:10:01"}, {"09:01:40", "09:04:50", "09:11:50"}, {"active", "active", "active"}}, {"start","stop","status"}),
    changedTypes = Table.TransformColumnTypes(someTable, {{"start", type duration}, {"stop", type duration}, {"status", type text}}),
    listOfRecords = Table.ToRecords(changedTypes),
    transformList = List.Accumulate(List.Skip(List.Positions(listOfRecords)), {listOfRecords{0}}, (listState, currentIndex) =>
        let
            previousRecord = listOfRecords{currentIndex-1},
            currentRecord = listOfRecords{currentIndex},
            thereIsAGap = currentRecord[start] <> previousRecord[stop],
            recordsToAdd = if thereIsAGap then {[start=previousRecord[stop], stop=currentRecord[start], status="passive"], currentRecord} else {currentRecord},
            append = listState & recordsToAdd
        in
            append
    ),
    backToTable = Table.FromRecords(transformList, type table [start=duration, stop=duration, status=text])
in
    backToTable

Это то, с чего я начинаю (на шаге changedTypes):

Input table

Вот что я получаю в итоге:

Output table

Для интеграции с существующим кодом M вам, вероятно, потребуется:

  • удалить someTable и changedTypes из моего кода (и заменить существующим запросом)
  • замените changedTypes в шаге listOfRecords на то, что называется вашим последним шагом (в противном случае вы получите ошибку, если в вашем коде нет выражения changedTypes).

Edit:

В дополнение к моему ответу я хотел бы предложить следующее:

Попробуйте изменить эту строку в коде выше:

listOfRecords = Table.ToRecords(changedTypes),

до

listOfRecords = List.Buffer(Table.ToRecords(changedTypes)),

Я обнаружил, что хранение списка в памяти значительно сократило время обновления (возможно, ~ 90%, если определить его количественно). Я предполагаю, что есть ограничения и недостатки (например, если список не может соответствовать), но может быть хорошо для вашего варианта использования.

Peformance graphed

Вы испытываете подобное поведение? Кроме того, мой базовый график, к сожалению, показывает нелинейную сложность кода.

Последнее замечание: я обнаружил, что генерация и обработка строк по 100 КБ приводили к переполнению стека при обновлении запроса (это могло произойти из-за генерации входных строк и может не включать вставку новых строк, не знаю). Ясно, что у этого подхода есть пределы.

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