Поиск  Пользователи  Правила 
Закрыть
Логин:
Пароль:
Забыли свой пароль?
Регистрация
Войти
 
Страницы: 1
RSS
POWER QUERY - Создать функцию которая создает и удаляет столбцы по условию, Создать функцию создающую и удаляющую столбец по условию
 
Добрый день.
Тема навеяна ответами и вопросами из этой темы: Форум
Пожалуйста помогите создать функцию которая создает временный столбец, набирает в него значения построчно по условию, после чего удаляет исходные столбцы и переименовывает временный столбец в исходный.

Честно и усиленно пытался постичь исскуство дзен сам, но не получается.
Для меня масса нелогичных условий. Например почему я не могу передать в написанную функцию результат, а передаю исключительно саму функцию когда в 3м параметре записываю условия выборки для столбца.
Если же я передаю функцию внутрь другой функции для меня не понятно как получить результат и как его использовать внутри другой функции.

Также, более чем непонятно, почему я немогу использовать навзвание переменной (столбца) внутри функции и должен извращаться с
Код
Record.Field(_, "КакаяТоКолонка")
Этот вариант я нагуглил, но я его откровенно непонимаю, почему я немогу передать просто столбец. Буду признателен если кто-то это пояснит почему именно так строится синтаксис.
Целиком код функции ниже, а также запрос вызова еще ниже, если кому-то лень скачивать фаил примера.
Код
(TABLE as table, OLDCOLUMN as text, NEWCOLUMN as text, CONDITION as function)=>

let
    RESULT = 
Table.RenameColumns(
   Table.RemoveColumns(
         Table.AddColumn(TABLE, "TEMPColumn", each (if CONDITION=true then Record.Field(_, NEWCOLUMN) else Record.Field(_, OLDCOLUMN ))  ),
   {OLDCOLUMN, NEWCOLUMN}
   ),
{"TEMPColumn", OLDCOLUMN })
 
 
in
    RESULT

Тут код вызова:
Код
= ReplaceColumn(ИзмененТип,"Старый", "Новый", each([Новый]-[Старый] = 1))
Изменено: lostandleft - 23 Мар 2020 12:29:58
 
Доброе время суток.
Вариант
Код
// НовыйСтарый
let
    Источник = Excel.CurrentWorkbook(){[Name="Таблица1"]}[Content],
    ИзмененТип = Table.TransformColumnTypes(Источник,{{"Старый", Int64.Type}, {"Новый", Int64.Type}}),
    ReplaceColumn = ReplaceColumn(Источник,"Старый", "Новый", CONDITION)
in
    ReplaceColumn

// ReplaceColumn
(TABLE as table, OLDCOLUMN as text, NEWCOLUMN as text, CONDITION as function)=>

let
    RESULT = 
Table.RenameColumns(
   Table.RemoveColumns(
         Table.AddColumn(TABLE, "TEMPColumn", each (if CONDITION(_, OLDCOLUMN, NEWCOLUMN) then Record.Field(_, NEWCOLUMN) else Record.Field(_, OLDCOLUMN ))  ),
   {OLDCOLUMN, NEWCOLUMN}
   ),
{"TEMPColumn", OLDCOLUMN })
 
 
in
    RESULT

// CONDITION
(RECORD as record, OLDCOLUMN as text, NEWCOLUMN as text) as logical =>
Record.Field(RECORD, NEWCOLUMN) - Record.Field(RECORD, OLDCOLUMN) = 1
 
Андрей VG,
Не понимаю, нужно еще одну функцию создать CONDITION?
 
Цитата
lostandleft написал:
нужно еще одну функцию создать CONDITION?
Не, обязательно. Можно передать и анонимную функцию
Код
// НовыйСтарый
let
    Источник = Excel.CurrentWorkbook(){[Name="Таблица1"]}[Content],
    ИзмененТип = Table.TransformColumnTypes(Источник,{{"Старый", Int64.Type}, {"Новый", Int64.Type}}),
    ReplaceColumn = ReplaceColumn(Источник,"Старый", "Новый", (RECORD as record, OLDCOLUMN as text, NEWCOLUMN as text) as logical => Record.Field(RECORD, NEWCOLUMN) - Record.Field(RECORD, OLDCOLUMN) = 1)
