Страницы: 1
RSS
Power Query. Брать в дате только определённые дни и по ним составить список, есть дата начала и конца,
 
Здравствуйте. В запросах есть очень полезная функция
Код
List.Dates(#date(2011, 12, 31), 5, #duration(1, 0, 0, 0))

Она позволяет создать список кратный одному дню от начала даты.
В моём случае нужно сделать аналогичное, но даты должны будут выбраны такие, которые соответствуют определённому дню недели.
Вот и думаю, как можно причесать данный запрос? Может разложить список от начала и конца даты, затем определить день недели и потом по условию удалить ненужные дни. Подскажите, как бы поаккуратнее так сделать?
Спасибо.
Изменено: AlexMakarov - 05.02.2020 00:23:09
 
Можно так:
Код
let
    Source = Excel.CurrentWorkbook(){[Name="Таблица1"]}[Content],
    #"Changed Type" = Table.TransformColumnTypes(Source,{{"Начало", type date}, {"Окончание", type date}, {"По каким дням недели", type text}, {"Номер", type text}}),
    #"Added Custom" = Table.AddColumn(#"Changed Type", "Список дат", each 
        [ListDates = List.Dates( [Начало], Number.From( [Окончание] - [Начало] ) + 1, #duration(1, 0, 0, 0) ),
         ListDays = List.Transform( Text.ToList([По каким дням недели]), Number.From ),
         Out = List.Select( ListDates, (x) => List.Contains( ListDays, Date.DayOfWeek( x ) + 1 ) )][Out]
    )
in
    #"Added Custom"
Вот горшок пустой, он предмет простой...
 
Доброе время суток
Цитата
PooHkrd написал:
Можно так:
Ну, это реализация
Цитата
AlexMakarov написал:
может разложить список от начала и конца даты, затем определить день недели и потом по условию удалить ненужные дни.
А генерацию по заданным дням недели? Или, учитывая входной массив, скорее всего явно пересекающийся по датам, то генерация календаря, а затем связывание по дням недели и ограничением по диапазону дат?
Цитата
AlexMakarov написал:
Подскажите, как бы поаккуратнее так сделать?
Увы, единственно верного учения для этого класса задач нет. В каких-то случаях так, в каких-то этак или ещё как-то. Сильно зависит от структуры данных и объёма генерации. Если у вас сотня другая таких строк, то без разницы в общем-то. Вам очень важно, что один алгоритм решает за 0,1 секунды, а другой 0,2 секунды?
 
Цитата
Андрей VG написал:
А генерацию по заданным дням недели?
побаловался:
Код
let
    Source = Excel.CurrentWorkbook(){[Name="Таблица1"]}[Content],
    #"Changed Type" = Table.TransformColumnTypes(Source,{{"Начало", type date}, {"Окончание", type date}, {"По каким дням недели", type text}, {"Номер", type text}}),

    #"Added Custom" = Table.AddColumn(#"Changed Type", "list", each
        let
            start = [Начало],
            finish = [Окончание],
            startWday = Date.DayOfWeek([Начало], Day.Monday)+1,
            weekDays = List.Transform(Text.ToList([По каким дням недели]), Number.From)
        in
            List.Sort(
                List.Combine(
                    List.Transform(
                        weekDays,
                        (currentWday)=> 
                            let 
                                startShift = currentWday - startWday + (if currentWday > startWday then 0 else 7),
                                first = start + #duration(startShift, 0, 0, 0)

                            in 
                                List.Generate(
                                    ()=>[day=first],
                                    each [day]<=finish,
                                    each [day=[day]+#duration(7,0,0,0)],
                                    each [day]
                                    )
                        )
                )
            )
        )
in
    #"Added Custom"

но как по мне, так овчинка не будет стоить выделки тут
F1 творит чудеса
 
Цитата
Максим Зеленский написал:
так овчинка не будет стоить выделки тут
Добрый день, Максим.
Ну, почему же нет? Размножил исходник до 64к строк. Вывод 557к строк. Алексея - 8 секунд, ваш - 5 секунд. Выигрыш есть.
 
И я решил мальца заморочиться, и вот такое сочинил:
Код
let
    Source = Excel.CurrentWorkbook(){[Name="Таблица1"]}[Content],
    #"Changed Type" = Table.TransformColumnTypes(Source,{{"Начало", type date}, {"Окончание", type date}, {"По каким дням недели", type text}, {"Номер", type text}}),
    #"Added Custom1" = Table.AddColumn(#"Changed Type", "Пользовательская", each 
        [ListDays = List.Buffer( List.Transform( Text.ToList([По каким дням недели]), Number.From ) ),
         Start = [Начало],
         Finish = [Окончание],
         StartWeekDay = Date.DayOfWeek( Start, Day.Monday) + 1,
         RealStart = try List.Select( ListDays, each _ >= StartWeekDay ){0} otherwise ListDays{0},
         DaysCount = List.Count( ListDays ),
         Calc = List.Generate(
            () => [id = List.PositionOf( ListDays, RealStart ), runningDate = Start + #duration(7 * Number.From( RealStart < StartWeekDay ) + RealStart - StartWeekDay, 0, 0, 0)],
            each [runningDate] <= Finish,
            each [id = Number.Mod( [id] + 1, DaysCount ), runningDate = [runningDate] + #duration( 7 * Number.From( id <= [id] ) + ListDays{id} - ListDays{[id]}, 0, 0, 0)],
            each [runningDate] )][Calc]    ),
    #"Expanded {0}" = Table.ExpandListColumn(#"Added Custom1", "Пользовательская")
in
    #"Expanded {0}"
Изменено: PooHkrd - 18.02.2020 11:34:11
Вот горшок пустой, он предмет простой...
 
Ещё вариант:
Код
let
    Source = Excel.CurrentWorkbook(){[Name="Таблица1"]}[Content],
    types = Table.TransformColumnTypes(Source,{{"Начало", type date}, {"Окончание", type date}, {"По каким дням недели", type text}, {"Номер", type text}}),
    add = Table.AddColumn(types, "list", each let a = List.Transform(Text.ToList([По каким дням недели]),Number.From)
                                               in List.RemoveNulls(
                                                                    List.TransformMany(List.Dates([Начало],Number.From([Окончание]-[Начало])+1,#duration(1,0,0,0)),
                                                                                       (x)=>{Date.DayOfWeek(x)},
                                                                                       (x,y)=>if List.Contains(a,y+1) then x else null)))
in
    add
Изменено: Aleksei_Zhigulin - 05.02.2020 18:37:33
 
Коллеги, всем спасибо, всё получилось. PQ только начал осваивать, и вижу, что отстал в этом вопросу. Подскажите, Вы код писали, сперва изучив архитектуру "M"? Просто интересно.
 
Цитата
AlexMakarov написал:
Вы код писали, сперва изучив архитектуру "M"?
Только первый вариант, второй запрос это кот по клаве пришёлся и вот такое получилось.  :D
Вот горшок пустой, он предмет простой...
 
Aleksei_Zhigulin,
Цитата
PooHkrd написал:
Ещё вариант:
Вот Ваш вариант классный. У меня вопрос, а обязательно создавать таблицу? Попробовал этот запрос на "подключении" и ошибка в синтаксисе.
 
Погонял тесты и вот что получилось:

Вывод: 2 варианта с генераторами требуют доработки напильником (свой запрос я уже подправил, он в некоторых случаях не выходил из цикла, но количество строк все равно не верное), но какой-то явной критики по быстродействию все равно нет. Промежутки между датами генерил рандомом, 0-50 дней, если дней делать больше, то количество строк при развороте заваливало за 1кк.
Изменено: PooHkrd - 06.02.2020 09:27:46
Вот горшок пустой, он предмет простой...
 
Алексей, сможете протестировать ещё такой вариант?
Код
let
    Source = Excel.CurrentWorkbook(){[Name="Таблица1"]}[Content],
    typed = Table.TransformColumnTypes(Source, {{"Начало", Date.Type}, {"Окончание", Date.Type}, {"По каким дням недели", Text.Type}, {"Номер", Text.Type}}),
    calc = Table.AddColumn(typed, "temp", (rec) =>
    let
        intDates = List.Transform(Text.ToList(rec[#"По каким дням недели"]), (textNum) =>
        let
            weekNum = Number.From(textNum),
            startWeekNum = Date.DayOfWeek(rec[Начало], Day.Monday) + 1,
            startDate = rec[Начало] + #duration(weekNum - startWeekNum + (if startWeekNum > weekNum then 7 else 0), 0, 0, 0),
            count = Number.IntegerDivide(Duration.TotalDays(rec[Окончание] - startDate), 7) + 1
        in
            List.Dates(startDate, count, #duration(7, 0, 0, 0))
        )
    in
        List.Combine(intDates)
    ),
    #"Expanded {0}" = Table.ExpandListColumn(calc, "temp"),
    #"Changed Type" = Table.TransformColumnTypes(#"Expanded {0}",{{"temp", type date}})
in
    #"Changed Type"
 
Цитата
Андрей VG написал:
Алексея - 8 секунд, ваш - 5 секунд
Цитата
PooHkrd написал:
Погонял тесты и вот что получилось:
Добрый день! Подскажите как вы замеряете время выполнения запроса?
 
Легко. Вы чемпион как по скорости, так и по количеству выводимых строк.  ;)
Вот горшок пустой, он предмет простой...
 
Цитата
PooHkrd написал:
как вы замеряете время выполнения запроса?
Простым макросом
Код
Public Sub testSpeed()
    Dim pLo As ListObject, t As Single
    t = Timer
    Set pLo = ActiveSheet.ListObjects(1)
    pLo.QueryTable.Refresh False
    MsgBox CStr(Timer - t)
End Sub
Предполагается, что умная таблица результата запроса на активном листе единственная.
Цитата
PooHkrd написал:
так и по количеству выводимых строк.
Правильность выполнения ограничений не проверял - ТС не позаботился представить пример с учётом слева/справа. Всё как всегда - это обязанность отвечающих учитывать все нюансы. :)
Изменено: Андрей VG - 06.02.2020 09:51:04
 
Murderface_, секундомером. с момента нажатия кнопки и до полной выгрузки на лист.
Андрей VG, проверяйте ваш запрос на таких данных, здесь косяк:

на данном периоде список должен быть пустой, а вас выводятся значения выходящие за дату окончания.
Цитата
Андрей VG написал:
Простым макросом
Так, объясните нулю в ВБА, куда этот макрос копировать, и как он активируется? сам при обновлении запроса? Или надо еще что-то нажимать?
Изменено: PooHkrd - 06.02.2020 09:53:31
Вот горшок пустой, он предмет простой...
 
Цитата
PooHkrd написал:
Так, объясните нулю в ВБА, куда этот макрос копировать, и как он активируется? сам при обновлении запроса? Или надо еще что-то нажимать?
Так, одна тема - один вопрос!  8)  :D

PS
В книгу макросов, при запуске он сам вкл. "Умную таблицу", а по завершении - сообщает время выполнения. Ну, как и с секундомером, в общем :)
Всё сложное - не нужно. Всё нужное - просто /М. Т. Калашников/
 
Михаил Лебедев, спасибо.
Вот горшок пустой, он предмет простой...
 
Цитата
PooHkrd написал:
список должен быть пустой
Хе, хе - некорректная постановка интервалов, однако. Следовательно вопрос к адекватности постановщика. Смысл создавать такое? Может ещё отслеживать случай, когда начало больше окончания? :)
Изменено: Андрей VG - 06.02.2020 10:40:17
 
Андрей VG, я вообще неадекват.   :D
Тем не менее, т.к. генерил рандомайзером, то попадались и такие варианты.
Изменено: PooHkrd - 06.02.2020 10:43:47
Вот горшок пустой, он предмет простой...
 
Цитата
AlexMakarov написал:
У меня вопрос, а обязательно создавать таблицу? Попробовал этот запрос на "подключении" и ошибка в синтаксисе.
Нет, не обязательно. Если у Вас ошибка в синтаксисе, вероятнее всего, одно из двух: либо Ваша таблица / столбцы называются как-то иначе, либо Вы не в полном объёме вставили код в расширенный редактор.
Изменено: Aleksei_Zhigulin - 06.02.2020 17:02:49
 
Странно, фактически все отметились, только тёзка мимо прошёл.... :(
 
были мозги всякой фигней забиты, сейчас про эту тему вспомнил, еле нашел
как-то так получилось
Код
let
    Source = 
        Excel.CurrentWorkbook(){[Name="Таблица1"]}[Content],
    ExactDates = 
        Table.AddColumn(
            Source, 
            "Даты", 
            each let 
                a = Date.StartOfWeek([Начало]),
                b = [Окончание],
                c = List.Buffer(List.Transform(Text.ToList(Text.From([По каким дням недели])),Number.From)),
                d = List.Count(c) 
            in 
                List.Skip(
                    List.Generate(
                        ()=> [  i=1,
                                e=Number.Mod(i-1,d),
                                f=a,
                                g=Date.AddDays(f,c{0}-1) ],
                        each [g]<=b,
                        each [  i = [i]+1,
                                e = Number.Mod(i-1,d),
                                f = if e<=[e] then Date.AddDays([f],7) else [f],
                                g = Date.AddDays(f,c{e}-1) ],
                        each [g]
                    ),
                    (h)=>h<[Начало])
                ),
    Expand =
        Table.TransformColumnTypes(
            Table.ExpandListColumn(ExactDates,"Даты"),
            {{"Даты", type date}}
        )
in
    Expand
Изменено: Андрей Лящук - 15.02.2020 04:52:04
 
PooHkrd, а все-таки, понять бы, количество выводимых строк совпадает только в двух случаях. Где неправда? Сколько должно быть и на чем ошибки? Вот что интересно...
F1 творит чудеса
 
Цитата
Максим Зеленский написал:
Сколько должно быть и на чем ошибки?
У себя в коде нашел косяк в расчете переменной RealStart, там я проверяю равен ли день недели начальной даты диапазона одному из искомых в списке от пользователя, и если не равен, то нахожу ближайший следующий. Код заменил на корректный в своем посте №6.
В результате количество строк вывода стало равным предыдущему моему коду где просто генерились промежутки и из них List.Select'ом выковыривал нужные дни. В вашем коде ковыряться честно говоря времени нет, посему если есть желание, то можете по-дебажить самостоятельно. Наверняка также ошибка в условиях входа либо выхода. Файл-полигон прилагаю, там сразу увидите на какой строке начинаются расхождения с корректными алгоритмами.
Кстати, после избавления от глюка мой код и выполняться стал шустрее. Добавляю в итоговую таблицу генератор от Андрея Лящук и скорректированные данные по моему генератору.
Изменено: PooHkrd - 18.02.2020 12:26:15
Вот горшок пустой, он предмет простой...
 
abc никак не успокоится. А я ведь предложил Вам решение. Не хотите? Тогда зачем Вы здесь? Продолжать гадить?
Страницы: 1
Наверх