Table.ReplaceValue или самая гибкая функция
#АнатомияФункций - Table.ReplaceValue
Всем привет!
За последнее время несколько раз всплывал вопрос по Table.ReplaceValue.
Редко ей пользуюсь, поскольку она немножко совсем не шустрая, и в общем-то задачи могут быть решены другими способами.
Но, с другой стороны, функция гибкая, немножко нестандартная и может представлять интерес для любителей пользовательских функций в аргументах )))
Итак, справка гласит:
oldVlue – какое значение,
newValue – на какое значение,
replacer – с помощью какой функции,
columnsToSearch – в каких столбцах меняем
Ну поехали:
to1 – простая замена null на 0 по столбцам b и e, Replacer.ReplaceValue говорит, что меняем значение целиком
to2 – усложняем – в newValue можно прописать условие, в данном случае меняем null на 0 только если в столбце «e» содержится текст
to3 – другой вариант замены – Replacer.ReplaceText, т.е. замена в тексте, в данном случае меняем «о» на «@» в столбце «d»
to4 – oldValue также не обязано быть фиксированным – в данном случае производим замену в столбце «d», но в качестве oldValue берем текст из столбца «c»
Промежуточный итог – oldValue и newValue могут быть представлены фиксированным значением, значением из конкретного столбца и даже более сложным условием.
А что же с Replacer-ом? И вот тут самое интересное:
to5 – во-первых, это функция от трёх аргументов ("в каком значении", "что","на что меняем") – для наглядности вывел в виде записи передаваемые аргументы
to6 – и собственно говоря, теперь творим всякое – в данном случае вообще не переданы oldValue и newValue, поскольку мы просто проверяем является ли значение текстом, причём сразу по всем столбцам
to7 – а так мы ставим прочерки во всех столбцах, если столбец «b» не null
to8 – почти то же самое, просто значение из столбца «b» передали не во второй, а в третий аргумент – я же говорил, что функция гибкая )))
to9 – ну а вот так можно задать условие по двум столбцам, передав их значения в два аргумента
to10 – ну и на закуску – все предыдущие варианты я написал через each, хотя внимательные читатели помнят, что это всего лишь синтаксический сахар для (x)=>x. Т.е. на самом деле и во второй, и в третий аргумент можно передать всю строку целиком в виде записи – соответственно, можно написать любую функцию от любого набора столбцов, а пятый аргумент обеспечит нас значением из конкретной ячейки и наши возможности ограничиваются только фантазией.
Как-то так – крайне гибкая функция, но платой за гибкость является не самая высокая скорость.
Надеюсь, было полезно.
Всех благ!
@buchlotnik
#АнатомияФункций - Table.ReplaceValue
Всем привет!
За последнее время несколько раз всплывал вопрос по Table.ReplaceValue.
Редко ей пользуюсь, поскольку она немножко совсем не шустрая, и в общем-то задачи могут быть решены другими способами.
Но, с другой стороны, функция гибкая, немножко нестандартная и может представлять интерес для любителей пользовательских функций в аргументах )))
Итак, справка гласит:
Table.ReplaceValue(table as table, oldValue as any, newValue as any, replacer as function, columnsToSearch as list)table – в какой таблице,
oldVlue – какое значение,
newValue – на какое значение,
replacer – с помощью какой функции,
columnsToSearch – в каких столбцах меняем
Ну поехали:
letfrom – входные данные
from = Table.FromRecords({[a=1,b=1,c="по",d="топот",e=null],[a=2,b=null,c="го",d="загород",e="ф"],[a=3,b=null,c="да",d="дар",e=null],[a=4,b=2,c="хо",d="хорь",e=null],[a=5,b=null,c="ро",d="роща",e="ы"],[a=6,b=null,c="ша",d="шапка",e=null],[a=7,b=3,c="се",d="север",e=null],[a=8,b=null,c="год",d="годнота",e="в"],[a=9,b=null,c="ня",d="няша",e=null]}),
to1 = Table.ReplaceValue(from,null,0,Replacer.ReplaceValue,{"b","e"}),
to2 = Table.ReplaceValue(from,null,each if [e] is text then 0 else [a],Replacer.ReplaceValue,{"b"}),
to3 = Table.ReplaceValue(from,"о","@",Replacer.ReplaceText,{"d"}),
to4 = Table.ReplaceValue(from,each [c],"@",Replacer.ReplaceText,{"d"}),
to5 = Table.ReplaceValue(from,each [a],each [b],(x,y,z)=>[x=x,y=y,z=z],{"d"}),
to6 = Table.ReplaceValue(from,null,null,(x,y,z)=>if x is text then "здесь был текст" else x,Table.ColumnNames(from)),
to7 = Table.ReplaceValue(from,each [b],null,(x,y,z)=>if y<>null then "-" else x,Table.ColumnNames(from)),
to8 = Table.ReplaceValue(from,null,each [b],(x,y,z)=>if z<>null then "-" else x,Table.ColumnNames(from)),
to9 = Table.ReplaceValue(from,each [a],each [b],(x,y,z)=>if y=z then "-" else x,Table.ColumnNames(from)),
to10 = Table.ReplaceValue(from,(x)=>x,(x)=>x,(x,y,z)=>[x=x,y=y,z=z],Table.ColumnNames(from))
in
to10
to1 – простая замена null на 0 по столбцам b и e, Replacer.ReplaceValue говорит, что меняем значение целиком
to2 – усложняем – в newValue можно прописать условие, в данном случае меняем null на 0 только если в столбце «e» содержится текст
to3 – другой вариант замены – Replacer.ReplaceText, т.е. замена в тексте, в данном случае меняем «о» на «@» в столбце «d»
to4 – oldValue также не обязано быть фиксированным – в данном случае производим замену в столбце «d», но в качестве oldValue берем текст из столбца «c»
Промежуточный итог – oldValue и newValue могут быть представлены фиксированным значением, значением из конкретного столбца и даже более сложным условием.
А что же с Replacer-ом? И вот тут самое интересное:
to5 – во-первых, это функция от трёх аргументов ("в каком значении", "что","на что меняем") – для наглядности вывел в виде записи передаваемые аргументы
to6 – и собственно говоря, теперь творим всякое – в данном случае вообще не переданы oldValue и newValue, поскольку мы просто проверяем является ли значение текстом, причём сразу по всем столбцам
to7 – а так мы ставим прочерки во всех столбцах, если столбец «b» не null
to8 – почти то же самое, просто значение из столбца «b» передали не во второй, а в третий аргумент – я же говорил, что функция гибкая )))
to9 – ну а вот так можно задать условие по двум столбцам, передав их значения в два аргумента
to10 – ну и на закуску – все предыдущие варианты я написал через each, хотя внимательные читатели помнят, что это всего лишь синтаксический сахар для (x)=>x. Т.е. на самом деле и во второй, и в третий аргумент можно передать всю строку целиком в виде записи – соответственно, можно написать любую функцию от любого набора столбцов, а пятый аргумент обеспечит нас значением из конкретной ячейки и наши возможности ограничиваются только фантазией.
Как-то так – крайне гибкая функция, но платой за гибкость является не самая высокая скорость.
Надеюсь, было полезно.
Всех благ!
@buchlotnik
Производственный календарь 2
#АнатомияФункций – CustomFunctions
Всем привет!
Подкинули повод спарсить производственный календарь с Консультанта. Тема уже поднималась Лёхой, но код не был выложен в силу громоздкости.
Подумал, что надо бы исправить ситуацию )))
Посему функция consultant:
()=> Обращаем внимание – функция без обязательных аргументов
path – откуда тащим
yr – год, за который нужен календарь - если аргумент не передан – берём текущий год (у кого не работает ?? меняем выражение на
from – тащим информацию с сайта. Есть нюанс – 2020 был непростым годом, поэтому тогда календарь перезаливали и он получил адресацию 2020b, что собственно здесь и учитывается. В данном случае просто получаем текст со страницы
split – хитро делим текст – помесячные блоки отбиваются тройным табом – обращаю внимание как это записано, ну и первый кусок (до помесячных) отбрасываем
lst – соединяем блоки текста с номерами месяцев через List.Zip
tr – немножко перескочим – полученный список отправляем в List.TransformMany, для которой нам нужны две функции:
f – делит текстовый блок по "<td class=", оставляет фрагменты без "inactively" (у них на сайте месячные блоки стандартные 7 на 6, и так обозначены пустые), и их сплитуем по ">" и "<", оставив первые два фрагмента – статус и номер дня соответственно
g – собираем из полученного дату и статус даты без лишних кавычек
tbl – из полученного списка получаем таблицу, сразу задав типизацию
Как бы всё)))
Как этим пользоваться:
Не думаю, что сообщил что-то прям новое, но синтаксически выглядит прикольно.
Надеюсь, было полезно.
Всех благ!
@buchlotnik
#АнатомияФункций – CustomFunctions
Всем привет!
Подкинули повод спарсить производственный календарь с Консультанта. Тема уже поднималась Лёхой, но код не был выложен в силу громоздкости.
Подумал, что надо бы исправить ситуацию )))
Посему функция consultant:
(optional year as nullable number) =>
[path = "http://www.consultant.ru/law/ref/calendar/proizvodstvennye/",
yr = year??Date.Year(DateTime.LocalNow()),
from = Text.FromBinary(Web.Contents(path&Text.From(yr)&(if yr = 2020 then "b" else ""))),
splt = List.Skip(Text.Split(from,"#(tab,tab,tab)<td class=")),
lst=List.Zip({{1..12},splt}),
f=(x)=>[a=Text.Split(x{1},"<td class="),
b=List.Select(a,(x)=>not Text.Contains(x,"inactively")),
c=List.Transform(b,(x)=>List.Range(Text.SplitAny(x,"><"),0,2))][c],
g=(x,y)=>{#date(yr,x{0},Number.From(y{1})),Text.Remove(y{0},"""")},
tr=List.TransformMany(lst,f,g),
tbl=Table.FromList(tr,(x)=>x,type table [Date=date,DateType=text])][tbl]
()=> Обращаем внимание – функция без обязательных аргументов
path – откуда тащим
yr – год, за который нужен календарь - если аргумент не передан – берём текущий год (у кого не работает ?? меняем выражение на
if year is null then Date.Year(DateTime.LocalNow()) else year
)from – тащим информацию с сайта. Есть нюанс – 2020 был непростым годом, поэтому тогда календарь перезаливали и он получил адресацию 2020b, что собственно здесь и учитывается. В данном случае просто получаем текст со страницы
split – хитро делим текст – помесячные блоки отбиваются тройным табом – обращаю внимание как это записано, ну и первый кусок (до помесячных) отбрасываем
lst – соединяем блоки текста с номерами месяцев через List.Zip
tr – немножко перескочим – полученный список отправляем в List.TransformMany, для которой нам нужны две функции:
f – делит текстовый блок по "<td class=", оставляет фрагменты без "inactively" (у них на сайте месячные блоки стандартные 7 на 6, и так обозначены пустые), и их сплитуем по ">" и "<", оставив первые два фрагмента – статус и номер дня соответственно
g – собираем из полученного дату и статус даты без лишних кавычек
tbl – из полученного списка получаем таблицу, сразу задав типизацию
Как бы всё)))
Как этим пользоваться:
consultant() // календарь на текущий год
consultant(2024) //календарь на конкретный год
Table.Combine(List.Transform({2010..2024},consultant)) //календарь за период лет
Не думаю, что сообщил что-то прям новое, но синтаксически выглядит прикольно.
Надеюсь, было полезно.
Всех благ!
@buchlotnik
Telegram
Для тех, кто в танке
Производственный календарь.
#ПолезныеСсылки
Всем добра!
Со скуки запилил парсер производственного календаря за произвольный период (настраивается в параметрах) с сайта Консультант плюс. В телегу код не выкладываю - слишком громоздко, поэтому ссылочкой на…
#ПолезныеСсылки
Всем добра!
Со скуки запилил парсер производственного календаря за произвольный период (настраивается в параметрах) с сайта Консультант плюс. В телегу код не выкладываю - слишком громоздко, поэтому ссылочкой на…
Уважаемый Михаил. Поздравляю с днём рожденья! Желаю щастья в личной жизни! ПуХ.
Всем желающим поздравить виновника торжества напоминаю что реквизиты у него в профиле @buchlotnik
Всем желающим поздравить виновника торжества напоминаю что реквизиты у него в профиле @buchlotnik
Forwarded from Академия Excel
Media is too big
VIEW IN TELEGRAM
Продвинутое владение Power Query
начинается с написание кода на языке М.
Миша Музыкин один из тех, кто очень круто разбирается в PQ и активно делится своими знаниями через посты и ответы на вопросы в своих ТГ каналах
Принять участие
Мы долго шли к этому курсу и вот программа обучения
16 (пн) окт. 19:30 Мск Как вообще писать код на М
23 (пн) окт. 19:30 Мск Что это за списки, на которых быстрее?
26 (чт) окт. 19:30 Мск Таблицы вдоль и поперек – и это не фигура речи, а разное представление (основная тема - table)
30 (пн) окт. 19:30 Мск Записи или как перестать бояться квадратных скобок в коде (основная тема – record)
02 (чт) ноя. 19:30 Мск Работа с файлами и папками – просто кодом, просто в запросе, просто так можно (основная тема Excel.* + Folder.*)
Подробности смотрите на сайте
Регистрируйтесь. Принимаем оплату от компаний, можем подождать до 30 октября
Тут должен быть текст по возможности, которые открывает PQ, но я фиг знает что сюда писать.
Если вы в теме PQ (то вам не нужно объяснять что писать запросы ручками на М - это ПУШКА).
Если вы не в теме PQ, то вам скорее всего рано на этот курс.
Всем отличных выходных и до встречи в понедельник.
начинается с написание кода на языке М.
Миша Музыкин один из тех, кто очень круто разбирается в PQ и активно делится своими знаниями через посты и ответы на вопросы в своих ТГ каналах
Принять участие
Мы долго шли к этому курсу и вот программа обучения
16 (пн) окт. 19:30 Мск Как вообще писать код на М
23 (пн) окт. 19:30 Мск Что это за списки, на которых быстрее?
26 (чт) окт. 19:30 Мск Таблицы вдоль и поперек – и это не фигура речи, а разное представление (основная тема - table)
30 (пн) окт. 19:30 Мск Записи или как перестать бояться квадратных скобок в коде (основная тема – record)
02 (чт) ноя. 19:30 Мск Работа с файлами и папками – просто кодом, просто в запросе, просто так можно (основная тема Excel.* + Folder.*)
Подробности смотрите на сайте
Регистрируйтесь. Принимаем оплату от компаний, можем подождать до 30 октября
Тут должен быть текст по возможности, которые открывает PQ, но я фиг знает что сюда писать.
Если вы в теме PQ (то вам не нужно объяснять что писать запросы ручками на М - это ПУШКА).
Если вы не в теме PQ, то вам скорее всего рано на этот курс.
Всем отличных выходных и до встречи в понедельник.
Table.TransformColumnTypes 1 – определяем типы данных в столбцах пачкой без привязки к именам
#АнатомияФункций - Table.TransformColumnTypes
Всем привет!
Очередной раз всплыл вопрос про типизацию столбцов. Кто не в курсе напомню суть проблемы – можно выставить в настройках, чтобы типы определялись при загрузке таблицы автоматически, можно уже после загрузки таблицы в интерфейсе нажать кнопку «Определить тип данных» - и всё будет замечательно… НО! На самом деле в ходе данной операции PQ создаёт шаг, использующий функцию Table.TransformColumnTypes и во втором аргументе оказывается фиксированный список столбцов с типами. Соответственно, если в дальнейшем в таблицу будут добавляться столбцы – они просто не будут типизироваться, а если какой-то столбец будет переименован или удалён, то вообще вылезет ошибка.
Посему задача – уйти от фиксированного списка и соорудить всё кодом. Поехали:
from - в простейшем варианте у нас красивая табличка без пропусков
lst – поэтому берем первую стоку, преобразуем её в список значений, определяем их типы
tr – соединяем названия столбцов с полученными типами через List.Zip
to – преобразуем столбцы
Правда, если б всё было так просто, не было бы поста… В данных могут быть пропуски:
lst – поэтому мы не можем опираться на первую строку, а берем несколько, в данном случае 10 (стандартно интерфейс работает с первой 1000), разбиваем таблицу на списки по столбцам и к каждому полученному списку применяем функцию f
f – функция удаляет null-ы, определяет тип оставшихся значений и берёт наиболее часто встречающийся (List.Mode)
tr и to – аналогично первому случаю – собрали список преобразований и применили.
Вуаля, задача решена!
Правда если бы она была решена полностью в названии поста не было бы номера 1…
Потому что на самом деле у нас ещё остаются – определение целые или дробные числа у нас в столбце, датавремя или дата, даты/время/датавремя в виде текста (привет 1С) и т.п. И этим мы займемся в следующем посте )))
Надеюсь, было полезно.
Всех благ!
@buchlotnik
#АнатомияФункций - Table.TransformColumnTypes
Всем привет!
Очередной раз всплыл вопрос про типизацию столбцов. Кто не в курсе напомню суть проблемы – можно выставить в настройках, чтобы типы определялись при загрузке таблицы автоматически, можно уже после загрузки таблицы в интерфейсе нажать кнопку «Определить тип данных» - и всё будет замечательно… НО! На самом деле в ходе данной операции PQ создаёт шаг, использующий функцию Table.TransformColumnTypes и во втором аргументе оказывается фиксированный список столбцов с типами. Соответственно, если в дальнейшем в таблицу будут добавляться столбцы – они просто не будут типизироваться, а если какой-то столбец будет переименован или удалён, то вообще вылезет ошибка.
Посему задача – уйти от фиксированного списка и соорудить всё кодом. Поехали:
let
from = #table({"a".."e"},{{123,"мама",12.34,#date(2023,1,1),#time(1,2,3)},{234,"мыла",23.45,#date(2023,2,2),#time(2,3,4)},{345,"раму",34.56,#date(2023,3,3),#time(3,4,5)}}),
lst = List.Transform(Record.FieldValues(from{0}),Value.Type),
tr = List.Zip({Table.ColumnNames(from),lst}),
to = Table.TransformColumnTypes(from,tr)
in
to
from - в простейшем варианте у нас красивая табличка без пропусков
lst – поэтому берем первую стоку, преобразуем её в список значений, определяем их типы
tr – соединяем названия столбцов с полученными типами через List.Zip
to – преобразуем столбцы
Правда, если б всё было так просто, не было бы поста… В данных могут быть пропуски:
letfrom – уже менее красивая табличка
from = #table({"a".."e"},{{123,null,12.34,#date(2023,1,1),null},{null,"мыла",null,#date(2023,2,2),#time(2,3,4)},{345,"раму",34.56,null,#time(3,4,5)}}),
lst=List.Transform(Table.ToColumns(Table.Range(from,0,10)),f),
f=(x)=>List.Mode(List.Transform(List.RemoveNulls(x),Value.Type)),
tr = List.Zip({Table.ColumnNames(from),lst}),
to = Table.TransformColumnTypes(from,tr)
in
to
lst – поэтому мы не можем опираться на первую строку, а берем несколько, в данном случае 10 (стандартно интерфейс работает с первой 1000), разбиваем таблицу на списки по столбцам и к каждому полученному списку применяем функцию f
f – функция удаляет null-ы, определяет тип оставшихся значений и берёт наиболее часто встречающийся (List.Mode)
tr и to – аналогично первому случаю – собрали список преобразований и применили.
Вуаля, задача решена!
Правда если бы она была решена полностью в названии поста не было бы номера 1…
Потому что на самом деле у нас ещё остаются – определение целые или дробные числа у нас в столбце, датавремя или дата, даты/время/датавремя в виде текста (привет 1С) и т.п. И этим мы займемся в следующем посте )))
Надеюсь, было полезно.
Всех благ!
@buchlotnik
List.Generate, накопленная сумма, списки, записи и какие-то метаданные
#АнатомияФункций – List.Generate
Всем привет!
В своё время мы уже разбирали использование List.Generate и считали накопленную сумму.
Стандартный расчёт выглядит так (описание работы смотрим по ссылке):
И тут был задан вопрос – а обязательно ли использовать запись (record) при вычислении?
Ну вообще-то необязательно, давайте примотаем списки:
т.е. вместо записи через вычисления тащим список из двух значений, приходится дважды вычислять счётчик, но в целом это работает.
Однако, пока писал ответ, возникла другая мысль – а давайте примотаем метаданные:
т.е. вместо списка значений запихиваем накопленную сумму в метаданные нашего значения, при этом приходится использовать Value.Metadata и Value.ReplaceMetadata, что делает код несколько громоздким, зато см. промеры по скорости в первом комментарии под постом – это вполне себе шустро.
Поэтому делаем выводы: накопленная сумма – это тот случай, когда НА ЗАПИСЯХ быстрее, а вариант с метаданными, пусть чуть медленнее, зато весьма экзотичен.
Решайте задачи разными путями – это бывает интересно 😉
Надеюсь, было полезно.
Всех благ!
@buchlotnik
#АнатомияФункций – List.Generate
Всем привет!
В своё время мы уже разбирали использование List.Generate и считали накопленную сумму.
Стандартный расчёт выглядит так (описание работы смотрим по ссылке):
let
lst = List.Buffer({1..250000}),
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])
in
gen
И тут был задан вопрос – а обязательно ли использовать запись (record) при вычислении?
Ну вообще-то необязательно, давайте примотаем списки:
let
lst = List.Buffer({1..250000}),
n=List.Count(lst),
gen = List.Generate(()=>{0,lst{0}},
(x)=>x{0}<n,
(x)=>{x{0}+1,x{1}+lst{x{0}+1}},
(x)=>x{1})
in
gen
т.е. вместо записи через вычисления тащим список из двух значений, приходится дважды вычислять счётчик, но в целом это работает.
Однако, пока писал ответ, возникла другая мысль – а давайте примотаем метаданные:
let
lst = List.Buffer({1..250000}),
n = List.Count(lst),
gen = List.Generate(()=>0 meta [s=lst{0}],
(x)=>x<n,
(x)=>Value.ReplaceMetadata(x+1,[s=Value.Metadata(x)[s]+lst{x+1}]),
(x)=>Value.Metadata(x)[s])
in
gen
т.е. вместо списка значений запихиваем накопленную сумму в метаданные нашего значения, при этом приходится использовать Value.Metadata и Value.ReplaceMetadata, что делает код несколько громоздким, зато см. промеры по скорости в первом комментарии под постом – это вполне себе шустро.
Поэтому делаем выводы: накопленная сумма – это тот случай, когда НА ЗАПИСЯХ быстрее, а вариант с метаданными, пусть чуть медленнее, зато весьма экзотичен.
Решайте задачи разными путями – это бывает интересно 😉
Надеюсь, было полезно.
Всех благ!
@buchlotnik
Telegram
Для тех, кто в танке
List.Generate, осмысленный и пощадный
#АнатомияФункций – List.Generate
Думаю несложно догадаться, что для начала отправлю читать спецификацию, ибо там сказано:
List.Generate( initial as function,
condition as function,
next as function,
optional…
#АнатомияФункций – List.Generate
Думаю несложно догадаться, что для начала отправлю читать спецификацию, ибо там сказано:
List.Generate( initial as function,
condition as function,
next as function,
optional…
ЧИСТВНДОХ на М – а почему бы и нет?
#АнатомияФункций – CustomFunctions
Всем привет!
В чат закинули задачку – можно ли посчитать экселевскую ЧИСТВНДОХ через PQ?
Ну было бы нельзя, не было бы поста )))
На вход нам подают список дат и список потоков, вычислить нужно внутреннюю ставку доходности, что в переводе на русский означает численно решить уравнение.
Получился такой код:
Где
sum – функция, вычисляющая сумму, которую мы должны обнулить
min – стартовая дата
zip, lst – получаем список для sum – по каждой дате получаем период в годах и сам поток
base – знак исходной суммы (нам это знание нужно, чтобы ловить переход через ноль)
gen – ну и запускаем генератор – обычный метод половинного деления (краевые значения и точность задал вручную, кому интересно может сделать параметрами или вообще вычислить)
to – результат последней итерации и есть искомое.
Как-то так – тут мало М, в основном численные методы.
Кому код покажется громоздким – можно переписать на рекурсию:
Так немножко лаконичнее, суть та же, как в общем и скорость.
Ну и пример использования:
Вроде работает и даже не вешает комп, пользуйтесь!
Надеюсь, было полезно.
Всех благ!
@buchlotnik
#АнатомияФункций – CustomFunctions
Всем привет!
В чат закинули задачку – можно ли посчитать экселевскую ЧИСТВНДОХ через PQ?
Ну было бы нельзя, не было бы поста )))
На вход нам подают список дат и список потоков, вычислить нужно внутреннюю ставку доходности, что в переводе на русский означает численно решить уравнение.
Получился такой код:
ЧИСТВНДОХ =(даты as list,потоки as list) as number =>
[ sum=(x)=>List.Sum(List.Transform(lst,(y)=>y{1}/Number.Power(1+x,y{0}))),
min = List.Min(даты),
zip= List.Zip({даты,потоки}),
lst = List.Buffer(List.Transform(zip,(x)=>{Duration.TotalDays(x{0}-min)/365,x{1}})),
base = Number.Sign(sum(0)),
gen=List.Generate( ()=>[i=0,a=0,b=100,p=(a+b)/2,s=sum(p),t=Number.Sign(s), f=t=base],
(x)=>x[i]<100 and x[b]-x[a]>0.0000001,
(x)=>[ i=x[i]+1,
a=if x[f] then x[p] else x[a],
b=if x[f] then x[b] else x[p],
p=(a+b)/2,
s=sum(p),
t=Number.Sign(s),
f=t=base],
(x)=>x[p]),
to = List.Last(gen)][to]
Где
sum – функция, вычисляющая сумму, которую мы должны обнулить
min – стартовая дата
zip, lst – получаем список для sum – по каждой дате получаем период в годах и сам поток
base – знак исходной суммы (нам это знание нужно, чтобы ловить переход через ноль)
gen – ну и запускаем генератор – обычный метод половинного деления (краевые значения и точность задал вручную, кому интересно может сделать параметрами или вообще вычислить)
to – результат последней итерации и есть искомое.
Как-то так – тут мало М, в основном численные методы.
Кому код покажется громоздким – можно переписать на рекурсию:
ЧИСТВНДОХ =(даты as list,потоки as list) as number =>
[ sum=(x)=>List.Sum(List.Transform(lst,(y)=>y{1}/Number.Power(1+x,y{0}))),
min = List.Min(даты),
zip= List.Zip({даты,потоки}),
lst = List.Buffer(List.Transform(zip,(x)=>{Duration.TotalDays(x{0}-min)/365,x{1}})),
base = Number.Sign(sum(0)),
func=(i,a,b)=> [ p=(a+b)/2, s=sum(p), t=Number.Sign(s), f=t=base,
out =if i<100 and (b-a)>0.0000001
then if f
then @func(i+1,p,b)
else @func(i+1,a,p) else p ][out],
to = func(0,0,100)][to]
Так немножко лаконичнее, суть та же, как в общем и скорость.
Ну и пример использования:
let
from = #table({"Даты","Потоки"},{{#date(2019,1,1),-1090},{#date(2019,12,31),123},{#date(2020,12,31),123},{#date(2021,12,31),123},{#date(2022,12,31),123},{#date(2023,12,31),1353}}),
to = ЧИСТВНДОХ(from[Даты],from[Потоки])
in
to //0.13257
Вроде работает и даже не вешает комп, пользуйтесь!
Надеюсь, было полезно.
Всех благ!
@buchlotnik
1С.Разбираем многоуровневую шапку
#1C - шапка
Всем привет!
Многоуровневые шапки таблиц, выгруженных из 1С никогда не добавляют счастья (Примеры таких таблиц найдете в Первом комментарии к посту.)
Зачастую пользователь начинает «руками» исправлять шапку таблицы, чтобы привести ее в удобную форму для дальнейшей работы.
Со своей стороны предложим несколько способов в помощь:
1. Для простой многоуровневой шапки таблицы, когда заполнены все заголовки полей (см. Пример1 в приложении) на просторах интернета можно увидеть такое:
Коротко про подход: Транспонируем таблицу, комбайним через разделитель , снова транспонируем и поднимаем заголовки. Далее таблицу можно свернуть для работы со сводной:
2. Для шапки, в которой заполнены не все поля (см. Пример 2) можно воспользоваться следующим кодом:
Подход так же не сложный: «Откусили» шапку, преобразовали и приставили заново к телу таблицы.
Но что если уровней в таблице три и более или структура шапки похожа на таблицу в Примере 3 ?
3. На помощь может прийти решение, которое в большинстве случаев преобразует шапку таблицы в «плоский» формат:
Разберем данный код.
Эта функция анализирует первые несколько строк таблицы table, указанных в параметре num, далее по столбцам идёт объединение текста с использованием в качестве разделителя параметра delim. Опционально четвертый параметр fill позволяет задать строковое значение, которое будет использовано для заголовков нижних уровней в случае их отсутствия в данном столбце.
lst: Преобразуем в список строки таблицы с шапкой
delim: Опрашиваем входящий параметр-разделитель и при его отсутствии устанавливаем пробел
g: проверка параметра fill и если оно ненулевое, то добавляем к значению поля параметр, переданный в функцию fnParshead.
f: на вход подаем два списка, связываем попарно и обрабатываем.
gen: Генератор, который собирает список для будущего заголовка таблицы в комплексе с функциями f и g.
Попробуйте на приложенных примерах разобрать каждый подход. И если что-то окажется полезным – значит все было не зря.
В большей степени пост предназначен для начинающих и продолжающих изучать функциональное программирование, хотя и профессионалы, надеюсь, смогут найти тут для себя что-то интересное.
Примеры, представленные выше показывают, как через редактор можно решить насущную задачу по разбору многоуровневой шапки. Так же предлагаю попробовать решить приложенные примеры, не прибегая к коду:
(
С уважением к Вам. @CubRoot
PS^ Михаил, спасибо за помощь )
#1C - шапка
Всем привет!
Многоуровневые шапки таблиц, выгруженных из 1С никогда не добавляют счастья (Примеры таких таблиц найдете в Первом комментарии к посту.)
Зачастую пользователь начинает «руками» исправлять шапку таблицы, чтобы привести ее в удобную форму для дальнейшей работы.
Со своей стороны предложим несколько способов в помощь:
1. Для простой многоуровневой шапки таблицы, когда заполнены все заголовки полей (см. Пример1 в приложении) на просторах интернета можно увидеть такое:
from=ИсточникДанных,
t=Table.Transpose(from),
mergecol = Table.CombineColumns(t,{"Column1", "Column2"},Combiner.CombineTextByDelimiter(".", QuoteStyle.None),"Headers"),
tr=Table.Transpose(mergecol),
to=Table.PromoteHeaders(tr)
Коротко про подход: Транспонируем таблицу, комбайним через разделитель , снова транспонируем и поднимаем заголовки. Далее таблицу можно свернуть для работы со сводной:
Table.UnpivotOtherColumns ()
2. Для шапки, в которой заполнены не все поля (см. Пример 2) можно воспользоваться следующим кодом:
from = ИсточникДанных,
lst=Table.ToColumns(Table.Range(from,0,2)),
newl=Table.ToList(Table.FillDown(Table.FromList(lst,(x)=>x),{"Column1"}),(x)=>x),
tr=List.Transform(newl,(x)=>Text.Combine(x,".")),
to=Table.RenameColumns(Table.Range(from,2),List.Zip({Table.ColumnNames(from),tr}))
Подход так же не сложный: «Откусили» шапку, преобразовали и приставили заново к телу таблицы.
Но что если уровней в таблице три и более или структура шапки похожа на таблицу в Примере 3 ?
3. На помощь может прийти решение, которое в большинстве случаев преобразует шапку таблицы в «плоский» формат:
fnParsHead=(table,num,optional delim,optional fill)=>
[ lst = List.Buffer(Table.ToColumns(Table.Range(table,0,num))),
delim = if delim=null then " " else delim,
f=(x,y)=>List.Accumulate( List.Zip({x,y}),
[i=false,j={}],
(s,c)=>[i=s[i]=true or c{0}<>null,j=if i then s[j]&{c{0}} else s[j]&{c{0}??c{1}}]
)[j],
g=(x)=>if fill=null then Text.Combine(x,delim) else Text.Combine(List.ReplaceValue(x,null,fill,Replacer.ReplaceValue),delim),
gen = List.Generate( ()=>[i=0,l=lst{i},o=l],
(x)=>x[i]<List.Count(lst),
(x)=>[i=x[i]+1,l=lst{i},o=f(l,x[o])],
(x)=>g(x[o])
),
out = Table.RenameColumns(Table.Range(table,num),List.Zip({Table.ColumnNames(table),gen}))][out],
from=ИсточникДанных,
to=fnParsHead(from,3,".")
Разберем данный код.
Эта функция анализирует первые несколько строк таблицы table, указанных в параметре num, далее по столбцам идёт объединение текста с использованием в качестве разделителя параметра delim. Опционально четвертый параметр fill позволяет задать строковое значение, которое будет использовано для заголовков нижних уровней в случае их отсутствия в данном столбце.
lst: Преобразуем в список строки таблицы с шапкой
delim: Опрашиваем входящий параметр-разделитель и при его отсутствии устанавливаем пробел
g: проверка параметра fill и если оно ненулевое, то добавляем к значению поля параметр, переданный в функцию fnParshead.
f: на вход подаем два списка, связываем попарно и обрабатываем.
gen: Генератор, который собирает список для будущего заголовка таблицы в комплексе с функциями f и g.
Попробуйте на приложенных примерах разобрать каждый подход. И если что-то окажется полезным – значит все было не зря.
В большей степени пост предназначен для начинающих и продолжающих изучать функциональное программирование, хотя и профессионалы, надеюсь, смогут найти тут для себя что-то интересное.
Примеры, представленные выше показывают, как через редактор можно решить насущную задачу по разбору многоуровневой шапки. Так же предлагаю попробовать решить приложенные примеры, не прибегая к коду:
(
мышкоклацем
) – что получится?С уважением к Вам. @CubRoot
PS^ Михаил, спасибо за помощь )
Telegram
Для тех, кто в танке
1C - Новая рубрика на канале
#1C - Содержание
Дамы и господа, Вашему вниманию предлагается еще одно направление в «Танке» - обработка сохраненных файлов с отчетами из системы 1С с созданием нормализованной таблицы (таблиц).
На скрине (см. первый комментарий)…
#1C - Содержание
Дамы и господа, Вашему вниманию предлагается еще одно направление в «Танке» - обработка сохраненных файлов с отчетами из системы 1С с созданием нормализованной таблицы (таблиц).
На скрине (см. первый комментарий)…
Table.CombineColumns или где окажется результирующий столбец?
#АнатомияФункций - Table.CombineColumns
Всем привет!
Пилил небольшую задачку и вспомнил один приём, которым и хочу поделиться.
Итак, имеем таблицу, в которой несколько столбцов нужно объединить – в примере просто нужно найти сумму и запихнуть её в столбец "sum". Собственно, ничего сложного – пишем код:
from – таблица,
to – объединяем столбцы {"a","c","e"} и на выходе получаем таблицу со следующим порядком столбцов - {"b","d","sum","f"} – и внимательный читатель заметит, что сумма оказалась на месте столбца "e", т.е. последнего в нашем списке.
Интересно, давайте поэкспериментируем:
to1 – поставили последним столбец "c" и теперь результат оказался на его месте
to2 – а теперь результат вообще вначале, на месте столбца "a", поскольку именно его мы указали последним.
Как-то так – для CombineColumns важен порядок имён в списке объединяемых столбцов – так мы можем сразу регулировать, где окажется результат.
Надеюсь, было полезно.
Всех благ!
@buchlotnik
#АнатомияФункций - Table.CombineColumns
Всем привет!
Пилил небольшую задачку и вспомнил один приём, которым и хочу поделиться.
Итак, имеем таблицу, в которой несколько столбцов нужно объединить – в примере просто нужно найти сумму и запихнуть её в столбец "sum". Собственно, ничего сложного – пишем код:
let
from = #table({"a".."f"},{{1..6},{2..7},{3..8}}),
to = Table.CombineColumns(from,{"a","c","e"},List.Sum,"sum"),//{"b","d","sum","f"}
to1 = Table.CombineColumns(from,{"a","e","c"},List.Sum,"sum"),//{"b","sum","d","f"}
to2 = Table.CombineColumns(from,{"c","e","a"},List.Sum,"sum")//{"sum","b","d","f"}
in
to2
from – таблица,
to – объединяем столбцы {"a","c","e"} и на выходе получаем таблицу со следующим порядком столбцов - {"b","d","sum","f"} – и внимательный читатель заметит, что сумма оказалась на месте столбца "e", т.е. последнего в нашем списке.
Интересно, давайте поэкспериментируем:
to1 – поставили последним столбец "c" и теперь результат оказался на его месте
to2 – а теперь результат вообще вначале, на месте столбца "a", поскольку именно его мы указали последним.
Как-то так – для CombineColumns важен порядок имён в списке объединяемых столбцов – так мы можем сразу регулировать, где окажется результат.
Надеюсь, было полезно.
Всех благ!
@buchlotnik
buchOfficePack - анонс, теперь не отверчусь
#АнатомияФункций - buchOfficePack
Всем привет!
Небольшая задачка, которую я пилил вчера, доступна в рамках следующего запроса:
Ну а что это, зачем и почему - рассказываю на Ютубе
Если тема интересная, прошу наследить в каментах под роликом
Надеюсь, будет полезно.
Всех благ!
@buchlotnik
#АнатомияФункций - buchOfficePack
Всем привет!
Небольшая задачка, которую я пилил вчера, доступна в рамках следующего запроса:
Expression.Evaluate(Text.FromBinary(Binary.Buffer(Web.Contents("https://raw.githubusercontent.com/buchlotnik/buchlotnik_functions/main/buchOfficePack"))),#shared)
Ну а что это, зачем и почему - рассказываю на Ютубе
Если тема интересная, прошу наследить в каментах под роликом
Надеюсь, будет полезно.
Всех благ!
@buchlotnik
YouTube
00 buchOfficePack - начинаем пилить библиотеку для офисного пакета
Слишком часто приходится обращаться к разным файлам офисного пакета. По этому поводу уже написано немало кода:
https://t.me/pbi_pq_from_tank/101
https://t.me/pbi_pq_from_tank/104
https://t.me/pbi_pq_from_tank/199
https://t.me/pbi_pq_from_tank/200
настало…
https://t.me/pbi_pq_from_tank/101
https://t.me/pbi_pq_from_tank/104
https://t.me/pbi_pq_from_tank/199
https://t.me/pbi_pq_from_tank/200
настало…
buchOfficePack – fxUnzip – часть 1 ядро
#АнатомияФункций - buchOfficePack
Всем привет!
Ну что ж – началось – пилим библиотеку на камеру.
Видос уже на Ютубе
Код для страждущих тут:
Если это начинание кажется полезным просьба не забывать подписываться на ютуб, ставить лайки и оставлять комментарии.
Надеюсь, было полезно.
Всех благ!
@buchlotnik
#АнатомияФункций - buchOfficePack
Всем привет!
Ну что ж – началось – пилим библиотеку на камеру.
Видос уже на Ютубе
Код для страждущих тут:
(ZIP)=>
[
u16 =BinaryFormat.ByteOrder(BinaryFormat.UnsignedInteger16,ByteOrder.LittleEndian),
u32 =BinaryFormat.ByteOrder(BinaryFormat.UnsignedInteger32,ByteOrder.LittleEndian),
get=(x)=>BinaryFormat.Record(
if Binary.Range(x,0,4)=#binary({0x50,0x4b,0x03,0x04})
then [ Name=BinaryFormat.Text(u16(Binary.Range(x,26,2))),
Extr=BinaryFormat.Binary(u16(Binary.Range(x,28,2))),
Value=BinaryFormat.Transform(BinaryFormat.Binary(u32(Binary.Range(x,18,4))),(x)=>Binary.Decompress(x,Compression.Deflate)),
flag=true]
else [flag=false]
),
lst = BinaryFormat.List(BinaryFormat.Choice(BinaryFormat.Binary(30),get),(x)=>x[flag]=true)(ZIP),
to = Table.FromRecords(List.RemoveLastN(lst,1),type table [Name=text,Value=binary])][to]
Если это начинание кажется полезным просьба не забывать подписываться на ютуб, ставить лайки и оставлять комментарии.
Надеюсь, было полезно.
Всех благ!
@buchlotnik
YouTube
01 buchOfficePack - fxUnzip - часть 1 - ядро
Пишем ядро функции fxUnzip, без которой нам не прочитать начинку офисных файлов. В первой части рассказываю, как написать само ядро.
Кому интересно:
мой курс по Power Query - https://akademia-excel.ru/powerquery?gcpc=9ae40
телега тут- https://t.me/pbi_pq_from_tank…
Кому интересно:
мой курс по Power Query - https://akademia-excel.ru/powerquery?gcpc=9ae40
телега тут- https://t.me/pbi_pq_from_tank…
buchOfficePack – fxUnzip – часть 2 обвес
#АнатомияФункций - buchOfficePack
Всем привет!
Продолжаем пилить библиотеку. Видос уже на Ютубе
Добавили всё по красоте – параметры, справку и залили на гитхаб.
Итоговый код ниже, все мои комментарии в видосе.
Также напоминаю, что, если это начинание кажется полезным просьба не забывать подписываться на ютуб, ставить лайки и оставлять комментарии. От этого зависит будут ли в принципе появляться новые видео
Надеюсь, было полезно.
Всех благ!
@buchlotnik
#АнатомияФункций - buchOfficePack
Всем привет!
Продолжаем пилить библиотеку. Видос уже на Ютубе
Добавили всё по красоте – параметры, справку и залили на гитхаб.
Итоговый код ниже, все мои комментарии в видосе.
fxUnzip=[func=(ZIP, optional options)=>
[
encod = [a=options[Encoding]?,b=if a = null then 866 else a][b],
compr = (x)=> if x =0 then Compression.None else Compression.Deflate,
u16 =BinaryFormat.ByteOrder(BinaryFormat.UnsignedInteger16,ByteOrder.LittleEndian),
u32 =BinaryFormat.ByteOrder(BinaryFormat.UnsignedInteger32,ByteOrder.LittleEndian),
get=(x)=>BinaryFormat.Record(
if Binary.Range(x,0,4)=#binary({0x50,0x4b,0x03,0x04})
then [ Name=BinaryFormat.Text(u16(Binary.Range(x,26,2)),encod),
Extr=BinaryFormat.Binary(u16(Binary.Range(x,28,2))),
Value=BinaryFormat.Transform(BinaryFormat.Binary(u32(Binary.Range(x,18,4))),(y)=>Binary.Decompress(y,compr(u16(Binary.Range(x,8,2)))))]
else []
),
lst = BinaryFormat.List(BinaryFormat.Choice(BinaryFormat.Binary(30),get),(x)=>x<>[])(ZIP),
to = Table.FromRecords(List.RemoveLastN(lst,1),type table [Name=text,Value=binary])][to],
typ =type function (ZIP as (type binary meta [Documentation.FieldCaption="ZIP - бинароное содержимое (zip, xlsx, docx, pptx и т.д.)"]), optional options as record) as table meta
[Documentation.Name = "fxUnzip (@buchlotnik)",
Documentation.LongDescription ="функция считывает содержимое архива и возвращает таблицу с полями:<p> <b>Name</b> - имя файла (с путём к файлу) <p><b>Value</b> - бинарное содержимое файла. <p>Необязательный аргумент <b>options</b> на текущий момент поддерживает следующие поля: <p>1) <b>Encoding</b> - требуется для корректного чтения не латинских имён файлов (по умолчанию <b>866 - кириллица</b>)"],
result = Value.ReplaceType(func,typ)][result]
Также напоминаю, что, если это начинание кажется полезным просьба не забывать подписываться на ютуб, ставить лайки и оставлять комментарии. От этого зависит будут ли в принципе появляться новые видео
Надеюсь, было полезно.
Всех благ!
@buchlotnik
YouTube
02 buchOfficePack - fxUnzip - часть 2 - обвес
Продолжаем пилить fxUnzip - добавляем параметры и описание.
Кому интересно:
мой курс по Power Query - https://akademia-excel.ru/powerquery?gcpc=9ae40
телега тут- https://t.me/pbi_pq_from_tank
сама библиотека тут - https://github.com/buchlotnik/buchlotni…
Кому интересно:
мой курс по Power Query - https://akademia-excel.ru/powerquery?gcpc=9ae40
телега тут- https://t.me/pbi_pq_from_tank
сама библиотека тут - https://github.com/buchlotnik/buchlotni…
buchOfficePack - fxExcelGetRowsAttributes - часть 1 - ядро
#АнатомияФункций - buchOfficePack
Всем привет!
Продолжаем пилить библиотеку. Видос уже на Ютубе
Сегодня разбираемся с атрибутами строк. Код пока не привожу - есть пара мыслей, скорее всего функций будет не одна, а две. Но это уже в последующих видео.
Особого энтузиазма по просмотру видео пока не наблюдаю, поэтому если это начинание кажется полезным просьба не забывать подписываться на ютуб, ставить лайки и оставлять комментарии. От этого зависит будут ли в принципе появляться новые видео
Надеюсь, было полезно.
Всех благ!
@buchlotnik
#АнатомияФункций - buchOfficePack
Всем привет!
Продолжаем пилить библиотеку. Видос уже на Ютубе
Сегодня разбираемся с атрибутами строк. Код пока не привожу - есть пара мыслей, скорее всего функций будет не одна, а две. Но это уже в последующих видео.
Особого энтузиазма по просмотру видео пока не наблюдаю, поэтому если это начинание кажется полезным просьба не забывать подписываться на ютуб, ставить лайки и оставлять комментарии. От этого зависит будут ли в принципе появляться новые видео
Надеюсь, было полезно.
Всех благ!
@buchlotnik
YouTube
03 buchOfficePack - fxExcelGetRowsAttributes - часть 1 - ядро
Переходим к добыче информации из файлов. Пишем ядро функции для получения информации о свойствах строк в экселевском файле
Кому интересно:
мой курс по Power Query - https://akademia-excel.ru/powerquery?gcpc=9ae40
телега тут- https://t.me/pbi_pq_from_tank…
Кому интересно:
мой курс по Power Query - https://akademia-excel.ru/powerquery?gcpc=9ae40
телега тут- https://t.me/pbi_pq_from_tank…
buchOfficePack - fxExcelGetRowsAttributes - часть 2 - обвес и не только
#АнатомияФункций - buchOfficePack
Всем привет!
Продолжаем пилить библиотеку. Видос уже на Ютубе
Функцию сшивки таблицы листа и xml вынесли как вспомогательную.
Добавили пару опций.
Вопрос строк закрыт, далее будем мучать столбцы.
Надеюсь, было полезно.
Всех благ!
@buchlotnik
#АнатомияФункций - buchOfficePack
Всем привет!
Продолжаем пилить библиотеку. Видос уже на Ютубе
Функцию сшивки таблицы листа и xml вынесли как вспомогательную.
Добавили пару опций.
Вопрос строк закрыт, далее будем мучать столбцы.
Надеюсь, было полезно.
Всех благ!
@buchlotnik
YouTube
04 buchOfficePack - fxExcelGetRowsAttributes - часть 2 - обвес и не только
Кому интересно:
мой курс по Power Query - https://akademia-excel.ru/powerquery?gcpc=9ae40
телега тут- https://t.me/pbi_pq_from_tank
сама библиотека тут - https://github.com/buchlotnik/buchlotnik_functions/blob/main/buchOfficePack
мой курс по Power Query - https://akademia-excel.ru/powerquery?gcpc=9ae40
телега тут- https://t.me/pbi_pq_from_tank
сама библиотека тут - https://github.com/buchlotnik/buchlotnik_functions/blob/main/buchOfficePack
buchOfficePack - fxTableRemoveEmptyColumns – удаляем пустые столбцы в таблице
#АнатомияФункций - buchOfficePack
Всем привет!
Продолжаем пилить библиотеку. Видос уже на Ютубе
Само решение уже было на канале, но поскольку надо обеспечить совместимость с 2016 пришлось отказаться от второго аргумента в Table.Profile. В итоге получилось решение в три строчки )))
Соответственно библиотека продолжит свое развитие, но следующее видео по данной тематике будет последним - трудоёмко и мало кому интересно.
Надеюсь, было полезно.
Всех благ!
@buchlotnik
#АнатомияФункций - buchOfficePack
Всем привет!
Продолжаем пилить библиотеку. Видос уже на Ютубе
Само решение уже было на канале, но поскольку надо обеспечить совместимость с 2016 пришлось отказаться от второго аргумента в Table.Profile. В итоге получилось решение в три строчки )))
fxTableRemoveEmptyColumns=[func =(table, optional options)=>
[tbl = Table.Buffer(Table.Profile(table)),
lst = Table.SelectRows(tbl,(r)=>r[Count]=r[NullCount])[Column],
to = Table.RemoveColumns(table,lst)][to],
typ=type function (table as table, optional options as record) as table meta
[Documentation.Name="fxTableRemoveEmptyColumns (@buchlotnik)",
Documentation.LongDescription="функция, удаляющая пустые столбцы из таблицы"],
result = Value.ReplaceType(func,typ)
][result]
Соответственно библиотека продолжит свое развитие, но следующее видео по данной тематике будет последним - трудоёмко и мало кому интересно.
Надеюсь, было полезно.
Всех благ!
@buchlotnik
YouTube
05 buchOfficePack - fxTableRemoveEmptyColumns
Кому интересно:
мой курс по Power Query - https://akademia-excel.ru/powerquery?gcpc=9ae40
телега тут- https://t.me/pbi_pq_from_tank
сама библиотека тут - https://github.com/buchlotnik/buchlotnik_functions/blob/main/buchOfficePack
мой курс по Power Query - https://akademia-excel.ru/powerquery?gcpc=9ae40
телега тут- https://t.me/pbi_pq_from_tank
сама библиотека тут - https://github.com/buchlotnik/buchlotnik_functions/blob/main/buchOfficePack
fxGetMCode – получаем код М, используя М
#АнатомияФункций – buchOfficePack
Всем привет!
Дошли руки до вынимания кода запросов из файлов. В целом, после fxUnZip это уже не должно быть особо сложным. Просто надо ещё немножко почитать спецификацию.
А там, как всегда, всё написано и по этому поводу функция (она является частью библиотеки, поэтому убедитесь, что fxUnZip у вас имеется в наличии):
По шагам
u32 – вспомогательная функция для чтения 4-байтного беззнакового целого (размер массива)
f – ещё одна вспомогательная – отрезает название запроса от собственно кода запроса
А теперь основное тело
from – открываем файл как архив и вынимаем customXml/item1.xml – код хранится там
bin – внутри этого xml лежит в виде текста сжатый бинарник (DataMashup) – возвращаем ему первозданный бинарный вид
data – из полученного бинарника пропускаем первые 4 байта, а вторые 4 указывают на размер массива, содержащего код - читаем
unz – и поскольку этот массив ещё и сжатый – снова используем fxUnZip и вынимаем Formulas/Section1.m – файл с кодом
txt – превращаем бинарку в текст и дописываем в конце перевод каретки с разрывом строки (там просто запросы отделены точкой с запятой с последующим переводом каретки, но в самом конце идёт просто точка с запятой – короче добиваемся единообразия)
splt – теперь делим общий текст на отдельные запросы
lst – отбрасываем лишние первый и последний фрагменты
tbl – собираем в табличку, параллельно отделяя название запроса от его кода
to – ну и на выход подаём либо табличку, либо пустую табличку, когда запросов в файле не оказалось.
Как обычно, код короче своего описания ))) Видос с более подробным разбором будет, но уже в новом году. Пользуйтесь!
Надеюсь, было полезно.
Всех благ!
@buchlotnik
#АнатомияФункций – buchOfficePack
Всем привет!
Дошли руки до вынимания кода запросов из файлов. В целом, после fxUnZip это уже не должно быть особо сложным. Просто надо ещё немножко почитать спецификацию.
А там, как всегда, всё написано и по этому поводу функция (она является частью библиотеки, поэтому убедитесь, что fxUnZip у вас имеется в наличии):
fxGetMCode=(file)=>
[u32 = BinaryFormat.ByteOrder(BinaryFormat.UnsignedInteger32,ByteOrder.LittleEndian),
f=(x)=>[a=Text.Split(x," = "),b={Text.Trim(Text.Replace(a{0},"shared","")),Text.Combine(List.Skip(a)," = ")}][b],
from = fxUnzip(file){[Name="customXml/item1.xml"]}?[Value]?,
bin = Binary.FromText(Xml.Document(from){0}[Value],BinaryEncoding.Base64),
data = BinaryFormat.Choice(BinaryFormat.Binary(8),(x)=>BinaryFormat.Binary(u32(Binary.Range(x,4,4))))(bin),
unz = fxUnzip(data){[Name="Formulas/Section1.m"]}[Value],
txt = Text.FromBinary(unz)&"#(cr,lf)",
splt = Text.Split(txt,";#(cr,lf)"),
lst = List.Range(splt,1,List.Count(splt)-2),
tbl = Table.FromList(lst,f,{"Name","Value"}),
to = if from=null then #table({"Name","Value"},{{null,null}}) else tbl][to]
По шагам
u32 – вспомогательная функция для чтения 4-байтного беззнакового целого (размер массива)
f – ещё одна вспомогательная – отрезает название запроса от собственно кода запроса
А теперь основное тело
from – открываем файл как архив и вынимаем customXml/item1.xml – код хранится там
bin – внутри этого xml лежит в виде текста сжатый бинарник (DataMashup) – возвращаем ему первозданный бинарный вид
data – из полученного бинарника пропускаем первые 4 байта, а вторые 4 указывают на размер массива, содержащего код - читаем
unz – и поскольку этот массив ещё и сжатый – снова используем fxUnZip и вынимаем Formulas/Section1.m – файл с кодом
txt – превращаем бинарку в текст и дописываем в конце перевод каретки с разрывом строки (там просто запросы отделены точкой с запятой с последующим переводом каретки, но в самом конце идёт просто точка с запятой – короче добиваемся единообразия)
splt – теперь делим общий текст на отдельные запросы
lst – отбрасываем лишние первый и последний фрагменты
tbl – собираем в табличку, параллельно отделяя название запроса от его кода
to – ну и на выход подаём либо табличку, либо пустую табличку, когда запросов в файле не оказалось.
Как обычно, код короче своего описания ))) Видос с более подробным разбором будет, но уже в новом году. Пользуйтесь!
Надеюсь, было полезно.
Всех благ!
@buchlotnik
Telegram
Для тех, кто в танке
buchOfficePack – fxUnzip – часть 2 обвес
#АнатомияФункций - buchOfficePack
Всем привет!
Продолжаем пилить библиотеку. Видос уже на Ютубе
Добавили всё по красоте – параметры, справку и залили на гитхаб.
Итоговый код ниже, все мои комментарии в видосе.…
#АнатомияФункций - buchOfficePack
Всем привет!
Продолжаем пилить библиотеку. Видос уже на Ютубе
Добавили всё по красоте – параметры, справку и залили на гитхаб.
Итоговый код ниже, все мои комментарии в видосе.…
fxDistribute – немножко решения задач оптимизации на М
#АнатомияФункций – custom
Всем привет!
Я упорно не пытаюсь решать задачи оптимизации на М, но они упорно возникают в чате. Поэтому, раз уж поддержали пост, решаем задачу:
на вход подаётся список из N позиций с указанием их «веса», список нужно разделить на M равных (почти равных если целочисленное деление невозможно) групп с максимально равной суммой весов. Ок, у меня вышло примерно так:
По шагам:
n – определяем общую длину списка, чтобы делилось поровну
sort – дополняем список до нужной длины нулями и сортируем
av – находим среднее по списку
tr – преобразуем фактические веса в величину отклонения от среднего
zip – нарезаем список по N элементов и получаем M достаточно одинаковых групп – опорный план
теперь немножко перескочим
gen – итеративно применяем к полученным спискам функцию f и следим за остаточной суммой отклонений – продолжаем пока она уменьшается
tbl -результат последней итерации превращаем в таблицу
to – возвращаем «весам» их первозданный вид
Теперь по функции f – реализованный алгоритм ищет локальный минимум – это важно, потому как решил не усложнять жизнь ни себе, ни оперативе.
tst, last, sum1, sum2,l st1, lst2 – находим группы с наибольшей (положительной) и наименьшей (отрицательной) суммами
cr, lst – находим все сочетания элементов этих двух групп и как их обмен повлияет на общий результат
slct – выбираем наиболее подходящую пару
new – осуществляем обмен элементами между группами
Как-то так. Поиск локального минимума не всегда оптимален, зато работает шустро и пишется просто )))
Надеюсь, было полезно.
Всех благ!
@buchlotnik
#АнатомияФункций – custom
Всем привет!
Я упорно не пытаюсь решать задачи оптимизации на М, но они упорно возникают в чате. Поэтому, раз уж поддержали пост, решаем задачу:
на вход подаётся список из N позиций с указанием их «веса», список нужно разделить на M равных (почти равных если целочисленное деление невозможно) групп с максимально равной суммой весов. Ок, у меня вышло примерно так:
(lst,num)=>
let
n=Number.RoundUp(List.Count(lst)/num)*num,
sort = List.Sort(lst&List.Repeat({0},n-List.Count(lst))),
av=List.Average(sort),
tr=List.Transform(sort,(x)=>x-av),
zip = List.Zip(List.Split(tr,num)),
f=(base)=>
[tst=List.Buffer(List.Sort(base,(x)=>List.Sum(x))),
last=List.Last(tst),
sum1=Number.Abs(List.Sum(tst{0})),
sum2=Number.Abs(List.Sum(last)),
lst1=if sum2>sum1 then tst{0} else last,
lst2=if sum2>sum1 then last else tst{0},
cr=List.Sum(lst2),
lst=List.TransformMany(lst2,(x)=>lst1,(x,y)=>{x,y,cr-x+y}),
slct= if cr <0
then List.Max(List.Select(lst,(x)=>x{2}<0),null,(x)=>x{2})
else List.Min(List.Select(lst,(x)=>x{2}>0),null,(x)=>x{2}),
f=(x,y,z)=>List.ReplaceRange(x,List.PositionOf(x,y),1,{z}),
new={f(lst2,slct{0},slct{1})}&List.Range(tst,1,num-2)&{f(lst1,slct{1},slct{0})}][new],
gen=List.Generate(()=>[i=0,l=zip,s=List.Sum(List.Transform(l,(x)=>Number.Abs(List.Sum(x)))),fl=true],(x)=>x[fl],
(x)=>[i=x[i]+1,l=f(x[l]),s=List.Sum(List.Transform(l,(x)=>Number.Abs(List.Sum(x)))),fl=s<x[s]],
(x)=>x[l]),
tbl = Table.FromColumns(List.Last(gen)),
to=Table.TransformColumns(tbl,{},(x)=>x+av)
in
to
По шагам:
n – определяем общую длину списка, чтобы делилось поровну
sort – дополняем список до нужной длины нулями и сортируем
av – находим среднее по списку
tr – преобразуем фактические веса в величину отклонения от среднего
zip – нарезаем список по N элементов и получаем M достаточно одинаковых групп – опорный план
теперь немножко перескочим
gen – итеративно применяем к полученным спискам функцию f и следим за остаточной суммой отклонений – продолжаем пока она уменьшается
tbl -результат последней итерации превращаем в таблицу
to – возвращаем «весам» их первозданный вид
Теперь по функции f – реализованный алгоритм ищет локальный минимум – это важно, потому как решил не усложнять жизнь ни себе, ни оперативе.
tst, last, sum1, sum2,l st1, lst2 – находим группы с наибольшей (положительной) и наименьшей (отрицательной) суммами
cr, lst – находим все сочетания элементов этих двух групп и как их обмен повлияет на общий результат
slct – выбираем наиболее подходящую пару
new – осуществляем обмен элементами между группами
Как-то так. Поиск локального минимума не всегда оптимален, зато работает шустро и пишется просто )))
Надеюсь, было полезно.
Всех благ!
@buchlotnik
fxColNumFromText – номер столбца по адресу ячейки
#АнатомияФункций – custom
Всем привет!
Очередной видос на Ютубе.
В этот раз решаем частную задачу – определить номер столбца по его буквенному обозначению. Собственно, нужно просто вспомнить про то, что у каждого символа есть его числовой код, ну и как устроены позиционные системы исчисления. Код вышел весьма лаконичный:
Надеюсь, было полезно.
Всех благ!
@buchlotnik
#АнатомияФункций – custom
Всем привет!
Очередной видос на Ютубе.
В этот раз решаем частную задачу – определить номер столбца по его буквенному обозначению. Собственно, нужно просто вспомнить про то, что у каждого символа есть его числовой код, ну и как устроены позиционные системы исчисления. Код вышел весьма лаконичный:
f=(x)=>[a = List.Transform(List.Reverse(Text.ToList(Text.SplitAny(x,"0123456789"){0})),(x)=>Character.ToNumber(x)-64),
b = List.Sum(List.Transform(List.Zip({a,List.Positions(a)}),(x)=>x{0}*Number.Power(26,x{1})))][b]
Надеюсь, было полезно.
Всех благ!
@buchlotnik
YouTube
01 - На М - номер столбца по адресу ячейки
Решаем задачу, возникающую при вытаскивании данных из xml - получение номера столбца из его буквенного обозначения
Кому интересно:
мой курс по Power Query - https://akademia-excel.ru/powerquery?gcpc=9ae40
телега тут- https://t.me/pbi_pq_from_tank
Кому интересно:
мой курс по Power Query - https://akademia-excel.ru/powerquery?gcpc=9ae40
телега тут- https://t.me/pbi_pq_from_tank
С Новым 2024 Годом!!!
Всем привет!!!
Поздравляю с наступающим (Петропавловск-Камчатский с уже наступившим) Новым Годом!!!
Как положено, кратко по итогам года:
- количество подписчиков удвоилось (и вплотную приблизилось к 2500);
- в среднем по году пост раз в неделю;
- читают эти посты 53% подписчиков (24% в первые сутки после публикации)
Ещё в уходящем году был запущен курс по pq, запущен канал на ютубе;
Игорь (@CubRoot) начал и продолжит (да, Игорь?) серию постов по 1С;
а ещё появились посты про численные методы и даже не надейтесь – они продолжатся.
Спасибо всем, кто был с нами в этом году, читал, смотрел и даже комментировал.
Отдельно надо поблагодарить:
- серьезных дядек – Леха (@PooHkrd), Макс (@MaximZelensky), Илья (@IlyaNazarov), Серёга (@sboy_ko) – вы лучшие, спасибо вам, что вы есть и все такое;
- партнёров – Дима (@AkademiaExcel), спасибо, что распинал, продолжаем пилить учебный контент;
- конкурентов ))) – Андрей (@te1ns), продолжаем мериться скоростями – получаю искреннее удовольствие;
- и вообще всех кто это читает – значит пишу не зря )))
Немного красоты на формулах прилагается
С наступающим Новым Годом!
Всех благ!
@buchlotnik
Всем привет!!!
Поздравляю с наступающим (Петропавловск-Камчатский с уже наступившим) Новым Годом!!!
Как положено, кратко по итогам года:
- количество подписчиков удвоилось (и вплотную приблизилось к 2500);
- в среднем по году пост раз в неделю;
- читают эти посты 53% подписчиков (24% в первые сутки после публикации)
Ещё в уходящем году был запущен курс по pq, запущен канал на ютубе;
Игорь (@CubRoot) начал и продолжит (да, Игорь?) серию постов по 1С;
а ещё появились посты про численные методы и даже не надейтесь – они продолжатся.
Спасибо всем, кто был с нами в этом году, читал, смотрел и даже комментировал.
Отдельно надо поблагодарить:
- серьезных дядек – Леха (@PooHkrd), Макс (@MaximZelensky), Илья (@IlyaNazarov), Серёга (@sboy_ko) – вы лучшие, спасибо вам, что вы есть и все такое;
- партнёров – Дима (@AkademiaExcel), спасибо, что распинал, продолжаем пилить учебный контент;
- конкурентов ))) – Андрей (@te1ns), продолжаем мериться скоростями – получаю искреннее удовольствие;
- и вообще всех кто это читает – значит пишу не зря )))
Немного красоты на формулах прилагается
С наступающим Новым Годом!
Всех благ!
@buchlotnik
fxGetMCode – получаем код М, используя М – обзор на Ютубе
#АнатомияФункций – buchOfficePack
Всем привет!
Как и обещал, выложил обзор fxGetMCode на Ютубчик
В целом ничего нового – опять показываю, что всё написано прямым текстом в документации, надо просто прочитать и выполнить )))
Надеюсь, было полезно.
Всех благ!
@buchlotnik
#АнатомияФункций – buchOfficePack
Всем привет!
Как и обещал, выложил обзор fxGetMCode на Ютубчик
В целом ничего нового – опять показываю, что всё написано прямым текстом в документации, надо просто прочитать и выполнить )))
Надеюсь, было полезно.
Всех благ!
@buchlotnik