Страницы: 1 2 След.
RSS
СУММЕСЛИМН в Power Query
 
Добрый день!

Буду благодарен за помощь в упрощении файла.

Есть две таблицы: 1) Операции по сделке с активами в определенную дату 2) Цена на активы в разрезе дат
Задача: Расчет стоимости текущих активов в разрезе дат через Power Query

На текущий момент приходится использовать третью таблицу, в которой через СУММЕСЛИМН определяются текущие активы в разрезе дат, а уже затем в PQ идет слияние запросов Таблица2 (Цены) + Таблица3 (Количество), чтобы получить стоимость.

Ввиду того что активов много, равно как и дат, которых будет становится все больше, хочу уйти от слияния Цена+Количество и перейти к слиянию Цена+Сделки, чтобы количество рассчитывалось в PQ, без дополнительной нагрузки файла Таблицей3.

Заранее спасибо! Предполагаю, что эту операцию можно провернуть формулами PQ M, но знаний пока не хватает. Думал использовать Группировку в PQ, но пока тоже не получилось
Изменено: summon - 29.03.2017 13:10:28
 
Я бв посоветовал такие расчеты всё же делать в DAX
Код
// Prices
let
    Источник = Excel.CurrentWorkbook(){[Name="Таблица2"]}[Content],
    #"Измененный тип" = Table.TransformColumnTypes(Источник,{{"Дата", type date}, {"Актив1", Int64.Type}, {"Актив2", Int64.Type}}),
    #"Другие столбцы с отмененным свертыванием" = Table.UnpivotOtherColumns(#"Измененный тип", {"Дата"}, "Атрибут", "Значение"),
    #"Переименованные столбцы" = Table.RenameColumns(#"Другие столбцы с отмененным свертыванием",{{"Атрибут", "Актив"}, {"Значение", "Цена"}})
in
    #"Переименованные столбцы"

// Deals
let
    Источник = Excel.CurrentWorkbook(){[Name="Таблица1"]}[Content],
    #"Измененный тип" = Table.TransformColumnTypes(Источник,{{"Дата", type date}, {"Актив", type text}, {"Вид операции", type text}, {"Количество", Int64.Type}}),
    #"Added Custom" = Table.AddColumn(#"Измененный тип", "Движение", each if [Вид операции]="Продажа" then -1*[Количество] else [Количество], type number)
in
    #"Added Custom"