in
    ReplaceColumn

// ReplaceColumn
(TABLE as table, OLDCOLUMN as text, NEWCOLUMN as text, CONDITION as function)=>

let
    RESULT = 
Table.RenameColumns(
   Table.RemoveColumns(
         Table.AddColumn(TABLE, "TEMPColumn", each (if CONDITION(_, OLDCOLUMN, NEWCOLUMN) then Record.Field(_, NEWCOLUMN) else Record.Field(_, OLDCOLUMN ))  ),
   {OLDCOLUMN, NEWCOLUMN}
   ),
{"TEMPColumn", OLDCOLUMN })
 
 
in
    RESULT
 
Андрей VG, Простите, но я не вижу никакого удобства в коде.
Почему я просто не могу передать условие? Я ведь хочу написать функцию, чтобы сократить код а не увеличить его.
Нет ли способа все действия сделать в функции а в нее передавать только один маленький параметр с условиями?

Могу ли я просто построчно передать true false?

Если мне нужно будет вычислить разницу между значениями равную 2м, или 4м то странно получается, мне нужно писать новую функцию под CONDITION.
Это как-то нелогично, неудобно, неправильно.
Изменено: lostandleft - 23 Мар 2020 12:57:49
 
Цитата
lostandleft написал:
Почему я просто не могу передать условие?
Какое и как?
Цитата
lostandleft написал:
все действия сделать в функции а в нее передавать только один маленький параметр с условиями?
Что такое параметр с условиями?
Цитата
lostandleft написал:
Могу ли я просто построчно передать true false?
А ваша функция ReplaceColumn разве построчная?
 
Цитата
Андрей VG написал:
Какое и как?
Разное и по разному и в разных случаях различное.
Например сейчас я хочу передать этот вариант с итоговыми занчениями true или false
Код
each([Новый]-[Старый] = 1)
А через 3 минуты захочу передать вот такой вариант
Код
each([Новый]-[Старый] = 4)
А немного позже вот такой
Код
each([Новый] = 6)

И хочу, чтобы каждый раз Колонка TEMPColumn выбирала из Нового столбца только те значения, которые попадают под условие True во всех остальных случаях чтобы выбирались значения из старого столбца.

Неужели я это недостаточно четко пояснил в задаче выше?

Если использовать Ваш вариант выше, то мне необходимо каждый раз писать новую функцию под конкретное условие. Это неправильное решение, нисколько не уменьшающее размер кода а кратно его увеличивающее.
Изменено: lostandleft - 23 Мар 2020 13:19:53
 
lostandleft, непонятно, вы хотели в теме функцию обсудить или какое то решение получить?
Например,
Код
= Table.AddColumn(Источник, "старый", each let a=[Новый],b=[Старый] in if a-b=1 then a else b)[[Префикс],[старый]]
 
Цитата
Виктор А написал:
вы хотели в теме функцию обсудить

Я хотел обсудить функцию, и получить решение на основе функции, хотел чтобы помогли написать ее правильно. с тем, чтобы она выполняла требования обозначенные в первом сообщение темы, а также соответсвовала дополнительным пояснениям, сделанным в 7м сообщении
 
Цитата
lostandleft написал:
А через 3 минуты захочу
Вот вы что хотите
Цитата
lostandleft написал:
каждый раз писать новую функцию под конкретное условие
Условие, хотя бы один раз, но надо прописать
Например, так
Код
let
    Источник = Excel.CurrentWorkbook(){[Name="Таблица1"]}[Content],
    Add = Table.AddColumn(Источник, "старый", each let 
i=Excel.CurrentWorkbook(){[Name="фильтр"]}[Content]{0}[Column1],
l=Excel.CurrentWorkbook(){[Name="фильтр"]}[Content]{0}[Column2],
a=[Новый],b=[Старый] in 
if l="a" then if a-b=i then a else b
         else if a = i then a else b
)[[Префикс],[старый]]
in
    Add
 
