Artykuły | 28 kwiecień, 2019

Power Query M – jak pisać skrypty z wykorzystaniem języka M

Wszystko, co zostało zaprojektowane w dodatku Power Query, jest tłumaczone na język M. W tym artykule przeczytasz, czym jest Microsoft Power Query, poznasz podstawy języka M, najważniejsze komendy i polecenia, a także techniki, które mogą pomóc podczas pracy z Power Query M i uczynić ten dodatek jeszcze bardziej przydatnym.

Power Query M – jak pisać skrypty z wykorzystaniem języka M

Czym jest Power Query?

Microsoft Power Query to dodatek do pracy z danymi w Excelu. Umożliwia pobranie danych z różnych miejsc oraz zintegrowanie i przygotowanie do wizualizacji w ciągu kilku minut za pomocą paru kliknięć – bez programowania. Dodatek Microsoft Power Query (PQ) został zaprojektowany przez firmę Microsoft do wspierania rozwiązań klasy Self-Service Business Intelligence. Po raz pierwszy został udostępniony w programie Excel 2010 dla systemu operacyjnego Windows. W Microsoft 365 dodatek jest dostępny z poziomu wstążki w: Przekształcanie danych (karta Dane).

Power Query a Power BI

Dodatek Power Query może być używany w wielu produktach, jak na przykład Microsoft Power BI czy Excel.  Może być on pomocny w codziennej pracy z danymi z różnych źródeł danych, w gromadzeniu oraz czerpaniu z nich wiedzy. Power Query pozwala pobierać dane z wielu różnych źródeł (relacyjne bazy danych, dane pochodzące z SharePointa,  systemu operacyjnego, dane z wybranych stron internetowych). Umożliwia również wstępną obróbkę danych oraz przygotowanie ich do dalszej analizy lub wizualizacji. Wszystko to można zrobić za pomocą specjalnego języka „M”, lub za pomocą kilku ruchów myszy.

Język m, czyli pisanie skryptu w Power Query

Dlaczego uczyć się pisać coś, co jest generowane z interfejsu przez program? Przede wszystkim głównym powodem jest to, iż nie wszystkie funkcjonalności zostały wdrożone bezpośrednio w menu graficznym Power Query. Ingerencja w kod skryptu oprócz wykorzystywania tych gotowych, ale „ukrytych” z poziomu menu, funkcji oraz transformacji pozwala również tworzyć funkcje własne i obsługi błędu co jeszcze bardziej zwiększa zakres przydatności tego narzędzia. O tym właśnie będzie dzisiejszy post. Post ma na celu przybliżyć podstawowe założenia języka M, pomóc zrozumieć co dzieje się w pasku formuły programu, przedstawić podstawowe komendy i polecenia oraz przykład skryptu, który realizuje konkretne zadanie. Zostanie on podzielony na dwie części:

  • Wstęp, założenia oraz koncepcje języka,
  • Jak to wygląda w praktyce, czyli przykład.
Power Query - Excel

W tym momencie zostanie utworzone automatycznie nowe źródło dla PQ oraz ukaże się jego okno edycji. Na zakładce „Home” wszystkie dostępne transformacje oraz polecenia będą jednak niedostępne, ponieważ w naszym źródle tak naprawdę nie ma żadnej definicji i nie wskazuje na żadne dane / żadne miejsce z danymi. Transformacje są niedostępne, ponieważ nie ma danych, na których te transformacje można by wykorzystać. Aby przejść do właściwego edytora skryptu, należy wybrać zakładkę „View” oraz następnie „Advanced Editor”.

Teraz na ekranie pojawi się edytor wraz ze szkieletem skryptu. Tak naprawdę szkielet ten prezentuje kompletne minimum, które musi posiadać każdy skrypt – po usunięciu dowolnego elementu z tego szkieletu skrypt będzie nieprawidłowy i pojawi się ostrzeżenie. Na tym etapie warto zaznaczyć, że Power Query rozróżnia wielkość liter i tak na przykład zamiana słowa kluczowego „let” na „Let” w skrypcie spowoduje, że przestanie być on prawidłowy. Tyczy się to zarówno poleceń, jak i nazw zmiennych.

