Kontekst biznesowy: po co wyodrębniać pierwsze i ostatnie słowo z opisu
Marka na początku, wariant na końcu – klasyczny schemat opisu produktu
W wielu systemach sprzedażowych i magazynowych opis produktu ma powtarzalną strukturę. Bardzo często pierwsze słowo w opisie to marka lub linia produktu, a ostatnie słowo określa wariant, rozmiar, pojemność albo model. Jeśli uda się te elementy automatycznie wyodrębnić w Power Query, można je dalej wykorzystywać w raportach, tabelach przestawnych czy modelach Power BI.
Typowy przykład z praktyki to opis w rodzaju: „NIVEA Krem nawilżający twarz 50ml”. Pierwsze słowo „NIVEA” to marka, natomiast ostatnie „50ml” to wariant pojemności. Innym schematem może być „Lenovo ThinkPad T14 Gen3”, gdzie „Lenovo” jest producentem, a „Gen3” oznacza generację modelu. Wystarczy kilka prostych transformacji w Power Query, aby z jednego opisu otrzymać oddzielne kolumny: Marka, Linia, Model, Wariant.
Takie rozbicie opisu na części składowe ma kilka konsekwencji: łatwiejsze filtrowanie, możliwość tworzenia reguł rabatowych dla konkretnych marek, grupowanie sprzedaży po rozmiarach, a nawet budowanie hierarchii produktów. Jeżeli raporty powstają cyklicznie, automatyczna ekstrakcja pierwszego i ostatniego słowa z opisu produktu szybko zwraca zainwestowany czas.
Różne działy – różne potrzeby analizy opisów
Dział sprzedaży patrzy na opisy inaczej niż magazyn czy controlling. Sprzedawców interesuje najczęściej marka, linia produktowa i cechy marketingowe, które zwiększają szansę sprzedaży. Logistyk zwraca uwagę na rozmiar, wagę, jednostkę miary, informacje o opakowaniu zbiorczym. Controlling natomiast analizuje rentowność na poziomie grup asortymentowych, gdzie liczy się spójność w nazywaniu produktów.
Wyodrębnienie pierwszego i ostatniego słowa z opisu produktu w Power Query pozwala każdemu z tych działów wykorzystać te same dane w inny sposób. Sprzedaż może tworzyć raporty po marce („pierwsze słowo”), logistyka – po pojemności („ostatnie słowo” z jednostką), a controlling – identyfikować produkty z tym samym wariantem, ale różnymi kodami. Kluczowe jest, że raz przygotowana transformacja w Power Query działa przy każdym odświeżeniu danych, niezależnie od liczby wierszy czy plików wejściowych.
Automatyzacja vs. proste formuły Excela
Jeżeli trzeba od czasu do czasu wyciągnąć pierwsze słowo z kilku opisów, da się to zrobić zwykłymi funkcjami Excela. Wystarczy połączyć funkcje typu LEWY, ZNAJDŹ, PRAWY czy FRAGMENT.TEKSTU. Problem zaczyna się, gdy:
- danych są dziesiątki tysięcy wierszy,
- pliki z opisami produktów przychodzą co tydzień lub codziennie,
- schemat transformacji musi być identyczny w wielu arkuszach lub raportach.
W takim scenariuszu formuły Excela szybko stają się trudne w utrzymaniu, a ręczne kopiowanie ich do kolejnych plików generuje błędy. Power Query rozwiązuje ten problem inaczej: opisuje się raz serię kroków przekształcenia (m.in. wyodrębnianie pierwszego i ostatniego słowa z opisu produktu), a następnie wystarczy przycisk „Odśwież”. Zmieni się plik źródłowy, nazwy arkuszy czy liczba wierszy – logika ekstrakcji pozostanie taka sama.
Proste rozwiązania w Excelu wystarczają, gdy dane są statyczne, jednorazowe i niezbyt liczne. Gdy tylko pojawia się powtarzalność, wiele plików i potrzeba stabilnej automatyzacji, Power Query staje się zdecydowanie pewniejszym wyborem.
Dane wejściowe i ich problemy – jak wyglądają „prawdziwe” opisy produktów
Najczęstsze formy opisów produktów
Struktura opisów produktów różni się między branżami, ale da się zauważyć kilka schematów, które mają znaczenie przy wyodrębnianiu pierwszego i ostatniego słowa w Power Query. Najczęściej spotykane formaty to:
- Marka na początku – np. „Adidas Koszulka sportowa męska L”, „Samsung Galaxy A14 64GB Czarny”. Tu pierwsze słowo jest zwykle marką.
- Typ produktu na początku, marka w środku – np. „Laptop Lenovo ThinkPad T14”, „Monitor Dell 24 cali”. W takim wypadku pierwsze słowo niekoniecznie jest marką, ale nadal może mieć znaczenie (kategoria).
- Wariant / rozmiar na końcu – np. „Płyn do naczyń Fairy 1L”, „Skarpety sportowe 3-pak 43-46”. Tu ostatnie słowo (lub dwa ostatnie słowa) opisują rozmiar, pojemność lub ilość.
- Numer katalogowy na końcu – np. „Zasilacz impulsowy 12V 5A XY-1234”, „Czajnik elektryczny stalowy 1.7L JK-100”. Ostatnie słowo bywa wtedy kodem producenta.
Wyodrębnianie pierwszego i ostatniego słowa z opisu produktu w Power Query musi uwzględniać takie schematy. Innego podejścia wymaga sytuacja, gdy ostatnie słowo to liczba z jednostką („50ml”), a innego – gdy jest to symbol katalogowy z myślnikami.
Zanieczyszczenia tekstu: spacje, znaki specjalne, mieszane języki
Rzeczywiste dane rzadko są idealne. Opisy produktów mogą zawierać:
- nadmiarowe spacje – podwójne, potrójne odstępy między wyrazami, spacje na początku i końcu tekstu,
- znaki specjalne – myślniki, ukośniki, kropki, średniki, cudzysłowy,
- mieszane języki – np. część opisu po polsku, część po angielsku, co utrudnia prostą segmentację,
- nietypowe znaki białe – tabulatory, twarde spacje, znaki końca linii.
Przykładowy opis „NIVEA Krem nawilżający twarz 50ml ” zawiera zarówno wielokrotne spacje, jak i spację na końcu. Jeżeli bez przygotowania próbujemy w Power Query wydzielić ostatnie słowo poprzez podział po ostatniej spacji, w skrajnych przypadkach otrzymamy pusty tekst zamiast „50ml”. Dlatego czyszczenie opisów produktów (przycinanie spacji, usuwanie zbędnych znaków) jest pierwszym warunkiem poprawnego działania dalszych kroków.
Jednosłowne opisy, puste wartości i numery katalogowe
W strumieniu danych z wielu systemów zawsze pojawią się skrajne przypadki:
- puste opisy – brak wartości w kolumnie,
- opisy jednowyrazowe – np. „Voucher”, „Usługa”, „Transport”,
- opisy-kody – np. „XY-1234”, bez żadnych spacji.
Jeżeli wyodrębniamy pierwsze słowo z opisu produktu i zakładamy, że w każdym wierszu jest co najmniej jedna spacja, pojawią się błędy. W Power Query konieczna jest obsługa sytuacji „brak ogranicznika”, czyli brak spacji w tekście. Analogicznie, przy ostatnim słowie musimy wziąć pod uwagę, że czasem opis składa się z jednego słowa i wtedy pierwsze i ostatnie słowo są po prostu identyczne.
Utrudnieniem są także opisy kończące się znakami interpunkcyjnymi, przykładowo „Krem do rąk 50ml.” albo „Szampon 250 ml,”. Ostatnim „słowem” według prostej logiki staje się wtedy „50ml.” lub „ml,”, co może komplikować filtrowanie po pojemności. Lepsze podejście to najpierw usunąć znaki końcowe, a dopiero potem szukać ostatniej spacji.
Przykładowa próbka danych testowych
Dobrą praktyką jest przygotowanie krótkiej tabeli testowej, na której można bezpiecznie dopracować logikę wyodrębniania pierwszego i ostatniego słowa z opisu produktu. Przykładowa próbka może wyglądać tak:
| OpisProduktu |
|---|
| NIVEA Krem nawilżający twarz 50ml |
| Adidas Koszulka sportowa męska L |
| Laptop Lenovo ThinkPad T14 Gen3 |
| Fairy Płyn do naczyń 1L. |
| Voucher |
| XY-1234 |
Na takim zestawie od razu widać, gdzie występują spacje, znaki interpunkcyjne, jednowyrazowe opisy i puste wartości. Pozwala to dopracować formuły M w Power Query tak, aby były odporne na różne warianty danych rzeczywistych.

