Страницы: 1
RSS
Извлечение текста в Power Query по условию
 
Добрый день! Прошу помощи. Необходимо с помощью PQ извлечь фрагмент текста из строки. В примере указан требуемый формат для некоторых строк.

Как мне кажется, в подавляющем большинстве случаев будет применим следующий алгоритм: ищем с права налево число, которое состоит из >=4 символов, затем удаляем все, что левее этого числа в т. ч. и само число, также удаляем один символ правее найденного числа (это пробел). Не смог решить данную задачу с помощью "мышки", а в языке M не силён  :(  
 
Доброе время суток
Цитата
Murderface_ написал:
в подавляющем большинстве случаев будет применим следующий алгоритм: ищем с права налево число, которое состоит из >=4 символов
Вариант
Код
let
    sign = Table.Buffer(Table.FromRows(List.Transform({"0".."9"}, each {_, "d"}))),
    Source = Excel.CurrentWorkbook(){[Name="data"]}[Content],
    calcer = Table.AddColumn(Source, "Извлечение", (rec) =>
    let
        items = Table.FromColumns({List.Reverse(Text.ToList(rec[Текст]))}, {"char"}),
        addId = Table.AddIndexColumn(items, "id"),
        joined = Table.Sort(Table.Join(addId, {"char"}, sign, {"Column1"}, JoinKind.LeftOuter), "id"),
        local = Table.Group(joined, {"Column2"}, {{"store", each _}, {"count", each Table.RowCount(_)}}, GroupKind.Local),
        lastLess4 = Table.FirstN(local, each if [Column2] = "d" and [count] >= 4 then false else true)
    in
        Text.Trim(Text.Combine(List.Reverse(Table.Combine(lastLess4[store])[char])))
    )
in
    calcer

Updated
Чуть удачнее вариант
Скрытый текст
Изменено: Андрей VG - 08.10.2019 13:55:40
 
Андрей VG, большое спасибо! Второй вариант это почти то, что нужно  :)

не совсем корректно извлеклись только 2 строки, начинаются на "г. ".
Изменено: Murderface_ - 08.10.2019 14:05:12
 
Андрей VG! Вы гений
 
Добрый день! Пытаюсь разобраться в решении Андрея и мне не всё в нем понятно. Насколько я понял внутри запроса была использована пользовательская функция calcer. На первом шаге она преобразует каждый символ строки в отдельную строку, затем добавляет индекс и если значение в строке является цифрой, то подставляет в новый столбец "d" из таблицы sign. Далее происходит группировка и с этого шага я не понимаю что происходит в запросе. Подскажите пожалуйста можно ли увидеть каждый шаг данной функции calcer в разделе "Примененные шаги"? Чтобы было наглядно понятно, что происходит в результате каждого шага.
 
Murderface_, вроде бы "не первый день замужем"  ;) , не понимаю в чем сложность развернуть все функции в последовательные шаги, я ведь такой прием уже показывал не раз, и вам в том числе.
Скрытый текст

Два последних шага, это детализация одной из таблиц из шага calcer3 и на её примере можно посмотреть, что именно Андрей там делал при помощи локальной группировки. И если честно, тут применить по примеру не особо получится, тут уже чуть глубже в теоретический материал нужно погружаться если есть желание действительно разобраться в механике.
Изменено: PooHkrd - 03.12.2019 09:11:32
Вот горшок пустой, он предмет простой...
 
Цитата
PooHkrd написал:
я ведь такой прием уже показывал
Да из головы вылетело, не смог вспомнить как это делается. Большое спасибо! Буду сидеть дальше разбираться  ;)  
 
Цитата
PooHkrd написал:
развернуть все функции в последовательные шаги
спасибо. теперь буду знать как разбирать функции
 
И до кучи, чуть модифицированный вариант кода Андрея но без джойна. Не знаю как будет быстрее работать, скорее всего примерно одинаково.
Код
let
    Source = Excel.CurrentWorkbook(){[Name="data"]}[Content],
    calcer = Table.AddColumn(Source, "Извлечение", (rec) =>
    let
        items = Table.FromColumns({List.Reverse(Text.ToList(rec[Текст]))}, {"char"}),
        addId = Table.AddIndexColumn(items, "id"),
        joined = Table.AddColumn( addId, "Column2", each try (Number.From([char]) + 1) / (Number.From([char]) + 1) otherwise null, type number),
        local = Table.Group(joined, {"Column2"}, {{"store", each _}, {"count", each Table.RowCount(_)}, {"min", each List.Min([id])}}, GroupKind.Local),
        lastLess4 = Table.FirstN(local, each not ([Column2] = 1 and [count] >= 4 and [min] > 5) )
    in
        Text.Trim(Text.Combine(List.Reverse(Table.Combine(lastLess4[store])[char]) ) ) )
in 
    calcer
Вот горшок пустой, он предмет простой...
 
Цитата
PooHkrd написал:
модифицированный вариант
8-0 . Спасибо!
 
Murderface_, да там ничего страшного, изменился только метод определения циферок в тексте, вместо джойна заранее объявленного списка использую конструкцию
Код
= try (Number.From([char]) + 1) / (Number.From([char]) + 1) otherwise null

т.е. пытаюсь преобразовать символ в число и в случае ошибки вывожу null. Деление здесь нужно чтобы для любой цифры получить на выходе единицу, а +1 тут нужно чтобы обойти деление на нуль.
З.Ы. тут даже эффективнее будет написать так:
Код
= let a = try (Number.From([char]) + 1) otherwise null in a / a 
Изменено: PooHkrd - 03.12.2019 10:26:43
Вот горшок пустой, он предмет простой...
 
Немного изменил вариант Андрея,
Скрытый текст

Добавил в таблицу sign еще и пробел и на шаге lastLess4 заменил 4 на 6, а 5 на 7. Теперь стали корректно отображаться все операции в списке (о чем писал в этом сообщении). Осталось проверить это вариант на большей выборке. Еще раз всем спасибо  :)  
 
Цитата
PooHkrd написал:
чуть модифицированный вариант
Привет, Алексей.
Размножил исходный пример до 33 тысяч строк. Ваш около минуты, мой последний 8 секунд. Видимо, try ... oherwise - дорогостоящая операция :(
 
Андрей VG, спасибо за тест, прогнал сам на 40к строк вышло ваш - 11 сек и мой 92 сек. Значит будем иметь ввиду в дальнейшем.
Попробовал сделать без него, и объехать за счет Table.ReplaceErrorValues, но получилось еще медленнее, что логично, там механизм поиска ошибок явно построен на той же конструкции.
И кстати, по этой причине я стараюсь везде где можно обходиться без if then else, тоже дорогая штука. Особенно если разветвиться хорошенько на несколько вложенных. Работа со списками в этом случае прямо выручает.
Изменено: PooHkrd - 03.12.2019 11:47:00
Вот горшок пустой, он предмет простой...
Страницы: 1
Наверх