Страницы: 1
RSS
PQ. Как отлаживать пользовательские функции?
 
Уважаемые форумчане!

Вопрос возник в результате попытки осмысления решения, предложенного на задачу участника под ником stanislove. Надеюсь, он не обидится на меня, если я сошлюсь на его задачу и предложенное решение в данной теме.

Задача была сформулирована в теме "Power Bi: посчитать разговоры сотрудников, между которыми была пауза более указанной ".
В результате было предложено решение, в котором активно используется достаточно сложная (прошу не кидаться помидорами), по крайней мере, состоящая из нескольких операторов пользовательская функция.

Вот запрос с содержанием этой функции:
Код
 Source = Excel.CurrentWorkbook(){[Name="Лист1"]}[Content],
    calc = Table.Group(Source, {"Источник (кто)"}, {"temp", (sub) =>
    let
        setOrder = Table.Sort(sub, {"Дата вызова", "Время начала разговора"}),
        addInfo = Table.AddColumn(setOrder, "temp", each 
            [start = [Дата вызова] + Duration.From([Время начала разговора]),
             finish = start + #duration(0, 0, 0, [#"Продолжительность разговора (сек)"])
        ]),
        baseData = Table.ExpandRecordColumn(addInfo[[temp]], "temp", {"start", "finish"}),
        before = Table.ToColumns(Table.RemoveLastN(baseData, 1)),
        after = Table.ToColumns(Table.RemoveFirstN(baseData, 1)),
        result = Table.FromColumns(before & after)
    in
        Table.AddColumn(result, "Column5", each Duration.TotalSeconds([Column3] - [Column2]))
    }),
    #"Expanded {0}" = Table.ExpandTableColumn(calc, "temp", {"Column1", "Column2", "Column3", "Column4", "Column5"}, {"Column1", "Column2", "Column3", "Column4", "Column5"}),
    #"Renamed Columns" = Table.RenameColumns(#"Expanded {0}",{{"Column1", "Звонок1"}, {"Column2", "Завершён1"}, {"Column3", "Звонок2"}, {"Column4", "Завершён2"}, {"Column5", "Простой (сек)"}})
in
    #"Renamed Columns"

Собственно, к вопросу.
Пользовательская функция достаточно сложная. Очевидно её нужно отлаживать. Во избежание...

Вопрос: но как? Какие механизмы?
Есть ли способ пройти пользовательскую функцию по шагам, чтобы посмотреть, как отрабатывает каждый оператор, входящий в  функцию? А то в окне "примененные шаги" вся функция, совместно с оператором вызова - это всё один большой шаг...
 
Доброе время суток
Цитата
quasarrr написал:
Очевидно её нужно отлаживать
Справедливо. Разберем. Пусть есть функция
Код
let
  myfunc = (a, b) => 
    let
      c = a + b
    in
      c,
  result = myfunc(5, 6)
in
  result
Что мы делаем, когда используем функцию? Передаём ей некоторые значения. Тогда чтобы отладить преобразуем функцию в запрос, где её аргументы будут иметь некоторые значения.
Код
let
  // указываем значения аргументов
  a = 5,
  b = 6,
  // собственно сами шаги функции
  c = a + b
in
  c
Основная задача - это для анонимных функций, как в приведённой вами, знать что будет на входе, чтобы правильно задать значения аргументов.
В Table.Group аргументом для (sub) => является подтаблица, в которой значения списка столбцов группировки - одинаковые.
Тогда создаём вспомогательную таблицу и используем её в качестве такого значения аргумента.
В примере запрос - Отладка функции. Плюс, это файл, предложенный АБСАБС, в котором он предложил, чуть другой подход.
Изменено: Андрей VG - 15.02.2020 00:09:44
 
Большое спасибо всем откликнувшимся.

Пошёл вкуривать.
 
Для группировки я обычно делаю так: создаю сначала группировку "Все строки":
Код
calc = Table.Group(Source, {"Источник (кто)"}, {"temp", each _, type table})