Skrypt Power Query zawsze składa się z kombinacji dwóch elementów poprzedzonych dwoma słowami kluczowymi: „let” oraz „in”. Wyrażenie „let” poprzedza wszystkie „kroki” w skrypcie, czyli np. „Source = „””. Krok jest odzwierciedleniem pewnego stanu w skrypcie, do którego można się odwoływać, ale również reprezentuje daną wartość/wartości lub funkcję. Póki co może to wydawać się nieintuicyjne, ale wkrótce na pewno się rozjaśni. Dla przykładu zmiana powyższego skryptu na:

let

Source = 1

in

Source

… spowoduje, że „zmienna” czy też słowo kluczowe o nazwie „Source” będzie od tej pory równe 1. Użycie natomiast tego słowa jako źródła za poleceniem „in” sprawi, że Power Query przejdzie do danego kroku, czyli zwróci wartość 1.

Power Query

Po zakończeniu edycji źródła w arkuszu pojawi się wartość „1”.

W przypadku zmiany kodu na:

let

Source = 1,

Source2 = 2

in

Source

Skrypt również zwróci wartość 1, ponieważ w źródle („in”) jest odwołanie właśnie do „Source”. Pokazuje to właśnie schemat działania Power Query, który nie zatrzymuje się na ostatnim zdefiniowanym poleceniu (kroku), tylko wskazanym po słowie kluczowym „in”. W tym miejscu należy również dodać, że poszczególne kroki (zwane dalej również poleceniami) należy oddzielać przecinkiem. Warto również nadmienić, że w jednym skrypcie można łączyć wiele skryptów, to znaczy w jednym skrypcie można zapisać wiele sekwencji typu „let … in …”. Jak widać, pisanie skryptów w Power Query może trochę przypominać między innymi pisanie skryptów w języku R czy dla programu MatLab.

Podstawy zostały przedstawione już wyżej, ale uściślając, to przede wszystkim podczas pisania skryptów w języku M należy szczególnie pamiętać o tym, że:

  • W skrypcie musi znajdować się przynajmniej jedna para słów kluczowych „let” i „in” oraz przynajmniej jedno polecenie oraz odwołanie do niego,
  • Power Query rozróżnia wielkość liter,
  • Każde polecenie musi być oddzielone za pomocą przecinka,
  • Nie można używać dwa razy polecenia (kroku) o tej samej nazwie.

Na tym etapie warto również dodać, że aby dodać komentarz do skryptu, można użyć następujących kombinacji:

// to jest komentarz jednolinijkowy

/* to jest komentarz,

Który może znajdować się w wielu liniach */

Kolejną istotną kwestią w każdym języku są typy danych oraz ich obsługa przez środowisko. Power Query automatycznie identyfikuje typ danych – analizuje oraz sam sprawdza, jaki typ danych zwróci poszczególne polecenie (krok) i dlatego nie ma potrzeby jawnego definiowania typu. Oczywiście można zmienić typ danego atrybutu lub rzutować do konkretnego typu, ale nie jest to zupełnie konieczne przy deklaracji. Oznacza to zatem, że wszystkie z poniższych poleceń będą poprawne:

a = 1, // liczba 1

b= “jeden”, // słowo jeden

c=1.11, // liczba zmiennoprzecinkowa 1.11

d = null // wartość null

Co więcej następujące definicje również będą poprawne:

a= 1 + 1.11, // a będzie równe 2.11

b = “hello” & ” ” & “world”// a będzie równie: „hello world”

Listy, rekordy, tablice

Oprócz tradycyjnych typów danych język M wyróżnia również pewne złożone struktury: listy, rekordy oraz tablice.

Lista

Lista to uporządkowany zbiór wartości (wartości mogą być dowolnego typu). Definiuje się ją w klamrach.

a = {1, null, “jeden”} ,// lista z trzema elementami: numerem, wartością null oraz słowem

b= {1, 2, 3} // lista trzech numerów

Rekord

Rekord to zbiór pól, przy czym pole należy rozumieć jako parę nazwa oraz wartość, czyli na przykład nazwa: rok, wartość: 2014.

b=

[

A = 1,

B = “jeden”,

C = null

]

Tablica

Tablica to zbiór rekordów lub pól zorganizowanych w kolumny. Tabelę tworzy się za pomocą funkcji #table(). Na przykład następujące polecenie utworzy tablicę o wymiarach 2×2