// Assets
let
    #"Merged Queries" = Table.NestedJoin(Prices,{"Дата", "Актив"},Deals,{"Дата", "Актив"},"NewColumn",JoinKind.LeftOuter),
    Exp = Table.ExpandTableColumn(#"Merged Queries", "NewColumn", {"Движение"}, {"Движение"}),
    #"Added Custom" = Table.AddColumn(Exp, "Количество", each List.Sum(Table.SelectRows(Exp, (r)=>r[Актив]=[Актив] and r[Дата]<=[Дата])[Движение]), type number),
    #"Removed Columns" = Table.RemoveColumns(#"Added Custom",{"Движение"}),
    #"Filtered Rows" = Table.SelectRows(#"Removed Columns", each ([Количество] <> null and [Количество] <> 0)),
    #"Added Custom1" = Table.AddColumn(#"Filtered Rows", "Сумма", each [Цена]*[Количество], type number)
in
    #"Added Custom1"
F1 творит чудеса
 
Цитата
Максим Зеленский написал:
Я бв посоветовал такие расчеты всё же делать в DAX
Максим, это прекрасное решение. Буду изучать формулы List.Sum и Table.SelectRows, спасибо большое!
Почему вы советуете использовать DAX? Я так полюбил PQ, все расчеты там стараюсь делать в фоновом режиме, мне кажется компьютер меньше грузит. Для данного конкретного примера, я потом стоимость активов сливаю уже в одну базу со всеми другими показателями, а уже из этой одной единой базы строю всю аналитику, включая DAX.
 
summon, потому что механизм вот этого расчета
Код
List.Sum(Table.SelectRows(Exp, (r)=>r[Актив]=[Актив] and r[Дата]<=[Дата])[Движение])

в DAX сработает быстрее, даже в виде доп.столбца (это мое субъективное мнение, нужно сравнивать быстродействие разных вариантов).
Всё, конечно, зависит от задач и дальнейших действий с данными. Может быть, нужен именно столбец, а может быть, можно обойтись мерами, не нагружая оперативку хранением доп.столбцов.
F1 творит чудеса
 
Цитата
Максим Зеленский написал:
Всё, конечно, зависит от задач и дальнейших действий с данными.
Чисто из спортивного интереса, если в таблице цен, даты будут фиксироваться не каждый день, а только в момент изменения цены. Что поменять в коде?
Excel + SQL = Activetables
 
PowerBoy, если нужна стоимость на каждый день, то к таблице дат (полной) нужно сджоинить имеющуюся таблицу цен, заполнить пустые цены вниз, дальше как в примере
F1 творит чудеса
 
Цитата
Максим Зеленский написал:
заполнить пустые цены вниз
Решение не очень, просто я представил как бы я такую задачу решал SQL запросами, и у меня получается там вложенный запрос по максимальной дате сделки, чтобы получить нужную цену, вот и подумал, а насколько вообще гибок язык DAX или Query M, могут ли они эмулировать сложные вложенные запросы и соединения между таблицами.
Excel + SQL = Activetables
 
Можно и так, конечно, даже лучше, наверное. Инструмент налагает свои привычки, да и отвечал в духе "что изменить", а не "как сделать с нуля".
Только принципиальной разницы нет - если в учете хранятся только движения активов, расчет ежедневных остатков все равно делается как сумма начального остатка и сальдо предыдущих движений.
Если начальный остаток 0, то просто сальдо движений.
Может быть, не до конца понимаю ваш прием, но даже если подтянем цену движения и посчитаем его величину в деньгах, все равно придется считать ежедневное сальдо.
Если движений немного, то проще, конечно, сделать сначала расчет конечного остатка только на даты движений, затем подтянуть к нему последние цены и посчитать стоимость, затем заполнить вниз.
Предварительно сгруппировать по активам. И так далее. Но насколько это экономнее и будет ли - не знаю. Здесь речь не об инструменте, а об алгоритме, и, кстати, вы меня натолкнули на хорошую мысль, спасибо :)

Вложенные запросы и связи (в том числе эмулированные) - основа DAX.
PQ в меньшей степени, но тоже можно сказать, что это его хлеб. Пока не знаю, что из того, что есть в SQL, нет в PQ. Легче сказать, чего нет в SQL, хотя я не большой его знаток.
F1 творит чудеса
 
Вот примерный запрос решения, правда он на 1с, но так даже понятней в нем разобраться, обратите внимание на связку таблиц где в соединении есть вложенный запрос, возможно такое в DAX?
Код
ВЫБРАТЬ
    Продажи.Период,
    Продажи.Контрагент,
    Продажи.Номенклатура,
    Продажи.КоличествоОборот КАК Количество,
    Продажи.СтоимостьОборот КАК Стоимость,
    ЦеныНоменклатуры.Цена
ИЗ
    РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, День, ) КАК Продажи
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
        ПО Продажи.Номенклатура = ЦеныНоменклатуры.Номенклатура
            И (ЦеныНоменклатуры.ТипЦен = &ТипЦен)
            И (ЦеныНоменклатуры.Период В
                (ВЫБРАТЬ МАКСИМУМ(Цены.Период)
                ИЗ
                    РегистрСведений.ЦеныНоменклатуры КАК Цены
                ГДЕ
                    Цены.Период <= Продажи.Период
                    И Цены.Номенклатура = Продажи.Номенклатура
                    И Цены.ТипЦен = &ТипЦен
))


Excel + SQL = Activetables
 
Доброе время суток.
Цитата
PowerBoy написал:
у меня получается там вложенный запрос по максимальной дате сделки, чтобы получить нужную цену,
И здесь тоже самое :)
Успехов.
 
