Web.Contents и динамические ссылки
#НеВсеЙогуртыОдинаковоПолезны
Всем добра.
Что-то меня работой привалило малость. Но я выбрался и снова научу вас наносить добро и причинять пользу себе и окружающим.
В моем самом втором посте я кое чего обещал, вот сдерживаю слово.
Как формировать веб ссылку с изменяемым параметром чтобы облачная служба PBI не ругалась на динамическое содержимое:
#НеВсеЙогуртыОдинаковоПолезны
Всем добра.
Что-то меня работой привалило малость. Но я выбрался и снова научу вас наносить добро и причинять пользу себе и окружающим.
В моем самом втором посте я кое чего обещал, вот сдерживаю слово.
Как формировать веб ссылку с изменяемым параметром чтобы облачная служба PBI не ругалась на динамическое содержимое:
let
// ВОТ ТАК СЕРВИС БУДЕТ РУГАТЬСЯ, если ссылку формировать из динамических параметров в текстовую строку
Source = Xml.Tables(Web.Contents( "http://www.cbr.ru/scripts/XML_daily.asp?date_req=02/03/2002" )),
// ВОТ ТАК ругаться не будет, если Url это константа, а не элемент списка/записи
// при этом в параметр Query можно пихать все что хочется, лишь бы API вас понял
Url = "http://www.cbr.ru/scripts/XML_daily.asp",
Query = [Query = [date_req="02/03/2002"]],
Source1 = Xml.Tables(Web.Contents( Url, Query )),
// ВОТ ТАК тоже ругаться не будет, если Url это константа, а не элемент списка/записи
// но при этом оба элемента записи Query2 можно использовать как динамические параметры
Url2 = "http://www.cbr.ru/scripts/",
Query2 = [ RelativePath = "XML_daily.asp",
Query = [date_req="02/03/2002"]
],
Source2 = Xml.Tables(Web.Contents( Url2, Query2 ))
in
Source2
Здесь продублировал, чтобы удобнее код было на комп переносить.Telegram
Для тех, кто в танке
Folder.Files, PBI service и все, все, все...
#НеВсеЙогуртыОдинаковоПолезны
Всем добра! Начнём с Folder.Files. Сегодня в чудесном чате задали вопрос: "а чойта я тащу файлы из папки, а pbi service мне ругается про динамический контент?" А вся проблема в том…
#НеВсеЙогуртыОдинаковоПолезны
Всем добра! Начнём с Folder.Files. Сегодня в чудесном чате задали вопрос: "а чойта я тащу файлы из папки, а pbi service мне ругается про динамический контент?" А вся проблема в том…
Как сформировать таблицу на листе Excel с помощью формул DAX
#ВсякоРазно
Всем добра. И снова граждане интересуются странным, а я их учу нехорошему. Расскажу и вам.
Сценарий такой:
- Хочу увидеть на листе Excel таблицу из модели данных.
- Ну, раз хотите...
Итак, можно всегда сослаться на любую таблицу из модели, вот пример. Добавляйте любые столбцы в модели данных с формулами DAX, они тут же появятся в таблице на листе. Хотя таблица в модель вставлена просто из буфера и вообще не имеет внешних источников.
Небольшой гайд как такое сделать:
Если источником таблицы является запрос PQ, тогда просто в меню Загрузить в... кроме галочки о загрузки в модель данных, нужно одновременно выбрать вариант выгрузки еще и в таблицу.
Допустим источник - не запрос PQ,
1. Имеем в модели данных некую таблицу, к которой хотим подключить таблицу с листа и видеть все изменения, допустим в модели она называется Продажи
2. создаем пустой запрос с названием не совпадающим ни с одной из таблиц в модели данных, содержимое запроса не имеет значения, это может быть одна ячейка без значений.
3. результат загружаем и в модель и в таблицу на лист
4. тыкаем ПКМ по выгруженной таблице и выбираем пункт изменить DAX как на картинке
5. указываем имя нужной таблицы из модели
6. удаляем теперь не нужный запрос (он вместе с собой удалит и загруженную ненужную таблицу из модели данных)
7. пользуемся и радуемся.
З.Ы. На все вопросы, типа:
- Алексей, если это такое ненужное и странное, то откуда ты сам про такое знаешь?
Я отвечу:
- Я и сам был когда-то странным, а теперь я странный уж совсем.😜
Пользуйтесь на здоровье. Все картинки и файлы с примерами упомянутые в тексте можно поглядеть пройдя по ссылке.
#ВсякоРазно
Всем добра. И снова граждане интересуются странным, а я их учу нехорошему. Расскажу и вам.
Сценарий такой:
- Хочу увидеть на листе Excel таблицу из модели данных.
- Ну, раз хотите...
Итак, можно всегда сослаться на любую таблицу из модели, вот пример. Добавляйте любые столбцы в модели данных с формулами DAX, они тут же появятся в таблице на листе. Хотя таблица в модель вставлена просто из буфера и вообще не имеет внешних источников.
Небольшой гайд как такое сделать:
Если источником таблицы является запрос PQ, тогда просто в меню Загрузить в... кроме галочки о загрузки в модель данных, нужно одновременно выбрать вариант выгрузки еще и в таблицу.
Допустим источник - не запрос PQ,
1. Имеем в модели данных некую таблицу, к которой хотим подключить таблицу с листа и видеть все изменения, допустим в модели она называется Продажи
2. создаем пустой запрос с названием не совпадающим ни с одной из таблиц в модели данных, содержимое запроса не имеет значения, это может быть одна ячейка без значений.
3. результат загружаем и в модель и в таблицу на лист
4. тыкаем ПКМ по выгруженной таблице и выбираем пункт изменить DAX как на картинке
5. указываем имя нужной таблицы из модели
6. удаляем теперь не нужный запрос (он вместе с собой удалит и загруженную ненужную таблицу из модели данных)
7. пользуемся и радуемся.
З.Ы. На все вопросы, типа:
- Алексей, если это такое ненужное и странное, то откуда ты сам про такое знаешь?
Я отвечу:
- Я и сам был когда-то странным, а теперь я странный уж совсем.😜
Пользуйтесь на здоровье. Все картинки и файлы с примерами упомянутые в тексте можно поглядеть пройдя по ссылке.
www.planetaexcel.ru
можно ли вставить на лист эксель таблицу из модели данных?
сводную понимаю как, но хочу добавить обычную таблицу с вычисляемыми столбцами, которые я добавил в модели
Попытка намбер Ту
#ВсякиеКоннекторы
Всем бобра!
Итак, по просьбам трудящихся собрал ещё один коннектор, который на входе получает текстовую строку, а на выходе возвращает ее хэш при помощи алгоритма sha256. Использовать его очень легко:
положили в папку согласно инструкции. Запускаем pbi и находим его в разделе other.
Вызов осуществляется такой формулой
Если кто желает потестировать. Велкам. Архив тут.
З.Ы. На подходе аналогичный коннектор для получения HMAC на базе того же sha256.
#ВсякиеКоннекторы
Всем бобра!
Итак, по просьбам трудящихся собрал ещё один коннектор, который на входе получает текстовую строку, а на выходе возвращает ее хэш при помощи алгоритма sha256. Использовать его очень легко:
положили в папку согласно инструкции. Запускаем pbi и находим его в разделе other.
Вызов осуществляется такой формулой
=GetHashSHA256.Contents( "text" )
Пока от единственного пользователя поступил отзыв что запрос к api с полученным хэшем работает в редакторе запросов, но не пашет при попытке загрузить результат запроса в модель.Если кто желает потестировать. Велкам. Архив тут.
З.Ы. На подходе аналогичный коннектор для получения HMAC на базе того же sha256.
Docs
Develop a connector using the Power Query SDK - Power Query
Using the Power Query SDK to create new Power Query connectors
Для тех, кто в танке pinned «Попытка намбер Ту #ВсякиеКоннекторы Всем бобра! Итак, по просьбам трудящихся собрал ещё один коннектор, который на входе получает текстовую строку, а на выходе возвращает ее хэш при помощи алгоритма sha256. Использовать его очень легко: положили в папку согласно…»
Попытка намбер три
#ВсякиеКоннекторы
Всем добра!
Как и обещал, выкладываю последний коннектор из нужных для подключения к API с хэшированием ключей через Power BI. Сие творение используется для формирования HMAC (hash-based message authentication code) на основе двух аргументов, а именно текст запроса и ключа авторизации.
Для использования по инструкции из предыдущего поста кладем файл в нужную папку и наблюдаем новый коннектор в PBI Desktop в разделе others.
Для вызова функции используем код:
Функция протестирована, на API Ламоды - работает отлично. В теории должна работать и в DataFlows, но пока не тестировал.
Кстати, перевыложил предыдущий коннектор, исправил в нем блок для прохождения проверки авторизации при настройке подключения через шлюз.
Заранее предупреждаю, что с точки зрения безопасности коннектор не оптимален, т.к. подразумевает передачу логина и ключа в код в открытом виде. В планах исправить данную ситуацию и использовать креды для хранения конфиденциальной информации. Просто пока я так не умею, но обязательно научусь.
Удачных подключений!
#ВсякиеКоннекторы
Всем добра!
Как и обещал, выкладываю последний коннектор из нужных для подключения к API с хэшированием ключей через Power BI. Сие творение используется для формирования HMAC (hash-based message authentication code) на основе двух аргументов, а именно текст запроса и ключа авторизации.
Для использования по инструкции из предыдущего поста кладем файл в нужную папку и наблюдаем новый коннектор в PBI Desktop в разделе others.
Для вызова функции используем код:
= GetHMAC.Contents( "text", "key" )
На выходе получаем хэш созданный на базе этих двух аргументов. Далее скармливаем его вашему API согласно приведенной к нему документации.Функция протестирована, на API Ламоды - работает отлично. В теории должна работать и в DataFlows, но пока не тестировал.
Кстати, перевыложил предыдущий коннектор, исправил в нем блок для прохождения проверки авторизации при настройке подключения через шлюз.
Заранее предупреждаю, что с точки зрения безопасности коннектор не оптимален, т.к. подразумевает передачу логина и ключа в код в открытом виде. В планах исправить данную ситуацию и использовать креды для хранения конфиденциальной информации. Просто пока я так не умею, но обязательно научусь.
Удачных подключений!
Для тех, кто в танке
Как сформировать таблицу на листе Excel с помощью формул DAX #ВсякоРазно Всем добра. И снова граждане интересуются странным, а я их учу нехорошему. Расскажу и вам. Сценарий такой: - Хочу увидеть на листе Excel таблицу из модели данных. - Ну, раз хотите...…
Как сформировать таблицу на листе Excel с помощью формул DAX 2
#ПолезныеСсылки
Всем добра!
В продолжение темы вывода таблиц из модели данных на лист Экселя.
На днях "наше все" @MaximZelensky показал на пальцах как при помощи функции DAX EVALUATE() можно формировать справочники в таблицах на листе, после чего данные попадают в модель данных.
Таким образом, получаем возможность формирования справочников из таблиц фактов без многократного изнасилования их при помощи pq. Такое нельзя потерять, поэтому вот.
#ПолезныеСсылки
Всем добра!
В продолжение темы вывода таблиц из модели данных на лист Экселя.
На днях "наше все" @MaximZelensky показал на пальцах как при помощи функции DAX EVALUATE() можно формировать справочники в таблицах на листе, после чего данные попадают в модель данных.
Таким образом, получаем возможность формирования справочников из таблиц фактов без многократного изнасилования их при помощи pq. Такое нельзя потерять, поэтому вот.
Telegram
Maxim Zelensky in Power Query ru
Ну там не обязательно через PQ загружать. Короче, простор для развлечений :)
Релизная версия коннекторов для хэширования текстовых значений на лету
#ВсякиеКоннекторы
Всем добра.
Итак, завершаю свою эпопею с пользовательскими коннекторами и хэшированием всякого. Допилил оба коннектора для возможности передачи в них данных через учётные данные, залил двуязычную справку по использованию и залил исходники на гит.
Уже имеется как минимум 2 благодарных пользователя, у которых получилось даже с моими черновыми версиями. Это уже релиз.
https://github.com/PooHkrd/GetHashSHA256
https://github.com/PooHkrd/GetHMACwithCreds
Надеюсь кому-то ещё поможет. Инструкции по установке в репозиториях по ссылкам выше.
Все вопросы по использованию направляйте в личку, либо на планету, либо на гитхаб.
#ВсякиеКоннекторы
Всем добра.
Итак, завершаю свою эпопею с пользовательскими коннекторами и хэшированием всякого. Допилил оба коннектора для возможности передачи в них данных через учётные данные, залил двуязычную справку по использованию и залил исходники на гит.
Уже имеется как минимум 2 благодарных пользователя, у которых получилось даже с моими черновыми версиями. Это уже релиз.
https://github.com/PooHkrd/GetHashSHA256
https://github.com/PooHkrd/GetHMACwithCreds
Надеюсь кому-то ещё поможет. Инструкции по установке в репозиториях по ссылкам выше.
Все вопросы по использованию направляйте в личку, либо на планету, либо на гитхаб.
GitHub
GitHub - PooHkrd/GetHashSHA256: Custom connector for Microsoft Power BI. Creates hash for the text.
Custom connector for Microsoft Power BI. Creates hash for the text. - PooHkrd/GetHashSHA256
Для тех, кто в танке pinned «Релизная версия коннекторов для хэширования текстовых значений на лету #ВсякиеКоннекторы Всем добра. Итак, завершаю свою эпопею с пользовательскими коннекторами и хэшированием всякого. Допилил оба коннектора для возможности передачи в них данных через учётные…»
Ковыряемся в XML структуре файлов xlsx/xlsm/docx
#ВсякоРазно
Всем добра!
До сих пор часто задают вопросы: а может ли PQ вытянуть из книги эксель что-то кроме данных с листа/таблицы/именованного диапазона? Например формат ячейки, или только отфильтрованные пользователем строки? Обычно стандартный ответ: нет. Но это не совсем так, если файл имеет расширение xlsx/xlsm, то по сути своей он представляет собой zip-архив, который можно расковырять и покопаться во внутренней xml-структуре файла. А уж оттуда можно при должном желании и усидчивости вытащить вообще все что угодно. В общем все вышеизложенное для кого-то, наверняка уже не новость. Зато я предлагаю вашему вниманию подборку уже готовых решений, которые могут пригодиться в нашем замечательном ремесле:
- Достаем свойства ячеек от Камрада XL
- Достаем иерархию (вложенность) строк от Максима Зеленского
- Добываем числовые форматы ячеек от Максима Зеленского
- Вытаскиваем только отфильтрованные строки с листа от Михаил L
- Вытаскиваем таблички из docx.
- Сопоставляем какие таблицы/именованные диапазоны на каких листах книги расположены от вашего скромного слуги.
#ВсякоРазно
Всем добра!
До сих пор часто задают вопросы: а может ли PQ вытянуть из книги эксель что-то кроме данных с листа/таблицы/именованного диапазона? Например формат ячейки, или только отфильтрованные пользователем строки? Обычно стандартный ответ: нет. Но это не совсем так, если файл имеет расширение xlsx/xlsm, то по сути своей он представляет собой zip-архив, который можно расковырять и покопаться во внутренней xml-структуре файла. А уж оттуда можно при должном желании и усидчивости вытащить вообще все что угодно. В общем все вышеизложенное для кого-то, наверняка уже не новость. Зато я предлагаю вашему вниманию подборку уже готовых решений, которые могут пригодиться в нашем замечательном ремесле:
- Достаем свойства ячеек от Камрада XL
- Достаем иерархию (вложенность) строк от Максима Зеленского
- Добываем числовые форматы ячеек от Максима Зеленского
- Вытаскиваем только отфильтрованные строки с листа от Михаил L
- Вытаскиваем таблички из docx.
- Сопоставляем какие таблицы/именованные диапазоны на каких листах книги расположены от вашего скромного слуги.
www.planetaexcel.ru
[Power Query] Добываем иерархию (вложенность) строк из файла Excel
Делюсь.Есть такая проблема, что Power Query не берет форматы из ячеек листа, только сами значения.Зачастую на это можно наплевать, но часто информация на листе организована в структуру - иерархию строк, которые "плюсиками" сворачиваются/разворачиваются.Бывает…
Как правильно работать с API (2 видео от Chris Webb и @IlyaNazarov)
#ПолезныеСсылки
Всем добра.
Набрёл тут случайно на видео 4х летней давности от Криса Вебба про то как правильно использовать Web.Contents при работе с api. Сохраняю здесь. Если с английским не дружите, то запускайте в яндекс.браузере, он сносно переводит на русский в режиме реального времени.
Очень полезный видос.
Также добавляю супер инструкцию от @IlyaNazarov
#ПолезныеСсылки
Всем добра.
Набрёл тут случайно на видео 4х летней давности от Криса Вебба про то как правильно использовать Web.Contents при работе с api. Сохраняю здесь. Если с английским не дружите, то запускайте в яндекс.браузере, он сносно переводит на русский в режиме реального времени.
Очень полезный видос.
Также добавляю супер инструкцию от @IlyaNazarov
YouTube
Working with web services in Power Query/Excel and Power BI
You can use web services as data sources in Power Query/Excel and Power BI but to do so you need write some M code. In this session, you'll learn how to use the M Web. Contents() function to call web services to make GET and POST requests, how to handle authentication…
Как удалить все пустые столбцы с сохранением названий столбцов
#ВсякоРазно
Всем снова здрасьте.
Делюсь функцией, которая удаляет все пустые столбцы в таблице, и при этом не ломает заголовки столбцов. Оказывается это довольно востребованный функционал, особенно при обработке выгрузок из 1С или файлов, заполняемых шаловливыми ручками. Код приведен сразу с примером применения. Саму функцию публиковал тут.
#ВсякоРазно
Всем снова здрасьте.
Делюсь функцией, которая удаляет все пустые столбцы в таблице, и при этом не ломает заголовки столбцов. Оказывается это довольно востребованный функционал, особенно при обработке выгрузок из 1С или файлов, заполняемых шаловливыми ручками. Код приведен сразу с примером применения. Саму функцию публиковал тут.
let
FnRemoveEmptyColumns = (tab as table) =>
Table.RemoveColumns(
tab,
Table.SelectRows(
Table.Buffer( Table.Profile( tab ) ),
each [NullCount] = [Count] )[Column] ),
Source = #table( {"a","b","c"}, {{1,1,null},{2,2,null}} ),
Custom1 = FnRemoveEmptyColumns( Source )
in
Custom1
Функция удаления пустых столбцов
#АнатомияФункций - Table.Profile
Всем привет.
Делюсь решением достаточно часто возникающей задачки - а именно: на входе имеем таблицу, часть столбцов которой являются пустыми, соответственно нужно их удалить
Что ж, напишем функцию:
fnRemEmptyColumns = (tbl)=>
[ a = List.Sort(Table.ColumnNames(tbl)),
b = Table.Profile(tbl,{{"tmp", (x)=>true,List.NonNullCount}})[tmp],
c = List.PositionOf(b,0,Occurrence.All),
d = List.Transform(c,(x)=>a{x}),
e = Table.RemoveColumns(tbl,d)
][e]
Разберем по шагам:
a - получаем список имён столбцов и сортируем его по алфавиту
b - изюминка решения - используем Table.Profile(), но со вторым аргументом (более подробно про него - см. сцыль ниже). Т.е. добавляем дополнительную агрегацию "tmp" для всех столбцов ((x)=>true) с функцией List.NonNullCount. И забираем только этот столбец Table.Profile(...)[tmp]
с - в полученном списке определяем позиции пустых столбцов (обращаю внимание на Occurence.All - мощная штука)
d - номера позиций заменяем названиями (тоже приемчик интересный - как вытащить несколько позиций из списка)
e - удаляем - Вуаля! Задачка решена.
Надеюсь было полезно. Всех благ!
@buchlotnik
источник вдохновения:
https://ssbi-blog.de/blog/technical-topics-english/table-profile-and-its-unknown-second-parameter/
#АнатомияФункций - Table.Profile
Всем привет.
Делюсь решением достаточно часто возникающей задачки - а именно: на входе имеем таблицу, часть столбцов которой являются пустыми, соответственно нужно их удалить
Что ж, напишем функцию:
fnRemEmptyColumns = (tbl)=>
[ a = List.Sort(Table.ColumnNames(tbl)),
b = Table.Profile(tbl,{{"tmp", (x)=>true,List.NonNullCount}})[tmp],
c = List.PositionOf(b,0,Occurrence.All),
d = List.Transform(c,(x)=>a{x}),
e = Table.RemoveColumns(tbl,d)
][e]
Разберем по шагам:
a - получаем список имён столбцов и сортируем его по алфавиту
b - изюминка решения - используем Table.Profile(), но со вторым аргументом (более подробно про него - см. сцыль ниже). Т.е. добавляем дополнительную агрегацию "tmp" для всех столбцов ((x)=>true) с функцией List.NonNullCount. И забираем только этот столбец Table.Profile(...)[tmp]
с - в полученном списке определяем позиции пустых столбцов (обращаю внимание на Occurence.All - мощная штука)
d - номера позиций заменяем названиями (тоже приемчик интересный - как вытащить несколько позиций из списка)
e - удаляем - Вуаля! Задачка решена.
Надеюсь было полезно. Всех благ!
@buchlotnik
источник вдохновения:
https://ssbi-blog.de/blog/technical-topics-english/table-profile-and-its-unknown-second-parameter/
Зачем писать (x)=> вместо each _
#АнатомияФункций – основы, List.Transform
Возьмём простую задачку – имеем список текста
Решение банальное:
List.Transform(list as list, transform as function)
Т.е. вторым аргументом идёт на самом деле ФУНКЦИЯ – мы её указали, и всё заработало, логично.
В документации на язык M указано, что конструкция
Во-первых, можно сообразить, что List.Transform хочет от нас ЛЮБУЮ функцию, которая принимает на вход один аргумент x и что-то с ним делает
А во-вторых, понимаем, что таким образом можно вкладывать одну функцию в другую.
Например задачка – из двух списков получить один (причем к элементам первого списка нужно добавить результат сцепки этого элемента с каждым элементом второго), попробуем решить:
В целом синтаксис (x)=> является основным (ещё раз напоминаю – от нас обычно хотят ФУНКЦИЮ преобразования и мы пишем её в явном виде) и по хорошему ставит мозги на место, позволяя в конечном счёте уйти от наклацанных мышкой шагов к написанию кода на функциях.
Ну и пытливые умы конечно скажут – а можно ли в последнем примере из шага lst сделать функцию? Конечно, мы же теперь знаем, как обозначать переменные:
Надеюсь было полезно, всех благ!
@buchlotnik
#АнатомияФункций – основы, List.Transform
Возьмём простую задачку – имеем список текста
lst = {“1”,”2”,”3”}и хотим получить список чисел
Решение банальное:
to = List.Transform(lst, each Number.From(_))Продвинутые могут сказать, что можно проще:
to = List.Transform(lst,Number.From)И будут совершенно правы, ведь справка нам говорит
List.Transform(list as list, transform as function)
Т.е. вторым аргументом идёт на самом деле ФУНКЦИЯ – мы её указали, и всё заработало, логично.
В документации на язык M указано, что конструкция
each _
является синтаксическим сахаром для (x)=> x
; давайте попробуем:to = List.Transform(lst, (x)=>Number.From(x))Так тоже работает, но зачем нам это? А вот зачем:
Во-первых, можно сообразить, что List.Transform хочет от нас ЛЮБУЮ функцию, которая принимает на вход один аргумент x и что-то с ним делает
f=(x)=>Number.From(x)*8-11т.е. пишем любую нужную нам функцию от одного аргумента, и далее просто её передаем – для отладки удобно, код более читаем.
to = List.Transform(lst,f)
А во-вторых, понимаем, что таким образом можно вкладывать одну функцию в другую.
Например задачка – из двух списков получить один (причем к элементам первого списка нужно добавить результат сцепки этого элемента с каждым элементом второго), попробуем решить:
letПосмотрите на шаг lst – в функцию от (x)=> вложена функция от (y)=> . С each _ так сделать не получится (потому что во вложенных each _ от _ никак нельзя отличить))), а теперь мы получили (на самом деле всегда имели, но не использовали) возможность давать переменным осмысленные имена вместо _ и использовать их в нужных местах кода.
lst1 = {"1".."3"},
lst2 = {"1".."9"},
lst = List.Transform(
lst1,
(x)=>{x}&List.Transform(
lst2,
(y)=>x&y
)
),
to = List.Combine(lst)
in
to
В целом синтаксис (x)=> является основным (ещё раз напоминаю – от нас обычно хотят ФУНКЦИЮ преобразования и мы пишем её в явном виде) и по хорошему ставит мозги на место, позволяя в конечном счёте уйти от наклацанных мышкой шагов к написанию кода на функциях.
Ну и пытливые умы конечно скажут – а можно ли в последнем примере из шага lst сделать функцию? Конечно, мы же теперь знаем, как обозначать переменные:
letВроде не сложно, а главное прозрачно – именно поэтому я почти не использую each _ и предпочитаю (x)=>
f=(x,y)=>List.Transform(
x,
(i)=>{i}&List.Transform(
y,
(j)=>i&j
)
),
lst1 = {"1".."3"},
lst2 = {"1".."9"},
lst = f(lst1,lst2),
to = List.Combine(lst)
in
to
Надеюсь было полезно, всех благ!
@buchlotnik
#PQ, #pbi
Всем добра! Сегодня покажу как синхронно разворачивать списки из соседних ячеек.
Дано. Получили из API некий JSON который на выходе выдал структуру со вложенными списками как в таблице tbl (см. в коде ниже). Надо развернуть содержимое списков в новые строки, но если мы это сделаем стандартным мышиным способом с разворачиванием списков в строки по очереди то получится декартово произведение, не такое красивое как это но тоже ничего. Что же делать? А на самом деле все довольно просто: надо не разворачивать столбцы, а при помощи функции
Но может еще случиться, что столбцов с простым содержимым (не списки) будет много, и тогда использование
Всем добра! Сегодня покажу как синхронно разворачивать списки из соседних ячеек.
Дано. Получили из API некий JSON который на выходе выдал структуру со вложенными списками как в таблице tbl (см. в коде ниже). Надо развернуть содержимое списков в новые строки, но если мы это сделаем стандартным мышиным способом с разворачиванием списков в строки по очереди то получится декартово произведение, не такое красивое как это но тоже ничего. Что же делать? А на самом деле все довольно просто: надо не разворачивать столбцы, а при помощи функции
Table.FromColumns
собрать из них готовую таблицу в новом столбце:let
tbl = Table.FromRecords (
{
[ Company = 1, Phone = { "1".."9" }, Mail = { "1".."6", "", "8".."9" } ],
[ Company = 2, Phone = { "1".."5" }, Mail = { "2".."6" } ]
}
),
gentabs = Table.AddColumn(
tbl,
"tabs",
each Table.FromColumns(
{List.Repeat({[Company]}, List.Count([Phone])),[Phone],[Mail]},
Table.ColumnNames(tbl)
)
),
tabs = Table.Combine(gentabs[tabs])
in
tabs
Но может еще случиться, что столбцов с простым содержимым (не списки) будет много, и тогда использование
List.Repeat
станет узким местом с точки зрения производительности, да и код сильно потеряет свою наглядность. Но для этого случая можно применить мою любимую функцию Table.FillDown
до объединения таблиц и получим точно такой же результат.let
tbl = Table.FromRecords (
{
[ Company = 1, Phone = { "1".."9" }, Mail = { "1".."6", "", "8".."9" } ],
[ Company = 2, Phone = { "1".."5" }, Mail = { "2".."6" } ]
}
),
gentabs = Table.AddColumn(
tbl,
"tabs",
each Table.FromColumns(
{{[Company]},[Phone],[Mail]},
Table.ColumnNames(tbl)
)
),
filled = Table.AddColumn(gentabs, "tabs_", each Table.FillDown([tabs],{"Company"})),
tabs = Table.Combine(filled[tabs_])
in
tabs
Telegram
Для тех, кто в танке
Decart. Beautiful join.
#ВсякоРазно
Как получить все сочетания элементов из двух столбцов, оно же декартово произведение в pq?
Есть несколько вариантов, но самый красивый на мой взгляд этот:
Table.Join( tab1,{},tab2,{})
Где tab1 и tab2 таблицы, состоящие…
#ВсякоРазно
Как получить все сочетания элементов из двух столбцов, оно же декартово произведение в pq?
Есть несколько вариантов, но самый красивый на мой взгляд этот:
Table.Join( tab1,{},tab2,{})
Где tab1 и tab2 таблицы, состоящие…
Зачем нужен [ ][ ] когда есть let in
#АнатомияФункций - основы
Возьмем абстрактный как сферический конь в вакууме запрос:
Фишка в том, что в недрах спецификации на язык написано «Выражение let можно рассматривать как синтаксический сахар для неявного выражения записи».
Давайте проверим:
Почему? Запись – это набор полей (её ещё представляют в виде строки таблицы), но кто сказал, что не бывает вычисляемых полей? Пример выше как раз и показывает, что вполне себе бывают.
Соответственно, конструкция
Зачем нам всё это? Ну для начала – а если мы хотим вывести и произведение, и частное:
На мой взгляд это несколько проще и понятнее, чем
А дальше – вычисления бывают разной степени сложности, иногда отдельный этап требует нескольких связанных вычислений и тогда приходится, либо вкладывать let in друг в друга:
Синтаксис через записи обычно более компактный и позволяет визуально локализовать связанные вычисления внутри [ ].
На скорости никак не сказывается, поэтому как удобнее - решать вам.
Надеюсь было полезно, всех благ!
@buchlotnik
#АнатомияФункций - основы
Возьмем абстрактный как сферический конь в вакууме запрос:
letНа выходе, разумеется, получим 0,5.
a = 8,
b = 16,
c = a*b,
d = a/b
In
d
Фишка в том, что в недрах спецификации на язык написано «Выражение let можно рассматривать как синтаксический сахар для неявного выражения записи».
Давайте проверим:
[ a = 8,Выдаст то же самое.
b = 16,
c = a*b,
d = a/b ][d]
Почему? Запись – это набор полей (её ещё представляют в виде строки таблицы), но кто сказал, что не бывает вычисляемых полей? Пример выше как раз и показывает, что вполне себе бывают.
Соответственно, конструкция
[ ][d]
– это обращение к конкретному полю записи (аналогично in d).Зачем нам всё это? Ну для начала – а если мы хотим вывести и произведение, и частное:
[ a = 8,Сразу даст [c=128, d=0,5]
b = 16,
c = a*b,
d = a/b ][[c],[d]]
На мой взгляд это несколько проще и понятнее, чем
letХотя эффект будет тем же.
a = 8,
b = 16,
c = a*b,
d = a/b
in
[c=c, d=d]
А дальше – вычисления бывают разной степени сложности, иногда отдельный этап требует нескольких связанных вычислений и тогда приходится, либо вкладывать let in друг в друга:
letлибо можно упаковать связанные шаги в запись:
f=(x)=>let
a=Text.From(x),
b=Text.ToList(a),
c=List.Distinct(b),
d=b=c
in
d,
from = List.Numbers(0,1024),
to = List.Select(from,f)
in
to
letПо мне второй вариант проще, хотя это дело вкуса.
f=(x)=>[a=Text.From(x), b=Text.ToList(a), c=List.Distinct(b), d=b=c][d],
from = List.Numbers(0,1024),
to = List.Select(from,f)
in
to
Синтаксис через записи обычно более компактный и позволяет визуально локализовать связанные вычисления внутри [ ].
На скорости никак не сказывается, поэтому как удобнее - решать вам.
Надеюсь было полезно, всех благ!
@buchlotnik
List.Generate, осмысленный и пощадный
#АнатомияФункций – List.Generate
Думаю несложно догадаться, что для начала отправлю читать спецификацию, ибо там сказано:
List.Generate( initial as function,
condition as function,
next as function,
optional selector as nullable function)
Итак, функция, у которой в качестве аргументов выступают четыре функции. Уже страшно.
Для прозрачности далее буду использовать синтаксис с (x)=> (подробнее читаем тут).
Для начала сгенерим список от 1 до 10 (если что я знаю про {1..10} но у нас тут про другое:
condition – это функция, вопрос в том от чего? Как ни странно, от текущего состояния. Таким образом, x[i] – это текущее состояние счётчика и мы проверяем не превысило ли оно 10
next – а эта функция говорит, что необходимо сделать с текущим состоянием. Поскольку на входе у нас была запись [i=…], на выходе тоже будет она [i=x[i]+1] – т.е. в поле i записали текущее, увеличенное на единицу
selector – нужен, чтобы выводить только интересующие нас поля, в данном случае x[i].
Да, немножко громоздко, но теперь давайте выведем не просто числа от 1 до 10, а накопленную сумму (собственно, для этой задачки функция обычно и используется):
initial – это уже связанные поля [i,s], причем s вычисляется на основе i (про [ ] можно почитать тут)
condition – такой же
next – теперь в записи i вычисляется на основе предыдущего значения прибавлением единицы, а s – прибавлением i к предыдущему значению
selector – на выходе нас интересует именно s, так что счётчик мы используем, но не выводим.
Ну и что-нибудь похожее на боевой пример:
tbl - на вход поступила таблица, допустим нам нужна накопленная сумма по столбцу b
lst – получаем столбец в виде списка (обращаю внимание на List.Buffer – здесь оно нужно, потому как к этому списку мы будем многократно обращаться
n – посчитали число элементов в списке (нам же нужно ограничение для счётчика)
gen – генерация столбца, фактически как в примере выше, но с оговорками:
initial - i=0 – потому как нумерация элементов в списке начинается с нуля, s=lst{i} – в сумму идёт i-ый элемент списка lst
condition – здесь условие x[i]<n, потому как нумерация была с нуля и <= даст нам на один элемент больше, чем надо
next – увеличиваем счётчик на 1, к сумме прибавляем i-ый элемент
selector – нас интересует только сумма
to – а вот теперь нужно прилепить новый столбец к таблице, для этого :
Table.ToColumns(tbl)&{gen} - получаем список имеющихся столбцов и добавляем к нему сгенерированный
Table.ColumnNames(tbl)&{"cusum"} – получаем список имен столбцов и добавляем к нему нужно е имя
Ну и упаковываем это всё через Table.FromColumns
Вуаля – столбец накопленной суммы добавлен!
@buchlotnik
#АнатомияФункций – List.Generate
Думаю несложно догадаться, что для начала отправлю читать спецификацию, ибо там сказано:
List.Generate( initial as function,
condition as function,
next as function,
optional selector as nullable function)
Итак, функция, у которой в качестве аргументов выступают четыре функции. Уже страшно.
Для прозрачности далее буду использовать синтаксис с (x)=> (подробнее читаем тут).
Для начала сгенерим список от 1 до 10 (если что я знаю про {1..10} но у нас тут про другое:
List.Generate( ()=>[i=1],initial - просто даёт нам начальное значение 1, но в более сложных случаях мы будем использовать промежуточные вычисления, там нам потребуются вычисляемые поля, поэтому сразу пишем как надо – т.е. через запись
(x)=>x[i]<=10,
(x)=>[i=x[i]+1],
(x)=>x[i])
condition – это функция, вопрос в том от чего? Как ни странно, от текущего состояния. Таким образом, x[i] – это текущее состояние счётчика и мы проверяем не превысило ли оно 10
next – а эта функция говорит, что необходимо сделать с текущим состоянием. Поскольку на входе у нас была запись [i=…], на выходе тоже будет она [i=x[i]+1] – т.е. в поле i записали текущее, увеличенное на единицу
selector – нужен, чтобы выводить только интересующие нас поля, в данном случае x[i].
Да, немножко громоздко, но теперь давайте выведем не просто числа от 1 до 10, а накопленную сумму (собственно, для этой задачки функция обычно и используется):
List.Generate( ()=>[i=1, s= i],Теперь
(x)=>x[i]<=10,
(x)=>[i=x[i]+1, s=x[s]+i],
(x)=>x[s])
initial – это уже связанные поля [i,s], причем s вычисляется на основе i (про [ ] можно почитать тут)
condition – такой же
next – теперь в записи i вычисляется на основе предыдущего значения прибавлением единицы, а s – прибавлением i к предыдущему значению
selector – на выходе нас интересует именно s, так что счётчик мы используем, но не выводим.
Ну и что-нибудь похожее на боевой пример:
letРазбираем по шагам
tbl = Table.FromRecords({[a="Вася", b = 2],
[a="Коля", b = 4],
[a="Петя", b = 8],
[a="Евлампий", b = 16]}),
lst = List.Buffer(tbl[b]),
n = List.Count(lst),
gen = List.Generate(()=>[i=0,s=lst{i}],
(x)=>x[i]<n,
(x)=>[i=x[i]+1,s=x[s]+lst{i}],
(x)=>x[s]),
to = Table.FromColumns(Table.ToColumns(tbl)&{gen},Table.ColumnNames(tbl)&{"cusum"})
in
to
tbl - на вход поступила таблица, допустим нам нужна накопленная сумма по столбцу b
lst – получаем столбец в виде списка (обращаю внимание на List.Buffer – здесь оно нужно, потому как к этому списку мы будем многократно обращаться
n – посчитали число элементов в списке (нам же нужно ограничение для счётчика)
gen – генерация столбца, фактически как в примере выше, но с оговорками:
initial - i=0 – потому как нумерация элементов в списке начинается с нуля, s=lst{i} – в сумму идёт i-ый элемент списка lst
condition – здесь условие x[i]<n, потому как нумерация была с нуля и <= даст нам на один элемент больше, чем надо
next – увеличиваем счётчик на 1, к сумме прибавляем i-ый элемент
selector – нас интересует только сумма
to – а вот теперь нужно прилепить новый столбец к таблице, для этого :
Table.ToColumns(tbl)&{gen} - получаем список имеющихся столбцов и добавляем к нему сгенерированный
Table.ColumnNames(tbl)&{"cusum"} – получаем список имен столбцов и добавляем к нему нужно е имя
Ну и упаковываем это всё через Table.FromColumns
Вуаля – столбец накопленной суммы добавлен!
@buchlotnik
Telegram
Для тех, кто в танке
Зачем писать (x)=> вместо each _
#АнатомияФункций – основы, List.Transform
Возьмём простую задачку – имеем список текста
lst = {“1”,”2”,”3”}
и хотим получить список чисел
Решение банальное:
to = List.Transform(lst, each Number.From(_))
Продвинутые могут…
#АнатомияФункций – основы, List.Transform
Возьмём простую задачку – имеем список текста
lst = {“1”,”2”,”3”}
и хотим получить список чисел
Решение банальное:
to = List.Transform(lst, each Number.From(_))
Продвинутые могут…
Я знаю, что примеры в справке попроще и написаны через each, и код выше можно было оформить проще, но в общем виде оно выглядит именно так – запись на входе, функции от текущего состояния – одна для проверки, вторая для изменения, и функция вывода (которая вообще не обязательная, но вот почему-то в реальности почти всегда используется).
Надеюсь было полезно, всех благ!
Надеюсь было полезно, всех благ!
Замыкания, или что за зверь (y)=>(x)=>…
#АнатомияФункций – основы
Всем привет, с оказией хочу разобрать такую штуку, как замыкания (closure).
Суть вот в чём. Напишем простой пример:
Но можно написать и по-другому:
c – передали функции f только один аргумент, a подставилось вместо x и на выходе мы получили…
to – этой новой функции передали b, и уже она выдала конечный результат.
Таким образом, замыкание (кложура!) – это упаковка функции в функцию (это упрощение - да простят меня функциональные теоретики). Думаю, у вас один вопрос – зачем оно надо?! А вот зачем. Разберем немного упрощенный боевой пример:
nms – получили все имена,
lst – получен список имён столбцов, по которым нужна оценка
acc – ну самое разумное пройтись по списку lst -
(s,c)=>Table.AddColumn(s,g(c),f(c))
Т.е. добавить к таблице s оценку по полю c - f(c) и назвать столбец g(c)
Зачем надо было писать ещё и функцию g? Да просто преобразование это нужно ещё и в последнем шаге – так немножко короче. Мы же хотим оценку получить рядом, а не в конце таблицы, потому-то на шаге to мы и выбираем столбцы в интересующем нас порядке.
Собственно, всё. Замыкания встречаются нечасто, но бывают просто незаменимы.
Надеюсь было полезно, всех благ!
@buchlotnik
#АнатомияФункций – основы
Всем привет, с оказией хочу разобрать такую штуку, как замыкания (closure).
Суть вот в чём. Напишем простой пример:
letОчевидно, на выходе получим 0,4
f=(x,y)=>x/y,
a=2,
b=5,
to=f(a,b),
in
to
Но можно написать и по-другому:
letНа выходе получим то же самое. Но в этой ситуации мы передали аргументы функции последовательно. И разрыв мозга наступает здесь:
f=(x)=>(y)=>x/y,
a=2,
b=5,
to=f(a)(b)
in
to
letТоже выдаст 0,4! Теперь давайте разбираться:
f=(x)=>(y)=>x/y,
a=2,
b=5,
c=f(a),
to = c(b)
in
to
c – передали функции f только один аргумент, a подставилось вместо x и на выходе мы получили…
с=(y)=>2/y
, т.е. ФУНКЦИЮto – этой новой функции передали b, и уже она выдала конечный результат.
Таким образом, замыкание (кложура!) – это упаковка функции в функцию (это упрощение - да простят меня функциональные теоретики). Думаю, у вас один вопрос – зачем оно надо?! А вот зачем. Разберем немного упрощенный боевой пример:
letfrom – на вход подана таблица с несколькими столбцами числовых значений, для каждого столбца нужно добавить столбец с оценкой (проверить нахождение значения в диапазоне от 80 до 120). (а самый жесткий вариант у меня был - 34 столбца и ABC-анализ)
from = Table.FromRecords({ [n="Вася",a=90,b=70,c=50],
[n="Коля",a=120,b=130,c=110],
[n="Петя",a=75,b=80,c=130]}),
nms= Table.ColumnNames(from),
lst=List.Skip(nms),
f=(y)=>(x)=>[ a = Record.Field(x,y) ,
b = if a < 80
then "ниже предела"
else if a > 120
then "выше предела"
else "ок"][b],
g=(x)=>"Проверка "&x,
acc = List.Accumulate(lst,from,(s,c)=>Table.AddColumn(s,g(c),f(c))),
to = Table.SelectColumns(acc,{nms{0}}&List.Combine(List.Transform(lst,(x)=>{x,g(x)})))
in
to
nms – получили все имена,
lst – получен список имён столбцов, по которым нужна оценка
acc – ну самое разумное пройтись по списку lst -
List.Accumulate(lst…
и добавить для каждого оценку Table.AddColumn(…
; только есть проблема – для функции Table.AddColumn мы должны передать функцию от одного аргумента, а именно – от записи (текущей строки) в таблице. А нам нужно обращаться к разным полям, вот так и возникает конструкцияf=(y)=>(x)=>[ a = Record.Field(x,y) , b = if a < 80 then "ниже предела" else if a > 120 then "выше предела" else "ок"][b],Т.е. сначала мы функции f будем передавать y – и это будет имя нужного поля записи, и на выходе получать функцию для обработки именно этого поля, что мы и используем в аккумуляторе:
(s,c)=>Table.AddColumn(s,g(c),f(c))
Т.е. добавить к таблице s оценку по полю c - f(c) и назвать столбец g(c)
Зачем надо было писать ещё и функцию g? Да просто преобразование это нужно ещё и в последнем шаге – так немножко короче. Мы же хотим оценку получить рядом, а не в конце таблицы, потому-то на шаге to мы и выбираем столбцы в интересующем нас порядке.
Собственно, всё. Замыкания встречаются нечасто, но бывают просто незаменимы.
Надеюсь было полезно, всех благ!
@buchlotnik
Коннектор к папке OneDrive Personal
#ВсякиеКоннекторы
Всем добра!
К новому году небольшой подарочек. С наступлением на наши компьютеры ОС Windows 10/11 все больше граждан получают в свое распоряжение облачный сервис OneDrive Personal. В отличие от такого же облака для бизнеса для него отсутствует встроенный в PQ коннектор подключения к папке, чтобы динамически обновлять данные из добавляемых в неё файлов. Тем не менее сама возможность такого забора со стороны Microsoft предусмотрена при помощи API. Ниже прилагаю код функции, которая по аналогии с функцией Folder.Files точно также показывает вам какие файлы имеются в папке на облаке и позволяет работать с их содержимым. Как использовать? Заходим в облако, тыкаем правой кнопкой по папке и выбираем пункт "поделиться", в появившемся меню жмем "копировать ссылку" и уже эту ссылку скармливаем моей функции. Тип авторизации выбираем анонимный. Пользуйтесь на здоровье.
З.Ы. Если для работы нужны всякие дополнительные свойства файлов или еще какие-то атрибуты, то достаточно в шаге TableFromRecords удалить вот это в квадратных скобках: [[name],[webUrl]]. Тогда увидите все, что может показать API про вашу папку.
#ВсякиеКоннекторы
Всем добра!
К новому году небольшой подарочек. С наступлением на наши компьютеры ОС Windows 10/11 все больше граждан получают в свое распоряжение облачный сервис OneDrive Personal. В отличие от такого же облака для бизнеса для него отсутствует встроенный в PQ коннектор подключения к папке, чтобы динамически обновлять данные из добавляемых в неё файлов. Тем не менее сама возможность такого забора со стороны Microsoft предусмотрена при помощи API. Ниже прилагаю код функции, которая по аналогии с функцией Folder.Files точно также показывает вам какие файлы имеются в папке на облаке и позволяет работать с их содержимым. Как использовать? Заходим в облако, тыкаем правой кнопкой по папке и выбираем пункт "поделиться", в появившемся меню жмем "копировать ссылку" и уже эту ссылку скармливаем моей функции. Тип авторизации выбираем анонимный. Пользуйтесь на здоровье.
OneDriveFolderFiles = (url)=>
let
//Функция перекодирования ссылки в понятный формат для API
fx = (t)=> Binary.ToText( Text.ToBinary( t, TextEncoding.Utf8 ), BinaryEncoding.Base64 ),
API_URL = "https://api.onedrive.com/v1.0/shares/"
//тащим путь из параметра с адресом к общей папке из облака Onedrive
FolderUrl = url,
//преобразовываем ссылку для получения токена для API
UrlToBase64 = fx( FolderUrl ),
//заменяем всякое согласно инструкции по ссылке:
//https://docs.microsoft.com/ru-ru/onedrive/developer/rest-api/api/shares_get?view=odsp-graph-online#encoding-sharing-urls
Replaced = Text.Replace( Text.Replace( Text.TrimEnd( UrlToBase64, "=" ), "/", "_" ), "+", "-" ),
//формируем итоговый текстовый параметр для передачи в RelativePath
EncodedPath = "u!" & Replaced & "/root/children",
//тащим содержимое папки из API
Source = Json.Document(Web.Contents( API_URL, [RelativePath = EncodedPath] ) ),
//преобразовываем полученный JSON в табличку с содержимым папки
TableFromRecords = Table.FromRecords( Source[value] )[[name],[webUrl]],
//добавляем столбец с текстовыми параметрами для передачи в RelativePath
AddColEncodedPaths = Table.AddColumn(
TableFromRecords,
"GetRelativePath",
each "u!" & fx([webUrl]) & "/root/content"
),
//достаем бинарники по сформированным ссылкам, дальше по аналогии как с обычными файлами с диска
GetBinaries = Table.AddColumn(
AddColEncodedPaths,
"Content",
each Web.Contents(API_URL, [RelativePath = [GetRelativePath]] ),
Binary.Type
)
in
GetBinaries
З.Ы. Если для работы нужны всякие дополнительные свойства файлов или еще какие-то атрибуты, то достаточно в шаге TableFromRecords удалить вот это в квадратных скобках: [[name],[webUrl]]. Тогда увидите все, что может показать API про вашу папку.
GitHub
GitHub - PooHkrd/OneDriveFolderFiles: Функция для Microsoft Power BI/Power Query (язык М) подключения к общей папке в облаке OneDrive…
Функция для Microsoft Power BI/Power Query (язык М) подключения к общей папке в облаке OneDrive Personal. - PooHkrd/OneDriveFolderFiles
Измерение скорости выполнения запросов
#ПолезныеСсылки
Всем привет!
Имеется небольшая надстройка, предназначенная для оценки скорости выполнения запросов PQ в среде MS Excel. Вместе со справкой сложена на гитхаб:
https://github.com/buchlotnik/Merka
Надеюсь, будет полезна.
Всех благ!
@buchlotnik
#ПолезныеСсылки
Всем привет!
Имеется небольшая надстройка, предназначенная для оценки скорости выполнения запросов PQ в среде MS Excel. Вместе со справкой сложена на гитхаб:
https://github.com/buchlotnik/Merka
Надеюсь, будет полезна.
Всех благ!
@buchlotnik
GitHub
GitHub - buchlotnik/Merka: небольшая надстройка, предназначенная для оценки скорости выполнения запросов PQ и формул на листах…
небольшая надстройка, предназначенная для оценки скорости выполнения запросов PQ и формул на листах в среде MS Excel в среде MS Excel - buchlotnik/Merka