b= #table( {“kolumna 1”, “kolumna 2”}, { {“wiersz 1 kolumn 1”, “wiersz 1 kolumn 2”}, {“wiersz 2 kolumn 1”, “wierwsz 2 kolumna 2”} } )

Jak już zaznaczono, do konkretnych kroków można oprócz wartości definiować funkcje.

let

myFunction = (x) =>

{

x*2

},

a = myFunction(2)

in

a

Funkcje tworzy się za pomocą operatora „=>”, a poprzedzając go w nawiasie, definiuje się nazwy parametrów wejściowych funkcji. Power Query sam analizuje oraz sam obsługuje typ danych, jaki funkcja będzie zwracać (mimo wszystko można rzutować do konkretnego typu). Jak widać, powyższa funkcja przyjmuje jeden parametr, czyli zmienną o nazwie x, a treść funkcji wskazuje, że jest ona odpowiedzialna za podwojenie wartości przekazywanego do niej parametru.

Oczywiście w PQ istnieje mnóstwo gotowych, predefiniowanych funkcji i transformacji, które można w skrypcie wykorzystać. Najpopularniejsze zostaną zaprezentowane poniżej.

Zobacz, jak to wygląda w praktyce. Skrypt języka M

Praktycznym przykładem będzie skrypt języka M. Niecierpliwych i dociekliwych zapraszam do całego opisu wszystkich funkcji oraz kompleksowych omówień, które można znaleźć w literaturze, którą podam na końcu.

Poniżej zostaną zaprezentowane wybrane funkcje oraz sposób rozwiązania jednego z problemów, jaki można zrealizować z wykorzystaniem Power Querry i języka M.