lostandleft, вы считаете, что отвечать нужно только на избранные вопросы из заданных вам?
Цитата
Андрей VG написал:
А ваша функция ReplaceColumn разве построчная?
 
Андрей VG, Наверное, у меня иное понимание того, что такое построчность, и вероятно оно не есть корректное.
Поэтому, я не знаю как ответить на Ваш вопрос.
К сожалению, я не воспринимаю легкости в решении предложенном вами ранее, оно мне кажется тяжелее чем более наглядное и простое решение без использования функции как таковой вообще.
Если решения не существует, то давайте закроем на этом тему.
После получения ответа в этой теме, с ее помощью, я хотел научиться делать функции, хотел научиться передавать параметры, хотел понять почему казалось бы логичные вещи, не логичны в синтаксисе PQ совсем. Ничего более. Видимо этому быть не суждено, и нужно искать какие-то другие варианты. Или и дальше писать код методом дровосека.
 
Цитата
lostandleft написал:
простое решение без использования функции
без использования функций - это выражение. Выражение же может быть вычислено только по месту использования. Его нельзя где-то отдельно определить, а потом использовать. Для повторного использования служит функция. Со своей стороны из темы вышел. Рекомендовал бы вам прочитать всё же спецификацию языка.
Цитата
lostandleft написал:
у меня иное понимание того, что такое построчность
Вы уверены, что это называется пониманием? На мой взгляд в вашем случае лучше использовать следующие слова предположение, гипотеза.
 
Поддержу lostandleft,

Для меня, человека воспитанного на FOXPRO, тяжело воспринимать концепцию, заложенную в Power Query и используемый в нём язык m.
Действительно, иногда на этой почве и поэмоционировать хочется!   :D

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

Спасибо.
 
Цитата
quasarrr написал:
тяжело воспринимать концепцию
Концепция простая - это функциональный язык программирования. В нём нет переменных. each что-то задаёт неявную функцию итерации на выражением после each. Но итерация возможно только в рамках контекста функции, содержащую вызов функции. Это такой способ как бы замены явного указания функции на использование выражения.
Код
// определяем функцию используемую в функции, в которой есть итератор над элементами
fn = (x) => Record.FieldCount(x),
// в данном случае AddColumn выполняет итерацию по строкам (записям record) и ждёт функцию с одним аргументом типа record
result1 = Table.AddColumn(Source, "field count", fn),
// а можно и неявной функцией итерации у которой аргумент _
result2 = Table.AddColumn(Source, "field count", each Record.FieldCount(_)),
// а можно указать функцию анонимно без определения имени
result3 = Table.AddColumn(Source, "field count", (x) => Record.FieldCount(x))

По спецификации
Цитата

9.7 Simplified declarations
The each-expression is a syntactic shortand for declaring untyped functions taking a single
formal parameter named _ (underscore).
each-expression:
each each-expression-body
each-expression-body:
function-body
Simplified declarations are commonly used to improve the readability of higher-order
function invocation.
For example, the following pairs of declarations are semantically equivalent:
93
each _ + 1
(_) => _ + 1
each [A]
(_) => _[A]
Table.SelectRows( aTable, each [Weight] > 12 )
Table.SelectRows( aTable, (_) => _[Weight] > 12 )

P. S. Для SQLистов (имеется в виду пишущих Select запросы) и формулистов не должен из себя представлять сложность в освоении. Подход концептуально ровно тот же.
Изменено: Андрей VG - 23 Мар 2020 15:11:40
 
Андрей VG,
Как я понял, в общем и в целом, обозначенная мной задача в тех рамках, которые я ставлю и хотел бы видеть - нерешаемая и точка.

Невозможно передать условие, или результат от вычислений, или само вычисление как параметр в функцию, в частности невозможно передать true или falce.

К сожалению, никакой универсальности в пользовательских функциях сделать не получится.

Спасибо, извините за отнятое время.

Если Вас не затруднит, не могли бы Вы пояснить что означет прочерк в
Код
 Record.Field(_, NEWCOLUMN)
И почему без использования Record не работает вообще ничего выдавая огромное число ошибок.
Изменено: lostandleft - 23 Мар 2020 15:15:30
 