PowerBoy, Андрей VG, бывают же совпадения. В моем случае цена актива тоже есть не на каждый день, а стоимость хочется видеть на каждый календарный, в динамике, без провалов. Сейчас выхожу из ситуации через доп. таблицу, но теперь и здесь сделаю через PQ, спасибо Андрей!
 
Можно даже ещё проще (что-то я перемудрил)
Код
Table.MaxN(workHistory, each _[Дата] <= #date(2016, 11, 11) and _[ИмяАктива] = "Актив1", 1){0}[Цена]
 
Если я правильно понял, здесь вы подтягиваете цену, действующую на момент продажи, к продаже в разрезе периода и номенклатуры. Но это задача попроще.
В DAX будет так (создаем таблицу с дополнительным столбцом):
Код
ADDCOLUMNS (
    Sales,
    "Цена", CALCULATE (
        VALUES ( Price[Цена] ),
        FILTER ( Price, Price[Код] = EARLIER ( Sales[Код] ) && Price[ТипЦен] = "ТЦ" ),
        CALCULATETABLE (
            LASTDATE ( Price[Период] ),
            FILTER ( Price, Price[Период] <= EARLIER ( Sales[Период] ) )
        )
    )
)


Для меры формула будет другая, но похожая
F1 творит чудеса
 
Цитата
Максим Зеленский написал:
В DAX будет так (создаем таблицу с дополнительным столбцом):
Максим, всё же думается, что это как раз работа для Power Query или SQL - постявлять нормальные данные для DAX, а не городить в нём бесконечный набор формул. Если посмотреть базы примеров для изучения DAX от Microsoft так это дело так и организовано. Все FactSales таблицы уже содержат стоимость товара в себе на каждую дату продажи. Хотя если посмотреть на OLDT версию, то там это вроде как разнесено.
 
Цитата
Андрей VG написал:
Все FactSales таблицы уже содержат стоимость товара в себе на каждую дату продажи.
Всё же стоимость продажи и прайсовая цена - разные вещи. Прайсовая цена часто не включается в таблицы типа "Продажи", особенно в 1С. Да и это может быть не совсем цена, а какой-то другой признак, чья история фиксируется на момент изменения. Slowly Changing Dimension.
Изменено: Максим Зеленский - 30.03.2017 16:55:02
F1 творит чудеса
 
Максим, вы оказались правы. Все зависает на попытке обработать 32к+ строк, при этом в дальнейшем количество строк будет только увеличиваться.
Объясню, что я пытаюсь вообще сделать.
Есть список сделок с акциями, покупка/продажа. Я хочу на основании этого списка сделок отслеживать изменение стоимости портфеля в периоде.
Для этого я беру все тикеры (наименования акций, по которым были сделки) и период с момента первой сделки до сегодняшнего дня.
На сегодня получается 38 тикеров * 845 дней = 32 110 строк. К этому я уже пытаюсь подтянуть количество, затем удалить <=0 и подтягиваю цену.
 
summon, итого, есть таблица движений активов.
Стоимость портфеля определяется не по ценам покупки, а по ценам на рынке, так? соответственно у вас таблица цен должна быть 32 110 строк, правильно? Или она с дырками?
F1 творит чудеса
 
Максим Зеленский, все правильно. Есть таблица цен, она с дырками (выходные дни биржи). Пока я пришел к тому, что нужно через DAX закрыть эти дырки в ценах, а потом количество получить простой суммой движения с нарастающим итогом. Затем уже эту сводную я переведу в таблицу и буду дальше вертеть ей в PQ.

То есть на текущий момент остается закрыть эти дырки в ценах, изучаю LASTNONBLANK и EARLIER  :)
Изменено: summon - 31.03.2017 14:42:06
 
Сделайте две таблицы подстановки - полный календарь дат и уникальные тикеры. Свяжите таблицу цен и таблицу движений с ними. Это значительно облегчит жизнь в DAX
F1 творит чудеса
 
Максим Зеленский, я так и сделал, но в PQ, потом подтянул к слитым полным таблицам дат+тикеров движение и имеющиеся цены. Движение преобразую в баланс за счет суммы нарастающим итогом, а с пропусками в ценах пока не разобрался.
 