Celem przykładu jest przeprowadzenie podstawowej analizy tekstu. Dla tego celu pobrano jedną z dostępnych książek w formacie txt ze strony projektu Gutenberg (http://www.gutenberg.org), a konkretnie tekst książki „Alicja w krainie czarów” oraz zapisano go na dysku twardym komputera. Założeniem jest zbadanie podstawowych parametrów analizy tekstu, takich jak:

  • liczba słów
  • liczba unikalnych słów
  • usunięcie słów niekluczowych (stopwords)
  • sprawdzenie najczęściej występujących słów
  • sprawdzenie najczęściej występujących słów z pominięciem słów niekluczowych
  • sprawdzenie najczęściej występujących n-gramów w tekście (2-gramów)

W tym celu zostaną przygotowane trzy skrypty, które zbadają częstotliwość pojedynczych słów dla całego tekstu, częstotliwość słów z pominięciem słów niekluczowych oraz częstotliwość 2-gramów. Reszta zagadek rozwiąże się tak naprawdę sama i będzie możliwa do odczytania po wykonaniu tych trzech skryptów. Wszystkie trzy skrypty są do siebie bardzo podobne i różnią się kilkoma funkcjami, tutaj zostanie zaprezentowany jeden z nich.

Na samym początku funkcja, która pozwala usunąć wszystkie znaki inne aniżeli litery. Patrząc pod kątem analizy tekstu znaki typu przecinek, kropka lub wykrzyknik są zwykle bezwartościowe i są usuwane z badanego tekstu. W tym celu należy skorzystać z funkcji Text.Remove(), która z podanego ciągu znaków usuwa wskazane znaki. W tym przypadku ciąg tekstowy jest przekazywany jako parametr funkcji, a listę znaków specjalnych zdefiniowano bezpośrednio w funkcji.

uncRemoveSpecialCharacters = (x) =>

{

Text.Remove(x,

{

“0”, “1”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”,

“`”, “~”, “!”, “@”, “#”, “$”, “%”, “^”, “&”, “*”,

“(“, “)”, “-“, “_”, “=”, “+”, “{“, “[“, “]”, “}”,

“;”, “:”, “‘”, “”””,””, “|”, “”, “|”, “,”, “<“,

“.”, “>”, “”, “?”,

“

“

}

)

Następnie, w pierwszej linii kodu, należy wczytać dane z pliku tekstowego. Tak naprawdę cała operacja musi zostać podzielona na kilka etapów: wczytanie pliku jako pliku binarnego:

File.Contents(), konwersja z pliku binarnego na zestaw linii tekstu Lines.FromBinary(), konwersja tych linii do tablicy Table.FromColumns().

Table.FromColumns({Lines.FromBinary(File.Contents(“C:TmpAlice.txt”),null,null,1250

Przed przejściem do jakichkolwiek dalszych operacji trzeba, jak wyżej wspomniano, usunąć wszystkie znaki specjalne. W tym celu skorzystano z wcześniej przygotowanej funkcji, a także wykorzystano jeden z tricków w PQ. Mianowicie, w języku M, nie ma czegoś takiego jak pętle. Wszystkie operacje definiowane są tak naprawdę na podstawie kolumn, a nie rekordów, ale można skorzystać z metody, która jest wykorzystywana podczas dodawania nowej kolumny, który zawiera słowo kluczowe „each” i dla każdego rekordu nowej kolumny wykonuje jakąś operację dla każdego rekordu właśnie. Do osiągnięcia powyższego celu można również użyć kilku innych funkcji, które działają na podobnej zasadzie. Tak więc należy wykorzystać funkcję Table.AddColumn(), skorzystać z wcześniej wczytanych danych „sourceData”, wskazać nazwę dla nowej kolumny „DataWithoutStopWords” oraz wskazać, aby dla każdego rekordu w nowej kolumnie wartość z kolumny Column1 została przetworzona za pomocą przygotowanej funkcji.

_RemoveSpecialCharacters = Table.AddColumn(sourceData, “DataWithoutStopWords”, each funcRemoveSpecialCharacters([Column1]) ),

Jako, że zdefiniowana funkcja zwraca wartości nie jako ciąg znaków, ale listę wartości (mimo, że jest to lista jednoelementowa), należy przed dalszymi operacjami zmienić typ kolumny, a właściwie to „rozwinąć” listę dla każdego rekordu, używając funkcji Table.ExpandListColumn() oraz wskazując nazwę źródła danych „RemoveSpecialCharacters” oraz nazwę kolumny w tym źródle, która zawiera listę „DataWithoutStopWords”

_RemovedSpecialCharacters = Table.ExpandListColumn(_RemoveSpecialCharacters , “DataWithoutStopWords”),

Teraz, pierwotna kolumna z tekstem wraz ze znakami specjalnymi jest niepotrzebna i można ją usunąć. Służy do tego polecenie Table.RemoveColumns(), gdzie wskazuje się źródło danych oraz kolumnę lub listę kolumn, które są niepotrzebne.

removedSourceColumn = Table.RemoveColumns(_RemovedSpecialCharacters,{“Column1”}),

W celu lepszego przygotowania danych do analizy należy jeszcze zmienić wszystkie litery w tekście na małe (lub wielkie), ponieważ wyraz „kot” i „KOT” w późniejszej analizie reprezentowany by był przez dwa różne słowa podczas gdy jest to to naprawdę jedno słowo. W tym celu wystarczy użyć funkcji Table.TransformColumns(), w której należy wskazać źródło danych „removedSourceColumn”, wskazać kolumny „{}” – czyli wszystkie, oraz wybrać rodzaj transformacji: Text.Lower odpowiada właśnie za zamianę liter z wielkich na małe.

loweredText = Table.TransformColumns(removedSourceColumn,{},Text.Lower),

Po tych operacjach w kolejnych rekordach tabeli znajdują się kolejne linie tekstu z pliku, które są przygotowane do analizy. Do analizy wykorzystywane jednak powinny być pojedyncze słowa, a nie całe linie tekstu. Nadszedł więc czas, aby podzielić poszczególne linie na słowa. Aby podział ten był w pełni dynamiczny, można użyć pewnej sztuczki. Funkcja Table.SplitColumn(), która pozwala podzielić ciąg znaków względem znaku, oczekuje podania liczby kolumn, na które mają one zostać podzielone, i dzieli dany tekst na tablicę, w której kolejna kolumna to następny wyraz – jeżeli linia ma mniej wyrazów niż poprzednia, to komórka posiada wartość null, aby kod był w pełni dynamiczny, trzeba zatem najpierw określić w danym tekście maksymalną liczbę słów w linii. Oto przykładowy sposób rozwiązania tego problemu. Najpierw należy sprawdzić, ile w danej linii jest słów; aby to zrobić, można za pomocą funkcji Text.PositionOf() pobrać listę wszystkich pozycji znaku spacji – w tym celu użyto przełącznika Occurrence.All. Pozycje te nie są jednak ważne w tym przypadku, ale przy pomocy funkcji List.Count można policzyć ile było wystąpień w danym tekście – ile razy pojawiła się spacja (jedna pozycja w liście odpowiada jednej spacji, a jedna spacja jednemu słowu; licząc ilość elementów listy, można określić liczbę słów). Operacje taką można osadzić w funkcji dołączenia nowej kolumny, co spowoduje, że zostanie ona powtórzona dla każdego rekordu. Wynikiem będzie oryginalna linia tekstu oraz liczba wyrazów.

countedWords = Table.AddColumn(loweredText , “NumOfWord”, each List.Count(Text.PositionOf([DataWithoutStopWords], ” “, Occurrence.All))),

Dla pewności można zmienić typ nowododanej kolumny na numeryczny za pomocą funkcji Table. TransformColumnTypes.

changedType = Table.TransformColumnTypes(countedWords ,{{“NumOfWord”, type number}}),

W tym przypadku ważna jest jednak tylko największa wartość spośród wszystkich, tak więc należy posortować dane według ilości wyrazów, malejąco za pomocą funkcji Table.Sort()

sortedRows= Table.Sort(changedType ,{{“NumOfWord”, Order.Descending}}),

Następnie należy usunąć kolumnę z tekstem i zostawić tylko tę z liczbą słów. W tym celu trzeba użyć funkcji Table.RemoveColumns()

RemoveUnnecessaryCols= Table.RemoveColumns(sortedRows,{“DataWithoutStopWords”}),

Teraz wystarczy wybrać pierwszy z góry rekord, który będzie odpowiadał największej liczbie słów w linii. Do tego służy funkcja Table.FirstN(), która przyjmuje wyłącznie źródło danych oraz liczbę topowych rekordów do pobrania.

KeptFirstRows = Table.FirstN(RemoveUnnecessaryCols,1) ,

Poprzednia operacja zwróciła jednak wynik jako wartość listy, więc, aby otrzymać finalną odpowiedź, należy wybrać za pomocą funkcji List.First() pierwszy element listy.

maxNumberOfWordValue= List.First(Table.Column(KeptFirstRows , “NumOfWord”)),

Posiadając wiedzę o maksymalnej ilości słów w liniach, można użyć do podziału funkcji Splitter.SplitTextByDelimiter() wskazując znak podziału jako znak spacji oraz liczbę kolumn jako wcześniej wyliczoną wartość.

splitedColumnByDelimiter = Table.SplitColumn(loweredText ,”DataWithoutStopWords”,Splitter.SplitTextByDelimiter(” “),maxNumberOfWordValue),

W wyniku takiego podziału zwrócona została jednak tablica, która zawiera kilkadziesiąt kolumn z pojedynczymi słowami. Aby spłaszczyć te dane do jednej kolumny, można użyć funkcji Table.Unpivot(). Wymaga ona jednak podania nazwy kolumn, które mają zostać poddane procesowi spłaszczenia. Kontynuując skrypt z myślą o uniwersalności, należy listę tych kolumn dostarczyć za pomocą samego skryptu. Nie jest to jednak problemem w przypadku języka M i można w tym celu wykorzystać funkcję Table.ColumnNames(), która dla podanego źródła danych listę wszystkich kolumn.

addedColumnNames = Table.ColumnNames(splitedColumnByDelimiter ),

Poniżej przykład wyniku po podziale tekstu na słowa.

Teraz wreszcie można spłaszczyć dane do jednej kolumny tak, aby każde słowo było w osobnym wierszu. W tym celu należy użyć funkcji Table.Unpivot(), wskazać odpowiednie źródło „splitedColumnByDelimiter”, listę kolumn „addedColumnNames” oraz nazwy kolumn, które mają zostać poddane powstać po przekształceniu. Warto zaznaczyć, że wymagane jest podanie dwóch kolumn, z czego pierwsza będzie prezentować nazwę kolumny, z której pochodzi dana wartość, a druga właśnie wartość.

unpivoted = Table.Unpivot(splitedColumnByDelimiter ,addedColumnNames ,”key”,”value”),

Nie jest ona jednak zupełnie potrzebna, więc można ją po prostu usunąć funkcją Table.RemoveColumns()

removedColumns = Table.RemoveColumns(unpivoted,{“key”}),

Na końcu dla pewności można wybrać z tablicy tylko niepuste wartości. Można to zrobić za pomocą polecenia Table.SelectRows wskazując, że do tablicy ma zostać zakwalifikowana tylko i wyłącznie wartość różna od ciągu pustego „”.

filteredRows = Table.SelectRows(replacedValue, each ([value] <> “”)),

Ostatnim krokiem jest policzenie konkretnych wystąpień słów w tabeli. Można w tym celu wykorzystać funkcję grupującą Table.Group(), która sama zliczy, w ilu wierszach pojawiła się dana wartość.

GroupedRows = Table.Group(filteredRows, {“value”}, {{“Count”, each Table.RowCount(_), type number}}),

Przed zwróceniem danych do warstwy prezentacyjnej można jeszcze zmienić nazwy poszczególnych kolumn, aby były jednoznacznie identyfikowalne.

RenamedColumns = Table.RenameColumns(GroupedRows,{{“value”, “Word”}, {“Count”, “Quantity”}})

Cały kod prezentuje się następująco:

let

/* ================================ FUNCTIONS ================================= */

funcRemoveSpecialCharacters = (x) =>

{

Text.Remove(x,

{

“0”, “1”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”,

“`”, “~”, “!”, “@”, “#”, “$”, “%”, “^”, “&”, “*”,

“(“, “)”, “-“, “_”, “=”, “+”, “{“, “[“, “]”, “}”,

“;”, “:”, “‘”, “”””,””, “|”, “”, “|”, “,”, “<“,

“.”, “>”, “”, “?”,

“

“

}

)

},

// get max number of space in rows

countedWords = Table.AddColumn(loweredText , “NumOfWord”, each List.Count(Text.PositionOf([DataWithoutStopWords], ” “, Occurrence.All))),

changedType = Table.TransformColumnTypes(countedWords ,{{“NumOfWord”, type number}}),

sortedRows= Table.Sort(changedType ,{{“NumOfWord”, Order.Descending}}),

RemoveUnnecessaryCols= Table.RemoveColumns(sortedRows,{“DataWithoutStopWords”}),

KeptFirstRows = Table.FirstN(RemoveUnnecessaryCols,1) ,

maxNumberOfWordValue= List.First(Table.Column(KeptFirstRows , “NumOfWord”)),

/* ================================ OPERATIONS ================================= */

sourceData = Table.FromColumns({Lines.FromBinary(File.Contents(“C:TmpAlice.txt”),null,null,1250)}),

_RemoveSpecialCharacters = Table.AddColumn(sourceData, “DataWithoutStopWords”, each funcRemoveSpecialCharacters([Column1]) ),

_RemovedSpecialCharacters = Table.ExpandListColumn(_RemoveSpecialCharacters , “DataWithoutStopWords”),

removedSourceColumn = Table.RemoveColumns(_RemovedSpecialCharacters,{“Column1”}),

loweredText = Table.TransformColumns(removedSourceColumn,{},Text.Lower),

splitedColumnByDelimiter = Table.SplitColumn(loweredText ,”DataWithoutStopWords”,Splitter.SplitTextByDelimiter(” “),maxNumberOfWordValue),

addedColumnNames = Table.ColumnNames(splitedColumnByDelimiter ),

unpivoted = Table.Unpivot(splitedColumnByDelimiter ,addedColumnNames ,”key”,”value”),

removedColumns = Table.RemoveColumns(unpivoted,{“key”}),

removedErrors = Table.RemoveRowsWithErrors(removedColumns, {“value”}),

replacedValue = Table.ReplaceValue(removedErrors,” “,””,Replacer.ReplaceText,{“value”}),

filteredRows = Table.SelectRows(replacedValue, each ([value] <> “”)),

GroupedRows = Table.Group(filteredRows, {“value”}, {{“Count”, each Table.RowCount(_), type number}}),

RenamedColumns = Table.RenameColumns(GroupedRows,{{“value”, “Word”}, {“Count”, “Quantity”}})

in

RenamedColumns

Pozostałe skrypty są niemalże bliźniacze. Poniżej zostaną zaprezentowane tylko różnice. W przypadku skryptu, w którym lista słów miała zostać pomniejszona o słowa, które są tzw. stopwords, dodano trzy elementy. Pierwszym z nich jest wczytanie do tabeli listy takich słów z jednej ze stron interenetowych.

pp = Table.ToList(Table.FromColumns({Lines.FromBinary(Web.Contents(“http://jmlr.org/papers/volume5/lewis04a/a11-smart-stop-list/english.stop”),null,null,1250)})),

Drugim natomiast jest funkcja functionCheckIfStopword, która wyraz przekazywany do funkcji wykorzystuje w funkcji języka M List.Contains(), która porównuje go z listą wyrazów typu stopwords i zwraca warunek logiczny. Funkcja ta jest wykorzystywana tak jak poprzednio podczas operacji dodawania nowej kolumny.

functionCheckIfStopword = (x) =>

{

List.Contains(pp, x)

},

Na koniec wybierane są tylko te słowa, których funkcja nie znalazła na liście ze strony i dla których zwróciła wartość false.

filteredRowsFalse = Table.SelectRows(expandedColumn, each ([Custom] = false)),

W przypadku ostatniego skryptu została dopisana metoda tworzenia n-gramów,a konkretnie 2-gramów. Sztuczka polega na wstawieniu za pomocą funkcji Table.AddIndexColumn() nowej kolumny, która będzie zawierać numer wiersza – indeks,

insertedIndex = Table.AddIndexColumn(filteredRows ,”Index”)

Następnie za pomocą funkcji Table.Range() można pobrać z tej samej tabeli, która jest bieżącym zestawem danych wyraz z rekordu bieżącego oraz wyraz następny. Następnie należy wynik tej funkcji przekształcić na listę za pomocą funkcji Table.ToList() oraz połączyć poszczególne elementy listy – czyli poszczególne wyrazy – przecinkiem za pomocą funkcji Text.Combine().

,addedGram = Table.AddColumn(insertedIndex, “Word”, each Text.Combine(Table.ToList(Table.Range(filteredRows ,[Index],2)), ” “) ),

Ja z analizy tekstu „Alicji w Krainie Czarów” otrzymano wyniki takie jak na grafice poniżej. Prawda, że Power Query jest fajny? Zainteresowanym polecam przede wszystkim materiały dostępne na stronie: http://office.microsoft.com/en-us/excel-help/learn-about-power-query-formulas-HA104003958.aspx

Power Query przykład

Konsultant Business Intelligence, który na co dzień projektuje i implementuje rozwiązania BI w Microsoft SQL Server, korzystając zarówno z klasycznych narzędzi, jak i funkcjonalności Power BI. Posiada doświadczenie w pracy z Oracle, SAP, IBM, QlikView oraz Pervasive.

Zapisz się do newslettera, ekskluzywna zawartość czeka

Bądź na bieżąco z najnowszymi artykułami i wydarzeniami IT

Informacje dotyczące przetwarzania danych osobowych

Zapisz się do newslettera, ekskluzywna zawartość czeka

Bądź na bieżąco z najnowszymi artykułami i wydarzeniami IT

Informacje dotyczące przetwarzania danych osobowych

Zapisz się do newslettera, aby pobrać plik

Bądź na bieżąco z najnowszymi artykułami i wydarzeniami IT

Informacje dotyczące przetwarzania danych osobowych

Dziękujemy za zapis na newsletter — został ostatni krok do aktywacji

Potwierdź poprawność adresu e-mail klikając link wiadomości, która została do Ciebie wysłana w tej chwili.

 

Jeśli w czasie do 5 minut w Twojej skrzynce odbiorczej nie będzie wiadomości to sprawdź również folder *spam*.

Twój adres e-mail znajduje się już na liście odbiorców newslettera

Wystąpił nieoczekiwany błąd

Spróbuj ponownie za chwilę.

    Get notified about new articles

    Be a part of something more than just newsletter

    I hereby agree that Inetum Polska Sp. z o.o. shall process my personal data (hereinafter ‘personal data’), such as: my full name, e-mail address, telephone number and Skype ID/name for commercial purposes.

    I hereby agree that Inetum Polska Sp. z o.o. shall process my personal data (hereinafter ‘personal data’), such as: my full name, e-mail address and telephone number for marketing purposes.

    Read more

    Just one click away!

    We've sent you an email containing a confirmation link. Please open your inbox and finalize your subscription there to receive your e-book copy.

    Note: If you don't see that email in your inbox shortly, check your spam folder.