Андрей VG, спасибо!

Уважаемые метры Power Query!

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

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

Если возражений знатоков не будет (или, скажем, такие попытки уже делались и соответствующая тема где-то есть на просторах форума) сегодня - завтра создам отдельную тему.
Изменено: quasarrr - 23 Мар 2020 15:22:36
 
Цитата
lostandleft написал:
Record.Field(_, NEWCOLUMN)
Ровно то, что написано в процитированной выше спецификации.
_ - это аргумент функции (_) => .
Для начала
Код
let 
  var = 123,
  test = var = "var" // вы уверены, что имя определения var равно его текстовому написанию?
in
  test

Код
NEWCOLUMN = "FieldName", // это текстовое имя поля, а не его представление на уровне определений
test = Table.AddColumn(Source, (_) => Record.Field(_, NEWCOLUMN) + 3) // прибавляем значению поля, получаемому по его текстовому имени, текущей строки, задаваемой _, где значение поля извлекается методом Record.Field: первый аргумент определение записи, второй аргумент текстовое имя поля
Цитата
quasarrr написал:
окажется Вам чрезмерной
Увы покажется. Даже Максим Зеленский фактически забросил свои публикации на своём сайте. Я, по сравнению с ним, сверх лентяй и предпочитаю отвечать на конкретные вопросы.
 
Цитата
Андрей VG написал:
задача в тех рамках, которые я ставлю и хотел бы видеть - нерешаемая и точка.Невозможно передать условие, или результат от вычислений, или само вычисление как параметр в функцию, в частности невозможно передать true или falce.
вы не правы
Цитата
lostandleft написал:
Почему я просто не могу передать условие? Я ведь хочу написать функцию, чтобы сократить код а не увеличить его.Нет ли способа все действия сделать в функции а в нее передавать только один маленький параметр с условиями?
Как должен выглядеть параметр с условиями? В каком виде вы хотите задавать условие? Ведь оно у вас, в соотвествии с #7, вообще может быть любое. Вы с этим определитесь, пожалуйста, а то у вас ни число аргументов функции, ни ее условие не определены, но она должна делать то, что она заранее не знает.
F1 творит чудеса
 
В общем, вот вам универсальная функция, и не стоит так эмоционировать :)
Условие задается так же, как если бы вы писали формулу доп.столбца, т.е. имена столбцов в квадратных скобках

Код
// НовыйСтарый2
let
    Источник = Excel.CurrentWorkbook(){[Name="Таблица1"]}[Content],
    ИзмененТип = Table.TransformColumnTypes(Источник,{{"Старый", Int64.Type}, {"Новый", Int64.Type}}),
    Custom1 = myFn2(ИзмененТип, "Старый", "Новый", "[Новый]-[Старый]=1")
in
    Custom1

// myFn2
(
    TABLE as table, 
    Old as text, 
    New as text, 
    condition as text // "[Column1]-[Column2]>1", "[Column1]-[Column2]=3", "[Column2]=6"
) => 
        Table.RenameColumns(
            Table.RemoveColumns(
                Table.AddColumn(
                    TABLE, 
                    "TMPcol", 
                    each if Expression.Evaluate(condition, #shared & [_=_]) then Record.Field(_, New) else Record.Field(_, Old)), 
                {Old, New}), 
            {{"TMPcol", Old}})
F1 творит чудеса
 
quasarrr,  так вот же тема
Там и про each и про подчёркивания все расписано, надо только по-копать чутка. ;) Малость сумбурно, конечно, зато бесплатно.
Изменено: PooHkrd - 23 Мар 2020 21:33:33
Вот горшок пустой, он предмет простой...
 
Цитата
Максим Зеленский написал:
В общем, вот вам универсальная функция, и не стоит так эмоционировать
Максим, оно конечно же не плохо, только вот скорость... 20000 строк в секунду на миллионе сколько будет? А явное указание функции - скорость 4 секунды всего - и большая часть время вывода на лист :)
 