Максим Зеленский, создал пример. Простите, не смог уложиться в 100кб.
https://www.dropbox.com/s/suqn5ivnpizg20j/example2.xlsx?dl=0
 
summon, вы бы ещё показали, что хотите в конечном итоге получить.
Изменено: Андрей VG - 01.04.2017 13:22:35
 
Андрей VG, Получить хочется ровно тоже, что вы уже сделали в PQ для PowerBoy. Есть цены актива в определенные дни, задача - на дни, в которых цена актива не указана подставить предыдущее значение. Просто как я понимаю в PQ идет построчная обработка, и мои 32к строк обрабатываются бесконечно. Теперь пытаюсь найти решение в DAX, чтобы понять, что получится, ну и заодно научиться.

EDIT: Или я просто не понял, как правильно применить формулу из #12, чтобы PQ не зависал
Изменено: summon - 01.04.2017 14:57:51
 
Цитата
summon написал:
Получить хочется ровно тоже, что вы уже сделали в PQ для PowerBoy.
В вашем случае можно попроще (там более общий случай - даты подбора могут и не совпадать с датами поиска). 32000 у меня не получилось взять, только 2000 - но, думаю, основные траты на загрузку, а не на обработку. Буферизация помогает.
Успехов.
 
Андрей VG, спасибо огромное за оптимизацию кода! Я правильно понимаю, что для решения задачи, мне всего лишь нужно было изучить формулу Table.FillDown?
 
Цитата
summon написал:
мне всего лишь нужно было изучить формулу Table.FillDown?
Приём с FillDown лишь часть алгоритма и частный случай. Подумайте, что было бы, если бы в календаре не было бы части дат, в которые есть в загружаемых с yahoo данных?
Вариант по #12 - да, несколько медленнее, но зато работает если будут пропуски в данных календаря.
 
Цитата
summon написал: задача - на дни, в которых цена актива не указана подставить предыдущее значение.
??
Цитата
summon написал: Пока я пришел к тому, что нужно через DAX закрыть эти дырки в ценах, а потом количество получить простой суммой движения с нарастающим итогом.
дырки в датах... но цены-то не меняются в эти выходные, праздники... так и считать даты по нарастающей и смотреть последнюю имеющуюся... зачем городить огород с неторгуемыми днями? (мне показалось, вы усложняете себе жизнь - пытаясь включать в расчёты фиктивные(!) торговые дни)
чтобы не гадать на кофейной гуще, кто вам отвечает и после этого не совершать кучу ошибок - обратитесь к собеседнику на ВЫ - ответ на ваш вопрос получите - а остальное вас не касается (п.п.п. на форумах)
 
JeyCi, спасибо за замечание, но задача у меня комплексная, а не только в рамках озвученного. Существуют и другие активы, которые могут измениться в стоимости в эти пропущенные дни, соответственно при взгляде на картину в общем возникли бы провалы.

Простой пример: выходной день на биржах США не означает выходной день на биржах СНГ и наоборот.
Изменено: summon - 03.04.2017 10:58:37
 
Андрей VG, правильно понимаю, что добавление ключа вот здесь:
Код
dataTable = Table.Buffer(Table.AddKey(typed, {"Date"}, true)),

индексирует таблицу цен по датам и ускоряет сравнение дат на следующем шаге?
F1 творит чудеса
 
Доброе время суток.
Максим, Buffer точно ускоряет обработку, так как заставляет "ленивые" вычисления последующего кода начинать от буфферезированной таблицы, а вот по по поводу AddKey - честно говоря, не скажу. Надо бы поэксперементировать. В инете по поводу Table.AddKey ничего собственно не находится :(  Наличие же ключа, пусть даже первичного, не обязательно ведёт к индексированию. То что PQ делает временные индексы подобно Access для join - это точно. А вот создаёт ли индекс AddKey...
Здесь просто добавил - а вдруг? Пить есть не просит :)
Хотя эта версия работает медленнее, чем по join с календарём и последующим FillDown.
Страницы: 1 2 След.
Наверх