Podstawy pracy z tekstem w Power Query – krótkie przypomnienie
Kolumny tekstowe i konwersja typu danych
Większość operacji na opisach produktów w Power Query wymaga, aby kolumna miała typ danych: Tekst. Jeżeli po imporcie źródło zostało rozpoznane jako liczba, data lub typ „Any”, część funkcji tekstowych może generować błędy lub działać nieprzewidywalnie. Dlatego przed przystąpieniem do wyodrębniania pierwszego i ostatniego słowa z opisu produktu warto świadomie ustawić typ kolumny.
Zmiana typu danych w Power Query jest prosta: wystarczy kliknąć ikonę typu przy nagłówku kolumny i wybrać „Tekst”. Warto jednak pamiętać, że Power Query dodaje wtedy nowy krok („Zmieniony typ”), który może wpływać na kolejne działania. Jeżeli edytuje się już istniejącą kwerendę, trzeba zwrócić uwagę na kolejność kroków – czasami korzystniej jest przenieść zmianę typu na wcześniejszy lub późniejszy etap.
Podstawowe funkcje tekstowe M przydatne przy opisach produktów
Język M, na którym opiera się Power Query, oferuje wiele funkcji tekstowych. W kontekście wyodrębniania pierwszego i ostatniego słowa z opisu produktu przydadzą się szczególnie:
- Text.Length – zwraca długość ciągu znaków; pomocne przy weryfikacji, czy tekst jest pusty.
- Text.Start – zwraca określoną liczbę znaków od początku tekstu.
- Text.End – zwraca określoną liczbę znaków od końca tekstu.
- Text.Middle – zwraca fragment tekstu od określonej pozycji.
- Text.PositionOf – zwraca indeks pierwszego wystąpienia znaku lub ciągu (np. spacji).
- Text.PositionOfAny – wyszukuje dowolny znak z listy, z możliwością szukania od końca.
- Text.BeforeDelimiter – zwraca fragment tekstu przed określonym ogranicznikiem (np. pierwszą spacją).
- Text.AfterDelimiter – analogicznie zwraca tekst po ograniczniku, z opcją liczenia od końca.
- Text.Trim i Text.Clean – porządkują tekst, usuwając zbędne spacje i niewidoczne znaki.
Zrozumienie, jak działają te funkcje oraz jak zachowują się w przypadku pustych wartości i braku ogranicznika, jest kluczowe przy budowaniu bezpiecznych formuł dla opisów produktów.
Transformacje z interfejsu vs. własne formuły M
Power Query pozwala osiągnąć wiele rzeczy na dwa sposoby: klikając w interfejsie i korzystając z wbudowanych poleceń, albo pisząc ręcznie formuły w języku M. Oba podejścia można mieszać. Na przykład:
- „Przytnij” i „Oczyść” tekst można wywołać z menu Transformuj > Format,
- podział kolumny wg ogranicznika (spacji) jest dostępny w menu Rozdziel kolumny,
- bardziej złożone przekształcenia, np. warunkowe wycinanie fragmentu tekstu w zależności od długości, wymagają już kolumny niestandardowej z własną formułą M.
Efektem każdej operacji w interfejsie jest krok w kwerendzie zapisany w M. Można go później edytować, łączyć z innymi, kopiować między kwerendami. Wyodrębnianie pierwszego i ostatniego słowa z opisu produktu dobrze nadaje się do stopniowego podejścia: najpierw maksymalne wykorzystanie interfejsu, a potem dopisanie własnych formuł tam, gdzie standardowe opcje nie wystarczają.
Znaczenie kolejności kroków przy czyszczeniu opisów
Kolejność transformacji w Power Query jest ściśle liniowa – każdy krok bazuje na wyniku poprzedniego. Jeśli:
- najpierw podzielimy opis na słowa,
- a dopiero później usuniemy nadmiarowe spacje,
to wynik będzie gorszy, niż gdyby najpierw wyczyścić tekst, a dopiero potem go dzielić. W przypadku ekstrakcji pierwszego i ostatniego słowa z opisu produktu najbardziej efektywna sekwencja wygląda najczęściej tak:
- ustawienie typu kolumny na Tekst,
- przycięcie spacji (Trim) i oczyszczenie z niewidocznych znaków (Clean),
Standardowe czyszczenie opisów przed ekstrakcją słów
Stabilny schemat przygotowania kolumny z opisem przed wyciąganiem pierwszego i ostatniego słowa można zrealizować w kilku krokach, częściowo z interfejsu, częściowo z pomocą prostych formuł M. Klucz to uporządkowanie spacji, znaków końcowych i nietypowych białych znaków.
- Ustaw typ kolumny na Tekst – tak jak opisano wcześniej.
- Zastosuj „Przytnij” (Trim) i „Oczyść” (Clean) z karty Transformuj > Format.
- Ustandaryzuj wewnętrzne spacje – zamień wielokrotne spacje na pojedyncze.
- Usuń zbędne znaki końcowe – kropki, przecinki, średniki na końcu opisu.
Punkty 3–4 wygodnie zrealizować jedną kolumną niestandardową w języku M. Przykładowa formuła:
= let
txt0 = [OpisProduktu],
// krok 1: zabezpieczenie pustych i nulli
txt1 = if txt0 = null then "" else Text.From(txt0),
// krok 2: przycięcie i oczyszczenie
txt2 = Text.Clean(Text.Trim(txt1)),
// krok 3: zamiana wielokrotnych spacji na pojedyncze
txt3 = Text.Combine(List.Select(Text.SplitAny(txt2, " "), each _ <> ""), " "),
// krok 4: usunięcie wybranych znaków końcowych
txt4 = let
trimmed = Text.TrimEnd(txt3, {".", ",", ";", ":"})
in
trimmed
in
txt4
Taka kolumna (np. nazwana Opis_Czysty) staje się bazą do dalszych operacji. Jednokrotne przygotowanie tekstu ogranicza liczbę wyjątków podczas wyodrębniania pierwszego i ostatniego słowa.
Najprostsze podejście – dzielenie opisu na słowa z poziomu interfejsu
Rozdzielanie kolumny wg spacji
W wielu przypadkach wystarczy „mechaniczne” rozdzielenie opisu na słowa, a potem wskazanie pierwszej i ostatniej kolumny. Dla raportów jednorazowych jest to podejście szybkie i czytelne.
Typowy schemat:
- Zaznaczenie kolumny
Opis_Czysty. - Polecenie Rozdziel kolumny > Według ogranicznika….
- Wybór ogranicznika: Spacja.
- Tryb rozdzielania: Każde wystąpienie ogranicznika.
Powstaje kilka kolumn, np. Opis_Czysty.1, Opis_Czysty.2, Opis_Czysty.3 itd. Pierwsze słowo to po prostu Opis_Czysty.1. Problem pojawia się przy ostatnim słowie, bo liczba kolumn różni się w zależności od tego, ile słów zawiera opis.
Ograniczenia podejścia „pełny podział na kolumny”
Rozdzielenie opisu na słowa działa sensownie tylko przy spełnieniu kilku warunków:
- liczba słów w opisach jest mniej więcej stała (np. 3–5),
- nie trzeba dynamicznie ustalać „ostatniego słowa” – z góry wiadomo, że np. trzecia kolumna to zawsze rozmiar,
- nie analizuje się zbyt szerokiego strumienia danych, bo duża liczba kolumn utrudnia utrzymanie modelu.
Jeśli opisy w jednym źródle potrafią mieć od 1 do 10 wyrazów, podejście oparte na stałej liczbie kolumn zaczyna się łamać. Dochodzą też sytuacje:
- puste opisy – brak jakichkolwiek słów,
- opisy-kody bez spacji – całość ląduje w pierwszej kolumnie, reszta jest pusta,
- opisy z dodatkowymi spacjami wokół myślników lub ukośników – powstają słowa technicznie oddzielone spacją, ale semantycznie będące jednym kodem.
W takich scenariuszach korzystniej jest wyznaczyć pierwsze i ostatnie słowo bez pełnego dzielenia na kolumny, z pomocą funkcji języka M.
Dzielenie na wiersze jako alternatywa
Dla zaawansowanych przekształceń przydatne bywa rozdzielenie kolumny na wiersze, a nie na kolumny. Polecenie Rozdziel kolumny > Według ogranicznika… > Zaawansowane > Rozdziel na wiersze pozwala dla każdego produktu uzyskać tyle wierszy, ile słów zawiera opis. Potem można np.:
- ponumerować słowa w ramach produktu (Indeks od 1 po grupie),
- wybrać minimalny i maksymalny indeks jako pierwsze i ostatnie słowo,
- zwinąć z powrotem dane do jednej linii na produkt.
To rozwiązanie jest elastyczne, lecz bardziej rozbudowane i mniej wygodne w utrzymaniu dla prostych raportów. Przydaje się, gdy jednocześnie trzeba analizować środkowe słowa, np. ścieżkę kategorii albo listę atrybutów wpisanych w opisie.