Андрей VG, бесспорно. Но тут же человек хотел хотелку. Можно? Можно. Нужно? Нет
Нам так и не сказали, в каком виде хотят передать условие.
Чем не устраивает передача безымянной функции сравнения я тоже не понял.
F1 творит чудеса
 
Максим Зеленский, честно говоря, я вообще не понял хотелку ТС, делать функцию ради функции. Как по мне функция нужна в двух случаях:
1. В разных местах кода требуется один и тот же обработчик - выносим его в функцию чтобы сократить код.
2. Нужно в тот же Table.ReplaceValue или в Table.Group вместо стандартного обработчика всунуть свой собственный.
Вот горшок пустой, он предмет простой...
 
Господа всем спасибо за участие!
Попробую ответить на некоторые вопросы:
Цитата
Максим Зеленский написал:
Чем не устраивает передача безымянной функции сравнения я тоже не понял.
Размером кода.
Согласитесь вариант:
Код
ReplaceColumn = ReplaceColumn(Источник,"Старый", "Новый", (RECORD as record, OLDCOLUMN as text, NEWCOLUMN as text) as logical => Record.Field(RECORD, NEWCOLUMN) - Record.Field(RECORD, OLDCOLUMN) = 1)
 Немного более масштабный, и несколько менее удобый чем:  
Код
Custom1 = myFn2(ИзмененТип, "Старый", "Новый", "[Новый]-[Старый]=1")

Цитата
Максим Зеленский написал:
Нам так и не сказали, в каком виде хотят передать условие.
Условие хотелось передать в том виде, в котором оно обозначение в сообщение №7, просто передать условие true false. Разумеется условие могли быть разные, об этом и написано в сообщение №7.
Функцию замены столбца хотелось написать на все случаи жизни. Для самых разных участков кода, третьим параметром просто передавать True False как результат от вычислений (совершенно не обязательно связанных с конкретными столбцами), и передавать 2 колонки старую и новую.

Для чего была создана тема - В целом для того, чтобы научиться строить функции, понять как они работают, научиться передавать параметры.
Цитата
PooHkrd написал:
1. В разных местах кода требуется один и тот же обработчик - выносим его в функцию чтобы сократить код.
Именно так и есть!!!
Изменено: lostandleft - 24 Мар 2020 11:31:50
 
Цитата
lostandleft написал:
Немного более масштабный, и несколько менее удобый чем:  
краткость кода <> правильность и быстродействие.
Цитата
lostandleft написал:
просто передать условие true false
простите, у вас каша.
Цитата
lostandleft написал:
True False как результат от вычислений (совершенно не обязательно связанных с конкретными столбцами),
Каких вычислений? Не бывает абстрактных "вычислений", то есть вычислений "вообще". Есть конкретные вычисления. Эти конкретные вычисления должны быть где-то прописаны. Сравнение чего-то с чем-то (типа [Новый]=6) тоже вычисление. Но вы не хотите его прописывать - вы хотите передать результат - true/false, но он откуда берется?
Цитата
lostandleft написал:
Цитата
PooHkrd написал:
В разных местах кода требуется один и тот же обработчик - выносим его в функцию чтобы сократить код.
Именно так и есть!!!
Так в том то все и дело, что у вас обработчик не один и тот же, а разный! Как должен выглядеть обработчик неизвестного условия? Сейчас у вас это А-Б=1, затем это Б>5, потом у вас это "Б начинается с А и заканчивается В". Это совершенно разная проверка, разный код обработчика. Серьезно, даже странно это объяснять.

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

Код, который привел Андрей VG в #4, как раз и позволяет сделать так, чтобы в вашу функцию замены столбца передавался тот обработчик, который нужен в конкретном случае. Но сделать быстрый обработчик, который будет еще и угадывать мысли - ну нет.

Разобраться с функциями и конкретно этим кейсом - не сложно. Но для начала вам надо разобраться с работой встроенных функций.

подробное объяснение под катом

F1 творит чудеса
 
Максим Зеленский, Огромное спасибо за комментарии!
Столько времени потратили, спасибо!
Более-менее стало ясно!
Страницы: 1
Читают тему (гостей: 2)
Наверх