На днях один хороший друг поделился задачкой: есть таблица, в которой данные записаны "по уровням" и надо эти уровни извлечь
Т.е. из текста вида
И что самое главное - сделать это надо не функциями Excel и макросами(все было бы слишком просто, подобное решение я уже выкладывал на сайте - Как оставить в ячейке только цифры или только текст?) - исключительно средствами языка M в PowerQuery. Это я к тому, что формулы DAX тоже нельзя применять. С виду несложно, но есть проблемы. Даже на скрине видно, что уровни состоят из разного кол-ва цифр и плюс к этому после самого уровня может идти и точка, и пробел и запятая и еще много чего. А теоретически - может вообще ничего не идти, сразу буквы. И в довершение - текст может содержать как русские, так и английские буквы, а внутри обозначения уровней могла быть не только точка, но еще и пробелы:
Первое, что пришло в голову - самописная функция, которая искала бы любую букву. Т.е. нашли букву, запомнили её позицию в тексте и вытащили текст только до этой позиции. Однако в результате было найдено несколько решений.
Прежде чем читать далее и пробовать применить, необходимо знать азы работы в Power Query, а точнее: как создавать и редактировать запросы и как вызвать расширенный редактор. Или как минимум знать где он находится: переходим в нужный запрос -Главная -Расширенный редактор:
К статье приложен файл со всеми вариантами, но он может показаться сложным для понимания "сходу", т.к. в одном запросе использованы сразу все варианты преобразований:
Скачать пример: Удаление цифр или текста в Power Query.xlsx (57,2 КиБ, 1 586 скачиваний)
Друг после поисков в интернете набрел на статью в блоге Кена Пулса: http://www.excelguru.ca/blog/2015/11/19/keep-only-numbers-in-power-query/
Мне описанное там решение показалось несколько запутанным для решения именно нашей задачи, хотя и не лишенным своих плюсов и совершенно точно весьма оригинальным. На этом примере и разберем как применять все остальные и про какие плюсы и минусы речь.
Основной упор там сделан на строку
Если поставить эту строку в самое начало кода в расширенном редакторе, то далее можно использовать при вставке пользовательского столбца.
Теперь по шагам.
Предположим, что изначально был создан запрос из
let //подключаемся к таблице Таблица1 Источник = Excel.CurrentWorkbook(){[Name="Таблица1"]}[Content], #"Измененный тип" = Table.TransformColumnTypes(Источник,{{"Данные", type text}}) in #"Измененный тип" |
Теперь необходимо здесь добавить в самом начале строку создания списка символов:
let //формируем массив символов для замены CharsToRemove = List.Transform({33..45,47,58..126}, each Character.FromNumber(_)), //подключаемся к таблице Таблица1 Источник = Excel.CurrentWorkbook(){[Name="Таблица1"]}[Content], #"Измененный тип" = Table.TransformColumnTypes(Источник,{{"Данные", type text}}) in #"Измененный тип" |
И далее уже в обычном редакторе идем на вкладку Добавить столбец -Пользовательский столбец. В окне задаем имя столбца и вписываем следующее:
[Данные] - это столбец с текстом, из которого необходимо убрать все лишние символы(они перечислены как раз в строкеCharsToRemove )Text.Remove - текстовая функция языка M, удаляющая из указанного текста([Данные] ) все символы, перечисленные в списке(CharsToRemove )
В принципе это практически решает задачу, если список задать следующими символами:
Почему я вообще использовал
Теперь сразу стал очевидным
К тому же такой вариант удалит все перечисленные символы. С одной стороны это отлично, но с другой...Вдруг в тексте после обозначения уровня так же встретится цифра? Например "
Поэтому для уверенного решения задачи по корректному отбору именно уровней этот метод не всегда подходит. Равно как и его модификации, созданные мной в ходе решения задачи. Уверен, что кому-то варианты ниже придутся более по душе, т.к. они чуть проще в реализации и на мой взгляд более нагляды. К примеру, если сложно искать нужные коды, но мы знаем, что нам надо удалить весь текст(рус. и англ. буквы и пробелы)? Все просто, берем созданный выше запрос и меняем в нем строку
на такую:
Т.е. просто перечисляем первую и последнюю буквы алфавитов(обращаю внимание - начинать надо с заглавной буквы, а последней ставить строчную, чтобы захватить буквы и нижнего и верхнего региста). Плюс я добавил пробел. Если надо исключить еще какой-либо символ - просто добавляем его в кавычках через запятую. Например, чтобы убрать скобки:
Для тех, кто более тесно работает в Power Query станет очевидным, что можно вообще убрать теперь эту строку и напрямую при создании пользовательского столбца указывать диапазоны символов:
Это избавляет от необходимости лезть в Расширенный редактор, что делает такое решение вдвойне удобнее для "неиспорченного кодингом" пользователя. Просто подключились к таблице, перешли на вкладку Добавить столбец -Пользовательский столбец. Вставили туда текст выше и при необходимости лишь изменили имя столбца(вместо [Данные] указать имя нужного столбца).
Точно так же можно
я специально добавил еще точку, чтобы показать, как добавить еще какие-либо символы вместе с цифрами.
И опять следует учитывать, что в данном случае мы удалим цифры во всем тексте, даже если они там совсем не лишние. Т.е. отделить текст от уровней так тоже не получится. Но тем не менее у всех этих решений есть масса применений в различных ситуациях.
Это решение так же предложил друг в результате поиска оптимального решения. Оно основано на возможностях текстовой функции языка M -
- [Данные] - это столбец с текстом, из которого необходимо получить уровень.
- "абвгдеёжзийклмнопрстуфхцчшщъыьэюя-," - список символов, по которым надо разделить текст.
Чтобы применить это решение надо подключиться к таблице, перейти на вкладку Добавить столбец -Пользовательский столбец. Вставить туда следующий текст:
Т.к. здесь нет цифр, точки и пробела - то первым блоком всегда будет именно уровень. Все выглядит неплохо и полностью решает задачу, за исключением пары моментов. Да, мы не получим неверный результат, даже если внутри текста будет лишняя цифра: "
А применить здесь фокус с диапазонами не получится(
Чтобы не дублировать буквы в двух регистрах можно использовать для поиска только нижний регистр, добавив еще одну функцию:
Вполне приемлемо и отвечает требованиям. Но мне лично не нравится именно необходимость забивать каждую букву отдельно. Плюс, нельзя получить текст именно после уровней - т.е. до уровня разделить получится, а после мы получим кучу разбитых фрагментов на каждую букву в перечислении.
Я же хотел универсальную функцию, которая поделит текст на две части, найдя хоть один символ из списка. Чтобы можно было решить еще одну классическую задачу: разделение английского и русского текста в артикулах, вроде такого:
Это решение, которое пришло ко мне в голову изначально - найти любой из указанных символов и отобрать текст до этого символа. Изначально мысли были в сторону цикла, но после недолгого изучения функций M нашел нужное. Все решается при помощи двух простых функций:
этот текст также достаточно вставить в окно добавления пользовательского столбца. Заходить в расширенный редактор необходимости нет.
Теперь
[Данные] - это столбец с текстом, из которого необходимо получить уровень. Все как и в функциях выше.Text.PositionOfAny - находит числовую позицию любого символа из перечисленных. Т.е. она просматривает каждый символ текста в ячейке столбца[Данные] и если он совпадает хоть с одним из перечисленных в фигурных скобках({"А".."я","A".."z"} ) - то возвращает номер позиции этого символа в тексте.
При этом список символов можно задавать диапазонами: "А".."я" (как это работает я уже описывал в Решении 1). А это означает, что мы можем легко указать буквы русского и английского алфавита без необходимости вводить их одну за другой и добавить какие-то другие символы(через запятую).Text.Middle - эта функция берет указанный текст([Данные] ) и отбирает из него часть, начиная с указанной позиции и с указанным количеством символов(аналогПСТР в Excel). Иными словами, если взять текст "привет" и применить к нему функцию таким образом -Text.Middle("привет", 0, 3) , то получим текст"при" (отсчет начальных символов начинается с нуля). А если записать так -Text.Middle("привет", 1, 4) , то получим"риве" .
Таким образом для текста
Text.Middle(
Text.Middle(
Text.Middle(
далее будет взято 6 символов от начала текста(с первого символа)
1.1
Но здесь тоже не обошлось без нюансов. Если указать только буквы, то может остаться много "мусора" - лишних символов, которые могут встречаться(скобки, запятые, тире и пр.). Поэтому я добавил функцию удаления лишних символов, но уже после того, как отделили уровень:
При этом мы можем сократить список и оставить лишь русские буквы и тогда текст будет поделен на две части: сначала текст до первой русской буквы, а далее - весь текст после. Или тоже самое, но с английскими буквами. Чтобы получить текст, начиная с найденного символа и до конца текста функцию необходимо записать так:
все то же самое, но теперь мы сначала ищем позицию любого из указанных символов, а последним аргументом указываем заведомо большее количество символов - 9999, что заставит функцию Text.Middle отобрать весь текст до конца.
Как видно, Power Query предоставляет куда больше возможностей, чем можно подумать на первый взгляд. И при этом нет необходимости изучать все премудрости программирования - достаточно уметь комбинировать наборы функций. Единственно, справка по функциям языка M пока очень скудная, а примеров в интернете пока еще слишком мало. Будем надеяться, что ситуация в скором времени изменится в лучшую сторону.
Так же см.:
Как оставить в ячейке только цифры или только текст?
Работа с текстом
Дмитрий, добрый день! А не подскажете, как создать такой столбец, чтобы оставить только даты определенного года и текст, который содержит шт. Подробнее:
В одном столбце есть дата, название контрагента и наименование с шт. Нужно, чтобы это было 3 столбца: только даты, контрагент и названия с шт.
Добрый день!
Подскажите, пожалуйста, а можно ли вытащить слово из строки? Например, XÆA-12 - имя сына Илона Маска, вытащить слово "имя"?
Карина, а как понять, что вытащить нужно именно "имя"?
Подскажите, пжлст, как через PQ вытянуть из строки дробное число по маске, типа "5 знаков после запятой".
Например, надо из строки вытащить только 0.12345 или 10.54321 или 567.24689 или 1234.35716
Это единственный признак, по которому можно отфильтровать.
Alex, без обид, но Вы сами смогли бы ответить на свой вопрос хотя бы без применения PQ, не видя примера данных, из которых надо число вытянуть? Я вот не могу и не буду даже пытаться, т.к. не видя откуда чего получать выдумывать ответы неблагодарное дело. Угадать что там в реальности шансов очень мало, а я привык ценить свое время.PowerQuery, PowerPivot, PowerBI и запросы ), приложив пример данных и показав, какой ожидаете результат.
Лучше всего будет создать тему на форуме(
Дмитрий, там фильтрация только по признаку "дробное число с 5 знаками после запятой". Все остальное - не имеет значения. Кстати, уже нашел решение - Регулярные выражения в Power Query.
Здравствуйте. Можно как нибудь прописать это: {"А".."я"," ","A".."z"}), но только чтобы удалились любые символы кроме цифр?