Страницы: 1
RSS
power query развернуть каталог вида: category, id, parentId в отдельные столбцы с подкатегориями, xml файл со списком родительских и вложенных категорий в одном столбце связанных по id/parentid разнести по столбцам по вложенности
 
Доброго дня,
Возникла задача пересобрать получаемые из интернета данные в виде XML в читаемую таблицу.  В xml 2 таблицы - в одной дерево каталога в виде названия,id, и родительского id, во второй товары  c id категории.  В итоге хочется получить таблицу с товарами и столбцами всех подкатегорий. Т.е нужно развернуть список категорий c id/parent id в отдельные столбцы и добавить к товару.  Возникла мысль о Power Query. Но, так как я с PQ практически не сталкиваюсь и особо его не знаю, решать пришлось в лоб, через объединение. А потом еще сдвигать короткие цепочки. Да и число подкатегорий экспериментально только получил.  Решение как бы есть, но мне не особо нравиться.  Может что-нибудь более изящное кто предложит? А то самому знаний/опыта не хватает. Заодно и поучиться. Подобных задач что-то не нашел, может формулирую не правильно.
Мое решение ниже и в приложенном файле. Пример xml обрезал по товарам до приемлемого и сохранил в текстовом, В оригинале в источнике ссылка api
Код
let
    Источник = Xml.Tables(File.Contents("C:\...\sampleFeed.txt")){0}[shop],
    category = Источник{0}[categories]{0}[category],
    headrename = Table.RenameColumns(category,{{"Element:Text", "Tree"}, {"Attribute:id", "id"}, {"Attribute:parentId", "parentId"}}),
    listforfilter = List.Buffer(headrename[parentId]),
    tree_last = Table.SelectRows(headrename, each ([id] <> listforfilter)),
    joinL1 = Table.NestedJoin(tree_last,{"parentId"},headrename,{"id"},"t1",JoinKind.LeftOuter),
    #"treelast-1" = Table.ExpandTableColumn(joinL1, "t1", {"Tree", "parentId"}, {"t1.Tree", "t1.parentId"}),
    joinL2 = Table.NestedJoin(#"treelast-1",{"t1.parentId"},headrename,{"id"},"t1",JoinKind.LeftOuter),
    #"treelast-2" = Table.ExpandTableColumn(joinL2, "t1", {"Tree", "parentId"}, {"t1.Tree.1", "t1.parentId.1"}),
    joinL3 = Table.NestedJoin(#"treelast-2",{"t1.parentId.1"},headrename,{"id"},"t1",JoinKind.LeftOuter),
    #"treelast-3" = Table.ExpandTableColumn(joinL3, "t1", {"Tree"}, {"t1.Tree.2"}),
    recs_temp = Table.ToRecords(Table.ReorderColumns(#"treelast-3",{"t1.Tree.2", "t1.Tree.1", "t1.Tree", "Tree", "id", "parentId", "t1.parentId", "t1.parentId.1"})),
    recs_tree = List.Transform(recs_temp, each 
        if _[t1.Tree]=null then _&[t1.Tree.2=_[Tree], Tree = null] 
        else 
            if _[t1.Tree.1]=null then _&[t1.Tree.2=_[t1.Tree], t1.Tree.1=_[Tree], t1.Tree=null, Tree = null] 
            else 
                if _[t1.Tree.2]=null then _&[t1.Tree.2=_[t1.Tree.1], t1.Tree.1=_[t1.Tree], t1.Tree=_[Tree], Tree = null] 
                else _),
    table_tree = Table.FromRecords(recs_tree),
    offer = Источник{0}[offers]{0}[offer],
    join_offer_tree = Table.NestedJoin(offer,{"categoryId"},table_tree,{"id"},"temp",JoinKind.LeftOuter),
    result = Table.ExpandTableColumn(join_offer_tree, "temp", {"t1.Tree.2", "t1.Tree.1", "t1.Tree", "Tree"}, {"temp.t1.Tree.2", "temp.t1.Tree.1", "temp.t1.Tree", "temp.Tree"})
in
    result
Изменено: Sergey Stoyanov - 21.06.2024 16:34:00
 
Может и к лучшему, что никто не ответил, времени заниматься не было совершенно, работает и ладно. В фильтрации конечно ошибся, но в данном наборе не принципиально, и без нее работает. Но, как время появилось, решил немного поразбираться с PQ , написал функцию  для разворачивания дерева  по списку записей(таблицу с добавлением индекса  - в записи). функция с рекурсией
Код
    indx = Table.AddIndexColumn(table0, "Indx", 0, 1),
    id_list = List.Buffer( indx[id] ),
    recs = List.Buffer(Table.ToRecords(indx)),    
fn = (rec, spot, i) => 
    [new_spot = List.PositionOf( id_list, recs{spot}[parentId]),
    new_rec = Record.AddField(rec, "Tree"& Text.From(i), (try recs{new_spot} otherwise rec)[Tree]),
    new_i = List.Accumulate({i}, 1, (state, current) => state + current) ,
    result = if new_spot = -1 then new_rec else @fn(new_rec, new_spot, new_i)]
[result],
Однако, может кто-нибудь объяснить, почему в конструкции с добавлением столбца рекурсия прекрасно работает и результат в записях верный, со всеми новыми записями(столбцами) подкатегорий,
Код
recstree = Table.AddColumn( indx, "parentTree", (x) => fn(x, x[Indx], 0 )),
а, если пытаюсь вместо добавления столбца, просто трансформировать список записей, рекурсия не срабатывает и добавляется только одно поле?
Код
 recstree = List.Transform( recs, (x) => fn(x, x[Indx], 0 ))
Изменено: Sergey Stoyanov - 12.07.2024 10:54:00
 
Разобрался вроде. Трансформация работает как нужно. Это, когда я пытаюсь посмотреть итог,  Table.FromRecords  на основе первой записи обрезает остальные столбцы. А вот как выводить все, что-то пока не понятно..  
 
а если из списка категорий словарь на записях сделать?
m
Пришелец-прораб.
 
Спасибо. Разберусь, как работает, возможно интересно.  А для понимания как PQ обрабатывать так и вовсе отлично, на конкретном примере.
Но, пока результат явно не тот, что ожидался  - веб камера в детские товары попала, а мышь в "спорт и отдых"...
 
Sergey Stoyanov, на предоставленных данных таких проблем не наблюдаю.
Пришелец-прораб.
 
Ну, товары то я обрезал в xml,  там больше полумиллиона строк. А у себя на полном запустил, это нужно в xml эти id оставить. Cейчас опят занят, чуть позже попробую оставить в xml с ошибкой. cat id 215. И, по моему один уровень категорий выпал. 4 получалось, а здесь 3. Но, гораздо шустрее  отработало, чем мое собственное творчество. Нужно мне с функцией разобраться, там допилю, надеюсь..  
 
Посмотрел, что в образце исходника оставил... Ну, в общем понятно, структура столбцов в полном фиде другая. A функция вызывается по индексу столбца,  а не по имени. в общем к другому столбцу привязывался вместо  categoryId.  С самой функцией пока не разбирался, но ошибку уже исправил :). Спасибо!
Страницы: 1
Читают тему
Наверх