затем тыкаю в самую характерную из получившихся вложенных таблиц (например, тыкаем в первую). Получаем шаг типа
Код
#"Expanded{0}" = calc{0}[temp]

переименовываю его, например, в sub.
Дальше делаю нужные преобразования:
Код
setOrder = Table.Sort(sub, {"Дата вызова", "Время начала разговора"}),
        addInfo = Table.AddColumn(setOrder, "temp", each 
            [start = [Дата вызова] + Duration.From([Время начала разговора]),
             finish = start + #duration(0, 0, 0, [#"Продолжительность разговора (сек)"])
        ]),
        baseData = Table.ExpandRecordColumn(addInfo[[temp]], "temp", {"start", "finish"}),
        before = Table.ToColumns(Table.RemoveLastN(baseData, 1)),
        after = Table.ToColumns(Table.RemoveFirstN(baseData, 1)),
        result = Table.FromColumns(before & after),
        oneMoreResult = Table.AddColumn(result, "Column5", each Duration.TotalSeconds([Column3] - [Column2]))
in
       oneMoreResult

В общем, делаю то, что должна делать функция. В итоге у меня получается вот такой код:
Код
calc = Table.Group(Source, {"Источник (кто)"}, {"temp", each _, type table}),
sub = calc{0}[temp],
setOrder = Table.Sort(sub, {"Дата вызова", "Время начала разговора"}),
addInfo = Table.AddColumn(setOrder, "temp", each 
            [start = [Дата вызова] + Duration.From([Время начала разговора]),
             finish = start + #duration(0, 0, 0, [#"Продолжительность разговора (сек)"])
        ]),
baseData = Table.ExpandRecordColumn(addInfo[[temp]], "temp", {"start", "finish"}),
before = Table.ToColumns(Table.RemoveLastN(baseData, 1)),
after = Table.ToColumns(Table.RemoveFirstN(baseData, 1)),
result = Table.FromColumns(before & after),
oneMoreResult = Table.AddColumn(result, "Column5", each Duration.TotalSeconds([Column3] - [Column2]))
in
       oneMoreResult

все, что нужно теперь сделать, это в расширенном редакторе чуть-чуть почистить, заменив функцию "each _" на "(sub)=>" и добавив let/in для вложенных в функцию шагов, и еще один in в конце, вот так:
Код
calc = Table.Group(Source, {"Источник (кто)"}, {"temp", (sub)=>
// sub = calc{0}[temp],
let 
setOrder = Table.Sort(sub, {"Дата вызова", "Время начала разговора"}),
addInfo = Table.AddColumn(setOrder, "temp", each 
            [start = [Дата вызова] + Duration.From([Время начала разговора]),
             finish = start + #duration(0, 0, 0, [#"Продолжительность разговора (сек)"])
        ]),
baseData = Table.ExpandRecordColumn(addInfo[[temp]], "temp", {"start", "finish"}),
before = Table.ToColumns(Table.RemoveLastN(baseData, 1)),
after = Table.ToColumns(Table.RemoveFirstN(baseData, 1)),
result = Table.FromColumns(before & after),
oneMoreResult = Table.AddColumn(result, "Column5", each Duration.TotalSeconds([Column3] - [Column2]))
in
       oneMoreResult
})
in
    calc

Ну и естественно можно скопировать код в отдельную функцию, или сделать копию такого запроса (оставив в нем разобранный вариант, до "подчистки"), чтобы можно было вернуться.
F1 творит чудеса
 
Большое спасибо всем откликнувшимся :excl:  
 
quasarrr, можно еще вот так создавать функции связанные с запросом. Тогда любые изменения в запросе мигрируют в функцию, с ним связанную. Очень удобно при отладке.
НО, есть один нюанс, при создании запроса все входные данные, которые будут аргументами будущей функции необходимо задавать только с помощью параметров, иначе ваша итоговая функция будет неполноценной.
Вот горшок пустой, он предмет простой...
 
PooHkrd,

ага. Тоже вариант. Спасибо!
Страницы: 1
Наверх