{"id":29598,"date":"2019-04-28T08:36:02","date_gmt":"2019-04-28T06:36:02","guid":{"rendered":"https:\/\/nearshore-it.eu\/artykuly\/microsoft-power-query-edytor-kodu-zobacz-jak-pisac-skrypty-w-jezyku-m\/"},"modified":"2024-11-07T15:43:47","modified_gmt":"2024-11-07T14:43:47","slug":"microsoft-power-query-edytor-kodu-zobacz-jak-pisac-skrypty-w-jezyku-m","status":"publish","type":"post","link":"https:\/\/nearshore-it.eu\/pl\/artykuly\/microsoft-power-query-edytor-kodu-zobacz-jak-pisac-skrypty-w-jezyku-m\/","title":{"rendered":"Power Query M \u2013 jak pisa\u0107 skrypty z wykorzystaniem j\u0119zyka M"},"content":{"rendered":"\n<div class=\"table-of-contents\">\n    <p class=\"title\">Id\u017a do:<\/p>\n    <ol>\n                    <li><a href=\"#Czym-jest-Power-Query-?\">1.  Czym jest Power Query?<\/a><\/li>\n                    <li><a href=\"#Power-Query-a-Power-BI\">2.  Power Query a Power BI<\/a><\/li>\n                    <li><a href=\"#Pisanie-skryptu-w-Power-Query\">3.  Pisanie skryptu w Power Query<\/a><\/li>\n                    <li><a href=\"#Listy-rekordy-tablice\">4.  Listy, rekordy, tablice<\/a><\/li>\n            <\/ol>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"Czym-jest-Power-Query-?\">Czym jest Power Query?<\/h2>\n\n\n\n<p>Microsoft Power Query to dodatek do pracy z danymi w Excelu. Umo\u017cliwia pobranie danych z r\u00f3\u017cnych miejsc oraz zintegrowanie i przygotowanie do wizualizacji w ci\u0105gu kilku minut za pomoc\u0105 paru klikni\u0119\u0107 \u2013 bez programowania. <strong>Dodatek Microsoft Power Query (PQ<\/strong>) zosta\u0142 zaprojektowany przez&nbsp;firm\u0119 Microsoft do&nbsp;wspierania rozwi\u0105za\u0144 klasy&nbsp;<a href=\"https:\/\/nearshore-it.eu\/pl\/artykuly\/narzedzia-business-intelligence-dla-managera-cz-2-microsoft-bi\/\" target=\"_blank\" rel=\"noreferrer noopener\"><strong>Self-Service Business Intelligence<\/strong><\/a>. Po raz pierwszy zosta\u0142 udost\u0119pniony w<strong> programie Excel 2010 dla systemu operacyjnego Windows. <\/strong>W <strong>Microsoft 365<\/strong> dodatek jest dost\u0119pny z poziomu wst\u0105\u017cki w: <strong>Przekszta\u0142canie danych<\/strong> (karta <strong>Dane<\/strong>).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Power-Query-a-Power-BI\">Power Query a Power BI<\/h2>\n\n\n\n<p>Dodatek Power Query mo\u017ce by\u0107 u\u017cywany w wielu produktach, <a href=\"https:\/\/docs.microsoft.com\/pl-pl\/power-query\/power-query-what-is-power-query\" target=\"_blank\" rel=\"noopener noreferrer\">jak na przyk\u0142ad Microsoft Power BI czy Excel.&nbsp; <\/a>Mo\u017ce by\u0107 on pomocny w&nbsp;codziennej pracy z&nbsp;danymi z r\u00f3\u017cnych \u017ar\u00f3de\u0142 danych, w gromadzeniu oraz&nbsp;czerpaniu z&nbsp;nich wiedzy. Power Query pozwala pobiera\u0107 dane z&nbsp;wielu r\u00f3\u017cnych \u017ar\u00f3de\u0142 (relacyjne bazy danych, dane pochodz\u0105ce z&nbsp;SharePointa, &nbsp;systemu operacyjnego, dane z&nbsp;wybranych stron internetowych). Umo\u017cliwia r\u00f3wnie\u017c wst\u0119pn\u0105 obr\u00f3bk\u0119 danych oraz&nbsp;przygotowanie ich do&nbsp;dalszej analizy lub wizualizacji. Wszystko to&nbsp;mo\u017cna zrobi\u0107&nbsp;za&nbsp;pomoc\u0105 specjalnego j\u0119zyka \u201eM\u201d, lub za&nbsp;pomoc\u0105 kilku ruch\u00f3w myszy.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Pisanie-skryptu-w-Power-Query\">J\u0119zyk m, czyli pisanie skryptu w Power Query<\/h2>\n\n\n\n<p>Dlaczego uczy\u0107 si\u0119 pisa\u0107 co\u015b, co&nbsp;jest generowane z&nbsp;interfejsu przez&nbsp;program? Przede wszystkim g\u0142\u00f3wnym powodem jest to, i\u017c&nbsp;nie wszystkie funkcjonalno\u015bci zosta\u0142y wdro\u017cone bezpo\u015brednio w&nbsp;menu graficznym<strong>&nbsp;Power Query<\/strong>. Ingerencja w&nbsp;kod skryptu opr\u00f3cz wykorzystywania tych gotowych, ale&nbsp;\u201eukrytych\u201d z&nbsp;poziomu menu, funkcji oraz&nbsp;transformacji pozwala r\u00f3wnie\u017c tworzy\u0107 funkcje w\u0142asne i obs\u0142ugi b\u0142\u0119du co&nbsp;jeszcze bardziej zwi\u0119ksza zakres przydatno\u015bci tego narz\u0119dzia. O&nbsp;tym w\u0142a\u015bnie b\u0119dzie dzisiejszy post. Post ma&nbsp;na&nbsp;celu przybli\u017cy\u0107&nbsp;<strong>podstawowe za\u0142o\u017cenia j\u0119zyka M<\/strong>, pom\u00f3c zrozumie\u0107 co dzieje si\u0119 w pasku formu\u0142y programu, przedstawi\u0107 podstawowe komendy i&nbsp;polecenia oraz&nbsp;przyk\u0142ad skryptu, kt\u00f3ry&nbsp;realizuje konkretne zadanie. Zostanie on&nbsp;podzielony na&nbsp;dwie cz\u0119\u015bci:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Wst\u0119p, za\u0142o\u017cenia oraz&nbsp;koncepcje j\u0119zyka,<\/li>\n\n\n\n<li>Jak to&nbsp;wygl\u0105da w&nbsp;praktyce, czyli przyk\u0142ad.<\/li>\n<\/ul>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/pl\/artykuly\/wp-content\/uploads\/2023\/04\/1-Power-Query-Inetum-1.jpg\" alt=\"Power Query - Excel\" class=\"wp-image-25642\" title=\"\"><\/figure>\n<\/div>\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>W tym momencie zostanie utworzone automatycznie nowe \u017ar\u00f3d\u0142o dla PQ oraz uka\u017ce si\u0119 jego okno edycji. Na zak\u0142adce <strong>\u201eHome\u201d<\/strong> wszystkie dost\u0119pne transformacje oraz polecenia b\u0119d\u0105 jednak niedost\u0119pne, poniewa\u017c w naszym \u017ar\u00f3dle tak naprawd\u0119 nie ma \u017cadnej definicji i nie wskazuje na \u017cadne dane \/ \u017cadne miejsce z danymi. Transformacje s\u0105 niedost\u0119pne, poniewa\u017c nie ma danych, na kt\u00f3rych te transformacje mo\u017cna by wykorzysta\u0107. Aby przej\u015b\u0107 do w\u0142a\u015bciwego edytora skryptu, nale\u017cy wybra\u0107 zak\u0142adk\u0119<strong> \u201eView\u201d<\/strong> oraz nast\u0119pnie <strong>\u201eAdvanced Editor\u201d.<\/strong><\/p>\n\n\n\n<p>Teraz na ekranie pojawi si\u0119 edytor wraz ze szkieletem skryptu. Tak naprawd\u0119 szkielet ten prezentuje kompletne minimum, kt\u00f3re musi posiada\u0107 ka\u017cdy skrypt \u2013 po usuni\u0119ciu dowolnego elementu z tego szkieletu skrypt b\u0119dzie nieprawid\u0142owy i pojawi si\u0119 ostrze\u017cenie. Na tym etapie warto zaznaczy\u0107, \u017ce Power Query rozr\u00f3\u017cnia wielko\u015b\u0107 liter i tak na przyk\u0142ad zamiana s\u0142owa kluczowego \u201elet\u201d na \u201eLet\u201d w skrypcie spowoduje, \u017ce przestanie by\u0107 on prawid\u0142owy. Tyczy si\u0119 to zar\u00f3wno polece\u0144, jak i nazw zmiennych.<\/p>\n\n\n\n<p>Skrypt Power Query zawsze sk\u0142ada si\u0119 z&nbsp;kombinacji dw\u00f3ch element\u00f3w poprzedzonych dwoma s\u0142owami kluczowymi:&nbsp;<strong>\u201elet\u201d oraz&nbsp;\u201ein\u201d.<\/strong>&nbsp;Wyra\u017cenie &#8222;let&#8221; poprzedza wszystkie \u201ekroki\u201d w&nbsp;skrypcie, czyli np.&nbsp;\u201eSource = \u201e\u201d\u201d. Krok jest odzwierciedleniem pewnego stanu w&nbsp;skrypcie, do&nbsp;kt\u00f3rego&nbsp;mo\u017cna si\u0119 odwo\u0142ywa\u0107, ale&nbsp;r\u00f3wnie\u017c reprezentuje dan\u0105 warto\u015b\u0107\/warto\u015bci lub funkcj\u0119. P\u00f3ki co&nbsp;mo\u017ce to&nbsp;wydawa\u0107 si\u0119 nieintuicyjne, ale&nbsp;wkr\u00f3tce na&nbsp;pewno si\u0119 rozja\u015bni. Dla przyk\u0142adu zmiana powy\u017cszego skryptu na:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">let\n\nSource = 1\n\nin\n\nSource<\/pre>\n\n\n\n<p>\u2026 spowoduje, \u017ce \u201ezmienna\u201d czy te\u017c s\u0142owo kluczowe o nazwie \u201eSource\u201d b\u0119dzie od tej pory r\u00f3wne 1. U\u017cycie natomiast tego s\u0142owa jako \u017ar\u00f3d\u0142a za poleceniem \u201ein\u201d sprawi, \u017ce Power Query przejdzie do danego kroku, czyli zwr\u00f3ci warto\u015b\u0107 1.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/pl\/artykuly\/wp-content\/uploads\/2023\/04\/5-Power-Query-Inetum.jpg\" alt=\"Power Query\" class=\"wp-image-25643\" title=\"\"><\/figure>\n<\/div>\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Po zako\u0144czeniu edycji \u017ar\u00f3d\u0142a w arkuszu pojawi si\u0119 warto\u015b\u0107 \u201e1\u201d.<\/p>\n\n\n\n<p>W przypadku zmiany kodu na:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">let\n\nSource = 1,\n\nSource2 = 2\n\nin\n\nSource<\/pre>\n\n\n\n<p>Skrypt r\u00f3wnie\u017c zwr\u00f3ci warto\u015b\u0107 1, poniewa\u017c w \u017ar\u00f3dle (\u201ein\u201d) jest odwo\u0142anie w\u0142a\u015bnie do \u201eSource\u201d. Pokazuje to w\u0142a\u015bnie schemat dzia\u0142ania Power Query, kt\u00f3ry nie zatrzymuje si\u0119 na ostatnim zdefiniowanym poleceniu (kroku), tylko wskazanym po s\u0142owie kluczowym \u201ein\u201d. W tym miejscu nale\u017cy r\u00f3wnie\u017c doda\u0107, \u017ce poszczeg\u00f3lne kroki (zwane dalej r\u00f3wnie\u017c poleceniami) nale\u017cy oddziela\u0107 przecinkiem. Warto r\u00f3wnie\u017c nadmieni\u0107, \u017ce w jednym skrypcie mo\u017cna \u0142\u0105czy\u0107 wiele skrypt\u00f3w, to znaczy w jednym skrypcie mo\u017cna zapisa\u0107 wiele sekwencji typu \u201elet \u2026 in \u2026\u201d. Jak wida\u0107, pisanie skrypt\u00f3w w Power Query mo\u017ce troch\u0119 przypomina\u0107 mi\u0119dzy innymi pisanie skrypt\u00f3w w j\u0119zyku R czy dla programu MatLab.<\/p>\n\n\n\n<p>Podstawy zosta\u0142y przedstawione ju\u017c wy\u017cej, ale u\u015bci\u015blaj\u0105c, to przede wszystkim podczas pisania skrypt\u00f3w w j\u0119zyku M nale\u017cy szczeg\u00f3lnie pami\u0119ta\u0107 o tym, \u017ce:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>W skrypcie musi znajdowa\u0107 si\u0119 przynajmniej jedna para s\u0142\u00f3w kluczowych \u201elet\u201d i \u201ein\u201d oraz przynajmniej jedno polecenie oraz odwo\u0142anie do niego,<\/li>\n\n\n\n<li>Power Query rozr\u00f3\u017cnia wielko\u015b\u0107 liter,<\/li>\n\n\n\n<li>Ka\u017cde polecenie musi by\u0107 oddzielone za pomoc\u0105 przecinka,<\/li>\n\n\n\n<li>Nie mo\u017cna u\u017cywa\u0107 dwa razy polecenia (kroku) o tej samej nazwie.<\/li>\n<\/ul>\n\n\n\n<p>Na tym etapie warto r\u00f3wnie\u017c doda\u0107, \u017ce aby doda\u0107 komentarz do skryptu, mo\u017cna u\u017cy\u0107 nast\u0119puj\u0105cych kombinacji:<\/p>\n\n\n\n<p class=\"text-danger\"><strong>\/\/ to jest komentarz jednolinijkowy<\/strong><\/p>\n\n\n\n<p class=\"text-danger\"><strong>\/* to jest komentarz,<\/strong><\/p>\n\n\n\n<p class=\"text-danger\"><strong>Kt\u00f3ry mo\u017ce znajdowa\u0107 si\u0119 w wielu liniach *\/<\/strong><\/p>\n\n\n\n<p>Kolejn\u0105 istotn\u0105 kwesti\u0105 w ka\u017cdym j\u0119zyku s\u0105 typy danych oraz ich obs\u0142uga przez \u015brodowisko. Power Query automatycznie identyfikuje typ danych \u2013 analizuje oraz sam sprawdza, jaki typ danych zwr\u00f3ci poszczeg\u00f3lne polecenie (krok) i dlatego nie ma potrzeby jawnego definiowania typu. Oczywi\u015bcie mo\u017cna zmieni\u0107 typ danego atrybutu lub rzutowa\u0107 do konkretnego typu, ale nie jest to zupe\u0142nie konieczne przy deklaracji. Oznacza to zatem, \u017ce wszystkie z poni\u017cszych polece\u0144 b\u0119d\u0105 poprawne:<\/p>\n\n\n\n<p class=\"text-danger\"><strong>a = 1, \/\/ liczba 1<\/strong><\/p>\n\n\n\n<p class=\"text-danger\"><strong>b= \u201cjeden\u201d, \/\/ s\u0142owo jeden<\/strong><\/p>\n\n\n\n<p class=\"text-danger\"><strong>c=1.11, \/\/ liczba zmiennoprzecinkowa 1.11<\/strong><\/p>\n\n\n\n<p class=\"text-danger\"><strong>d = null \/\/ warto\u015b\u0107 null<\/strong><\/p>\n\n\n\n<p>Co wi\u0119cej nast\u0119puj\u0105ce definicje r\u00f3wnie\u017c b\u0119d\u0105 poprawne:<\/p>\n\n\n\n<p class=\"text-danger\"><strong>a= 1 + 1.11, \/\/ a b\u0119dzie r\u00f3wne 2.11<\/strong><\/p>\n\n\n\n<p class=\"text-danger\"><strong>b = \u201chello\u201d &amp; \u201d \u201d &amp; \u201cworld\u201d\/\/ a b\u0119dzie r\u00f3wnie: \u201ehello world\u201d<\/strong><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Listy-rekordy-tablice\">Listy, rekordy, tablice<\/h2>\n\n\n\n<p>Opr\u00f3cz tradycyjnych typ\u00f3w danych j\u0119zyk M wyr\u00f3\u017cnia r\u00f3wnie\u017c pewne z\u0142o\u017cone struktury: listy, rekordy oraz tablice.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"lista\">Lista<\/h3>\n\n\n\n<p>Lista to uporz\u0105dkowany zbi\u00f3r warto\u015bci (warto\u015bci mog\u0105 by\u0107 dowolnego typu). Definiuje si\u0119 j\u0105 w klamrach.<\/p>\n\n\n\n<p class=\"text-danger\"><strong>a = {1, null, \u201cjeden\u201d} ,\/\/ lista z trzema elementami: numerem, warto\u015bci\u0105 null oraz s\u0142owem<\/strong><\/p>\n\n\n\n<p class=\"text-danger\"><strong>b= {1, 2, 3} \/\/ lista trzech numer\u00f3w<\/strong><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"rekord\">Rekord<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"rekord-to-zbior-pol-przy-czym-pole-nalezy-rozumiec-jako-pare-nazwa-oraz-wartosc-czyli-na-przyklad-nazwa-rok-wartosc-2014\">Rekord to zbi\u00f3r p\u00f3l, przy czym pole nale\u017cy rozumie\u0107 jako par\u0119 nazwa oraz warto\u015b\u0107, czyli na przyk\u0142ad nazwa: rok, warto\u015b\u0107: 2014.<\/h3>\n\n\n\n<p class=\"text-danger\"><strong>b=<\/strong><\/p>\n\n\n\n<p class=\"text-danger\"><strong>[<\/strong><\/p>\n\n\n\n<p class=\"text-danger\"><strong>A = 1,<\/strong><\/p>\n\n\n\n<p class=\"text-danger\"><strong>B = \u201cjeden\u201d,<\/strong><\/p>\n\n\n\n<p class=\"text-danger\"><strong>C = null<\/strong><\/p>\n\n\n\n<p class=\"text-danger\"><strong>]<\/strong><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"tablica\">Tablica<\/h3>\n\n\n\n<p>Tablica to zbi\u00f3r rekord\u00f3w lub p\u00f3l zorganizowanych w kolumny. Tabel\u0119 tworzy si\u0119 za pomoc\u0105 funkcji #table(). Na przyk\u0142ad nast\u0119puj\u0105ce polecenie utworzy tablic\u0119 o wymiarach 2\u00d72<\/p>\n\n\n\n<p class=\"text-danger\">b= #table( {\u201ckolumna 1\u201d, \u201ckolumna 2\u201d}, { {\u201cwiersz 1 kolumn 1\u201d, \u201cwiersz 1 kolumn 2\u201d}, {\u201cwiersz 2 kolumn 1\u201d, \u201cwierwsz 2 kolumna 2\u201d} } )<\/p>\n\n\n\n<p>Jak ju\u017c zaznaczono, do konkretnych krok\u00f3w mo\u017cna opr\u00f3cz warto\u015bci definiowa\u0107 funkcje.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">let\n\nmyFunction = (x) =>\n\n{\n\nx*2\n\n},\n\na = myFunction(2)\n\nin\n\na<\/pre>\n\n\n\n<p>Funkcje tworzy si\u0119 za pomoc\u0105 operatora \u201e=&gt;\u201d, a poprzedzaj\u0105c go w nawiasie, definiuje si\u0119 nazwy parametr\u00f3w wej\u015bciowych funkcji. Power Query sam analizuje oraz sam obs\u0142uguje typ danych, jaki funkcja b\u0119dzie zwraca\u0107 (mimo wszystko mo\u017cna rzutowa\u0107 do konkretnego typu). Jak wida\u0107, powy\u017csza funkcja przyjmuje jeden parametr, czyli zmienn\u0105 o nazwie x, a tre\u015b\u0107 funkcji wskazuje, \u017ce jest ona odpowiedzialna za podwojenie warto\u015bci przekazywanego do niej parametru.<\/p>\n\n\n\n<p>Oczywi\u015bcie w PQ istnieje mn\u00f3stwo gotowych, predefiniowanych funkcji i transformacji, kt\u00f3re mo\u017cna w skrypcie wykorzysta\u0107. Najpopularniejsze zostan\u0105 zaprezentowane poni\u017cej.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"zobacz-jak-to-wyglada-w-praktyce-skrypt-jezyka-m\">Zobacz, jak to wygl\u0105da w praktyce. Skrypt j\u0119zyka M<\/h3>\n\n\n\n<p>Praktycznym przyk\u0142adem b\u0119dzie skrypt j\u0119zyka M. Niecierpliwych i dociekliwych zapraszam do ca\u0142ego opisu wszystkich funkcji oraz kompleksowych om\u00f3wie\u0144, kt\u00f3re mo\u017cna znale\u017a\u0107 w literaturze, kt\u00f3r\u0105 podam na ko\u0144cu.<\/p>\n\n\n\n<p>Poni\u017cej zostan\u0105 zaprezentowane wybrane funkcje oraz spos\u00f3b rozwi\u0105zania jednego z problem\u00f3w, jaki mo\u017cna zrealizowa\u0107 z wykorzystaniem Power Querry i j\u0119zyka M.<\/p>\n\n\n\n<p>Celem przyk\u0142adu jest przeprowadzenie podstawowej analizy tekstu. Dla tego celu pobrano jedn\u0105 z dost\u0119pnych ksi\u0105\u017cek w formacie txt ze strony projektu Gutenberg (<a href=\"http:\/\/www.gutenberg.org\" target=\"_blank\" rel=\"noopener\">http:\/\/www.gutenberg.org<\/a>), a konkretnie tekst ksi\u0105\u017cki \u201eAlicja w krainie czar\u00f3w\u201d oraz zapisano go na dysku twardym komputera. Za\u0142o\u017ceniem jest zbadanie podstawowych parametr\u00f3w analizy tekstu, takich jak:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>liczba s\u0142\u00f3w<\/li>\n\n\n\n<li>liczba unikalnych s\u0142\u00f3w<\/li>\n\n\n\n<li>usuni\u0119cie s\u0142\u00f3w niekluczowych (stopwords)<\/li>\n\n\n\n<li>sprawdzenie najcz\u0119\u015bciej wyst\u0119puj\u0105cych s\u0142\u00f3w<\/li>\n\n\n\n<li>sprawdzenie najcz\u0119\u015bciej wyst\u0119puj\u0105cych s\u0142\u00f3w z pomini\u0119ciem s\u0142\u00f3w niekluczowych<\/li>\n\n\n\n<li>sprawdzenie najcz\u0119\u015bciej wyst\u0119puj\u0105cych n-gram\u00f3w w tek\u015bcie (2-gram\u00f3w)<\/li>\n<\/ul>\n\n\n\n<p>W tym celu zostan\u0105 przygotowane trzy skrypty, kt\u00f3re zbadaj\u0105 cz\u0119stotliwo\u015b\u0107 pojedynczych s\u0142\u00f3w dla ca\u0142ego tekstu, cz\u0119stotliwo\u015b\u0107 s\u0142\u00f3w z pomini\u0119ciem s\u0142\u00f3w niekluczowych oraz cz\u0119stotliwo\u015b\u0107 2-gram\u00f3w. Reszta zagadek rozwi\u0105\u017ce si\u0119 tak naprawd\u0119 sama i b\u0119dzie mo\u017cliwa do odczytania po wykonaniu tych trzech skrypt\u00f3w. Wszystkie trzy skrypty s\u0105 do siebie bardzo podobne i r\u00f3\u017cni\u0105 si\u0119 kilkoma funkcjami, tutaj zostanie zaprezentowany jeden z nich.<\/p>\n\n\n\n<p>Na samym pocz\u0105tku funkcja, kt\u00f3ra pozwala usun\u0105\u0107 wszystkie znaki inne ani\u017celi litery. Patrz\u0105c pod k\u0105tem analizy tekstu znaki typu przecinek, kropka lub wykrzyknik s\u0105 zwykle bezwarto\u015bciowe i s\u0105 usuwane z badanego tekstu. W tym celu nale\u017cy skorzysta\u0107 z funkcji <strong>Text.Remove(),<\/strong> kt\u00f3ra z podanego ci\u0105gu znak\u00f3w usuwa wskazane znaki. W tym przypadku ci\u0105g tekstowy jest przekazywany jako parametr funkcji, a list\u0119 znak\u00f3w specjalnych zdefiniowano bezpo\u015brednio w funkcji.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">uncRemoveSpecialCharacters = (x) =>\n\n{\n\nText.Remove(x,\n\n{\n\n\u201c0\u201d, \u201c1\u201d, \u201c2\u201d, \u201c3\u201d, \u201c4\u201d, \u201c5\u201d, \u201c6\u201d, \u201c7\u201d, \u201c8\u201d, \u201c9\u201d,\n\n\u201c`\u201d, \u201c~\u201d, \u201c!\u201d, \u201c@\u201d, \u201c#\u201d, \u201c$\u201d, \u201c%\u201d, \u201c^\u201d, \u201c&amp;\u201d, \u201c*\u201d,\n\n\u201c(\u201c, \u201c)\u201d, \u201c-\u201c, \u201c_\u201d, \u201c=\u201d, \u201c+\u201d, \u201c{\u201c, \u201c[\u201c, \u201c]\u201d, \u201c}\u201d,\n\n\u201c;\u201d, \u201c:\u201d, \u201c\u2018\u201d, \u201c\u201d\u201d\u201d,\u201d\u201d, \u201c|\u201d, \u201c\u201d, \u201c|\u201d, \u201c,\u201d, \u201c&lt;\u201c,\n\n\u201c.\u201d, \u201c>\u201d, \u201c\u201d, \u201c?\u201d,\n\n\u201c\n\n\u201c\n\n}\n\n)\n<\/pre>\n\n\n\n<p>Nast\u0119pnie, w pierwszej linii kodu, nale\u017cy wczyta\u0107 dane z pliku tekstowego. Tak naprawd\u0119 ca\u0142a operacja musi zosta\u0107 podzielona na kilka etap\u00f3w: wczytanie pliku jako pliku binarnego:<\/p>\n\n\n\n<p><strong>File.Contents(), konwersja z pliku binarnego na zestaw linii tekstu Lines.FromBinary(), konwersja tych linii do tablicy Table.FromColumns().<\/strong><\/p>\n\n\n\n<p class=\"text-danger\"><strong>Table.FromColumns({Lines.FromBinary(File.Contents(\u201cC:TmpAlice.txt\u201d),null,null,1250<\/strong><\/p>\n\n\n\n<p>Przed przej\u015bciem do jakichkolwiek dalszych operacji trzeba, jak wy\u017cej wspomniano, usun\u0105\u0107 wszystkie znaki specjalne. W tym celu skorzystano z wcze\u015bniej przygotowanej funkcji, a tak\u017ce wykorzystano jeden z trick\u00f3w w PQ. Mianowicie, w j\u0119zyku M, nie ma czego\u015b takiego jak p\u0119tle. Wszystkie operacje definiowane s\u0105 tak naprawd\u0119 na podstawie kolumn, a nie rekord\u00f3w, ale mo\u017cna skorzysta\u0107 z metody, kt\u00f3ra jest wykorzystywana podczas dodawania nowej kolumny, kt\u00f3ry zawiera s\u0142owo kluczowe \u201eeach\u201d i dla ka\u017cdego rekordu nowej kolumny wykonuje jak\u0105\u015b operacj\u0119 dla ka\u017cdego rekordu w\u0142a\u015bnie. Do osi\u0105gni\u0119cia powy\u017cszego celu mo\u017cna r\u00f3wnie\u017c u\u017cy\u0107 kilku innych funkcji, kt\u00f3re dzia\u0142aj\u0105 na podobnej zasadzie. Tak wi\u0119c nale\u017cy wykorzysta\u0107 funkcj\u0119 <strong>Table.AddColumn(),<\/strong> skorzysta\u0107 z wcze\u015bniej wczytanych danych \u201esourceData\u201d, wskaza\u0107 nazw\u0119 dla nowej kolumny <strong>\u201eDataWithoutStopWords\u201d<\/strong> oraz wskaza\u0107, aby dla ka\u017cdego rekordu w nowej kolumnie warto\u015b\u0107 z kolumny Column1 zosta\u0142a przetworzona za pomoc\u0105 przygotowanej funkcji.<\/p>\n\n\n\n<p><strong><span class=\"text-danger\">_RemoveSpecialCharacters = Table.AddColumn(sourceData, \u201cDataWithoutStopWords\u201d, each funcRemoveSpecialCharacters([Column1]) ),<\/span><\/strong><\/p>\n\n\n\n<p>Jako, \u017ce zdefiniowana funkcja zwraca warto\u015bci nie jako ci\u0105g znak\u00f3w, ale list\u0119 warto\u015bci (mimo, \u017ce jest to lista jednoelementowa), nale\u017cy przed dalszymi operacjami zmieni\u0107 typ kolumny, a w\u0142a\u015bciwie to \u201erozwin\u0105\u0107\u201d list\u0119 dla ka\u017cdego rekordu, u\u017cywaj\u0105c funkcji <strong>Table.ExpandListColumn()<\/strong> oraz wskazuj\u0105c nazw\u0119 \u017ar\u00f3d\u0142a danych \u201eRemoveSpecialCharacters\u201d oraz nazw\u0119 kolumny w tym \u017ar\u00f3dle, kt\u00f3ra zawiera list\u0119 <strong>\u201eDataWithoutStopWords\u201d<\/strong><\/p>\n\n\n\n<p class=\"text-danger\"><strong>_RemovedSpecialCharacters = Table.ExpandListColumn(_RemoveSpecialCharacters , \u201cDataWithoutStopWords\u201d),<\/strong><\/p>\n\n\n\n<p>Teraz, pierwotna kolumna z tekstem wraz ze znakami specjalnymi jest niepotrzebna i mo\u017cna j\u0105 usun\u0105\u0107. S\u0142u\u017cy do tego polecenie <strong>Table.RemoveColumns(),<\/strong> gdzie wskazuje si\u0119 \u017ar\u00f3d\u0142o danych oraz kolumn\u0119 lub list\u0119 kolumn, kt\u00f3re s\u0105 niepotrzebne.<\/p>\n\n\n\n<p class=\"text-danger\"><strong>removedSourceColumn = Table.RemoveColumns(_RemovedSpecialCharacters,{\u201cColumn1\u201d}),<\/strong><\/p>\n\n\n\n<p>W celu lepszego przygotowania danych do analizy nale\u017cy jeszcze zmieni\u0107 wszystkie litery w tek\u015bcie na ma\u0142e (lub wielkie), poniewa\u017c wyraz \u201ekot\u201d i \u201eKOT\u201d w p\u00f3\u017aniejszej analizie reprezentowany by by\u0142 przez dwa r\u00f3\u017cne s\u0142owa podczas gdy jest to to naprawd\u0119 jedno s\u0142owo. W tym celu wystarczy u\u017cy\u0107 funkcji Table.TransformColumns(), w kt\u00f3rej nale\u017cy wskaza\u0107 \u017ar\u00f3d\u0142o danych \u201eremovedSourceColumn\u201d, wskaza\u0107 kolumny \u201e{}\u201d \u2013 czyli wszystkie, oraz wybra\u0107 rodzaj transformacji: Text.Lower odpowiada w\u0142a\u015bnie za zamian\u0119 liter z wielkich na ma\u0142e.<\/p>\n\n\n\n<p class=\"text-danger\"><strong>loweredText = Table.TransformColumns(removedSourceColumn,{},Text.Lower),<\/strong><\/p>\n\n\n\n<p>Po tych operacjach w kolejnych rekordach tabeli znajduj\u0105 si\u0119 kolejne linie tekstu z pliku, kt\u00f3re s\u0105 przygotowane do analizy. Do analizy wykorzystywane jednak powinny by\u0107 pojedyncze s\u0142owa, a nie ca\u0142e linie tekstu. Nadszed\u0142 wi\u0119c czas, aby podzieli\u0107 poszczeg\u00f3lne linie na s\u0142owa. Aby podzia\u0142 ten by\u0142 w pe\u0142ni dynamiczny, mo\u017cna u\u017cy\u0107 pewnej sztuczki. Funkcja <strong>Table.SplitColumn(),<\/strong> kt\u00f3ra pozwala podzieli\u0107 ci\u0105g znak\u00f3w wzgl\u0119dem znaku, oczekuje podania liczby kolumn, na kt\u00f3re maj\u0105 one zosta\u0107 podzielone, i dzieli dany tekst na tablic\u0119, w kt\u00f3rej kolejna kolumna to nast\u0119pny wyraz \u2013 je\u017celi linia ma mniej wyraz\u00f3w ni\u017c poprzednia, to kom\u00f3rka posiada warto\u015b\u0107 null, aby kod by\u0142 w pe\u0142ni dynamiczny, trzeba zatem najpierw okre\u015bli\u0107 w danym tek\u015bcie maksymaln\u0105 liczb\u0119 s\u0142\u00f3w w linii. Oto przyk\u0142adowy spos\u00f3b rozwi\u0105zania tego problemu. Najpierw nale\u017cy sprawdzi\u0107, ile w danej linii jest s\u0142\u00f3w; aby to zrobi\u0107, mo\u017cna za pomoc\u0105 funkcji <strong>Text.PositionOf()<\/strong> pobra\u0107 list\u0119 wszystkich pozycji znaku spacji \u2013 w tym celu u\u017cyto prze\u0142\u0105cznika <strong>Occurrence.All.<\/strong> Pozycje te nie s\u0105 jednak wa\u017cne w tym przypadku, ale przy pomocy funkcji <strong>List.Count<\/strong> mo\u017cna policzy\u0107 ile by\u0142o wyst\u0105pie\u0144 w danym tek\u015bcie \u2013 ile razy pojawi\u0142a si\u0119 spacja (jedna pozycja w li\u015bcie odpowiada jednej spacji, a jedna spacja jednemu s\u0142owu; licz\u0105c ilo\u015b\u0107 element\u00f3w listy, mo\u017cna okre\u015bli\u0107 liczb\u0119 s\u0142\u00f3w). Operacje tak\u0105 mo\u017cna osadzi\u0107 w funkcji do\u0142\u0105czenia nowej kolumny, co spowoduje, \u017ce zostanie ona powt\u00f3rzona dla ka\u017cdego rekordu. Wynikiem b\u0119dzie oryginalna linia tekstu oraz liczba wyraz\u00f3w.<\/p>\n\n\n\n<p class=\"text-danger\"><strong>countedWords = Table.AddColumn(loweredText , \u201cNumOfWord\u201d, each List.Count(Text.PositionOf([DataWithoutStopWords], \u201d \u201c, Occurrence.All))),<\/strong><\/p>\n\n\n\n<p>Dla pewno\u015bci mo\u017cna zmieni\u0107 typ nowododanej kolumny na numeryczny za pomoc\u0105 funkcji <strong>Table. TransformColumnTypes.<\/strong><\/p>\n\n\n\n<p class=\"text-danger\"><strong>changedType = Table.TransformColumnTypes(countedWords ,{{\u201cNumOfWord\u201d, type number}}),<\/strong><\/p>\n\n\n\n<p>W tym przypadku wa\u017cna jest jednak tylko najwi\u0119ksza warto\u015b\u0107 spo\u015br\u00f3d wszystkich, tak wi\u0119c nale\u017cy posortowa\u0107 dane wed\u0142ug ilo\u015bci wyraz\u00f3w, malej\u0105co za pomoc\u0105 funkcji T<strong>able.Sort()<\/strong><\/p>\n\n\n\n<p class=\"text-danger\"><strong>sortedRows= Table.Sort(changedType ,{{\u201cNumOfWord\u201d, Order.Descending}}),<\/strong><\/p>\n\n\n\n<p>Nast\u0119pnie nale\u017cy usun\u0105\u0107 kolumn\u0119 z tekstem i zostawi\u0107 tylko t\u0119 z liczb\u0105 s\u0142\u00f3w. W tym celu trzeba u\u017cy\u0107 funkcji <strong>Table.RemoveColumns()<\/strong><\/p>\n\n\n\n<p class=\"text-danger\"><strong>RemoveUnnecessaryCols= Table.RemoveColumns(sortedRows,{\u201cDataWithoutStopWords\u201d}),<\/strong><\/p>\n\n\n\n<p>Teraz wystarczy wybra\u0107 pierwszy z g\u00f3ry rekord, kt\u00f3ry b\u0119dzie odpowiada\u0142 najwi\u0119kszej liczbie s\u0142\u00f3w w linii. Do tego s\u0142u\u017cy funkcja <strong>Table.FirstN(),<\/strong> kt\u00f3ra przyjmuje wy\u0142\u0105cznie \u017ar\u00f3d\u0142o danych oraz liczb\u0119 topowych rekord\u00f3w do pobrania.<\/p>\n\n\n\n<p class=\"text-danger\"><strong>KeptFirstRows = Table.FirstN(RemoveUnnecessaryCols,1) ,<\/strong><\/p>\n\n\n\n<p>Poprzednia operacja zwr\u00f3ci\u0142a jednak wynik jako warto\u015b\u0107 listy, wi\u0119c, aby otrzyma\u0107 finaln\u0105 odpowied\u017a, nale\u017cy wybra\u0107 za&nbsp;pomoc\u0105 funkcji&nbsp;<strong>List.First()<\/strong>&nbsp;pierwszy element listy.<\/p>\n\n\n\n<p class=\"text-danger\"><strong>maxNumberOfWordValue= List.First(Table.Column(KeptFirstRows , \u201cNumOfWord\u201d)),<\/strong><\/p>\n\n\n\n<p>Posiadaj\u0105c wiedz\u0119 o maksymalnej ilo\u015bci s\u0142\u00f3w w liniach, mo\u017cna u\u017cy\u0107 do podzia\u0142u funkcji <strong>Splitter.SplitTextByDelimiter()<\/strong> wskazuj\u0105c znak podzia\u0142u jako znak spacji oraz liczb\u0119 kolumn jako wcze\u015bniej wyliczon\u0105 warto\u015b\u0107.<\/p>\n\n\n\n<p class=\"text-danger\"><strong>splitedColumnByDelimiter = Table.SplitColumn(loweredText ,\u201dDataWithoutStopWords\u201d,Splitter.SplitTextByDelimiter(\u201d \u201c),maxNumberOfWordValue),<\/strong><\/p>\n\n\n\n<p>W wyniku takiego podzia\u0142u zwr\u00f3cona zosta\u0142a jednak tablica, kt\u00f3ra zawiera kilkadziesi\u0105t kolumn z pojedynczymi s\u0142owami. Aby sp\u0142aszczy\u0107 te dane do jednej kolumny, mo\u017cna u\u017cy\u0107 funkcji <strong>Table.Unpivot().<\/strong> Wymaga ona jednak podania nazwy kolumn, kt\u00f3re maj\u0105 zosta\u0107 poddane procesowi sp\u0142aszczenia. Kontynuuj\u0105c skrypt z my\u015bl\u0105 o uniwersalno\u015bci, nale\u017cy list\u0119 tych kolumn dostarczy\u0107 za pomoc\u0105 samego skryptu. Nie jest to jednak problemem w przypadku j\u0119zyka M i mo\u017cna w tym celu wykorzysta\u0107 funkcj\u0119 <strong>Table.ColumnNames(),<\/strong> kt\u00f3ra dla podanego \u017ar\u00f3d\u0142a danych list\u0119 wszystkich kolumn.<\/p>\n\n\n\n<p class=\"text-danger\"><strong>addedColumnNames = Table.ColumnNames(splitedColumnByDelimiter ),<\/strong><\/p>\n\n\n\n<p>Poni\u017cej przyk\u0142ad wyniku po podziale tekstu na s\u0142owa.<\/p>\n\n\n\n<p>Teraz wreszcie mo\u017cna sp\u0142aszczy\u0107 dane do jednej kolumny tak, aby ka\u017cde s\u0142owo by\u0142o w osobnym wierszu. W tym celu nale\u017cy u\u017cy\u0107 funkcji<strong> Table.Unpivot(),<\/strong> wskaza\u0107 odpowiednie \u017ar\u00f3d\u0142o <strong>\u201esplitedColumnByDelimiter\u201d,<\/strong> list\u0119 kolumn \u201eaddedColumnNames\u201d oraz nazwy kolumn, kt\u00f3re maj\u0105 zosta\u0107 poddane powsta\u0107 po przekszta\u0142ceniu. Warto zaznaczy\u0107, \u017ce wymagane jest podanie dw\u00f3ch kolumn, z czego pierwsza b\u0119dzie prezentowa\u0107 nazw\u0119 kolumny, z kt\u00f3rej pochodzi dana warto\u015b\u0107, a druga w\u0142a\u015bnie warto\u015b\u0107.<\/p>\n\n\n\n<p class=\"text-danger\"><strong>unpivoted = Table.Unpivot(splitedColumnByDelimiter ,addedColumnNames ,\u201dkey\u201d,\u201dvalue\u201d),<\/strong><\/p>\n\n\n\n<p>Nie jest ona jednak zupe\u0142nie potrzebna, wi\u0119c mo\u017cna j\u0105 po prostu usun\u0105\u0107 funkcj\u0105 <strong>Table.RemoveColumns()<\/strong><\/p>\n\n\n\n<p class=\"text-danger\"><strong>removedColumns = Table.RemoveColumns(unpivoted,{\u201ckey\u201d}),<\/strong><\/p>\n\n\n\n<p>Na ko\u0144cu dla pewno\u015bci mo\u017cna wybra\u0107 z tablicy tylko niepuste warto\u015bci. Mo\u017cna to zrobi\u0107 za pomoc\u0105 polecenia <strong>Table.SelectRow<\/strong>s wskazuj\u0105c, \u017ce do tablicy ma zosta\u0107 zakwalifikowana tylko i wy\u0142\u0105cznie warto\u015b\u0107 r\u00f3\u017cna od ci\u0105gu pustego \u201e\u201d.<\/p>\n\n\n\n<p class=\"text-danger\"><strong>filteredRows = Table.SelectRows(replacedValue, each ([value] &lt;&gt; \u201c\u201d)),<\/strong><\/p>\n\n\n\n<p>Ostatnim krokiem jest policzenie konkretnych wyst\u0105pie\u0144 s\u0142\u00f3w w tabeli. Mo\u017cna w tym celu wykorzysta\u0107 funkcj\u0119 grupuj\u0105c\u0105 <strong>Table.Group(),<\/strong> kt\u00f3ra sama zliczy, w ilu wierszach pojawi\u0142a si\u0119 dana warto\u015b\u0107.<\/p>\n\n\n\n<p class=\"text-danger\"><strong>GroupedRows = Table.Group(filteredRows, {\u201cvalue\u201d}, {{\u201cCount\u201d, each Table.RowCount(_), type number}}),<\/strong><\/p>\n\n\n\n<p>Przed zwr\u00f3ceniem danych do warstwy prezentacyjnej mo\u017cna jeszcze zmieni\u0107 nazwy poszczeg\u00f3lnych kolumn, aby by\u0142y jednoznacznie identyfikowalne.<\/p>\n\n\n\n<p class=\"text-danger\"><strong>RenamedColumns = Table.RenameColumns(GroupedRows,{{\u201cvalue\u201d, \u201cWord\u201d}, {\u201cCount\u201d, \u201cQuantity\u201d}})<\/strong><\/p>\n\n\n\n<p>Ca\u0142y kod prezentuje si\u0119 nast\u0119puj\u0105co:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">let\n\n\/* ================================ FUNCTIONS ================================= *\/\n\nfuncRemoveSpecialCharacters = (x) =>\n\n{\n\nText.Remove(x,\n\n{\n\n\u201c0\u201d, \u201c1\u201d, \u201c2\u201d, \u201c3\u201d, \u201c4\u201d, \u201c5\u201d, \u201c6\u201d, \u201c7\u201d, \u201c8\u201d, \u201c9\u201d,\n\n\u201c`\u201d, \u201c~\u201d, \u201c!\u201d, \u201c@\u201d, \u201c#\u201d, \u201c$\u201d, \u201c%\u201d, \u201c^\u201d, \u201c&amp;\u201d, \u201c*\u201d,\n\n\u201c(\u201c, \u201c)\u201d, \u201c-\u201c, \u201c_\u201d, \u201c=\u201d, \u201c+\u201d, \u201c{\u201c, \u201c[\u201c, \u201c]\u201d, \u201c}\u201d,\n\n\u201c;\u201d, \u201c:\u201d, \u201c\u2018\u201d, \u201c\u201d\u201d\u201d,\u201d\u201d, \u201c|\u201d, \u201c\u201d, \u201c|\u201d, \u201c,\u201d, \u201c&lt;\u201c,\n\n\u201c.\u201d, \u201c>\u201d, \u201c\u201d, \u201c?\u201d,\n\n\u201c\n\n\u201c\n\n}\n\n)\n\n},\n\n\/\/ get max number of space in rows\n\ncountedWords = Table.AddColumn(loweredText , \u201cNumOfWord\u201d, each List.Count(Text.PositionOf([DataWithoutStopWords], \u201d \u201c, Occurrence.All))),\n\nchangedType = Table.TransformColumnTypes(countedWords ,{{\u201cNumOfWord\u201d, type number}}),\n\nsortedRows= Table.Sort(changedType ,{{\u201cNumOfWord\u201d, Order.Descending}}),\n\nRemoveUnnecessaryCols= Table.RemoveColumns(sortedRows,{\u201cDataWithoutStopWords\u201d}),\n\nKeptFirstRows = Table.FirstN(RemoveUnnecessaryCols,1) ,\n\nmaxNumberOfWordValue= List.First(Table.Column(KeptFirstRows , \u201cNumOfWord\u201d)),\n\n\/* ================================ OPERATIONS ================================= *\/\n\nsourceData = Table.FromColumns({Lines.FromBinary(File.Contents(\u201cC:TmpAlice.txt\u201d),null,null,1250)}),\n\n_RemoveSpecialCharacters = Table.AddColumn(sourceData, \u201cDataWithoutStopWords\u201d, each funcRemoveSpecialCharacters([Column1]) ),\n\n_RemovedSpecialCharacters = Table.ExpandListColumn(_RemoveSpecialCharacters , \u201cDataWithoutStopWords\u201d),\n\nremovedSourceColumn = Table.RemoveColumns(_RemovedSpecialCharacters,{\u201cColumn1\u201d}),\n\nloweredText = Table.TransformColumns(removedSourceColumn,{},Text.Lower),\n\nsplitedColumnByDelimiter = Table.SplitColumn(loweredText ,\u201dDataWithoutStopWords\u201d,Splitter.SplitTextByDelimiter(\u201d \u201c),maxNumberOfWordValue),\n\naddedColumnNames = Table.ColumnNames(splitedColumnByDelimiter ),\n\nunpivoted = Table.Unpivot(splitedColumnByDelimiter ,addedColumnNames ,\u201dkey\u201d,\u201dvalue\u201d),\n\nremovedColumns = Table.RemoveColumns(unpivoted,{\u201ckey\u201d}),\n\nremovedErrors = Table.RemoveRowsWithErrors(removedColumns, {\u201cvalue\u201d}),\n\nreplacedValue = Table.ReplaceValue(removedErrors,\u201d \u201c,\u201d\u201d,Replacer.ReplaceText,{\u201cvalue\u201d}),\n\nfilteredRows = Table.SelectRows(replacedValue, each ([value] &lt;> \u201c\u201d)),\n\nGroupedRows = Table.Group(filteredRows, {\u201cvalue\u201d}, {{\u201cCount\u201d, each Table.RowCount(_), type number}}),\n\nRenamedColumns = Table.RenameColumns(GroupedRows,{{\u201cvalue\u201d, \u201cWord\u201d}, {\u201cCount\u201d, \u201cQuantity\u201d}})\n\nin\n\nRenamedColumns<\/pre>\n\n\n\n<p>Pozosta\u0142e skrypty s\u0105 niemal\u017ce bli\u017aniacze. Poni\u017cej zostan\u0105 zaprezentowane tylko r\u00f3\u017cnice. W przypadku skryptu, w kt\u00f3rym lista s\u0142\u00f3w mia\u0142a zosta\u0107 pomniejszona o s\u0142owa, kt\u00f3re s\u0105 tzw. stopwords, dodano trzy elementy. Pierwszym z nich jest wczytanie do tabeli listy takich s\u0142\u00f3w z jednej ze stron interenetowych.<\/p>\n\n\n\n<p class=\"text-danger\">pp = <strong>Table.ToList(Table.FromColumns({Lines.FromBinary(Web.Contents(\u201c<a href=\"http:\/\/jmlr.org\/papers\/volume5\/lewis04a\/a11-smart-stop-list\/english.stop\" target=\"_blank\" rel=\"noopener\">http:\/\/jmlr.org\/papers\/volume5\/lewis04a\/a11-smart-stop-list\/english.stop<\/a>\u201d),null,null,1250)})),<\/strong><\/p>\n\n\n\n<p>Drugim natomiast jest funkcja functionCheckIfStopword, kt\u00f3ra wyraz przekazywany do funkcji wykorzystuje w funkcji j\u0119zyka M List.Contains(), kt\u00f3ra por\u00f3wnuje go z list\u0105 wyraz\u00f3w typu stopwords i zwraca warunek logiczny. Funkcja ta jest wykorzystywana tak jak poprzednio podczas operacji dodawania nowej kolumny.<\/p>\n\n\n\n<p class=\"text-danger\"><strong>functionCheckIfStopword = (x) =&gt;<\/strong><\/p>\n\n\n\n<p class=\"text-danger\"><strong>{<\/strong><\/p>\n\n\n\n<p class=\"text-danger\"><strong>List.Contains(pp, x)<\/strong><\/p>\n\n\n\n<p class=\"text-danger\"><strong>},<\/strong><\/p>\n\n\n\n<p>Na koniec wybierane s\u0105 tylko te s\u0142owa, kt\u00f3rych funkcja nie znalaz\u0142a na li\u015bcie ze strony i dla kt\u00f3rych zwr\u00f3ci\u0142a warto\u015b\u0107 false.<\/p>\n\n\n\n<p class=\"text-danger\"><strong>filteredRowsFalse = Table.SelectRows(expandedColumn, each ([Custom] = false)),<\/strong><\/p>\n\n\n\n<p>W przypadku ostatniego skryptu zosta\u0142a dopisana metoda tworzenia n-gram\u00f3w,a konkretnie 2-gram\u00f3w. Sztuczka polega na wstawieniu za pomoc\u0105 funkcji Table.AddIndexColumn() nowej kolumny, kt\u00f3ra b\u0119dzie zawiera\u0107 numer wiersza \u2013 indeks,<\/p>\n\n\n\n<p class=\"text-danger\"><strong>insertedIndex = Table.AddIndexColumn(filteredRows ,\u201dIndex\u201d)<\/strong><\/p>\n\n\n\n<p>Nast\u0119pnie za pomoc\u0105 funkcji<strong> Table.Range()<\/strong> mo\u017cna pobra\u0107 z tej samej tabeli, kt\u00f3ra jest bie\u017c\u0105cym zestawem danych wyraz z rekordu bie\u017c\u0105cego oraz wyraz nast\u0119pny. Nast\u0119pnie nale\u017cy wynik tej funkcji przekszta\u0142ci\u0107 na list\u0119 za pomoc\u0105 funkcji <strong>Table.ToList()<\/strong> oraz po\u0142\u0105czy\u0107 poszczeg\u00f3lne elementy listy \u2013 czyli poszczeg\u00f3lne wyrazy \u2013 przecinkiem za pomoc\u0105 funkcji <strong>Text.Combine().<\/strong><\/p>\n\n\n\n<p class=\"text-danger\"><strong>,addedGram = Table.AddColumn(insertedIndex, \u201cWord\u201d, each Text.Combine(Table.ToList(Table.Range(filteredRows ,[Index],2)), \u201d \u201c) ),<\/strong><\/p>\n\n\n\n<p>Ja z analizy tekstu \u201eAlicji w Krainie Czar\u00f3w\u201d otrzymano wyniki takie jak na grafice poni\u017cej. Prawda, \u017ce Power Query jest fajny? Zainteresowanym polecam przede wszystkim materia\u0142y dost\u0119pne na stronie: <a href=\"http:\/\/office.microsoft.com\/en-us\/excel-help\/learn-about-power-query-formulas-HA104003958.aspx\" target=\"_blank\" rel=\"noopener\">http:\/\/office.microsoft.com\/en-us\/excel-help\/learn-about-power-query-formulas-HA104003958.aspx<\/a><\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/11\/Microsoft-Power-Query-jcommerce-przyklad-1.jpg\" alt=\"Power Query przyk\u0142ad\" class=\"wp-image-25644\" title=\"\"><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>Wszystko, co zosta\u0142o zaprojektowane w dodatku Power Query, jest t\u0142umaczone na j\u0119zyk M. W tym artykule przeczytasz, czym jest Microsoft Power Query, poznasz podstawy j\u0119zyka M, najwa\u017cniejsze komendy i polecenia, a tak\u017ce techniki, kt\u00f3re mog\u0105 pom\u00f3c podczas pracy z Power Query M i uczyni\u0107 ten dodatek jeszcze bardziej przydatnym.<\/p>\n","protected":false},"author":10,"featured_media":29600,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"iawp_total_views":662,"footnotes":""},"categories":[1,582],"tags":[51],"offering":[521],"class_list":["post-29598","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-artykuly","category-technologie","tag-business-intelligence","offering-modern-data-solutions"],"acf":[],"_links":{"self":[{"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/posts\/29598","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/users\/10"}],"replies":[{"embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/comments?post=29598"}],"version-history":[{"count":5,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/posts\/29598\/revisions"}],"predecessor-version":[{"id":33974,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/posts\/29598\/revisions\/33974"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/media\/29600"}],"wp:attachment":[{"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/media?parent=29598"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/categories?post=29598"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/tags?post=29598"},{"taxonomy":"offering","embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/offering?post=29598"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}