Pierwsze słowo z opisu – podejście oparte na funkcjach M
Wyodrębnianie pierwszego słowa z użyciem Text.BeforeDelimiter
Jeśli opis jest już oczyszczony (kolumna Opis_Czysty), pierwsze słowo można wyciągnąć za pomocą kombinacji funkcji Text.BeforeDelimiter i prostego sprawdzenia, czy w tekście w ogóle występuje spacja.
Przykładowa kolumna niestandardowa PierwszeSlowo:
= let
txt = [Opis_Czysty],
wynik =
if txt = null or Text.Length(txt) = 0 then
null
else if Text.PositionOf(txt, " ") = -1 then
// brak spacji - cały tekst jest jednym "słowem"
txt
else
Text.BeforeDelimiter(txt, " ")
in
wynikTaka logika obsługuje:
- puste wartości – wynik
null, - opisy jednowyrazowe – zwracany jest cały tekst,
- standardowe opisy wielowyrazowe – wycinany jest fragment przed pierwszą spacją.
Wydzielenie pierwszego słowa i reszty opisu jednocześnie
W praktyce często potrzebne jest nie tylko pierwsze słowo, ale także „reszta opisu bez pierwszego słowa”. Przykład: pierwsze słowo jako marka, a reszta jako nazwa handlowa. Można to zrobić jedną formułą, tworząc dwie kolumny pośrednie albo jedną kolumnę typu rekord.
Podejście z dwiema kolumnami niestandardowymi:
- Kolumna
PierwszeSlowo– jak w poprzednim przykładzie. - Kolumna
Opis_BezPierwszego:= let txt = [Opis_Czysty], wynik = if txt = null or Text.Length(txt) = 0 then null else let pos = Text.PositionOf(txt, " ") in if pos = -1 then null // jednosłowny opis - brak "reszty" else Text.Middle(txt, pos + 1) in wynik
W takim układzie pierwsze słowo można wykorzystać np. jako osobną kolumnę w modelu danych (Marka), a resztę zmapować na słownik nazw lub użyć w wyszukiwaniu pełnotekstowym.
Specjalne traktowanie kodów i skrótów w pierwszym słowie
Pierwsze słowo bywa skrótem technicznym lub kodem, który w ogóle nie powinien występować jako „marka” – np. „ID”, „ART”, „Kod”. Rozsądne jest wtedy:
- zbudowanie prostej listy słów pomocniczych (np.
{"ID", "ART", "Kod"}), - przesunięcie „pierwszego słowa” na kolejne, jeśli należy do tej listy.
Prosty wariant takiej logiki:
= let
txt = [Opis_Czysty],
helpers = {"ID", "ART", "Kod"},
pierwsze =
if txt = null or Text.Length(txt) = 0 then
null
else if Text.PositionOf(txt, " ") = -1 then
txt
else
Text.BeforeDelimiter(txt, " "),
wynik =
if pierwsze = null then
null
else if List.Contains(helpers, pierwsze) then
// pobierz "drugie słowo"
let
bezPierwszego = Text.AfterDelimiter(txt, " "),
drugie =
if Text.PositionOf(bezPierwszego, " ") = -1 then
bezPierwszego
else
Text.BeforeDelimiter(bezPierwszego, " ")
in
drugie
else
pierwsze
in
wynikTakie rozwiązanie stabilizuje przypadki, w których pierwszy token jest technicznym prefiksem, a analizowana informacja zaczyna się od drugiego słowa.
Ostatnie słowo z opisu – bezpieczne i elastyczne rozwiązania
Podstawowa logika: szukanie ostatniej spacji
Ostatnie słowo da się wyznaczyć analogicznie do pierwszego, ale z wyszukiwaniem ogranicznika od końca tekstu. Ułatwia to funkcja Text.PositionOf z opcją Occurrence.Last.
Przykładowa kolumna OstatnieSlowo oparta na kolumnie Opis_Czysty:
= let
txt = [Opis_Czysty],
wynik =
if txt = null or Text.Length(txt) = 0 then
null
else
let
pos = Text.PositionOf(txt, " ", Occurrence.Last)
in
if pos = -1 then
// brak spacji - cały tekst jest jednym "słowem"
txt
else
Text.Middle(txt, pos + 1)
in
wynikTaka formuła poprawnie obsługuje:
- opisy jednowyrazowe,
- standardowe opisy z wieloma słowami,
- większość przypadków z prawidłowo oczyszczonymi spacjiami i końcówkami.
Usuwanie znaków interpunkcyjnych z końca ostatniego słowa
Jeśli w danych występują opisy kończące się kropką czy przecinkiem („1L.”, „250 ml,”), rozsądnie jest oczyścić już same ostatnie słowo, nawet jeśli wcześniej usunięto typowe znaki końcowe z całego opisu. Zdarzają się bowiem także inne, niestandardowe znaki.
Kolumnę OstatnieSlowo można od razu zapisać w wersji z czyszczeniem znaków końcowych:
= let
txt = [Opis_Czysty],
raw =
if txt = null or Text.Length(txt) = 0 then
null
else
let
pos = Text.PositionOf(txt, " ", Occurrence.Last)
in
if pos = -1 then txt else Text.Middle(txt, pos + 1),
cleaned =
if raw = null then
null
else
Text.TrimEnd(raw, {".", ",", ";", ":", ")", "]"})
in
cleanedW ten sposób wartości „50ml.”, „ml,” czy „250ml)” zostaną sprowadzone do „50ml”, „ml” i „250ml”.
Ostatnie słowo jako pojemność lub rozmiar – prosty filtr logiczny
Częsty scenariusz: ostatnie słowo zawiera pojemność lub rozmiar, ale nie zawsze. Żeby wykorzystać je np. w filtrach raportu, można:
- wyciągnąć ostatnie słowo (
OstatnieSlowo), - sprawdzić, czy zawiera cyfry (pojemność/rozmiar),
- opcjonalnie oddzielić część liczbową od jednostki.
Przykładowa kolumna CzyAtrybutNaKoncu (prawda/fałsz):
= let
sl = [OstatnieSlowo],
wynik =
if sl = null or Text.Length(sl) = 0 then
false
else
// sprawdź, czy w ostatnim słowie występuje jakakolwiek cyfra
let
chars = Text.ToList(sl),
maCyfre = List.AnyTrue(List.Transform(chars, each Value.Is(Value.FromText(_), type number)))
in
maCyfre
in
wynik
Gdy CzyAtrybutNaKoncu jest równe true, można dalej rozdzielić OstatnieSlowo na część liczbową (np. „50”) i jednostkę („ml”, „L”, „g”, „kg”, „XL”). W przypadku braku cyfry ostatnie słowo jest najprawdopodobniej zwykłym członem opisu („męska”, „dziecięcy”, „standard”) i może być ignorowane w analizie atrybutów.
Bezpieczne podejście przy pustych i jednowyrazowych opisach
Jeśli pierwsze i ostatnie słowo mają być później łączone, filtrowane lub przekazywane do modelu danych, opłaca się zadbać o spójną logikę zwracania wartości w przypadkach skrajnych:
- null – gdy opis jest pusty lub
null, - identyczne wartości w kolumnach
PierwszeSlowoiOstatnieSlowo– gdy opis składa się z jednego słowa, - opcjonalna flaga informująca, że opis jest jednowyrazowy.
Flagę można zbudować na podstawie pozycji spacji:
Flaga jednowyrazowego opisu i spójna obsługa wyjątków
Flaga jednowyrazowego opisu przydaje się m.in. przy budowaniu reguł DAX, filtrowaniu błędnych rekordów albo przy łączeniu tabel słownikowych. Da się ją zbudować na kilka sposobów, np. poprzez policzenie spacji albo szukanie pierwszej i ostatniej spacji.
Prosta wersja oparta na liczbie spacji w Opis_Czysty:
= let
txt = [Opis_Czysty],
wynik =
if txt = null or Text.Length(txt) = 0 then
null
else
let
// lista wszystkich pozycji spacji
chars = Text.ToList(txt),
spacePositions =
List.Positions(
List.Select(chars, (c) => c = " ")
)
in
// jeśli brak spacji - opis jednowyrazowy
List.Count(spacePositions) = 0
in
wynikBardziej bezpośrednia wersja, korzystająca z dwóch wyszukiwań spacji:
= let
txt = [Opis_Czysty],
wynik =
if txt = null or Text.Length(txt) = 0 then
null
else
let
firstSpace = Text.PositionOf(txt, " "),
lastSpace = Text.PositionOf(txt, " ", Occurrence.Last)
in
// brak spacji w ogóle
if firstSpace = -1 then
true
else
false
in
wynikW modelu danych taka flaga pozwala np. pominąć jednowyrazowe opisy przy analizie atrybutów (brak rozdzielnych członów marka/nazwa/pojemność) albo oznaczyć je jako kandydatów do ręcznej weryfikacji.
Jedna funkcja M zwracająca pierwsze i ostatnie słowo oraz flagi
Jeśli podobne operacje mają być używane w kilku zapytaniach, sensowne jest zbudowanie jednej, wielokrotnego użytku funkcji w M, która:
- przyjmuje pojedynczy tekst (opis produktu),
- czyści go w podstawowym zakresie,
- zwraca rekord zawierający pierwsze słowo, ostatnie słowo oraz kilka flag logicznych.
Przykładowa funkcja fxOpisNaSlowa:
let
fxOpisNaSlowa = (opis as nullable text) as nullable record =>
let
txt0 =
if opis = null then
null
else
Text.Trim(opis),
wynik =
if txt0 = null or Text.Length(txt0) = 0 then
null
else
let
// bazowe czyszczenie: zamiana wielokrotnych spacji na pojedynczą
partsRaw = List.Select(Text.SplitAny(txt0, " "), each _ <> ""),
txt = Text.Combine(partsRaw, " "),
// pierwsze słowo
firstSpace = Text.PositionOf(txt, " "),
firstWord =
if firstSpace = -1 then
txt
else
Text.BeforeDelimiter(txt, " "),
// ostatnie słowo (z oczyszczaniem końcówki)
lastSpace = Text.PositionOf(txt, " ", Occurrence.Last),
lastRaw =
if lastSpace = -1 then
txt
else
Text.Middle(txt, lastSpace + 1),
lastWord = Text.TrimEnd(lastRaw, {".", ",", ";", ":", ")", "]"}),
// flagi techniczne
isSingleWord = firstSpace = -1,
isNullOrEmpty = txt0 = null or Text.Length(txt0) = 0
in
[
OryginalnyOpis = opis,
OpisCzysty = txt,
PierwszeSlowo = firstWord,
OstatnieSlowo = lastWord,
JednoSlowo = isSingleWord,
PustyLubNull = isNullOrEmpty
]
in
wynik
in
fxOpisNaSlowaTaką funkcję można następnie zastosować do kolumny tabeli:
= Table.AddColumn(
#"Poprzedni krok",
"Slowa",
each fxOpisNaSlowa([Opis_Produkt]),
type nullable record
)A potem rozwinąć rekord na kolumny: PierwszeSlowo, OstatnieSlowo, JednoSlowo itd. Dzięki temu cała logika utrzymana jest w jednym miejscu, a pojedyncze zapytania pozostają czytelne.
Rozdzielenie ostatniego słowa na wartość liczbową i jednostkę
W opisach produktów ostatnie słowo często zawiera jednocześnie liczbę i jednostkę („50ml”, „0,5L”, „10x50ml”). Do celów analitycznych wygodniej jest przechowywać je w dwóch osobnych kolumnach: Atrybut_Wartosc i Atrybut_Jednostka. Stopień komplikacji zależy od standardu zapisów w firmie.
Prosty wariant zakłada, że liczba występuje na początku ostatniego słowa, a jednostka na końcu:
= let
sl = [OstatnieSlowo],
wynik =
if sl = null or Text.Length(sl) = 0 then
[Wartosc = null, Jednostka = null]
else
let
chars = Text.ToList(sl),
// podział znaku na "cyfra lub separator liczb" vs "reszta"
isNumericChar = (c as text) as logical =>
List.Contains({"0".."9"} & {",", "."}, c),
// znajdź ciąg początkowy złożony z cyfr i ewentualnie ,/.
numericPrefix =
Text.Combine(
List.FirstN(
chars,
(n) => isNumericChar(n)
)
),
lenNum = Text.Length(numericPrefix),
unit =
if lenNum = 0 then
null
else
Text.Range(sl, lenNum)
in
if lenNum = 0 then
[Wartosc = null, Jednostka = null]
else
[Wartosc = numericPrefix, Jednostka = Text.Trim(unit)]
in
wynikPo dodaniu takiej kolumny typu rekord można rozwinąć ją na dwie kolumny: Atrybut_WartoscTekst i Atrybut_Jednostka. W kolejnym kroku warto przekształcić wartość tekstową na liczbę, uwzględniając lokalne ustawienia separatora dziesiętnego.
Konwersja kolumny tekstowej Atrybut_WartoscTekst na liczbę przy użyciu ustawień regionalnych:
= Table.TransformColumnTypes(
#"Poprzedni krok",
{{"Atrybut_WartoscTekst", type number}},
"pl-PL"
)Jeśli w danych pojawiają się zapisy typu „10x50ml”, potrzebna jest bardziej zaawansowana logika – można jednak wykorzystać analogiczne podejście: rozdzielać części po „x”, przeliczać na całkowitą ilość produktu lub przechowywać osobno liczbę opakowań i pojemność jednostkową.
Obsługa popularnych jednostek pojemności i rozmiaru
Na etapie pracy z ostatnim słowem dobrze jest od razu znormalizować jednostki – dzięki temu w raporcie nie trzeba później obsługiwać osobno „ML”, „ml”, „Ml” i „mililitr”. Do prostych transformacji wystarczy lista dozwolonych jednostek i słownik zamiany.
Przykładowa normalizacja jednostki atrybutu:
= let
unit = Text.Upper([Atrybut_Jednostka]),
mapaJednostek = [
"ML" = "ml",
"L" = "l",
"G" = "g",
"KG" = "kg",
"SZT" = "szt.",
"XL" = "XL",
"LITR" = "l",
"LITRY" = "l"
],
wynik =
if unit = null or Text.Length(unit) = 0 then
null
else if Record.HasFields(mapaJednostek, unit) then
Record.Field(mapaJednostek, unit)
else
[Atrybut_Jednostka] // jednostka nierozpoznana - pozostaw oryginał
in
wynikTakie mapowanie można łatwo rozwinąć o kolejne skróty, np. w odpowiedniej tabeli parametrycznej. W wielu projektach wykorzystywana jest po prostu osobna tabela Jednostki, łączona potem relacją z tabelą faktów po znormalizowanym skrócie.
Wykrywanie „fałszywych” atrybutów na końcu opisu
Nie każde ostatnie słowo zawierające literę i cyfrę jest atrybutem technicznym. Często pojawiają się rozmiary odzieży („XL”, „XXL”), symbole kolorów („R01”), a nawet fragmenty nazw własnych. Żeby ograniczyć błędne klasyfikacje, można dodać kilka prostych reguł filtrujących.
Przykładowa logika:
- sprawdzenie, czy ostatnie słowo pasuje do prostego wzorca liczbowego (np.
d+([.,]d+)?na początku), - sprawdzenie, czy po wycięciu części liczbowej jednostka występuje na liście dozwolonych jednostek,
- oznaczenie takich przypadków flagą
CzyAtrybutTechniczny.
Przybliżona implementacja bez użycia wyrażeń regularnych (wykorzystuje wcześniejsze rozdzielenie na wartość i jednostkę):
= let
wartoscTxt = [Atrybut_WartoscTekst],
jednostka = [Atrybut_Jednostka],
wynik =
if wartoscTxt = null or jednostka = null then
false
else
let
// próba konwersji wartości na liczbę
wartoscNum =
try Number.FromText(wartoscTxt)
otherwise null,
// prosta kontrola, czy jednostka jest z akceptowanej listy
jednostkiDozwolone = {"ml", "l", "g", "kg", "szt.", "cm", "mm", "m", "XL", "L", "M", "S"},
okJednostka = List.Contains(jednostkiDozwolone, jednostka)
in
(wartoscNum <> null) and okJednostka
in
wynik
Dzięki temu w kolumnie CzyAtrybutTechniczny można odsiać opisy, gdzie ostatnie słowo nie spełnia minimalnych kryteriów bycia pojemnością czy rozmiarem.
Łączenie logiki pierwszego i ostatniego słowa w jednym kroku tabeli
W mniejszych modelach wystarczy kilka osobnych kolumn. Przy dużych tabelach produktów lepiej jednak połączyć wyliczenia pierwszego i ostatniego słowa w jednym kroku Power Query – ogranicza to liczbę przebiegów po tekście i poprawia czytelność skryptu.
Przykład kolumny niestandardowej Tokens, która zwraca rekord z najczęściej używanymi elementami:
= let
txt0 = [Opis_Produkt],
wynik =
if txt0 = null then
null
else
let
// bazowe czyszczenie
txtTrim = Text.Trim(txt0),
parts = List.Select(Text.SplitAny(txtTrim, " "), each _ <> ""),
txt = Text.Combine(parts, " "),
// pierwsze i ostatnie słowo
firstSpace = Text.PositionOf(txt, " "),
lastSpace = Text.PositionOf(txt, " ", Occurrence.Last),
firstWord =
if txt = "" then
null
else if firstSpace = -1 then
txt
else
Text.BeforeDelimiter(txt, " "),
lastRaw =
if txt = "" then
null
else if lastSpace = -1 then
txt
else
Text.Middle(txt, lastSpace + 1),
lastWord =
if lastRaw = null then
null
else
Text.TrimEnd(lastRaw, {".", ",", ";", ":", ")", "]"}),
// flagi
isSingleWord = (txt <> "") and (firstSpace = -1),
// prosty atrybut na podstawie ostatniego słowa
lastChars = if lastWord = null then {} else Text.ToList(lastWord),
containsDigit = List.AnyTrue(List.Transform(lastChars, each Value.Is(Value.FromText(_), type number)))
in
[
OpisCzysty = txt,
PierwszeSlowo = firstWord,
OstatnieSlowo = lastWord,
JednoSlowo = isSingleWord,
KoniecMaCyfre = containsDigit
]
in
wynikPo dodaniu takiej kolumny można ją rozwinąć na pola składowe, a w kolejnych krokach rozszerzać logikę (np. odwzorowanie marki, parsowanie atrybutu technicznego, mapowanie jednostek). Cały proces utrzymuje w jednym miejscu zarówno czyszczenie, jak i wyodrębnianie najważniejszych fragmentów opisu produktu.
Najczęściej zadawane pytania (FAQ)
Po co w ogóle wyodrębniać pierwsze i ostatnie słowo z opisu produktu w Power Query?
Najczęstszy powód to rozbicie długiego, nieustrukturyzowanego opisu na elementy, które da się łatwo filtrować i grupować: marka (pierwsze słowo), wariant/pojemność/rozmiar (ostatnie słowo), linia, model. Dzięki temu raporty sprzedaży można analizować po marce, rozmiarze czy generacji modelu, zamiast ręcznie przeglądać tekstowe opisy.
Jeśli opierasz rabaty, segmenty klientów lub planowanie stanów magazynowych na konkretnych markach czy wariantach, automatyczne wyciągnięcie pierwszego i ostatniego słowa z opisu produktu w Power Query pozwala zbudować powtarzalną, odporną na błędy logikę zamiast ciągle ręcznie poprawiać dane.
Jak w Power Query wyciągnąć pierwsze słowo z opisu produktu?
Najprościej jest najpierw wyczyścić tekst (przyciąć spacje), a potem skorzystać z funkcji M, które działają na ciągach znaków. Typowy schemat to: Text.Trim (usunięcie spacji z początku i końca), a następnie szukanie pozycji pierwszej spacji i użycie Text.Start lub podziału tekstu na listę słów.
W praktyce często stosuje się podejście „podziel na listę słów po spacji, usuń puste pozycje i weź pierwszy element listy”. Taka metoda jest odporna na podwójne spacje i nietypowe odstępy, które w rzeczywistych opisach występują bardzo często.
Jak w Power Query pobrać ostatnie słowo z opisu, gdy są dodatkowe spacje lub kropki na końcu?
Kluczowy krok to wyczyszczenie końca tekstu. Zazwyczaj zaczyna się od Text.Trim lub Text.TrimEnd, a następnie usuwa znaki interpunkcyjne z końca (kropki, przecinki, średniki). Dopiero po takim czyszczeniu sensowne jest szukanie ostatniej spacji albo dzielenie tekstu na listę słów i pobranie ostatniego elementu.
Jeśli opisy potrafią wyglądać jak „Fairy Płyn do naczyń 1L.”, bez usunięcia kropki ostatnim „słowem” będzie „1L.”, co utrudni filtrowanie po pojemności. Dobrze zbudowana transformacja w Power Query zacznie więc od pozbycia się końcowych znaków, a dopiero potem wyciągnie ostatni wyraz.
Co zrobić w Power Query z jednowyrazowymi opisami typu „Voucher” albo samym kodem „XY-1234”?
W takich przypadkach pierwsze i ostatnie „słowo” to ten sam fragment tekstu. Logika w Power Query powinna to uwzględniać: jeśli w opisie nie ma żadnej spacji, nie ma sensu szukać pierwszego i ostatniego wyrazu – po prostu zwracasz cały tekst zarówno jako pierwsze, jak i jako ostatnie słowo.
Warto dodać warunek: jeśli liczba elementów po podziale tekstu na słowa jest równa 1, to pierwsze i ostatnie słowo są identyczne. Dzięki temu unikniesz błędów przy danych zawierających tylko kody produktów lub skrótowe opisy usług.
Jak obsłużyć puste opisy produktów przy wyciąganiu pierwszego i ostatniego słowa?
Najbezpieczniej jest na początku sprawdzić, czy wartość w kolumnie opisu jest pusta (null) lub po przycięciu spacji staje się pustym tekstem. Jeśli tak, funkcja zwracająca pierwsze lub ostatnie słowo powinna oddać pustą wartość (null) albo np. specjalny znacznik „brak opisu” – zależnie od polityki danych w firmie.
Brak takiego warunku powoduje błędy kroków typu „podziel tekst” czy „weź pierwszy/ostatni element listy”. Dobrze przygotowana logika w Power Query rozpoznaje brak opisu i nie próbuje na siłę wyciągać z niego słów.
Kiedy lepiej użyć Power Query zamiast formuł Excela do wyodrębniania słów z opisu?
Formuły Excela (LEWY, PRAWY, FRAGMENT.TEKSTU, ZNAJDŹ) wystarczą, gdy jednorazowo obrabiasz niewielką tabelę i nie planujesz powtarzać tego procesu. Jeśli jednak: pliki z opisami przychodzą regularnie, danych są tysiące wierszy, a logika ma być identyczna w wielu raportach – Power Query jest znacznie stabilniejszym rozwiązaniem.
W Power Query raz opisujesz ciąg transformacji, w tym wyodrębnianie pierwszego i ostatniego słowa, a potem przy każdym odświeżeniu ta sama logika działa na nowych plikach. Nie trzeba kopiować formuł między arkuszami, co redukuje liczbę błędów i oszczędza czas całego zespołu.
Czy da się w Power Query rozbić opis na więcej elementów niż tylko pierwsze i ostatnie słowo (np. marka, linia, model, wariant)?
Tak, jeśli opisy produktów mają w miarę powtarzalną strukturę, z jednego pola tekstowego można zbudować kilka kolumn pomocniczych. Typowy schemat to: pierwsze słowo jako marka, środkowa część opisu jako linia/model, a ostatnie słowo (lub dwa słowa) jako wariant, rozmiar czy generacja.
W praktyce najczęściej stosuje się kombinację: czyszczenie tekstu, podział na listę słów, a następnie składanie odpowiednich segmentów z powrotem w nowe kolumny. Dzięki temu sprzedaż może raportować po marce i linii, logistyka po pojemności, a controlling po grupach asortymentu, korzystając z tych samych, raz przygotowanych kroków w Power Query.






