diff --git a/docs/..Rproj b/docs/..Rproj new file mode 100644 index 0000000..8e3c2eb --- /dev/null +++ b/docs/..Rproj @@ -0,0 +1,13 @@ +Version: 1.0 + +RestoreWorkspace: Default +SaveWorkspace: Default +AlwaysSaveHistory: Default + +EnableCodeIndexing: Yes +UseSpacesForTab: Yes +NumSpacesForTab: 2 +Encoding: UTF-8 + +RnwWeave: Sweave +LaTeX: pdfLaTeX diff --git a/docs/Notatka_temat2.rmd b/docs/01-podstawy.rmd similarity index 83% rename from docs/Notatka_temat2.rmd rename to docs/01-podstawy.rmd index 4c1b9ab..aff1ca6 100644 --- a/docs/Notatka_temat2.rmd +++ b/docs/01-podstawy.rmd @@ -1,29 +1,25 @@ ---- -title: "Wprowadzenie do R" -author: "Dylewicz, Kamchen, Krasoń, Kulon, Soból" -date: "12 01 2021" -output: pdf_document ---- +# Podstawy języka R -```{r setup, include=FALSE} -knitr::opts_chunk$set(echo = TRUE) -``` +Język R posiada kilka typów danych, które pokrótce postaramy sie omówić poniżej. Pokażemy ich budowe jak i operacje na nich, przytaczając stosowne przyklady. -\section{Podstawowe typy danych} +## Liczby + +Liczby całkowite i rzeczywiste (tutaj separator dziesiętny to kropka). Możemy używać również notacji naukowej. Operacje na liczbach to podstawowe działania matematyczne jak i trochę rozszerzone, ukazane niżej wraz z specjalnymi liczbami. -Język R posiada kilka typów danych, które pokrótce postaramy sie omówić poniżej. Pokażemy ich budowe jak i operacje na nich, przytaczając stosowne przyklady. -\subsection{Liczby} - Liczby całkowite i rzeczywiste (tutaj separator dziesiętny to kropka). Możemy używać również notacji naukowej. Operacje na liczbach to podstawowe działania matematyczne jak i trochę rozszerzone, ukazane niżej wraz z specjalnymi liczbami. ```{r} 5; 5.5; 5.5e-2; ``` + Tutaj liczby specjalne, + ```{r} NaN # not a number Inf # nieskończoność -Inf # - nieskończoność ``` + oraz kilka działań na liczbach + ```{r} 1 + 1 # podobnie '-' to odejmowanie 4/2 # dzielenie, a '*' to mnożenie @@ -35,9 +31,9 @@ sqrt(4) #pierwiastkowanie abs(-1) # wartość bezwzględna ``` -\subsection{Łańcuchy znaków} +## Łańcuchy znaków - Łańcuch znaków to po prostu napi. Napis jest otoczony przez " lub '. W napisie możemy umieszczać dowolne znaki, pamiętając że są też znaki specjalne (rozpoczynające się od \\ i mające specjalne funkcje). Na napisach istnieje wiele operacji (np. $\verb+paste()+,$ czyli sklejenie dwóch napisów), lecz je zobaczymy w notatce o napisach. +Łańcuch znaków to po prostu napi. Napis jest otoczony przez " lub '. W napisie możemy umieszczać dowolne znaki, pamiętając że są też znaki specjalne (rozpoczynające się od \\ i mające specjalne funkcje). Na napisach istnieje wiele operacji (np. $\verb+paste()+,$ czyli sklejenie dwóch napisów), lecz je zobaczymy w notatce o napisach. ```{r} "napis" @@ -47,9 +43,9 @@ abs(-1) # wartość bezwzględna cat("i znak \n specjalny, wstawiający nową linie") # cat() wyświetla napis w sposób niesformatowany ``` -\subsection{Wartości logiczne} +## Wartości logiczne - Logiczna Prawda ($\verb+TRUE+$ lub $\verb+T+$) oraz logiczny Fałsz ($\verb+FALSE+$ lub $\verb+F+$). Na tych obiektach możemy wykonywać operacje logiczne oraz algebraiczne. +Logiczna Prawda ($\verb+TRUE+$ lub $\verb+T+$) oraz logiczny Fałsz ($\verb+FALSE+$ lub $\verb+F+$). Na tych obiektach możemy wykonywać operacje logiczne oraz algebraiczne. ```{r} TRUE & TRUE # operator 'i' TRUE | FALSE # operator 'lub' @@ -59,9 +55,10 @@ TRUE | FALSE # operator 'lub' 2*FALSE # FALSE ma wartość 0 T ; `T` <- FALSE; T # używając `` możemy zmienić wartość logiczną wyrażenia ``` -\section{Wektory} - Wektor to w R uporządkowany zbiór elementów. Elementy te muszą mieć ten sam typ, także jeśli do wektora trafią elementy z różnym typem (poza NA), to nastąpi konwersja elementów do jednego typu. Proste wektory tworzymy przez polecenie $\verb+c()+$ i elementy wypisujemy w nawiasie po przecinku. Dodatkowo, element wektora jest traktowany jako jednoelementowy wektor. Wektory liczbowe jak i inne możemy tworzyć za pomocą wbudowanych funkcji do tego przeznaczonych. +## Wektory + +Wektor to w R uporządkowany zbiór elementów. Elementy te muszą mieć ten sam typ, także jeśli do wektora trafią elementy z różnym typem (poza NA), to nastąpi konwersja elementów do jednego typu. Proste wektory tworzymy przez polecenie $\verb+c()+$ i elementy wypisujemy w nawiasie po przecinku. Dodatkowo, element wektora jest traktowany jako jednoelementowy wektor. Wektory liczbowe jak i inne możemy tworzyć za pomocą wbudowanych funkcji do tego przeznaczonych. ```{r} v <- c(1, 2, 3) #przypisanie wektora do zmiennej @@ -83,7 +80,8 @@ v <- "a" # to też toupper(x) # zmieni stringi w argumencie na wielkie litery tolower(x) # zmieni stringi w argumencie na male litery ``` -\subsection{Indeksowanie} + +## Indeksowanie W R wektory są indeksowane od 1 (a nie od 0 jak w wielu językach programowania!). Aby odwołać się do konkretnego elementu wektora korzystamy z nawiasów kwadratowych $\verb+[]+.$ @@ -157,7 +155,7 @@ seq_vec[seq_vec < 0.3 | seq_vec > 0.8] seq_vec[seq_vec > 0.3 & seq_vec < 0.8] ``` -\subsection{Operacje na wektorach} +## Operacje na wektorach W R domyślnym i naturalnym zachowaniem funkcji na wektorach jest działanie element po elemencie @@ -633,7 +631,75 @@ a=function(x,y){return(x**y)} mapply(a,x=seq(1,101,by=10),y=seq(1:11)) ``` + +## R - funkcje + +Funkcje przydają się do zamknięcia w nich operacji, które się często powtarzają w naszym kodzie lub dla jego lepszej czytelności. Podstawowa składnia funkcji w R wygląda tak: +``` +nazwa_funkcja <- function(argument 1, argument 2, …){ + ciało funkcji + return(wartość lub obiekt zwracany) +} +``` +Napiszmy funkcję, która będzie mnożyła dowolny wektor przez podaną liczbę, a następnie zsumuje elementy wektora: +```{r} +funkcja1 <- function(wektor, liczba){ + rezultat <- wektor * liczba + rezultat <- sum(rezultat) + return(rezultat) +} +``` +Możemy także pominąc $\texttt{return}$ i zdefiniować funkcje: ```{r} +funkcja2 <- function(wektor, liczba){ + rezultat <- wektor * liczba + rezultat <- sum(rezultat) + rezultat +} +``` +Obie funkcje $\texttt{funkcja1}$ i $\texttt{funkcja2}$ robią to samo. Wykonajmy nasze funkcje dla dwóch zdefiniowanych zmiennych: +```{r} +v <- 1:5 +n <- 2 +funkcja1(v, n) + +funkcja2(v, n) ``` +Oczywiście do wykonania funkcji potrzebne jest zdefiniowanie obu argumentów. Jak ich nie dodamy wyświetli się błąd, że argument drugi zaginął i nie mamy zdefiniowanej jego wartości domyślnej. Zdefiniujmy zatem domyślną wartość argumentu $\texttt{liczba}$ jako $\texttt{NULL}$ i dopiszmy do naszej funkcji kod, który gdy ten argument będzie miał wartość domyślną zwróci tylko sumę elementów wektora: +```{r} +funkcja3 <- function(wektor, liczba = NULL){ + if(is.null(liczba)){ + rezultat <- sum(wektor) + } else{ + rezultat <- wektor * liczba + rezultat <- sum(rezultat) + } + rezultat +} +``` +Wykonajmy funckję $\texttt{funkcja3}$ na wcześniej zdefiniowanym wektorze $\texttt{v}$: +```{r} +funkcja3(v) +``` +Oprócz zdefiniowania wartości domyślnej argumentu poprzez trzy kropki możemy również dopuścić parametry dodatkowe. Zdefiniujmy funkcję z parametrami dodatkowymi: +```{r} +funkcja4 <- function(wektor, liczba = NULL, ...){ + if(is.null(liczba)){ + rezultat <- sum(wektor, ...) + } else{ + rezultat <- wektor * liczba + rezultat <- sum(rezultat, ...) + } + rezultat +} +``` +Wykonajmy funckję $\texttt{funkcja4}$ usuwając wartości brakujące z nowo zdefiniowanego wektora: +```{r} +v <- c(NA, 1, NA, 2:4, NA, 5) +v + +funkcja4(v, na.rm = TRUE) +``` +Funkcje są bardzo przydatne, gdy mamy do napisania długi skrypt. Pozwalają na podzielenie głównej części kodu na mniejsze kawałeczki, które kolejnemu użytkownikowi skryptu lub nam będzie łatwiej modyfikować. diff --git a/docs/02-wczytywanie-danych.rmd b/docs/02-wczytywanie-danych.rmd new file mode 100644 index 0000000..c4b750e --- /dev/null +++ b/docs/02-wczytywanie-danych.rmd @@ -0,0 +1,225 @@ +# Wczytywanie danych w R + +## Formaty danych + +### CSV/DSV +CSV (Comma Separated Values) to plik tekstowy, w którym wartości rozdzielane są przecinkami, a kolejne wiersze znakiem nowej linii. Plik CSV zazwyczaj przechowuje dane tabelaryczne. Nagłówki kolumn są często dołączane jako pierwszy wiersz (są to nazwy zmiennych), a każdy kolejny wiersz odpowiada jednej obserwacji (jednemu wierszowi w tabeli danych). + +CSV jest szczególnym przypadkiem formatu danych o nazwie Delimiter Seperated Values (DSV). Jest to plik tekstowy w którym pola w każdym wierszu oddzielone są dowolnym separatorem. Najczęściej spotykane separatory to: przecinek (CSV), tabulator (TSV), średnik. + +**Przykładowy plik CSV** + +![](img/csv_example.gif) + + + +### XML +XML to skrót od nazwy Extensible Markup Language. Dane przechowywane w tym formacie mają zagnieżdżoną strukturę: znaczniki oznaczają nazwy zmiennych, a wewnątrz przechowywane są ich wartości. XML swoją strukturą przypomina plik HTML. + +**Przykładowy plik XML** + +![](img/xml_example.gif) + + + + + +### JSON +JSON - JavaScript Object Notation - to format przydatny w przypadku pracy z danymi pochodzącymi z REST API, czyli pobieranymi z sieci. Niektóre bazy danych również komunikują się za pomocą tego formatu, np. MongoDB. + +Struktura: w pliku JSON obserwacje przechowywane są w słownikach, w których nazwy zmiennych są kluczami, a wartości zmiennych - wartościami. Obserwacje oddzielane są przecinkami, a dodatkowo, wszystkie dane spięte są nawiasami klamrowymi. + + +**Przykładowy plik JSON** + +![](img/json_example.jpeg) + + +### Excel (XLSX) +XLSX to format danych oparty na XML. Pliki tego typu są domyślnymi dokumentami wyjściowymi arkuszy kalkulacyjnych programu Microsoft Excel. Przedstawiają one głównie dane liczbowe i tekstowe w postaci tabel dwuwymiarowych. + +**Przykładowy arkusz kalkulacyjny w Excelu** + +![](img/excel_example.png){width=60%} + + +### Otwarte wersje programu Excel +Istnieją inne pakiety biurowe, np. LibreOffice, które - w przeciwieństwie do Excela - pozwalają na darmowe korzystanie z arkusza kalkulacyjnego. W przypadku LibreOffice, domyślnym formatem zapisu danych przez Calc (odpowiednik Excela) jest OpenDocument Format (.ods). + + +**Przykładowy arkusz kalkulacyjny w LibreOffice** + +![](img/libreoffice_example.png){width=60%} + + + +### Pliki tekstowe + + +Jednym z najczęściej występujących i najbardziej uniwersalnych formatów przechowujących dane (np. w postaci tabeli) są pliki tekstowe. Mają one najczęściej rozszerzenie ```txt``` lub ```csv``` (*comma separated values*). +Poniższą charakteryzację różnych metod wczytywania przedstawiamy na podstawie pliku ```listings.csv``` + +#### Base + + +Podstawową funkcją używaną do wczytywania tego typu plików w postaci tabeli jest funkcja ```read.table```. +Ze względu na specyfikację wewnętrzną plików, ```read.table``` posiada kilka wariantów, takie jak ```read.csv()```, ```read.csv2()``` czy ```read.delim()```. + +```read.csv()``` używana jest w przypadku, gdy domyślnym separatorem dziesiętnym jest ".", a wartości w wierszach oddzielone są poprzez ","; +```read.csv2()``` używana jest w przypadku, gdy domyślnym separatorem dziesiętnym jest ",", a wartości w wierszach oddzielone są poprzez ";"; +```read.delim()``` używana jest w przypadku, gdy domyślnym separatorem dziesiętnym jest ".", a wartości w wierszach oddzielone są poprzez ```TAB``` + + +Przykładowy sposób załadowania plików w formacie ```csv``` + +```{r, eval = FALSE} +read.csv('./data/csv/listings.csv', header = TRUE, sep = ",") +``` + +W przypadku ```read.table()``` dane zostają zaimportowane jako *data.frame*. + +Dla dużych plików wczytwanie za pomocą ```read.table()``` bywa jednak czasochłonne. Wówczas możemy użyć funkcji z paczki ```data.table``` lub ```readr```. + +#### readr + +```readr``` jest częścią pakietu ```tidyverse```. W tym przypadku import odbywa się za pomocą funkcji o podobnej nazwie, jak w przypadku ```read.table()```, a mianowicie ```read_csv()```. +```read_csv``` wczytuje dane oddzielone przecinkami, natomiast ```read_csv2()``` - dane oddzielone średnikami. + +```{r, eval = FALSE} +read_csv('./data/csv/listings.csv') + +``` + +W przeciwieństwie do ```read.csv```, funkcja ```read_csv``` na wyjściu daje dane w postaci tabeli w bardziej zwartej i przejrzystej formie. Oprócz tego podaje także specyfikację kolumn, tzn. informuje, jaka jest nazwa każdej kolumny oraz jej typ (np. ```col_double``` (**) oznaczają dane liczbowe). +Typ danych jaki dostajemy na wyjściu to ```tbl_df``` (tzw. *tibble*), który jest w pewnym sensie zmodyfikowaną wersją tradycyjnej ramki danych *data.frame*, pozwalającą na łatwiejszą pracę w obrębie *tidyverse*. + +#### data.table + +Do wczytywania danych z plików ```csv``` możemy także użyć funkcji ```fread``` z pakietu ```data.table```. + +```{r, eval = FALSE} +fread('./data/csv/listings.csv') +``` + +Na wyjściu otrzymujemy ramkę danych, jednak wyświetloną w inny sposób niż w przypadku użycia ```read.csv```. Różnica jest widoczna, gdyż po użyciu funkcji ```class()``` na ```fread()``` jako typ danych otrzymujemy ```"data.table" "data.frame"```. + + +#### Różnice + +Najważniejsze różnice pomiędzy wymienionymi sposobami wczytywania plików ```csv``` to: + +1) *Typ danych* + - Base: ```data.frame`` + - readr: ```tibble``` + - data.table: ```data.table data.frame`` + +2) *Postać wyświetlania* (co jest konsekwencją 1) + - Base: Wyświetla 62 początkowe wiersze każdej kolumny, wyświetlając informacje o liczbie pozostałych; + - readr: wyświetla 10 pierwszych wierszy z 10 pierwszych kolumn, z informacją o liczbie pozostałych wierszy i kolumn; automatycznie wyświetlane są też nazwy kolumn oraz skrót informujący o typie zmiennych + - data.table: wyświetla 5 początkowych i 5 końcowych wartości z każdej kolumny + +3) *Czas i użycie pamięci przy dużych rozmiarach danych* + Zarówno czas wczytania danych, jak i wykorzystanie pamięci najkorzystniejsze jest w przypadku funkcji ```fread```. Gdyby przez *time* oznaczyć czas potrzebny na wczytanie dużych plików, a przez *memory* zużycie pamięci, to *time*(fread) < *time*(read_csv) << *time*(read.csv) oraz *memory*(fread) < *memory*(read.csv) < *memory*(read_csv). + + +### Arkusze kalkulacyjne i pliki JSON + +Do wczytywania arkusza kalkulacyjnego (np. pliku excela) używa się funkcji ```read_excel``` z pakietu ```readxl``` będącego częścią `tidyverse`. + +```{r, eval = FALSE} +read_excel('./data/excel/listings.xlsx') +``` + + +Oprócz tego, można także użyć pakietu funkcji `read.xlsx` z pakietu `xlsx`. Wymaga ona jednak instalacji Javy. + + +Do zaimportowania plików JSON możemy użyć funkcji z pakietu ```jsonlite``` + +```{r, eval = FALSE} +listings_js <- jsonlite::fromJSON('./data/json/listings.json') +listings_js <- mutate(listings_js, + last_review = as_date(last_review)) +``` + + +## Locale + +Locale jest to *uniksowe narzędzie powłokowe* przechowujące ustawienia środowiskowe związane z ustawieniami regionalnymi. + +```{r } +Sys.getlocale() +``` + +LC_COLLATE - odpowiada za porządek znaków, ważny przy sortowaniu + +LC_CTYPE - odpowiada za kodowanie znaków + +LC_MONETARY - odpowiada za system monetarny: znak waluty, separator tysięcy, liczba cyfr po przecinku itd. + +LC_NUMERIC - określa separator ułamkowy, separator tysięcy, grupowanie cyfr + +LC_TIME - odpowiada za system wyświetlania daty + +```{r} +Sys.localeconv() +``` + +Powyższa funkcja wyświetla szczegóły dotyczące systemu numerycznego i monetarnego. + +### Ustawienie locale przez użytkownika + +```{r} +Sys.setlocale(category = "LC_ALL", locale = "polish") +``` + +W celu ustawienia innego locale niż domyślne systemowe należy użyć powyższej funkcji, przyjmowane przez nią argumenty to *category* i *locale*. + +category - odpowiada za określenie, które zmienne środowiskowe chcemy zmienić, gdzie opcje: "LC_ALL", "LC_COLLATE", "LC_CTYPE", "LC_MONETARY", "LC_NUMERIC" oraz "LC_TIME" są wspierane na każdym systemie operacyjnym, niektóre systemy wspierają również: "LC_MESSAGES", "LC_PAPER" i "LC_MEASUREMENT" + +locale - odpowiada za region, który chcemy ustawić dla systemu windows podajemy angielską nazwę języka (regionu) np.: 'polish', w systemach UNIXowych podajemy np.: 'pl_PL' lub 'pl_PL.UTF-8'. + + + +### Ustawienie locale poprzez readr + +Pakiet *readr* oferuje więcej możliwości dostosowywania locale, więcej informacji na ten temat można znaleźć w tym [odnośniku](https://readr.tidyverse.org/articles/locales.html). + + +## Natywne formaty R + +R ma dwa natywne sposoby przechowywania danych, RDA(od RData) i RDS. Główną zaletą takiej obsługi danych jest szybkość przetwarzania ich. Zachowuje on także informacje z R o danych(np. typy zmiennych). + +### RDS + +W formacie RDS mogą być przechowywane jedynie pojedyncze pliki R. Mogą być one za to przypisywane do dowolnej nazwy. +Aby załadować dane korzystamy z: +```{r readRDS, eval=FALSE} +listings_rds <- readRDS("./data/native/listings.rds") +``` +Do zapisania danych używamy: +```{r saveRDS, eval=FALSE} +saveRDS(object = listings, file = "listings.rds") +``` + +### RDA + +W plikach formatu RDA wczytane dane nie są przypisywane do zmiennej, tylko wywołujemy te funkcje i w efekcie plik pojawia się w środowisku. + +W RDA do załadowania danych służy +```{r load, eval=FALSE} +load("./data/native/listings.rda") +``` + +Natomiast, aby zapisać dane używamy: +```{r save_rda, eval=FALSE} +save(listings_rr, file = "listings_rr.rda") +``` +Korzystając z formatu RDA możemy jednocześnie zapisywać większą ilość plików + +```{r save, eval=FALSE} +save(iris, cars, file="data_frame.rda") +``` + + + diff --git a/docs/03-eda.Rmd b/docs/03-eda.Rmd new file mode 100644 index 0000000..a0cfe94 --- /dev/null +++ b/docs/03-eda.Rmd @@ -0,0 +1,67 @@ +# Eksploracyjna analiza danych + +Badanie eksploracyjne danych (ang. exploratory data analysis) dotyczy opisu, wizualizacji i badania zebranych danych bez potrzeby zakładania z góry hipotez badawczych. Badania ekploracyjne obejmują również wstępne sprawdzenie danych w celu skontrolowania założeń modeli statystycznych lub występowania błędów w danych (np. braków odpowiedzi). + +## Dane tabelaryczne + +Dane tabelaryczne to dane, które mają postać tabeli. Tabela to struktura danych, która składa się z wierszy i kolumn. Każdy wiersz odpowiada pewnej obserwacji, której cechy zostały zapisane w kolejnych kolumnach. + + +## Typy zmiennych + +Zmienne, które opisują kolejne obserwacje możemy podzielić na: + +* zmienne jakościowe (niemierzalne) + * porządkowe - np. klasyfikacja wzrostu (niski, średni, wysoki) + * nominalne - np. kolor oczu, płeć, grupa krwi +* zmienne ilościowe (mierzalne) + * dyskretne - np. liczba dzieci, liczba gospodarstw domowych, wiek (w rozumieniu ilości skończonych lat) + * ciągłe - np. wzrost, masa, wiek (w rozumieniu ilości dni między datą urodzin a datą badania) + * proporcjonalne - np. masa, długość, temperatura wyrażona w Kelwinach lub stopniach Rankine’a (przyjmujemy istnienie zera i możemy twierdzić, że jedno ciało jest dwukrotnie gorętsze od drugiego) + * interwałowe - np. temperatura wyrażona w stopniach Celsjusza lub Fahrenheita (możemy twierdzić, że coś jest o 20 °C cieplejsze od czegoś innego, ale nie możemy stwierdzić ilokrotnie cieplejsze jest ciało o temperaturze 40 °C od ciała o temperaturze –10 °C), data kalendarzowa (możemy mówić o stałej różnicy pomiędzy kolejnymi dniami) + +## Miary + +Zapoznając się z danymi chcielibyśmy sprawdzić wokół jakiej wartości są skupione oraz jak bardzo są zmienne wartości danej cechy. + +Miary lokacji (miary tendencji centralnej) pomagają nam umiejscowić dane na osi. Przykładami takich miar są: + +* średnia - najczęściej arytmetyczna określona jako $\overline{x} = \frac{1}{n}\sum\limits_{i=1}^n x_i$. +* dominanta (moda) - ozn. $Mo$ - dla zmiennych o rozkładzie dyskretnym, wartość o największym prawdopodobieństwie wystąpienia lub wartość najczęściej występująca w próbie. Dla zmiennej losowej o rozkładzie ciągłym jest to argument, dla którego funkcja gęstości prawdopodobieństwa ma wartość największą. +* mediana - ozn. $Me$ - wartość cechy w szeregu uporządkowanym, powyżej i poniżej której znajduje się jednakowa liczba obserwacji. +* kwantyle rzędu $p$ - wartość cechy w szeregu uporządkowanym, poniżej której znajduje się $p \cdot 100\%$ liczby obserwacji, a powyżej której znajduje się $(1 - p) \cdot 100\%$ liczby obserwacji. + +Natomiast miary rozrzutu dostarczają informacji jak bardzo zróżnicowane są obserwacje pod względem badanej cechy. Przykładami takich miar są: + +* wariancja - stopień rozrzutu badanej cechy wokół wartości oczekiwanej. Im większa wariancja, tym rozrzut zmiennej jest większy. Nieobciążony estymator wariancji wyraża się wzorem: $s^2 = \frac{1}{n}\sum\limits_{i=1}^n\left(x_i - \overline{x}\right)^2$ +* odchylenie standardowe - mówi nam o przeciętnym odchyleniu wartości zmiennej losowej od jej wartości oczekiwanej. Im odchylenie standardowe jest większe, tym większe zróżnicowanie wartości badanej cechy. Odchylenie standardowe z próby obliczamy jako pierwiastek z wariancji z próby, tzn. $s = \sqrt{s^2}$. +* rozstęp międzykwartylowy - różnica między trzecim a pierwszym kwartylem. Ponieważ pomiędzy tymi kwartylami znajduje się z definicji 50% wszystkich obserwacji (położonych centralnie w rozkładzie), dlatego im większa szerokość tego rozstępu, tym większe zróżnicowanie cechy. + +Wyróżniamy także miary asymetrii. Miary asymetrii mówią nam, czy większa część populacji klasuje się powyżej, czy poniżej przeciętnego poziomu badanej cechy. Asymetrię rozkładu można zbadać porównując średnią, dominantę i medianę. + +* W przypadku rozkładu symetrycznego wszystkie te parametry są równe. +* Jeśli zachodzi nierówność $Mo < Me < \mathbb{E} X$, to mamy do czynienia z prawostronną asymetrycznością rozkładu. Tzn. dużo małych wartości i bardzo mało dużych. +* Jeśli zachodzi nierówność $\mathbb{E} X < Me < Mo$, to mamy do czynienia z lewostronną asymetrycznością rozkładu. Tzn. mało małych i bardzo dużo dużych. + +## R - podsumowanie kolumn + +Podstawowymi funkcjami, które pomagają nam zapoznać się z danymi są funkcje: + +* $\texttt{head}$ - zwraca pierwszą część wektora, macierzy, tabeli lub ramki danych. Domyślnie 6 pierwszych elementów. +* $\texttt{nrow}$ - zwraca liczbę wierszy macierzy, tabeli lub ramki danych. +* $\texttt{ncol}$ - zwraca liczbę kolumn macierzy, tabeli lub ramki danych. + +Natomiast podstawowymi funkcjami, które podsumowują kolejne kolumny są funkcje: + +* $\texttt{str}$ - zwraca strukturę danego obiektu. Wyświetla np. klasę obiektu, liczbę wierszy i kolumn, a także nazwę danej kolumny, typ wartości w niej zawartych, jak i kilka początkowych wartości. +* $\texttt{summary}$ - zwraca podsumowanie każdej kolumny. Dla zmiennych ciagłych wyznacza wartości tj.: + + * wartość najmniejsza i największa + * średnia i mediana + * 1 (0.25) i 3 (0.75) kwartyl + * liczba wartości brakujących (NA) + + Natomiast w przypadku zmiennych dyskretnych wyznacza liczbę obserwacji, które przyjmują daną wartość zmiennej. + +* $\texttt{glimpse}$ - funkcja z pakietu $\texttt{tidyverse}$ podobna do $\texttt{str}$, ale stara się pokazać jak najwięcej danych. Wyświetla np. liczbę wierszy i kolumn, a także nazwę danej kolumny, typ wartości w niej zawartych oraz jak najwięcej wartości z tej kolumny. + diff --git a/notatka-5.rmd b/docs/04-przetwarzanie.rmd similarity index 59% rename from notatka-5.rmd rename to docs/04-przetwarzanie.rmd index 23cdb62..5d8c471 100644 --- a/notatka-5.rmd +++ b/docs/04-przetwarzanie.rmd @@ -1,237 +1,214 @@ ---- -title: "Operacje na danych w R" -author: - - Weronika Domaszewska - - Ewelina Grzmocińska - - Gracjan Hrynczyszyn - - Dominik Jaźwiecki - - Michał Ociepa -date: '' -output: - pdf_document: default - html_document: default - word_document: default ---- - -```{r setup, include=FALSE} -knitr::opts_chunk$set(echo = TRUE) -``` - -Operacje na danych w R są związane głównie z filtrowaniem, dodawaniem i modyfikowaniem kolumn, grupowaniem oraz podsumowywaniem danych. Można je wykonywać za pomocą funkcji bazowego R lub narzędzi z zaimportowanych pakietów: tidyverse, data.table. - -Załóżmy, że ramka danych jest przypisana do zmiennej $dane$, a nazwy jej kolumn to: $kol.1, kol.2, kol.3,...$\ . - -## Wybieranie kolumn - -Poniżej przedstawione są instrukcje pozwalające na wybieranie konkretnych kolumn z ramki danych w~zależności od metody. -Dla uproszczenia przyjmijmy, że wybieramy kolumny: $kol.1, kol.2, kol.3$. - -1. base - - * dane = dane[, c("kol.1", "kol.2", "kol.3")] - -2. tidyverse - - * dane = select(dane, `kol 1`, `kol 2`, `kol 3`) - * dane = dane %>% select(`kol 1`, `kol 2`, `kol 3`) - -3. data.table - - Nazwy kolumn ramki danych zawierą znak "." . Wprowadźmy zmienną pomocniczą $kolumny$. Będzie ona zawierać nazwy kolumn, ale zastępując znak "." znakiem " ". -```r -kolumny = c("kol 1", "kol 2", "kol 3") -``` - - * dane = dane[, kolumny] - * dane = dane[, kolumny, with = FALSE] - dana metoda nie zadziała bez argumentu $with~=~FALSE$, ponieważ szuka w ramce danych kolumn o nazwach zawartych w obiekcie $kolumny$, a nie konkretnie podanych nazw - * dane = dane[, colnames(dane) %in% kolumny, with = FALSE] - * dane = dane[, ..kolumny] - * dane = dane[, list(`kol 1`, `kol 2`, `kol 3`)] - * dane = dane[, .(`kol 1`, `kol 2`, `kol 3`)] -\newpage - -## Zmiana nazw kolumn - -Teraz zostaną zaprezentowane sposoby na zmianę nazw kolumn ramki danych. Przyjmijmy, że nowe nazwy kolumn są postaci $k1, k2, k3, ...$\ . - -1. base - - * colnames(dane) = c("k1", "k2", "k3") - -2. tidyverse - - * dane = dane %>% rename(k1 = `kol.1`, k2 = `kol.2`, k3 = `kol.3`) - -3. data.table - - * setnames(dane, c("kol.1", "kol.2", "kol.3"), c("k1", "k2", "k3")) - zaleta: nie kopiuje ramki danych - -## Filtrowanie - -Dany rozdział skupia się na sposobach filtrowania danych. Przydatne funkcje: - - * unique(dane$\$$k1) - zwraca unikalne wartości kolumny $k1$ - * table(dane$\$$k1) - zlicza ilość wystąpienia każdej wartości w kolumnie $k1$ - * prop.table(table(dane$\$$k1)) - pokazuje procentowo ilość wystąpienia każdej wartości w kolumnie $k1$ w$~$stosunku do wszystkich wartości - -Przyjmnijmy, że wybieramy z kolumny $k1$ określoną wartość $abc$. - -1. base - - * dane[dane$\$$k1 == "abc", ] - -2. tidyverse - - * dane %>% filter(k1 == "abc") - - można podać kilka warunków (po przecinku), będą one domyślnie rozdzielone spójnikiem $i$ - - aby połączyć warunki spójnikiem $i$ można również użyć operatora $\&$ - - aby połączyć warunki spójnikiem $lub$ należy użyć operatora $|$ - -3. data.table - - * dane[k1 == "abc"] - -## Usuwanie kolumn - -Załóżmy, że usuwamy pierwszą kolumnę - $k1$. - -1. base - - * dane = dane[, -1] - gdzie $1$ to numer usuwanej kolumny, a "-" oznacza usuwanie - -\newpage - -2. tidyverse - - * dane = select(dane, -k1) - jak powyżej, "-" oznacza usuwanie, ale w tym przypadku stosujemy nazwę kolumny a nie jej numer - -3. data.table - - * dane[, k1 := NULL] - operator $:=$ (referencja) oznacza, że operacja jest wykonywana bez kopiowania ramki danych - * dane = dane[, -1, with = FALSE] - -## Manipulacje na kolumnach - -Przyjmijmy, że kolumna $k2$ zawiera tylko liczby. Wartości ujemne zamieniamy na $0$. W tym celu posłużymy się funkcją $ifelse$: -$$ ifelse(warunek \ logiczny,\ wartość \ jeśli \ spełniony, \ wartość \ jeśli\ niespełniony).$$ - -1. base - - * dane[["k2"]] = ifelse(dane[["k2"]] < 0, 0, dane[["k2"]]) - -2. tidyverse - - * dane = dane %>% mutate(k2 = ifelse(k2 < 0, 0, k2)) - - możemy modyfikować kilka kolumn jednocześnie, rozdzielając je przecinkiem - -3. data.table - - * dane[, k2 := ifelse(k2 < 0, 0, k2)] - z użyciem referencji - * dane[["k2"]] = ifelse(dane[["k2"]] < 0, 0, dane[["k2"]]) - bez użycia referencji - -## Aplikowanie transformacji do każdej kolumny - -W tym rozdziale będziemy operować na wszystkich kolumnach ramki danych. Wartości w nich zawarte mogą być typu $factor$, które zamienimy na typ $character$. - -1. base - - * poprzez pętlę -```r - for (i in 1:ncol(dane)){ - if (is.factor(dane[, i])){ - dane[, i] = as.character(dane[, i]) - } - } -``` - - * poprzez funkcję $lapply$ -```r - lapply(dane, fun(x){ - if(is.factor(x)) - x = as.character(x) - }) -``` -\newpage - -2. tidyverse - - * przy użyciu funkcji $mutate\_all$ -```r - dane = dane %>% - mutate_all(function(x){ - if (is.factor(x)){ - as.character(x) - } - else{ - x - } - }) -``` - -3. data.table - - * przy użyciu funkcji lapply -```r - dane = dane[, lapply(.SD, function(x){ - if (is.factor(x)){ - as.character(x) - } - else{ - x - } - })] -``` - -## Grupowanie i podsumowanie - -Załóżmy, że do wyznaczenia wszystkich unkialnych wartości ramki danych potrzebne są kolumny $k1$, $k2$ i $k3$. Natomiast podsumowywana będzie kolumna $k4$ - zostanie wyliczona średnia dla każdej unikalnej wartości. - -1. base - - * przy użyciu funkcji $aggregate$ - zastosowana zostanie formuła $k4$ ~ $k1 + k2 + k3$, która oznacza, że będzie podsumowywana zmienna $k4$ w zależności od unikalnych zestawów wartości zmiennych $k1$, $k2$, $k3$ - - aggregate(k4 ~ k1 + k2 + k3, - data = dane, - FUN = function(x) mean(x, na.rm = TRUE)) - poprzez zastosowanie własnej funkcji - - - aggregate(k4 ~ k1 + k2 + k3, - data = dane, - FUN = mean, na.rm = TRUE) - poprzez zastosowanie istniejącej funkcji -\newpage - -2. tidyverse - - * dane %>% \newline - group_by(k1, k2, k3) %>% \newline - summarize(srednia = mean(k4, na.rm = TRUE), maksimum = max(k4, na.rm = TRUE)) - - $group\_by$ - grupuje po kolumnach $k1$, $k2$, $k3$ - - $summarize$ - podsumowuje według podanych elementów (w tym przypadku wylicza średnią i maksimum z kolumny $k4$) - -3. data.table - - * dane[, list(średnia = mean(k4, na.rm = TRUE), \newline - maksimum = max(k4, na.rm = TRUE)), \newline - by = c("k1", "k2", "k3")] - -## Podsumowywanie wszystkich kolumn - -W celu podsumowania kolumn zdefiniujemy poniższą funkcję, która zwróci ilość niepustych wartości. -```r - num_unique_noNA = function(input_vector){ - sum(!is.na(unique(input_vector))) - } -``` - -1. base - - * apply(dane, 2, num_unique_noNA) - gdzie $2$ oznacza, że wywołujemy podaną funkcję $num\_unique\_noNA$ po kolumnach - * lapply(dane, num_unique_noNA) - * sapply(dane, num_unique_noNA) - -2. tidyverse - - * summarise_all(dane, num_unique_noNA) - -3. data.table - - * dane[, lapply(.SD, num_unique_noNA)] - +# Przetwarzanie danych tabelarycznych + +Operacje na danych w R są związane głównie z filtrowaniem, dodawaniem i modyfikowaniem kolumn, grupowaniem oraz podsumowywaniem danych. Można je wykonywać za pomocą funkcji bazowego R lub narzędzi z zaimportowanych pakietów: tidyverse, data.table. + +Załóżmy, że ramka danych jest przypisana do zmiennej $dane$, a nazwy jej kolumn to: $kol.1, kol.2, kol.3,...$\ . + +## Wybieranie kolumn + +Poniżej przedstawione są instrukcje pozwalające na wybieranie konkretnych kolumn z ramki danych w~zależności od metody. +Dla uproszczenia przyjmijmy, że wybieramy kolumny: $kol.1, kol.2, kol.3$. + +1. base + + * dane = dane[, c("kol.1", "kol.2", "kol.3")] + +2. tidyverse + + * dane = select(dane, `kol 1`, `kol 2`, `kol 3`) + * dane = dane %>% select(`kol 1`, `kol 2`, `kol 3`) + +3. data.table + + Nazwy kolumn ramki danych zawierą znak "." . Wprowadźmy zmienną pomocniczą $kolumny$. Będzie ona zawierać nazwy kolumn, ale zastępując znak "." znakiem " ". +```r +kolumny = c("kol 1", "kol 2", "kol 3") +``` + + * dane = dane[, kolumny] + * dane = dane[, kolumny, with = FALSE] - dana metoda nie zadziała bez argumentu $with~=~FALSE$, ponieważ szuka w ramce danych kolumn o nazwach zawartych w obiekcie $kolumny$, a nie konkretnie podanych nazw + * dane = dane[, colnames(dane) %in% kolumny, with = FALSE] + * dane = dane[, ..kolumny] + * dane = dane[, list(`kol 1`, `kol 2`, `kol 3`)] + * dane = dane[, .(`kol 1`, `kol 2`, `kol 3`)] + +## Zmiana nazw kolumn + +Teraz zostaną zaprezentowane sposoby na zmianę nazw kolumn ramki danych. Przyjmijmy, że nowe nazwy kolumn są postaci $k1, k2, k3, ...$\ . + +1. base + + * colnames(dane) = c("k1", "k2", "k3") + +2. tidyverse + + * dane = dane %>% rename(k1 = `kol.1`, k2 = `kol.2`, k3 = `kol.3`) + +3. data.table + + * setnames(dane, c("kol.1", "kol.2", "kol.3"), c("k1", "k2", "k3")) - zaleta: nie kopiuje ramki danych + +## Filtrowanie + +Dany rozdział skupia się na sposobach filtrowania danych. Przydatne funkcje: + + * unique(dane$\$$k1) - zwraca unikalne wartości kolumny $k1$ + * table(dane$\$$k1) - zlicza ilość wystąpienia każdej wartości w kolumnie $k1$ + * prop.table(table(dane$\$$k1)) - pokazuje procentowo ilość wystąpienia każdej wartości w kolumnie $k1$ w$~$stosunku do wszystkich wartości + +Przyjmnijmy, że wybieramy z kolumny $k1$ określoną wartość $abc$. + +1. base + + * dane[dane$\$$k1 == "abc", ] + +2. tidyverse + + * dane %>% filter(k1 == "abc") + - można podać kilka warunków (po przecinku), będą one domyślnie rozdzielone spójnikiem $i$ + - aby połączyć warunki spójnikiem $i$ można również użyć operatora $\&$ + - aby połączyć warunki spójnikiem $lub$ należy użyć operatora $|$ + +3. data.table + + * dane[k1 == "abc"] + +## Usuwanie kolumn + +Załóżmy, że usuwamy pierwszą kolumnę - $k1$. + +1. base + + * dane = dane[, -1] - gdzie $1$ to numer usuwanej kolumny, a "-" oznacza usuwanie + +2. tidyverse + + * dane = select(dane, -k1) - jak powyżej, "-" oznacza usuwanie, ale w tym przypadku stosujemy nazwę kolumny a nie jej numer + +3. data.table + + * dane[, k1 := NULL] - operator $:=$ (referencja) oznacza, że operacja jest wykonywana bez kopiowania ramki danych + * dane = dane[, -1, with = FALSE] + +## Manipulacje na kolumnach + +Przyjmijmy, że kolumna $k2$ zawiera tylko liczby. Wartości ujemne zamieniamy na $0$. W tym celu posłużymy się funkcją $ifelse$: +$$ ifelse(warunek \ logiczny,\ wartość \ jeśli \ spełniony, \ wartość \ jeśli\ niespełniony).$$ +1. base + + * dane[["k2"]] = ifelse(dane[["k2"]] < 0, 0, dane[["k2"]]) + +2. tidyverse + + * dane = dane %>% mutate(k2 = ifelse(k2 < 0, 0, k2)) + - możemy modyfikować kilka kolumn jednocześnie, rozdzielając je przecinkiem + +3. data.table + + * dane[, k2 := ifelse(k2 < 0, 0, k2)] - z użyciem referencji + * dane[["k2"]] = ifelse(dane[["k2"]] < 0, 0, dane[["k2"]]) - bez użycia referencji + +## Aplikowanie transformacji do każdej kolumny + +W tym rozdziale będziemy operować na wszystkich kolumnach ramki danych. Wartości w nich zawarte mogą być typu $factor$, które zamienimy na typ $character$. + +1. base + + * poprzez pętlę +```r + for (i in 1:ncol(dane)){ + if (is.factor(dane[, i])){ + dane[, i] = as.character(dane[, i]) + } + } +``` + + * poprzez funkcję $lapply$ +```r + lapply(dane, fun(x){ + if(is.factor(x)) + x = as.character(x) + }) +``` + +2. tidyverse + + * przy użyciu funkcji $mutate\_all$ +```r + dane = dane %>% + mutate_all(function(x){ + if (is.factor(x)){ + as.character(x) + } + else{ + x + } + }) +``` + +3. data.table + + * przy użyciu funkcji lapply +```r + dane = dane[, lapply(.SD, function(x){ + if (is.factor(x)){ + as.character(x) + } + else{ + x + } + })] +``` + +## Grupowanie i podsumowanie + +Załóżmy, że do wyznaczenia wszystkich unkialnych wartości ramki danych potrzebne są kolumny $k1$, $k2$ i $k3$. Natomiast podsumowywana będzie kolumna $k4$ - zostanie wyliczona średnia dla każdej unikalnej wartości. + +1. base + + * przy użyciu funkcji $aggregate$ - zastosowana zostanie formuła $k4$ ~ $k1 + k2 + k3$, która oznacza, że będzie podsumowywana zmienna $k4$ w zależności od unikalnych zestawów wartości zmiennych $k1$, $k2$, $k3$ + - aggregate(k4 ~ k1 + k2 + k3, + data = dane, + FUN = function(x) mean(x, na.rm = TRUE)) - poprzez zastosowanie własnej funkcji + + - aggregate(k4 ~ k1 + k2 + k3, + data = dane, + FUN = mean, na.rm = TRUE) - poprzez zastosowanie istniejącej funkcji + +2. tidyverse + + * dane %>% \newline + group_by(k1, k2, k3) %>% \newline + summarize(srednia = mean(k4, na.rm = TRUE), maksimum = max(k4, na.rm = TRUE)) + - $group\_by$ - grupuje po kolumnach $k1$, $k2$, $k3$ + - $summarize$ - podsumowuje według podanych elementów (w tym przypadku wylicza średnią i maksimum z kolumny $k4$) + +3. data.table + + * dane[, list(średnia = mean(k4, na.rm = TRUE), \newline + maksimum = max(k4, na.rm = TRUE)), \newline + by = c("k1", "k2", "k3")] + +## Podsumowywanie wszystkich kolumn + +W celu podsumowania kolumn zdefiniujemy poniższą funkcję, która zwróci ilość niepustych wartości. +```r + num_unique_noNA = function(input_vector){ + sum(!is.na(unique(input_vector))) + } +``` + +1. base + + * apply(dane, 2, num_unique_noNA) - gdzie $2$ oznacza, że wywołujemy podaną funkcję $num\_unique\_noNA$ po kolumnach + * lapply(dane, num_unique_noNA) + * sapply(dane, num_unique_noNA) + +2. tidyverse + + * summarise_all(dane, num_unique_noNA) + +3. data.table + + * dane[, lapply(.SD, num_unique_noNA)] + diff --git a/docs/notatka_czyste_dane.Rmd b/docs/05-czyste-dane.Rmd similarity index 96% rename from docs/notatka_czyste_dane.Rmd rename to docs/05-czyste-dane.Rmd index e0db37f..23521aa 100644 --- a/docs/notatka_czyste_dane.Rmd +++ b/docs/05-czyste-dane.Rmd @@ -1,24 +1,12 @@ ---- -title: "Dane w formacie wąskim i szerokim. Łączenie tabel danych (join). Operacje na napisach i datach." -author: "Dominika Szewc, Helena Wołoch, Kacper Ambroży, Radosław Szudra" -date: "" -output: pdf_document -header-includes: - - \usepackage{polski} ---- -\newpage -\tableofcontents -\newpage -# Wstęp +# Czyste dane Transformacja danych jest niezwykle ważnym elementem dobrze zrobionego raportu. Dane te powinny być prezentowane w sposób czytelny i ułatwiający ich porównywanie. To od potrzeby biznesowej zależy w jaki sposób powinniśmy przedstwiać dane. Np. dysponując wynikami finansowymi zbieranymi co miesiąc przez trzy lata bo planowania budżetu na następny rok przyda nam się prezentacja ich w formacie wąskim, czyli skupionym na wydatkach względem każdego roku. Jednakże, jeżeli chcielibyśmy kontrolować wydatki w tym następnym roku prezentacja danych w formacie szerokim będzie bardziej korzystna, gdyż będziemy mieli informację ile średnio wydajemy w danym miesiącu i na bieżąco będziemy mogli podejmować decyzję o inwestowaniu lub zaciskaniu pasa. Niekiedy jednak dane mają bardziej skomplikowaną formę i np. składają się z wielu tabel. Wówczas dla łatwiejszego uzyskania informacji biznesowej będzie połączenie tych tabel. Takie operacje w połączeniu z odpowiednią agregacją i grupowaniem zdecydowanie ułatwia wgląd w aktualną sytuację. Ostatnim tematem, na temat któtego ta notatka traktuje są operacje na napisach i datach. Bardzo łatwo uzmysłowić sobie przydatność w posługiwaniu się takimi operacjami. Ułatwia to konstruowanie prostych funkcji, które są kluczowe w każdym projekcie. Chociażby bazując na imionach i nazwiskach pewnych obywateli Polski łatwo wskazać z dużą pewnością kobiety w tym zbiorze sprawdzając ostatnią literę ich imienia (tj. czy dane imie kończy się na literę "a"). -\newpage -# Dane w formacie wąskim i szerokim +## Dane w formacie wąskim i szerokim Dane najczęściej są przedstawiane w postaci tabelarycznej. Jednak mogą być w tej tabeli różnie sformatowane. Wyróżnia się między innymi szeroką reprezentacje danych i wąską reprezentacje danych. W zależności od tego, co chcemy z nimi zrobić czasami trzeba przejść z jednej postaci do drugiej. Aby przetransformować dane korzysta się z funkcji z pakietów **dplyr** i **tidyverse**. @@ -45,8 +33,6 @@ kable_styling(kable(szeroka), position = "center") Można zaobserwować, że wartości z kolumny `extra` zostały wpisane w poszczególne komórki, a kolumna `group` została podzielona na dwie oddzielne kolumny `group 1` i `group 2`. Tak sformatowane dane nazywamy szeroką reprezentacją danych. -\newpage - ## Rozdzielanie na kolumny (wąska -> szeroka) Aby przejść z wąskiego formatu przedstawiania danych do szerokiego, można użyć funkcji `spread()` z pakietu **dplyr**. @@ -79,8 +65,6 @@ sleep %>% pivot_wider(names_from = group, values_from = extra) ``` -\newpage - ## Scalanie kilku kolumn w jedną (szeroka -> wąska) Można wrócić z postaci szerokiej do wąskiej. W tym celu należy użyć funkcji `gather()` z pakietu **tidyr**. @@ -121,7 +105,7 @@ kable_styling(kable(head(szeroka %>% ``` -# Łączenie tabel danych +## Łączenie tabel danych Mamy dwie tabele danych **tab1** z małymi literami oraz **tab2** z wielkimi literami: @@ -140,7 +124,7 @@ gdzie `x` = **tab1**, a `y` = **tab2**. Aby połączyć dwie tabele danych na podstawie wskazanych kolumn lub kolumn o wspólnej nazwie można użyć przykładowych funkcji. -## merge() +### merge() Dostępna w bazowym R. Domyślnie funkcja ta łączy tabele względem nazw kolumn, które są wspólne. @@ -202,7 +186,7 @@ Bez sprecyzowania argumentu `sort` wiersze wyniku merge() zostaną posortowane l Kolumny złączonej tabeli to najpierw kolumny wspólne, następnie pozostałe z `x` a na końcu pozostałe z `y`, co widać na przykładach. -## join() +### join() ```{r, warning = FALSE, echo = FALSE} tab1 <- data.frame(indeks = c(1:6, 6), litery = c("a", "b", "c", "d", "e", "f", "z")) @@ -210,9 +194,9 @@ tab2 <- data.frame(indeks = 4:9, LITERY = c("E", "F", "G", "H", "I", "J")) ``` - ```{r, warning = FALSE, echo = FALSE, message=FALSE} +```{r, warning = FALSE, echo = FALSE, message=FALSE} library(dplyr) - ``` +``` Funkcja z paczki `dplyr`. Tabele `x` i `y` powinny zwykle pochodzić z tego samego źródła danych, ale jeśli `copy = TRUE`, `y` zostanie automatycznie skopiowany do tego samego źródła co `x`. @@ -247,14 +231,15 @@ kable(tabela) Argument `by` przyjmuje wektor nazw zmiennych do połączenia. Jeśli `by = NULL` funkcja `*_join()` domyślnie połączy tabele dopasowując wartości ze wszystkich kolumn o wspólnych nazwach w obu tabelach. -# Operacje na napisach i datach +## Operacje na napisach i datach Większość poniższych funkcji pochodzi z pakietu **string**i. ```{r echo=FALSE, warning=FALSE} library(stringi) ``` -## Operacje na napisach + +### Operacje na napisach 1. Wyznaczanie długości napisów. Funkcja **stri_lenght()** zwraca długości poszczególnych napisów w danym wektorze, a **stri_isempty()** sprawdza, które napisy są puste -> ''. @@ -325,7 +310,7 @@ x <- 'Lasy, pola, pastwiska, koszą traktorem' ``` -## Operacje na datach +### Operacje na datach 1. Funkcją zwracającą aktualną datę systemową jest **Sys.Date()**, a **Sys.time()** aktualny czas systemowy wraz z datą. diff --git a/Notes/ggplot_Notes.Rmd b/docs/06-ggplot2.Rmd similarity index 96% rename from Notes/ggplot_Notes.Rmd rename to docs/06-ggplot2.Rmd index 66a4ff5..f2c8fab 100644 --- a/Notes/ggplot_Notes.Rmd +++ b/docs/06-ggplot2.Rmd @@ -1,44 +1,17 @@ ---- -title: "Ggplot2" -author: "Katarzyna Frankiewicz, Maciej Grabias, Jakub Michałowski" -output: pdf_document - - - ---- - - - -```{r setup, include=FALSE} -knitr::opts_chunk$set(echo = TRUE) -``` - - +# Katarzyna Frankiewicz, Maciej Grabias, Jakub Michałowski ## Wprowadzenie - - Jednym z ważnych elementów przekazywania ciekawych informacji oraz ich analizy jest przedstawienie graficzne interesujących nas danych. W R istnieje kilka sposobów na wizualizację danych. Jednym z nich jest korzytanie z narzędzi oferowanych przez pakiet *ggplot2*. Bibiloteka *ggplot2* oprócz zwykłych funkcji plotowania, implementuje także gramatykę grafiki, co pozwala na wykonanie prawie każdego rodzaju (statystycznej) wizualizacji danych. - - ### Gramatyka grafiki - - Powyżej wspomnieliśmy o gramatyce grafiki. Dla dokładniejszego uporządkowania wiedzy przypomnijmy, że gramatyka grafiki daje nam możliwość zadawania odpowiednich parametórw dla wszystkich linii, słów, strzałek, itp., które połączone tworzą wykres. Dodatkowo możemy m.in. zmieniać układ współrzędnych, czy korygować położenie każdego obiektu znajdującego się na wykresie. Możliwości jakie oferuje nam gramatyka grafiki będą przedstawione dokładniej w dalszej części notatki. - - - ## Podstawy tworzenia wykresów w ggplot2 - - Na początku, aby móc tworzyć wizualizacje, musimy załadować pakiet oraz bibilotekę *ggplot2*. Warto zwrócić uwagę, że *ggplot2* posiada również szereg wbudowanych zestawów danych. Aby pokazać możliwości jakie oferuje nam ggplot, przeprowadzimy symulację danych mpg dostępnych w R. - ```{r start, include=TRUE} library(ggplot2) @@ -49,27 +22,20 @@ head(mpg) Składnia wykresów w ggplot polega na warstwowym budowaniu wykresów. - - 1. Zaczynamy od doboru danych, jakie chcielibyśmy zwizualizaować. 2. Określamy sposób mapowania zmiennych do aspektów wizualnych. 3. Definiujemy styl wykresu. 4. Dodajemy dodatkowe warstwy personalizujące wykres, tzn. dodajemy tytuł, etykiety, itp. (opcjonalnie) - - **Uwaga!** - - Do dodawania nowych warstw do wykresu używamy symbolu "+" . - ```{r first plot, fig.height = 2, fig.width = 2, fig.align = "center"} -# Budujemy podstwę wykresu, określając z jakich danych będziemy korzytsać +# Budujemy podstawę wykresu, określając z jakich danych będziemy korzytsać ggplot(mpg) @@ -358,4 +324,4 @@ Aby zadać względem, której zmiennej chcemy grupować, w funkcji 'facet_' po z -Kiedy korzystamy z funkcji tworzącej panele, automatycznie wszytskie wykresy będą pokazane w układzie współrzędnych dopasowanym do wszytkich okienek. Istnieje jednak możliwość dopasowania układu współrzędnych do każdego panelu osobno. W tym celu możemy wykorzystać funcję 'scale = "free"'. \ No newline at end of file +Kiedy korzystamy z funkcji tworzącej panele, automatycznie wszytskie wykresy będą pokazane w układzie współrzędnych dopasowanym do wszytkich okienek. Istnieje jednak możliwość dopasowania układu współrzędnych do każdego panelu osobno. W tym celu możemy wykorzystać funcję 'scale = "free"'. diff --git a/czysty_i_wydajny_kod_w_R.Rmd b/docs/07-czysty-kod.Rmd similarity index 70% rename from czysty_i_wydajny_kod_w_R.Rmd rename to docs/07-czysty-kod.Rmd index e70ad04..eb43436 100644 --- a/czysty_i_wydajny_kod_w_R.Rmd +++ b/docs/07-czysty-kod.Rmd @@ -1,97 +1,66 @@ ---- -title: "Czysty i wydajny kod w R" -author: Bannert Paulina, Bercz Natalia, Mrozik Piotr, Sudół Dariusz, Wyźnikiewicz - Monika -date: "`r format(Sys.time(), '%d %B, %Y')`" -output: - pdf_document: default - html_document: - df_print: paged - word_document: default -geometry: left = 3.5 cm, right = 2.5 cm, top = 2.5 cm, bottom = 2.5 cm -header-includes: -- \usepackage{polski} -- \usepackage{lmodern} -- \usepackage{hyperref} -- \usepackage{amssymb,amsmath,amsthm} -- \usepackage[T1]{fontenc} -- \usepackage{multirow} -- \usepackage{enumitem} -line-height: 1.5 cm -fontsize: 12 -classoption: twoside ---- -\begin{abstract} -Poniższy dokument zawiera notatkę dotyczącą pisania czystego i wydajnego kodu w R. -Została ona stworzona na podstawie laboratorium z ,,Programowania i analizy danych w R''. -\end{abstract} - -\tableofcontents -\newpage - -\section{Czysty kod} +# Czysty i wydajny kod w R + +## Czysty kod Na początku zajmiemy się szeroko pojętą czystością kodu. Aby dany kod mógł aspirować do takiego miana, musi przede wszystkim spełniać dwa podstawowe warunki: -\begin{itemize} -\item Być łatwym do zrozumienia + +1. Być łatwym do zrozumienia Aby kod był łatwy do zrozumienia musi być przede wszystkim czytelny. Niewątpliwie pomoże w$~$tym odpowiednie nazwanie zmiennych, zadbanie o to, żeby wszystkie użyte funkcje i obiekty miały swoją określoną rolę oraz by relacje między nimi były zrozumiałe. -\item Być łatwym do zmiany + +2. Być łatwym do zmiany Tworząc kod powinniśmy myśleć o tym, że będzie on w przyszłości wykorzystywany. Aby to ułatwić, musi być napisany w taki sposób, żeby można było nanieść drobne poprawki lub zmienić dane bez konieczności zmieniania całego kodu. -\end{itemize} Jeśli te dwa warunki nie są spełnione, istnieje obawa, że wprowadzenie nawet najmniejszych zmian całkowicie zniszczy kod. -\section{Co jeśli w kodzie jest ,,bałagan''?} +### Co jeśli w kodzie jest ,,bałagan''? Nieuporządkowany i nieklarowny kod może sprawić w przyszłości wiele kłopotów, takich jak na przykład: -\begin{itemize} -\item Zmarnowanie czasu + +1. Zmarnowanie czasu Jeśli my lub ktokolwiek inny będzie chciał w przyszłości wykorzystać taki kod z pewnością straci mnóstwo czasu na próby jego przeczytania i zrozumienia. Gdy już mu się to uda, może napotkać kolejny problem w postaci trudności z wprowadzeniem jakichkolwiek zmian. -\item Ograniczenie lub nawet brak możliwości rozwoju + +2. Ograniczenie lub nawet brak możliwości rozwoju Złe napisanie kodu może spowodować, że po jego jedynym użyciu stanie się bezwartościowy. Nie będzie sensu wprowadzać w nim jakichkolwiek zmian (gdyż będzie to zbyt pracochłonne), ani w żaden sposób rozwinąć by mógł posłużyć do przyszłych projektów (gdyż nawet najmniejsze zmiany mogą ,,zepsuć'' istniejący kod). -\item Podatność na wystąpienie błędów + +3. Podatność na wystąpienie błędów W nieczytelnym i napisanym w sposób niezrozumiały kodzie, łatwo przemycić błędy, które na pierwszy rzut oka są niewidoczne, ale wychodzą na jaw później. -\end{itemize} -\section{Opis zmiennych} +### Opis zmiennych + +### Opis intencji -\subsection{Opis intencji} Aby tworzyć czysty kod musimy pamiętać o kilku zasadach. Jedną z nich jest odpowiednie nazywanie zmiennych. Nie powinniśmy używać do tego skrótów, czy przypadkowych znaków. Idealna nazwa od razu wskazuje na to, czym jest dany obiekt oraz co oznacza. Przedstawia zamiary, jakie mamy do nazywanego obiektu. -\subsection{Unikanie błędnych informacji} +### Unikanie błędnych informacji + Równie ważne jest, aby w nazwach nie znajdywały się błędy lub informacje, które mogą wprowadzić potencjalnego czytelnika w błąd. Mówimy tu np. o: -\begin{itemize} -\item nazwaniu kilku obiektów zbyt podobnie, -\item użyciu do nazwania listy (np. osób) słowa $\mathtt{List}$, choć w rzeczywistości ta ,,lista'' osób może być wektorem, -\item użyciu trudno rozróżnialnych znaków (takich jak np. `0` i `O`), -\item nazwaniu wszystkich obiektów za pomocą jednej litery i cyfry (np. $x_1,x_2,...,x_n$). -\end{itemize} - -\subsection{Kilka wskazówek} +- nazwaniu kilku obiektów zbyt podobnie, +- użyciu do nazwania listy (np. osób) słowa $\mathtt{List}$, choć w rzeczywistości ta ,,lista'' osób może być wektorem, +- użyciu trudno rozróżnialnych znaków (takich jak np. `0` i `O`), +- nazwaniu wszystkich obiektów za pomocą jednej litery i cyfry (np. $x_1,x_2,...,x_n$). + +#### Kilka wskazówek + Jakie powinny być idealne nazwy obiektów w R? Oto kilka wskazówek: -\begin{itemize} -\item zrozumiałe dla osób, dla których jest przeznaczony kod, -\item utrzymane w jednym stylu, -\item łatwe do zrozumienia i napisania, -\item nazwa obiektu powinna być rzeczownikiem, który wskazuje na to, z czym mamy do czynienia, -\item nazwa funkcji powinna być czasownikiem wskazującym na to, co robi dana funkcja. -\end{itemize} +- zrozumiałe dla osób, dla których jest przeznaczony kod, +- utrzymane w jednym stylu, +- łatwe do zrozumienia i napisania, +- nazwa obiektu powinna być rzeczownikiem, który wskazuje na to, z czym mamy do czynienia, +- nazwa funkcji powinna być czasownikiem wskazującym na to, co robi dana funkcja. -\section{Funkcje} +### Funkcje W tym rozdziale dowiemy się jak pisać ,,dobre'' funkcje. Tutaj również musimy pamiętać o kilku zasadach. Funkcje powinny: -\begin{itemize} -\item być możliwie jak najkrótsze, -\item odpowiadać za jedno pojedyncze zadanie, -\item być na jednym poziomie abstrakcji, -\item mieć maksymalnie `3` parametry. -\end{itemize} +- być możliwie jak najkrótsze, +- odpowiadać za jedno pojedyncze zadanie, +- być na jednym poziomie abstrakcji, +- mieć maksymalnie `3` parametry. To znaczy, że nie jest wskazane, aby tworzyć jedną wielką funkcję, która np. wylicza kilkanaście rzeczy, aby na końcu wygenerować jeden wynik. Zamiast tego lepiej stworzyć kilka mniejszych funkcji, które będą się odwoływały do poprzednich. Dzięki temu nasz kod będzie bardziej przejrzysty oraz w prosty sposób będzie można sprawdzić, czy pojedyncze funkcje działają poprawnie. @@ -127,7 +96,6 @@ return(c(l,r))} ``` Główne problemy: -\begin{itemize} - czasem przypisanie jest za pomocą `=`, czasem `<-`, - brak spacji po przecinkach, - brak spacji pomiędzy `+`, `-`, `*`, `/`, itd, @@ -136,9 +104,8 @@ Główne problemy: - zmienne mają nic nieznaczące i jednoliterowe nazwy, - nazwa zmiennej `tmp` także nie mówi, czym ona jest, - dwa nieużywane parametry funkcji. -\end{itemize} -\section{Komentarze} +### Komentarze Zazwyczaj komentarze do kodu nie są potrzebne, a wręcz zbędne. Dzieje się tak, ponieważ dobrze napisany kod powinien sam się tłumaczyć, tzn. być na tyle zrozumiałym, żeby dodatkowe komentarze nie były potrzebne. @@ -146,11 +113,11 @@ Jeśli jednak w kodzie jest bałagan, dodatkowe komentarze mogą wręcz wprowadz Od tej reguły są jednak pewne wyjątki. Jeśli używamy niezbyt oczywistych implementacji lub ,,sztuczek programistycznych'' warto wspomnieć w komentarzu, co się w danej chwili dzieje. Wyjątkiem są też komentarze informujące o tym, co trzeba jeszcze zrobić lub o potrzebie poprawienia jakiejś części kodu. -\section{Obiekt a struktura danych} +### Obiekt a struktura danych W kontekście pisania czystego i wydajnego kodu, należy wziąć pod uwagę rozróżnienie pomiędzy klasami a strukturami danych. Te pierwsze zawierają atrybuty i funkcje, a instancje klasy nazywamy obiektem. Zastosowanie klas pozwala na stworzenie interfejsu definującego pewne dane. Struktury danych służą natomiast do reprezentacji danych dowolnego typu a nie ich opisu. -\section{Styl kodu i narzędzia pomagające w utrzymaniu czystego kodu} +## Styl kodu i narzędzia pomagające w utrzymaniu czystego kodu Dobry styl kodowania jest porównywany do prawidłowego stosowania interpunkcji. Jest możliwe nie stosowanie się do jej zasad, jednak przestrzeganie ich pozwala, aby w zapisie panował ład i porządek. W R dominują dwa style, które pomagają utrzymać dobry układ kodu. Jednym jest tidyverse style, a$~$drugim, wywodzącym się z poprzedniego, Google style. Istnieją przewodniki, które ułatwiają stosowanie się do zasad panujących w tych stylach. Style ustosunkowują się m.in. do stawiania spacji po przecinkach, przed operatorami matematycznymi oraz po nich, a także podkreślników w nazwach. diff --git a/docs/Notatka-Shiny_UI.Rmd b/docs/08-shiny-ui.Rmd similarity index 93% rename from docs/Notatka-Shiny_UI.Rmd rename to docs/08-shiny-ui.Rmd index c221402..a53e3e0 100644 --- a/docs/Notatka-Shiny_UI.Rmd +++ b/docs/08-shiny-ui.Rmd @@ -1,228 +1,215 @@ ---- -title: "Shiny: UI" -author: -- Stanisław Banaszek -- Mateusz Drobina -- Dominik Mika -- Adrian Płoszczyca -- Jakub Sobkowiak -date: '' -output: -pdf_document: default -html_document: default -word_document: default ---- - -```{r setup, include=FALSE} -library(shiny) -library(shinyWidgets) -library(shinydashboard) -knitr::opts_chunk$set(echo = TRUE) -``` - -## Wstęp - -Shiny jest pakietem R pozwalającym na tworzenie interaktywnych aplikacji webowych w łatwy i przystępny sposób. Aplikacja w shiny zbudowana jest z dwóch następujcych elementów: - -* ui - user interface, czyli obiekt, w którym zawarty jest wygląd aplikacji, - -* server - funkcja organizująca działanie aplikacji. - -Do uruchomienia aplikacji służy funkcja `shinyApp(ui, server)`. Stworzenie dobrej i czytelnej aplikacji może znacznie ułatwić analizowanie danych. - -W tej notatce zajmiemy się omówieniem elementów oraz podstawowych schematów budowy UI. - -## Tworzenie UI - -Do budowania podstawowego interfejsu w shiny będziemy korzystać z funkcji `fluidPage`, w której tworzymy cały UI. Wszystkie informacje o rodzajach wprowadznych danych, strukturze wyświetlanych danych oraz szeroko rozumianej estetyce aplikacji będą zawarte wewnątrz tej funkcji. - -```{r} -ui <- fluidPage( - # coś -) -``` - -## Układ strony - -Tym co jest bardzo ważne w UI jest oczywiście wygląd, a dokładniej mówiąc przejrzystość i czytelność, dlatego chcielibyśmy uporządkować wyświetlane elementy tak, aby umożliwić użytkownikowi intuicyjne korzystanie z aplikacji. Pakiet shiny oferuje wiele narzędzi pozwalających na zorganizowanie układu interfejsu zgodnie z naszymi oczekiwaniami. - -Przydadzą nam się do tego następujące funkcje: - -* `titlePanel` - funkcja tworząca panel tytułowy, w której podajemy tytuł aplikacji, - -* `sidebarLayout` - funkcja organizująca wygląd strony jako mniejszy panel boczny po lewej stronie oraz większy panel po prawej stronie, - -* `sidebarPanel` - funkcja, którą możemy umieścić w poprzedniej funkcji, aby uporządkować panel, w którym będziemy np. wprowadzać dane, - -* `mainPanel` - funkcja, w której umieszczamy treści, które chcemy, aby znalazły się w panelu głównym, - -* `tabsetPanel` - funkcja umożliwiająca organizowanie paska zakładek. Aby utworzyć zakładki w jej ciele używamy funkcji `tabPanel`, w której umieszczamy dowolne treści, np. wykresy lub tabele. - -Oprócz tego możemy bardziej modyfikować wygląd aplikacji dzięki funkcjom `fluidRow` i `column` pozwalającym na uporządkowanie obiektów odpowiednio w wierszach oraz kolumnach. - -## Inputy i outputy - -Układ strony należy oczywiście podporządkować temu jaką funkcję ma pełnić aplikacja, a także temu jaki rodzaj interakcji ma mieć z nią docelowo użytkownik. Interakcje użytkownika z aplikacją można intuicyjnie podzielić na to co zostaje do aplikacji wprowadzone (input) oraz to co ostatecznie w związku z tym aplikacja zwraca (output). Każdy input i output jest w kodzie identyfikowany dzięki nadanej mu przez nas nazwie. Wewnątrz `fluidPage` możemy zawrzeć różne rodzaje inputów i outputów w zależności od rodzaju wprowadzanych/wyświetlanych danych. - -#### Przykładowe inputy - -* `textInput` - funkcja tworząca pole, w którym użytkownik może wprowadzić dowolny tekst, - -```{r} -ui <- fluidPage( - # Okienko do wpisywania tekstu - textInput("nazwa_inputu_1", "Tekst wyświetlany w aplikacji") -) -``` - -* `numericInput` - funkcja tworząca pole, w którym użytkownik może wprowadzić wartość liczbową, - -```{r} -ui <- fluidPage( - # Okienko do wpisywania liczb - numericInput("nazwa_inputu_2", "Tekst wyświetlany w aplikacji", - # Wartość domyślna - value = 10) -) -``` - -* `selectInput` - funkcja tworząca listę, z której użytkownik może dokonać wyboru - domyślnie parametr `multiple` umożliwia wybór jednej pozycji z listy, - -```{r} -ui <- fluidPage( - # Możliwość wybrania z listy - selectInput("nazwa_inputu_3", "Tekst wyświetlany w aplikacji", - # Lista możliwości do wyboru - choices = c("Wybór_1", "Wybór_2")) -) -``` - -* `sliderInput` - funkcja tworząca suwak umożliwiający użytkownikowi wybór zakresu interesujących go wartości, - -```{r} -ui <- fluidPage( - # Suwak do wyboru wartości - sliderInput("nazwa_inputu_4", "Tekst wyświetlany w aplikacji", - # Wartość domyślna - value = 1, - # Wartość minimalna - min = 0, - # Wartość maksymalna - max = 10) -) -``` - -* `dateRangeInput` - funkcja tworząca pole wyboru zakresu interesujących dat. - -```{r} -ui <- fluidPage( - # Pole wyboru zakresu dat - dateRangeInput("nazwa_inputu_5", "Tekst wyświetlany w aplikacji", - # Data początkowa - start = "2001-01-01", - # Data końcowa - end = "2010-12-31") -) -``` - -#### Przykładowe outputy - -Używanie funkcji wyświetlających outputy jest bardzo proste, ponieważ w UI decydujemy jedynie gdzie i jak wyswietlić output, który jest obiektem utworzonym wewnątrz funkcji `server` na podstawie wprowadzonego przez użytkownika inputu. - -* `textOutput` - funkcja wyświetlająca tekst, - -```{r} -ui <- fluidPage( - # Wyświetla tekst, który stworzyliśmy w serwerze pod daną nazwą - textOutput("nazwa_outputu_1") -) -``` - -* `tableOutput` - podstawowa funkcja wyświetlająca tabelę, - -```{r} -ui <- fluidPage( - # Wyświetla tabelę stworzoną w serwerze pod daną nazwą - tableOutput("nazwa_outputu_2") -) -``` - -* `DTOutput` - funkcja wyświetlająca interaktywną ramkę danych z użyciem pakietu data.table, - -```{r} -ui <- fluidPage( - # Interaktywna ramka danych z użyciem data.table - DT::DTOutput("nazwa_outputu_3") -) -``` - -* `plotOutput` - funkcja wyświetlająca wykres. - -```{r} -ui <- fluidPage( - # Wyświetla wykres stworzony w serwerze - plotOutput("nazwa_outputu_4", - # Szerokość wykresu - width = "100%", - # Wysokość wykresu - height = "400px") -) -``` - - -## Przykład użycia - -Oczywiście powyższe kody były jedynie fragmentami większej całości. Poniżej możemy zobaczyć przykładowy kod obrazujący strukturę budowy interfejsu. Rzeczą, o której należy pamiętać jest oddzielanie funkcji przecinkami. - -```{r} -ui <- fluidPage( - # Tytuł - titlePanel("Tytuł"), - - # To co będzie wyświetlone z boku interfejsu - sidebarLayout( - - # Panel boczny - sidebarPanel( - - # Pierwszy input - wybór - selectInput("nazwa_inputu_1", "Tekst wyświetlany w aplikacji", - choices = c("Wybór_1", "Wybór_2")), - # Drugi input - suwak - sliderInput("nazwa_inputu_2", "Tekst wyświetlany w aplikacji", - value = 1, min = 0, max = 10) - ), - - # Główny panel - mainPanel( - - # Tworzymy zakładki - tabsetPanel( - - # Pierwsza zakładka - wykres - tabPanel("Tytuł wykresu", plotOutput("nazwa_outputu_1")), - # Druga zakładka - ramka danych - tabPanel("Tytuł ramki", DT::DTOutput("nazwa_outputu_2")) - ) - ) - ) -) -``` - -Dodatkowo warto zdawać sobie sprawę, że po wprowadzeniu danych przez użytkownika outputy aktualizują się automatycznie, dlatego często przydatne jest programowanie reaktywne z funkcją `observeEvent` oraz użycie `actionButton`, który pozwala na wykonanie danego działania dopiero po kliknięciu odpowiedniego przycisku przez użytkownika. - -## Wygląd aplikacji - -Ostatecznie chcielibyśmy, aby aplikacja wyglądała bardziej estetycznie. Możemy do tego użyć kilku narzędzi. Po pierwsze możemy zmienić motyw naszej aplikacji. - -Z pomocą przychodzi nam funkcja `shinythemes::themeSelector()`, którą musimy umieścić w naszym UI. Wtedy w naszej aplikacji pojawia się pole z możliwością wyboru motywu. - -Gdy już wybierzemy ulubiony motyw zamieniamy poprzednią funkcję w UI na `theme = shinythemes::shinytheme('NASZ_MOTYW')` i gotowe! - -Poza tym Shiny umożliwia całkowitą customizację wyglądu aplikacji przy użyciu HTML, CSS oraz JavaScript. - -Ostatnim narzędziem, o którym warto pamiętać, jest `shinyWidgetsGallery()`. Jest to bardzo użyteczna aplikacja stworzona w bibliotece `shinyWidgets`, dzięki której możemy między innymi zobaczyć w praktyce działanie różnego typu inputów oraz kod umożliwiający użycie ich w aplikacji. - -## Uwaga - -W tej notatce omówiliśmy podstawowe elementy pozwalające na zbudowanie interfejsu w shiny ale chcielibyśmy też dodać, że w poszukiwaniu bardziej zaawansowanych rozwiązań warto odwiedzić stronę https://shiny.rstudio.com/, gdzie można znaleźć dokumentację pakietu shiny, wiele przykładów oraz nieomówionych tu funkcji. +# Interaktywna wizualizacja danych z pakietem shiny: interfejs użytkownika + +## Wstęp + +Shiny jest pakietem R pozwalającym na tworzenie interaktywnych aplikacji webowych w łatwy i przystępny sposób. Aplikacja w shiny zbudowana jest z dwóch następujcych elementów: + +* ui - user interface, czyli obiekt, w którym zawarty jest wygląd aplikacji, + +* server - funkcja organizująca działanie aplikacji. + +Do uruchomienia aplikacji służy funkcja `shinyApp(ui, server)`. Stworzenie dobrej i czytelnej aplikacji może znacznie ułatwić analizowanie danych. + +W tej notatce zajmiemy się omówieniem elementów oraz podstawowych schematów budowy UI. + +```{r } +library(shiny) +library(shinyWidgets) +library(shinydashboard) +``` + + +## Tworzenie UI + +Do budowania podstawowego interfejsu w shiny będziemy korzystać z funkcji `fluidPage`, w której tworzymy cały UI. Wszystkie informacje o rodzajach wprowadznych danych, strukturze wyświetlanych danych oraz szeroko rozumianej estetyce aplikacji będą zawarte wewnątrz tej funkcji. + +```{r} +ui <- fluidPage( + # coś +) +``` + +## Układ strony + +Tym co jest bardzo ważne w UI jest oczywiście wygląd, a dokładniej mówiąc przejrzystość i czytelność, dlatego chcielibyśmy uporządkować wyświetlane elementy tak, aby umożliwić użytkownikowi intuicyjne korzystanie z aplikacji. Pakiet shiny oferuje wiele narzędzi pozwalających na zorganizowanie układu interfejsu zgodnie z naszymi oczekiwaniami. + +Przydadzą nam się do tego następujące funkcje: + +* `titlePanel` - funkcja tworząca panel tytułowy, w której podajemy tytuł aplikacji, + +* `sidebarLayout` - funkcja organizująca wygląd strony jako mniejszy panel boczny po lewej stronie oraz większy panel po prawej stronie, + +* `sidebarPanel` - funkcja, którą możemy umieścić w poprzedniej funkcji, aby uporządkować panel, w którym będziemy np. wprowadzać dane, + +* `mainPanel` - funkcja, w której umieszczamy treści, które chcemy, aby znalazły się w panelu głównym, + +* `tabsetPanel` - funkcja umożliwiająca organizowanie paska zakładek. Aby utworzyć zakładki w jej ciele używamy funkcji `tabPanel`, w której umieszczamy dowolne treści, np. wykresy lub tabele. + +Oprócz tego możemy bardziej modyfikować wygląd aplikacji dzięki funkcjom `fluidRow` i `column` pozwalającym na uporządkowanie obiektów odpowiednio w wierszach oraz kolumnach. + +## Elementy wejścia i wyjścia + +Układ strony należy oczywiście podporządkować temu jaką funkcję ma pełnić aplikacja, a także temu jaki rodzaj interakcji ma mieć z nią docelowo użytkownik. Interakcje użytkownika z aplikacją można intuicyjnie podzielić na to co zostaje do aplikacji wprowadzone (input) oraz to co ostatecznie w związku z tym aplikacja zwraca (output). Każdy input i output jest w kodzie identyfikowany dzięki nadanej mu przez nas nazwie. Wewnątrz `fluidPage` możemy zawrzeć różne rodzaje inputów i outputów w zależności od rodzaju wprowadzanych/wyświetlanych danych. + +#### Przykładowe elementy wejścia + +* `textInput` - funkcja tworząca pole, w którym użytkownik może wprowadzić dowolny tekst, + +```{r} +ui <- fluidPage( + # Okienko do wpisywania tekstu + textInput("nazwa_inputu_1", "Tekst wyświetlany w aplikacji") +) +``` + +* `numericInput` - funkcja tworząca pole, w którym użytkownik może wprowadzić wartość liczbową, + +```{r} +ui <- fluidPage( + # Okienko do wpisywania liczb + numericInput("nazwa_inputu_2", "Tekst wyświetlany w aplikacji", + # Wartość domyślna + value = 10) +) +``` + +* `selectInput` - funkcja tworząca listę, z której użytkownik może dokonać wyboru - domyślnie parametr `multiple` umożliwia wybór jednej pozycji z listy, + +```{r} +ui <- fluidPage( + # Możliwość wybrania z listy + selectInput("nazwa_inputu_3", "Tekst wyświetlany w aplikacji", + # Lista możliwości do wyboru + choices = c("Wybór_1", "Wybór_2")) +) +``` + +* `sliderInput` - funkcja tworząca suwak umożliwiający użytkownikowi wybór zakresu interesujących go wartości, + +```{r} +ui <- fluidPage( + # Suwak do wyboru wartości + sliderInput("nazwa_inputu_4", "Tekst wyświetlany w aplikacji", + # Wartość domyślna + value = 1, + # Wartość minimalna + min = 0, + # Wartość maksymalna + max = 10) +) +``` + +* `dateRangeInput` - funkcja tworząca pole wyboru zakresu interesujących dat. + +```{r} +ui <- fluidPage( + # Pole wyboru zakresu dat + dateRangeInput("nazwa_inputu_5", "Tekst wyświetlany w aplikacji", + # Data początkowa + start = "2001-01-01", + # Data końcowa + end = "2010-12-31") +) +``` + +#### Przykładowe elementy wyjścia + +Używanie funkcji wyświetlających outputy jest bardzo proste, ponieważ w UI decydujemy jedynie gdzie i jak wyswietlić output, który jest obiektem utworzonym wewnątrz funkcji `server` na podstawie wprowadzonego przez użytkownika inputu. + +* `textOutput` - funkcja wyświetlająca tekst, + +```{r} +ui <- fluidPage( + # Wyświetla tekst, który stworzyliśmy w serwerze pod daną nazwą + textOutput("nazwa_outputu_1") +) +``` + +* `tableOutput` - podstawowa funkcja wyświetlająca tabelę, + +```{r} +ui <- fluidPage( + # Wyświetla tabelę stworzoną w serwerze pod daną nazwą + tableOutput("nazwa_outputu_2") +) +``` + +* `DTOutput` - funkcja wyświetlająca interaktywną ramkę danych z użyciem pakietu data.table, + +```{r} +ui <- fluidPage( + # Interaktywna ramka danych z użyciem data.table + DT::DTOutput("nazwa_outputu_3") +) +``` + +* `plotOutput` - funkcja wyświetlająca wykres. + +```{r} +ui <- fluidPage( + # Wyświetla wykres stworzony w serwerze + plotOutput("nazwa_outputu_4", + # Szerokość wykresu + width = "100%", + # Wysokość wykresu + height = "400px") +) +``` + + +## Przykład użycia + +Oczywiście powyższe kody były jedynie fragmentami większej całości. Poniżej możemy zobaczyć przykładowy kod obrazujący strukturę budowy interfejsu. Rzeczą, o której należy pamiętać jest oddzielanie funkcji przecinkami. + +```{r} +ui <- fluidPage( + # Tytuł + titlePanel("Tytuł"), + + # To co będzie wyświetlone z boku interfejsu + sidebarLayout( + + # Panel boczny + sidebarPanel( + + # Pierwszy input - wybór + selectInput("nazwa_inputu_1", "Tekst wyświetlany w aplikacji", + choices = c("Wybór_1", "Wybór_2")), + # Drugi input - suwak + sliderInput("nazwa_inputu_2", "Tekst wyświetlany w aplikacji", + value = 1, min = 0, max = 10) + ), + + # Główny panel + mainPanel( + + # Tworzymy zakładki + tabsetPanel( + + # Pierwsza zakładka - wykres + tabPanel("Tytuł wykresu", plotOutput("nazwa_outputu_1")), + # Druga zakładka - ramka danych + tabPanel("Tytuł ramki", DT::DTOutput("nazwa_outputu_2")) + ) + ) + ) +) +``` + +Dodatkowo warto zdawać sobie sprawę, że po wprowadzeniu danych przez użytkownika outputy aktualizują się automatycznie, dlatego często przydatne jest programowanie reaktywne z funkcją `observeEvent` oraz użycie `actionButton`, który pozwala na wykonanie danego działania dopiero po kliknięciu odpowiedniego przycisku przez użytkownika. + +## Wygląd aplikacji + +Ostatecznie chcielibyśmy, aby aplikacja wyglądała bardziej estetycznie. Możemy do tego użyć kilku narzędzi. Po pierwsze możemy zmienić motyw naszej aplikacji. + +Z pomocą przychodzi nam funkcja `shinythemes::themeSelector()`, którą musimy umieścić w naszym UI. Wtedy w naszej aplikacji pojawia się pole z możliwością wyboru motywu. + +Gdy już wybierzemy ulubiony motyw zamieniamy poprzednią funkcję w UI na `theme = shinythemes::shinytheme('NASZ_MOTYW')` i gotowe! + +Poza tym Shiny umożliwia całkowitą customizację wyglądu aplikacji przy użyciu HTML, CSS oraz JavaScript. + +Ostatnim narzędziem, o którym warto pamiętać, jest `shinyWidgetsGallery()`. Jest to bardzo użyteczna aplikacja stworzona w bibliotece `shinyWidgets`, dzięki której możemy między innymi zobaczyć w praktyce działanie różnego typu inputów oraz kod umożliwiający użycie ich w aplikacji. + +### Uwaga + +W tej notatce omówiliśmy podstawowe elementy pozwalające na zbudowanie interfejsu w shiny ale chcielibyśmy też dodać, że w poszukiwaniu bardziej zaawansowanych rozwiązań warto odwiedzić stronę https://shiny.rstudio.com/, gdzie można znaleźć dokumentację pakietu shiny, wiele przykładów oraz nieomówionych tu funkcji. diff --git a/docs/09-shiny-server.Rmd b/docs/09-shiny-server.Rmd new file mode 100644 index 0000000..090c68b --- /dev/null +++ b/docs/09-shiny-server.Rmd @@ -0,0 +1,26 @@ +# Interaktywna wizualizacja danych z pakietem shiny: strona serwerowa + +## Wstęp + +Shiny to biblioteka w R pozwalająca na budowanie interaktywnych aplikacji w prosty i szybki sposób. Aplikacja Shiny składa się z dwóch części, opisywanych w dwóch osobnych plikach: interfejs użytkownika (UI), czyli jak aplikacja będzie wyglądać u użytkownika oraz sposób przetwarzania danych (serwer). W tej pracy zajmiemy się stroną serwerową Shiny. + + +## Serwer Shiny + +Aplikacje Shiny zazwyczaj budujemy w sytuacjach, w których mamy dane, chcemy obliczyć pewne rzeczy i narysować odpowiednie wykresy. Użytkownik widzi efekt końcowy, czyli to jak zaprogramowaliśmy gdzie ma się wyświetlać wynik, natomiast w części serwerowej opisujemy jak ten wynik ma być obliczony. Jest to więc część zależna od pliku UI. + +Musimy więc w kodzie serwera zamieścić obiekty opisane w UI. Zauważmy, że tworzymy kod serwera jako funkcję od dwóch parametrów: `input, output`. W środku serwera definiujemy zależności pomiędzy `inputami` i `outputami`. + +Jedną z zalet Shiny jest interaktywność. Dzięki temu użytkownik może na bieżąco zmieniać parametry i generować nowe wykresy. Jednak generowanie kodu na nowo przy każdej zmianie danych nie zawsze jest pożądane. Ważnym pojęciem przy pisaniu strony serwerowej jest reaktywność (żródło infografiki: [Shiny Cheat Sheet](https://rstudio.com/wp-content/uploads/2016/01/shiny-cheatsheet.pdf)). + +![reaktywnosc](img/przyklad3.png) + +Jeśli zmienna jest reaktywna, to znaczy że jakakolwiek jej zmiana powoduje ponowne uruchomienie funkcji z nią powiązanych. Do budowania reaktywnych wyrażeń używamy funkcji `reactive()`. Taka zmienna jest liczona tylko raz i wyrażenia z nią związane używają tej wartości aż do momentu aktualizacji wybranego przez użytkownika. Z pojęciem reaktywności wiąże się kilka ważnych funkcji: + +* `reactiveValues(...)`, które tworzy listą reaktywnych zmiennych, +* `isolate(expr)` - zapobiega zależności od reaktywnych zmiennych, +* `render*()` - funkcje tworzące obiekty do wyświetlenia, które zmieniają się wraz z reaktywnymi zmiennymi, +* `observeEvent(...)` - gdy nie chcemy aby model od razu się zaktualizował przy zmianie danych, a przy jakiejś określonej akcji, +* `reactive()` - tworzy reaktywne wyrażenia +* `eventReactive` - tworzy reaktywne wyrażenia, które nie zależą od wszystkich reaktywnych zmiennych, a zależą jedynie od akcji wymienionych w pierwszym argumencie. + diff --git a/docs/10-git.Rmd b/docs/10-git.Rmd new file mode 100644 index 0000000..c62e5a7 --- /dev/null +++ b/docs/10-git.Rmd @@ -0,0 +1,222 @@ +# Podstawy kontroli wersji przy pomocy Gita + +## Podstawowe informacje +System kontroli wersji to narzędzie, które zarządza zmianami wprowadzanymi w plikach i katalogach w projekcie. Istnieje wiele systemów kontroli wersji. Przykładem takiego systemu jest Git. Jego mocne strony to: + +* Nic, co jest zapisane w Git, nigdy nie jest tracone, więc zawsze możesz wrócić, aby zobaczyć, które wyniki zostały wygenerowane przez które wersje twoich programów. +* Git automatycznie powiadamia Cię, gdy Twoja praca koliduje z pracą innej osoby, więc jest trudniej (choć nie jest to niemożliwe) o przypadkowe nadpisanie pracy. +* Git może synchronizować pracę wykonywaną przez różne osoby na różnych komputerach. + +Kontrola wersji nie dotyczy tylko oprogramowania: książki, artykuły, zestawy parametrów i wszystko, co zmienia się w czasie lub wymaga udostępnienia, może i powinno być przechowywane i udostępniane za pomocą czegoś takiego jak Git. + +Każdy z projektów Git składa się z dwóch części: plików i katalogów, które tworzysz i edytujesz bezpośrednio, oraz dodatkowych informacji, które Git rejestruje o historii projektu. Połączenie tych dwóch rzeczy nazywa się repozytorium. + +Git przechowuje wszystkie dodatkowe informacje w katalogu o nazwie $\texttt{.git}$ znajdującym się w katalogu głównym repozytorium. + +## Podstawowe komendy + +Używając Gita zapewne często będziemy chcieli sprawdzić stan swojego repozytorium. Aby to zrobić, użyjemy polecenie $\texttt{git status}$. + +* $\texttt{git status}$ - wyświetla listę plików, które zostały zmodyfikowane od czasu ostatniego zapisania zmian + +Git ma obszar przejściowy, w którym przechowuje pliki ze zmianami, które chcemy zapisać, a które nie zostały jeszcze zapisane. + +* $\texttt{git status}$ - pokazuje, które pliki znajdują się w tym obszarze przejściowy i które mają zmiany, które nie zostały jeszcze zatwierdzone + +* $\texttt{git diff}$ - pokaże wszystkie zmiany w twoim repozytorium (porównując obecną postać plików z ostatnio zapisaną) +* $\texttt{git diff directory}$ - pokaże zmiany w plikach w jakimś katalogu (porównując obecną postać plików z ostatnio zapisaną) +* $\texttt{git diff filename}$ - pokaże zmiany w danym pliku (porównując obecną postać z ostatnio zapisaną) + +Git różnice między dwiema wersjami pliku wyświetla w poniższy sposób: + ``` +diff --git a/report.txt b/report.txt +index e713b17..4c0742a 100644 +--- a/report.txt ++++ b/report.txt +@@ -1,4 +1,5 @@ + -# Seasonal Dental Surgeries 2017-18 + +# Seasonal Dental Surgeries (2017) 2017-18 + +# TODO: write new summary + ``` +gdzie: + + * $\texttt{a/report.txt, b/report.txt}$ to pierwsza i druga wersja pliku, +* linia druga wypisuje klucze do wewnętrznej bazy danych zmian Gita, +* $\texttt{--- a/report.txt, +++ b/report.txt}$ oznacza, że usuwane linie oznaczone są przedrostkiem $\texttt{-}$, dodawane linie oznaczone są przedrostkiem $\texttt{+}$, +* linia zaczynająca się od $\texttt{@@}$ mówi, gdzie wprowadzane są zmiany. Pary liczb to numer lini ,,startowej'' i liczba linii, +* kolejne linie są listą zmian, które zostały wprowadzone. + + + +* $\texttt{git add filename}$ - dodaje plik do obszaru przejściowego + +* $\texttt{git diff -r HEAD}$ - porówna pliki z repozytorium z plikami z obszaru przejściowego +* $\texttt{git diff -r HEAD path/to/file}$ - porówna konkretny plik z repozytorium z plikiem z obszaru przejściowego + +* $\texttt{nano filename}$ - otwiera plik w edytorze tekstowym $\texttt{nano}$ + * poruszanie się strzałkami +* $\texttt{Backspace}$ - usuń znak +* $\texttt{Ctrl-K}$: usuń linię +* $\texttt{Ctrl-U}$: cofnij usunięcie linii +* $\texttt{Ctrl-O}$: zapisz plik +* $\texttt{Ctrl-X}$: wyjdź z edytora + +* $\texttt{git commit -m "comment"}$ - zapisuje zmiany w obszarze przejściowym z jednowierszowym komunikatem o wprowadzonych zmianach +* $\texttt{git commit --amend - m "new message"}$ - zmienia ostatni komunikat + +* $\texttt{git log}$ - wyświetlenie historii projektu (od najnowszych zmian). Wyświetlany zostaje unikatowy identyfikator dla zatwierdzenia oraz informacje na temat tego kto dokonał zmiany, kiedy i jaki komunikat napisał dokonując zmiany. +* $\texttt{spacja}$ - przejcie w dół o stronę +* $\texttt{q}$ - wyjście +* $\texttt{git log path}$ - wyświetlenie historii danego pliku lub katalogu + + + +## Repozytoria + +   Informacje dotyczące zatwiedzonych zmian przechowywane są poprzez trzypoziomową strukturę. Każde zatwierdzenie (tzw. *commit*) zwiera komunikat o zatwierdzeniu i informacje o autorze i czasie, w którym zatwierdzenie zmian zostało wykonane. Każdy *commit* ma również swoje drzewo, które śledzi, gdzie w repozytorium dokonano zmian. Dla każdego pliku w drzewie istnieje tzw. *blob* (*binary large object*). Każdy *blob* zawiera skompresowaną migawkę zawartości pliku, z chwili w której nastąpił *commit*. + +**Czym jest hash?** + + Każde zatwierdzenie zmian w repozytorium ma unikalny identyfikator zwany **hashem**. Jest on zapisywany jako 40-znakowy ciąg szesnastkowy. Zazwyczaj jednak wystarczy podać pierwsze 6 lub 8 znaków hasha, by odnaleźć konkretne zatwierdzenie (*commit*). Identyfikatory jakimi są hashe umożliwiają Gitowi wydajne udostępnianie danych pomiędzy repozytoriami. + +**Jak wyświetlić konkretny commit?** + + By wyświetlić szczegóły dotyczące konkretnego *commitu* należy użyć komendy **git show** z pierwszymi 6 znakami *hasha* danego *commmitu* np.: *git show Oda2f7*. + +**Czym jest odpowiednik ścieżki względnej w Git?** + +Innym sposobem identyfikacji zatwierdzenia jest użycie odpowiednika ściezki względnej. By wyświetlić zatem ostatni *commit* możemy użyć komendy *git show* z etykietą *HEAD*. Jeśli natomiast zamiast *HEAD* wpiszemy *HEAD~1* wyświetlony zostanie przedostatni *commit*, polecenie *git show HEAD~2* zwróci nam natomiast jeszcze wcześniejszy *commit* itp. + +## Podstawowe komendy + + +* **git log** - wyświetla całą historię danego pliku lub projektu. W Gicie możemy jednak sprawdzić bardziej szczegółowe informacje. Dzięki poleceniu **git annotate file** możemy sprawdzić kto i kiedy dokonał ostatniej zmiany w każdej linijce pliku. + +* **git diff ID1..ID2** - umożliwia sprawdzenie zmian pomiędzy dwoma commitami, których identyfikatory to odpowiednio ID1 i ID2. + +* **git add** - polecenie umożliwiające dodanie nowego pliku. Po wykonaniu tego polecenia Git zaczyna śledzić dodany plik. + +* **git clean -n** - pokazuje listę plików, które są w repozytorium, ale których historia nie jest śledzona przez Gita. + +* **git clean -f** - usuwa pliki, które są w repozytorium i których historii nie śledzi Git. Z używaniem tego polecenia należy uważać, ponieważ usuwa ono pliki z pamięci na stałe i nie da się ich już odzyskać. + +* **git config - -list** - wyświetla ustawienia Gita. + +* **git config - -system** - wyświetla ustawienia każdego użytkownika na danym komputerze. + +* **git config - -global** - wyświetla ustawienia każdego projektu. + +* **git config - -local** - wyświetla ustawienia poszczególnego projektu. + +Każdy poziom zastępuje poziom nad nim, więc ustawienia lokalne (na projekt) mają pierwszeństwo przed ustawieniami globalnymi (na użytkownika), które z kolei mają pierwszeństwo przed ustawieniami systemowymi (dla wszystkich użytkowników na komputerze). + +* **git config - -global setting value** - zmienia konfigurację odpowiedniej wartości dla wszystkich projektów na danym komputerze. Jako *setting* należy wpisać to co chcemy zmienić (np. *user.name*, *user.email* itp.), a jako *value* to co chcemy ustawić. + +## Cofanie zmian + +Teraz dowiemy się jak cofnąć wprowadzone zmiany. + +* $\texttt{git reset HEAD}$- usuwa ostatnio dodany plik ze śledzenia, + +* $\texttt{git checkout -- filename}$ - odrzuci zmiany, które nie zostały jeszcze dodane do śledzenia, + +* $\texttt{git reset HEAD path/to/file}$ - odrzuci ostatnie zmiany w pliku, który został juz dodany do śledzenia, + +* $\texttt{git checkout 2242bd filename}$- zamienia aktualna wersje pliku, na tę o hashu '2242bd'. + +Do ostatniej komendy przydatne może być wykonanie poniższzego polecenia, aby sprawdzić hashe plików. + +* $\texttt{git log - 3 filename}$- pokaże 3 ostatnie commity dotyczące wskazanego pliku. + +Poniższe dwie komendy pokazują, jak cofać zmiany na więcej niż jednym pliku. + +* $\texttt{git reset HEAD data}$- usuwa ze śledzenia wszystkie pliki z katalogu data. Jeżeli nie podamy nazwy katalogu( wtedy wystarczy samo $\texttt{git reset}$) wszystkie pliki zostaną usunięte. + +* $\texttt{git checkout -- data}$- wszystkie pliki w katalagu data zostaną cofnięte do poprzednich wersji. + +## Gałęzie + + +Jeśli nie używasz kontroli wersji, typowym przepływem pracy jest tworzenie różnych podkatalogów do przechowywania różnych wersji projektu w różnych stanach, na przykład deweloperskich i końcowych. Oczywiście zawsze kończy się to ostateczną aktualizacją i ostateczną aktualizacją-poprawioną. Problem polega na tym, że trudno jest to rozwiązać, jeśli masz odpowiednią wersję każdego pliku w odpowiednim podkatalogu i ryzykujesz utratę pracy. +\ +\ +Jednym z powodów, dla których Git jest popularny, jest jego obsługa tworzenia gałęzi (*branchy*), co pozwala na posiadanie wielu wersji Twojej pracy i pozwala na systematyczne śledzenie każdej wersji. +\ +\ +Każda gałąź jest jak wszechświat równoległy: zmiany, które wprowadzasz w jednej gałęzi, nie wpływają na inne gałęzie (dopóki nie połączysz ich z powrotem). +\ +\ +Domyślnie kazde repozytorium Gita ma branch zwany **master**. +\ +\ +Podstawowe komendy związanie z działaniem na branchach (gałęziach): \ + +* $\texttt{git branch}$ - pokazuje wszystkie branche w repozytorium (branch, w którym obecnie się znajdujesz będziesz wylistowany z $*$). \ + +* $\texttt{git diff branch1..branch2}$ - wyświetla różnice między dwoma branchami \ + +*Ciekawostka:* + + * $\texttt{git diff branch1..branch2}$ - -$\texttt{shortstat}$ - wyświetla konkretną liczbę plików które się różnią między dwoma branchami\ + +* $\texttt{git checkout branch1}$ - pozwala przełączyć się na *branch1*\ + +* $\texttt{git checkout -b branch-name}$ - pozwala utworzyć nowego brancha o nazwie *branch-name*\ +\ +Rozgałęzianie pozwala tworzyć równoległe wszechświaty. Scalanie (**merging**) to sposób, w jaki łączysz je z powrotem. Kiedy łączysz jedną gałąź (nazwijmy ją źródłową) z inną (nazwijmy ją docelową), Git włącza zmiany wprowadzone w gałęzi źródłowej do gałęzi docelowej. Jeśli te zmiany nie nakładają się, wynikiem jest nowe zatwierdzenie w gałęzi docelowej, które zawiera wszystko z gałęzi źródłowej. Do mergowania dwóch gałęzi używamy polecenia: \ + +* $\texttt{git merge source destination}$ - mergowanie dwóch branchy w jeden\ + +Czasami zmiany w dwóch gałęziach będą ze sobą kolidować: na przykład poprawki błędów mogą dotyczyć tych samych wierszy kodu lub analizy w dwóch różnych gałęziach mogą dołączać nowe (i różne) rekordy do pliku danych podsumowania. W takim przypadku ty decydujesz o sprzeczności zmian.\ +\ +Jeżeli podczas mergowania występuje konflikt Git informuje Cię, że wystapił problem a $\texttt{git status}$ poinformuje Cię, które pliki wmagają rozwiązania konfliktów.\ +\ +Git pozostawia na danym pliku znaczniki, aby poinformować Cię o konkretnym miejscu konfliktu. Znaczniki te wyglądają następująco: + ``` +<<<<<<< destination-branch-name +...changes from the destination branch... +======= + ...changes from the source branch... +>>>>>>> source-branch-name +``` +\ +Aby rozwiązać konflikt edytuj plik, usuwając znaczniki i wprowadź wszelkie zmiany potrzbne do rozwiązania kofilktu, a następnie zrób *commit* tych zmian.\ +\ + +## Tworzenie własnych repozytoriów + +Przejdźmy do kolejnego zagadnienia związanego z pracą w Gicie. \ +\ +Do tej pory wszystkie poznane funkcje Gita dotyczyły działań na repozytoriach już istniejących. Aby stworzyć własne repozytorium w bieżącym katalogu roboczym wystarczy komenda:\ + +* $\texttt{git init project-name}$ \ + +Warto wspomnieć, że chociaż Git pozwala tworzyć zagnieżdżone repozytoria nie powinieneś tego robić. Aktualizacja takich repozytoriów bardzo szybko staje się bardzo skomplikowana, ponieważ musisz powiedzieć Gitowi, w którym z dwóch katalogów .git ma być przechowywana aktualizacja.\ +\ +*Nie tworzymy repozytorium w innym już istniejącym!* \ +\ +Poniżej kilka ważnych komend:\ + +* $\texttt{git init}$ - inicjalizacja repozytorium w bieżącym katalogu \ + +* $\texttt{git init /path/to/project}$ - inicjalizacja repozytorium we wskazanym ścieżką katalogu \ + +* $\texttt{git clone URL}$ - tworzenie kopii istniejącego pod wskazanym adresem *URL* repozytorium + +* $\texttt{git clone /existing/project newprojectname}$ - tworzenie kopii istniejącego repozytroium o zadanej nazwie - *newprojectname* \ + +* $\texttt{git remote}$ - wyświetla informację o fizycznej lokalizacji na serwerze Gita, z której zostało sklonowane repo \ + +* $\texttt{git remote -v}$ - wyświetla informację o *URL* serwerze Gita, z którego zostało sklonowane repo \ + +* $\texttt{git remote add remote-name URL}$ - pozawala na dodanie własnego remota z podanego *URL* + + * $\texttt{git remote rm remote-name}$ - usuwanie istniejącego remota \ + +* $\texttt{git pull remote branch}$ - pobieranie zmian w *branchu* w lokalnym repozytorium i mergowanie ich z bieżacym brnachem w lokalnym repozytorium \ + +**Uwaga!** \ + +*Git powstrzymuje Cię przed pobieraniem ze zdalnego repozytorium zmian, które mogą nadpisać niezapisane lokalnie zmiany. Wystarczy zrobić* commit *tych zmian lub cofnąć je, a następnie spullować repo ponownie. * \ + +* $\texttt{git push remote-name branch-name}$ - pushuje zmiany wprowadzone lokalnie na danym branchu do zdalnego repozytorium \ diff --git a/docs/OOP_r.rmd b/docs/11-OOP.rmd similarity index 98% rename from docs/OOP_r.rmd rename to docs/11-OOP.rmd index 2497f16..5de853a 100644 --- a/docs/OOP_r.rmd +++ b/docs/11-OOP.rmd @@ -1,12 +1,4 @@ ---- -title: "Programowanie obiektowe w R: S3" -author: "Agata Cieślik" -date: "22.01.2021" -output: html_document ---- -```{r setup, include=FALSE} -knitr::opts_chunk$set(echo = TRUE) -``` +# Programowanie obiektowe w R: klasy S3 Programując w R jesteśmy oswojeni z myśleniem kategoriami funkcji - przekształceń nakładanych na macierze lub ramki danych. Jest to naturalne ze względu na zastosowanie R głównie w statystyce i pochodnych jej dziedzin. @@ -14,7 +6,7 @@ Tymczasem programowanie obiektowe, choć często niepotrzebne do przeprowadzenia Ponadto, znajomość podstaw systemów programowania obiektowego w R umożliwia nam lepsze zrozumienie działania bazowych funkcji i obiektów R oraz ewentualne ich rozbudowywanie. -# Systemy programowania obiektowego w R +## Systemy programowania obiektowego w R W przeciwieństwie do wielu popularnych języków programowania, R nie ma jednego ujednoliconego systemu programowania obiektowego - jest ich wiele, przy czym różnią się nie tylko składnią, ale też funkcjonalnościami. @@ -41,6 +33,7 @@ Każdemu obiektowi mogą być (ale nie muszą) przypisane **atrybuty**. Atrybuty ```{r} attributes(m) ``` + ### Klasy i atrybuty W systemie S3 nie tworzymy definicji klasy, nie określamy również, jakie atrybuty obiekt danej klasy ma. Obiektowi możemy nadać klasę przy jego utworzeniu, z użyciem funkcji *structure*: @@ -296,7 +289,8 @@ setMethod("nazwa_metody", "nazwa_klasy", function(x,...){ # działanie metody na obiekcie klasy }) ``` -### Bonus: wykorzystanie systemu S4 w *autoedzie* + +### Bonus: przykład wykorzystania systemu S4 S4, choć bardziej restrykcyjny niż S3, nadal daje dużo swobody w przypisywaniu klas, dziedziczeniu, w szczególności w manewrowaniu wbudowanymi klasami R. @@ -323,5 +317,3 @@ setClass("BuiltInMeasure", ``` Następnie dla każdej potrzebnej nam funkcji utworzyliśmy odpowiadający jej obiekt - funkcję generyczną klasy *BuiltInMeasure* i zdefiniowaliśmy metody tej funkcji generycznej dla możliwych typów zmiennych, zwracając NA domyślnie i wynik liczbowy, gdzie to możliwe. - - diff --git a/docs/12-shiny-moduly.Rmd b/docs/12-shiny-moduly.Rmd new file mode 100644 index 0000000..07a0a25 --- /dev/null +++ b/docs/12-shiny-moduly.Rmd @@ -0,0 +1,197 @@ +# Moduły w aplikacjach shiny + +## Czym jest moduł Shiny + +Modułem Shiny nazywamy odrębny kawałek aplikacji Shiny. Moduł nie może być wywołany niezależnie od reszty aplikacji. Traktuje się go jako część większej aplikacji lub większego modułu Shiny (moduł może składać się z modułów). + +### Dlaczego warto używać modułów Shiny? + +- **Uproszczenie kodu** - moduły pozwalają nam na uporządkowanie złożonego kodu w przypadku dużych i skomplikowanych aplikacji + +- **Własna przestrzeń nazw** - w aplikacjach shiny ID obiektów z inputów i outputów pochodzą ze wspólnej przestrzeni nazw. To znaczy, że ID każdego z obiektów w całej aplikacji musi być unikalne. Jako że moduł jest osobną funkcją wywołaną w aplikacji, posiada własną przestrzeń nazw. Wystarczy zatem, że ID obiektów są unikalne wewnątrz modułu. + +- **Recykling** - ponieważ moduł Shiny jest niezależną funkcją, może być użyty zarówno wiele razy w jednej aplikacji, jak i w wielu różnych aplikacjach. Dzięki temu można z łatwością przechowywać gotowe fragmenty aplikacji w eRowych pakietach i wykorzystywać je w razie potrzeby. + +## Budowa modułu Shiny. + +- kawałek UI - funkcja odpowiadająca za *User Interface* w module Shiny + +- kawałek serwera - funkcja zawierająca fragment serwera, który jest wykorzystywany w UI + +### Jak używać modułów Shiny? + +Rozważmy aplikację składającą się z dwóch paneli - każdy z wykresem i danymi dla dwóch rozkładów, otrzymaną za pomocą poniższego kodu:\newline + + +```{r eval = FALSE} +library(shiny) +library(ggplot2) + +ui <- fluidPage( + tabsetPanel( + #generujemy panel dla rozkładu normalnego + tabPanel(title = "Rozkład normalny", + tabsetPanel( + tabPanel( + title = "Wykres", + numericInput(inputId = "normal_n", + label = "Podaj wielkość próby", + value = 1000), + plotOutput("normal_plot") + ), + tabPanel( + title = "Dane", + tableOutput("normal_data") + ) + ) + ), + #generujemy panel dla rozkładu wykładniczego + tabPanel(title = "Rozkład wykładniczy", + tabsetPanel( + tabPanel( + title = "wykres", + numericInput(inputId = "exp_n", + label = "Podaj wielkość próby", + value = 1000), + plotOutput("exp_plot") + ), + tabPanel( + title = "Dane", + tableOutput("exp_data") + ) + ) + ) + ) +) + +server <- function(input, output, session) { + #generujemy dane + normal_data <- reactive({ + set.seed(17) + data.frame(id = 1:input[["normal_n"]], + sample = rnorm(input[["normal_n"]])) + }) + exp_data <- reactive({ + set.seed(17) + data.frame(id = 1:input[["exp_n"]], + sample = rnorm(input[["exp_n"]])) + }) + #generujemy tabele + output[["normal_data"]] <- renderTable({ + normal_data() + }) + output[["exp_data"]] <- renderTable({ + exp_data() + }) + #generuemy wykresy + output[["normal_plot"]] <- renderPlot({ + ggplot(normal_data(), aes(x = sample)) + + geom_density() + }) + output[["exp_plot"]] <- renderPlot({ + ggplot(exp_data(), aes(x = sample)) + + geom_density() + + xlim(0, 5) + }) +} +shinyApp(ui, server) +``` + +Aplikacja wygląda następująco: + +\begin{center} +\begin{figure} +\includegraphics[width=700pt, height = 300 pt]{img/app_screen1.PNG} +\end{figure} +\end{center} + +W naszej przestrzeni wykorzystaliśmy nazwy: + +- **inputy** - *normal_n, exp_n* + +- **outputy** - *normal_plot, normal_data, exp_plot, exp_data* + +Co daje razem 6 obiektów. W aplikacji *UI* zajmuje 36 linijek kodu, a *server* 29, razem 65 linijek. + + +Zrefaktoryzuemy kod powyższej aplikacji przy użyciu modułów Shiny. Za powtarzające się elementy (tj. panele z wykresem i danymi) będą odpowiedzialne następujące funkcje *module_UI* oraz *module_SERVER* (odpowiedniki UI oraz servera dla odrębnego fragmentu aplikacji). + +```{r eval = FALSE} +module_UI <- function(id) { + ns <- NS(id) + tagList( + tabsetPanel( + tabPanel( + title = "Wykres", + numericInput(inputId = ns("n"), + label = "Podaj wielkość próby", + value = 1000), + plotOutput(ns("plot")) + ), + tabPanel(title = "Dane", + tableOutput(outputId = ns("data")) + ) + ) + ) +} +``` + +Na szczególną uwagę w powyższym kodzie zasługuje linijka + +```{r eval = FALSE} +ns <- NS(id) +``` + +Za pomocą funkcji *NS()* tworzymy osobną przestrzeń nazw ID.\newline + +```{r eval = FALSE} +module_SERVER <- function(id) { + moduleServer(id, function(input, output, session) { + #generujemy dane + data <- reactive({ + set.seed(17) + data.frame(id = 1:input[["n"]], + sample = rnorm(input[["n"]])) + }) + #generujemy wykres + output[["plot"]] <- renderPlot({ + ggplot(data(), aes(x = sample)) + + geom_density() + }) + #generujemy tabelę + output[["data"]] <- renderTable({ + data() + }) + }) +} +``` + +Ostatecznie nasza aplikacja używająca pomocniczego modułu wygląda następująco + +```{r eval = FALSE} +library(shiny) + +ui <- fluidPage( + titlePanel("Przykładowe ciągłe rozkłady prawdopodobieństwa"), + tabsetPanel( + #generujemy panel dla rozkładu normalnego + tabPanel(title = "Rozkład normalny", + module_UI("norm") + ), + #generujemy panel dla rozkładu wykładniczego + tabPanel(title = "Rozkład wykładniczy", + module_UI("exp") + ) + ) +) + +server <- function(input, output, session) { + module_SERVER("norm") + module_SERVER("exp") +} + +shinyApp(ui, server) +``` + + +Powyższy kod jest czytelniejszy, krótszy, a także rozwiązuje problem wielu zmiennych. diff --git a/docs/404.html b/docs/404.html new file mode 100644 index 0000000..072e41a --- /dev/null +++ b/docs/404.html @@ -0,0 +1,402 @@ + + + + + + + Page not found | Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+
+ + +
+
+ +
+
+

Page not found

+

The page you requested cannot be found (perhaps it was moved or renamed).

+

You may want to try searching to find the page's new location, or use +the table of contents to find the page you are looking for.

+
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + diff --git a/docs/Notatka_temat2.pdf b/docs/Notatka_temat2.pdf deleted file mode 100644 index 5beb963..0000000 Binary files a/docs/Notatka_temat2.pdf and /dev/null differ diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..217c365 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,13 @@ +Welcome! + +This is a minimal example of a book based on R Markdown and **bookdown** (https://github.com/rstudio/bookdown). + +This template provides a skeleton file structure that you can edit to create your book. + +The contents inside the .Rmd files provide some pointers to help you get started, but feel free to also delete the content in each file and start fresh. + +Additional resources: + +The **bookdown** book: https://bookdown.org/yihui/bookdown/ + +The **bookdown** package reference site: https://pkgs.rstudio.com/bookdown diff --git a/docs/Serwer_shiny_notatka.pdf b/docs/Serwer_shiny_notatka.pdf deleted file mode 100644 index 9277bf8..0000000 Binary files a/docs/Serwer_shiny_notatka.pdf and /dev/null differ diff --git a/docs/_book/404.html b/docs/_book/404.html new file mode 100644 index 0000000..072e41a --- /dev/null +++ b/docs/_book/404.html @@ -0,0 +1,402 @@ + + + + + + + Page not found | Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+
+ + +
+
+ +
+
+

Page not found

+

The page you requested cannot be found (perhaps it was moved or renamed).

+

You may want to try searching to find the page's new location, or use +the table of contents to find the page you are looking for.

+
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + diff --git "a/docs/_book/_main_files/figure-html/dopasowanie po\305\202o\305\274enia-1.png" "b/docs/_book/_main_files/figure-html/dopasowanie po\305\202o\305\274enia-1.png" new file mode 100644 index 0000000..53ae0df Binary files /dev/null and "b/docs/_book/_main_files/figure-html/dopasowanie po\305\202o\305\274enia-1.png" differ diff --git "a/docs/_book/_main_files/figure-html/dopasowanie po\305\202o\305\274enia2-1.png" "b/docs/_book/_main_files/figure-html/dopasowanie po\305\202o\305\274enia2-1.png" new file mode 100644 index 0000000..78d0668 Binary files /dev/null and "b/docs/_book/_main_files/figure-html/dopasowanie po\305\202o\305\274enia2-1.png" differ diff --git "a/docs/_book/_main_files/figure-html/dopasowanie po\305\202o\305\274enia3-1.png" "b/docs/_book/_main_files/figure-html/dopasowanie po\305\202o\305\274enia3-1.png" new file mode 100644 index 0000000..925ed38 Binary files /dev/null and "b/docs/_book/_main_files/figure-html/dopasowanie po\305\202o\305\274enia3-1.png" differ diff --git "a/docs/_book/_main_files/figure-html/dopasowanie po\305\202o\305\274enia3-2.png" "b/docs/_book/_main_files/figure-html/dopasowanie po\305\202o\305\274enia3-2.png" new file mode 100644 index 0000000..db584e1 Binary files /dev/null and "b/docs/_book/_main_files/figure-html/dopasowanie po\305\202o\305\274enia3-2.png" differ diff --git a/docs/_book/_main_files/figure-html/first plot-1.png b/docs/_book/_main_files/figure-html/first plot-1.png new file mode 100644 index 0000000..192e82c Binary files /dev/null and b/docs/_book/_main_files/figure-html/first plot-1.png differ diff --git a/docs/_book/_main_files/figure-html/first plot-2.png b/docs/_book/_main_files/figure-html/first plot-2.png new file mode 100644 index 0000000..7212b79 Binary files /dev/null and b/docs/_book/_main_files/figure-html/first plot-2.png differ diff --git a/docs/_book/_main_files/figure-html/first plot-3.png b/docs/_book/_main_files/figure-html/first plot-3.png new file mode 100644 index 0000000..83cc79c Binary files /dev/null and b/docs/_book/_main_files/figure-html/first plot-3.png differ diff --git a/docs/_book/_main_files/figure-html/first plot-4.png b/docs/_book/_main_files/figure-html/first plot-4.png new file mode 100644 index 0000000..ba37f1e Binary files /dev/null and b/docs/_book/_main_files/figure-html/first plot-4.png differ diff --git a/docs/_book/_main_files/figure-html/geom plot-1.png b/docs/_book/_main_files/figure-html/geom plot-1.png new file mode 100644 index 0000000..f52bb6e Binary files /dev/null and b/docs/_book/_main_files/figure-html/geom plot-1.png differ diff --git a/docs/_book/_main_files/figure-html/geom plot-2.png b/docs/_book/_main_files/figure-html/geom plot-2.png new file mode 100644 index 0000000..59ab9c5 Binary files /dev/null and b/docs/_book/_main_files/figure-html/geom plot-2.png differ diff --git a/docs/_book/_main_files/figure-html/mapping plot-1.png b/docs/_book/_main_files/figure-html/mapping plot-1.png new file mode 100644 index 0000000..e9a9752 Binary files /dev/null and b/docs/_book/_main_files/figure-html/mapping plot-1.png differ diff --git a/docs/_book/_main_files/figure-html/mapping plot-2.png b/docs/_book/_main_files/figure-html/mapping plot-2.png new file mode 100644 index 0000000..1fab50f Binary files /dev/null and b/docs/_book/_main_files/figure-html/mapping plot-2.png differ diff --git a/docs/_book/_main_files/figure-html/panele-1.png b/docs/_book/_main_files/figure-html/panele-1.png new file mode 100644 index 0000000..b9e0176 Binary files /dev/null and b/docs/_book/_main_files/figure-html/panele-1.png differ diff --git a/docs/_book/_main_files/figure-html/two geoms on plot-1.png b/docs/_book/_main_files/figure-html/two geoms on plot-1.png new file mode 100644 index 0000000..36fda8d Binary files /dev/null and b/docs/_book/_main_files/figure-html/two geoms on plot-1.png differ diff --git "a/docs/_book/_main_files/figure-html/zarz\304\205dzanaie osiami wsp\303\263\305\202rz\304\231dnych-1.png" "b/docs/_book/_main_files/figure-html/zarz\304\205dzanaie osiami wsp\303\263\305\202rz\304\231dnych-1.png" new file mode 100644 index 0000000..903f0b8 Binary files /dev/null and "b/docs/_book/_main_files/figure-html/zarz\304\205dzanaie osiami wsp\303\263\305\202rz\304\231dnych-1.png" differ diff --git a/docs/_book/czyste-dane.html b/docs/_book/czyste-dane.html new file mode 100644 index 0000000..2d63b52 --- /dev/null +++ b/docs/_book/czyste-dane.html @@ -0,0 +1,1728 @@ + + + + + + + Chapter 6 Czyste dane | Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+
+ + +
+
+ +
+
+

Chapter 6 Czyste dane

+

Transformacja danych jest niezwykle ważnym elementem dobrze zrobionego raportu. Dane te powinny być prezentowane w sposób czytelny i ułatwiający ich porównywanie. To od potrzeby biznesowej zależy w jaki sposób powinniśmy przedstwiać dane. Np. dysponując wynikami finansowymi zbieranymi co miesiąc przez trzy lata bo planowania budżetu na następny rok przyda nam się prezentacja ich w formacie wąskim, czyli skupionym na wydatkach względem każdego roku. Jednakże, jeżeli chcielibyśmy kontrolować wydatki w tym następnym roku prezentacja danych w formacie szerokim będzie bardziej korzystna, gdyż będziemy mieli informację ile średnio wydajemy w danym miesiącu i na bieżąco będziemy mogli podejmować decyzję o inwestowaniu lub zaciskaniu pasa.

+

Niekiedy jednak dane mają bardziej skomplikowaną formę i np. składają się z wielu tabel. Wówczas dla łatwiejszego uzyskania informacji biznesowej będzie połączenie tych tabel. Takie operacje w połączeniu z odpowiednią agregacją i grupowaniem zdecydowanie ułatwia wgląd w aktualną sytuację.

+

Ostatnim tematem, na temat któtego ta notatka traktuje są operacje na napisach i datach. Bardzo łatwo uzmysłowić sobie przydatność w posługiwaniu się takimi operacjami. Ułatwia to konstruowanie prostych funkcji, które są kluczowe w każdym projekcie. Chociażby bazując na imionach i nazwiskach pewnych obywateli Polski łatwo wskazać z dużą pewnością kobiety w tym zbiorze sprawdzając ostatnią literę ich imienia (tj. czy dane imie kończy się na literę “a”).

+
+

6.1 Dane w formacie wąskim i szerokim

+

Dane najczęściej są przedstawiane w postaci tabelarycznej. Jednak mogą być w tej tabeli różnie sformatowane. Wyróżnia się między innymi szeroką reprezentacje danych i wąską reprezentacje danych. W zależności od tego, co chcemy z nimi zrobić czasami trzeba przejść z jednej postaci do drugiej. Aby przetransformować dane korzysta się z funkcji z pakietów dplyr i tidyverse.

+

O postaci szerokiej mówimy, gdy pojedyncza zmienna jest rozdzielona pomiędzy kilka kolumn. Różnicę najłatwiej jest pokazać na przykładzie. W tym celu wykorzystamy wbudowany zbiór danych sleep zawierający informacje o wpływie dwóch leków nasennych na ilość przespanych godzin. Kolumna extra zawiera informacje o ilości dodatkowo przespanych godzin.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+extra + +group + +ID +
+0.7 + +1 + +1 +
+-1.6 + +1 + +2 +
+-0.2 + +1 + +3 +
+-1.2 + +1 + +4 +
+-0.1 + +1 + +5 +
+3.4 + +1 + +6 +
+

Dane są przedstawione w postaci wąskiej, każda zmienna jest przedstawiona w oddzielnej kolumnie. Teraz ‘rozbijmy’ kolumnę group na group 1 i group 2.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ID + +group 1 + +group 2 +
+1 + +0.7 + +1.9 +
+2 + +-1.6 + +0.8 +
+3 + +-0.2 + +1.1 +
+4 + +-1.2 + +0.1 +
+5 + +-0.1 + +-0.1 +
+6 + +3.4 + +4.4 +
+7 + +3.7 + +5.5 +
+8 + +0.8 + +1.6 +
+9 + +0.0 + +4.6 +
+10 + +2.0 + +3.4 +
+

Można zaobserwować, że wartości z kolumny extra zostały wpisane w poszczególne komórki, a kolumna group została podzielona na dwie oddzielne kolumny group 1 i group 2. Tak sformatowane dane nazywamy szeroką reprezentacją danych.

+
+
+

6.2 Rozdzielanie na kolumny (wąska -> szeroka)

+

Aby przejść z wąskiego formatu przedstawiania danych do szerokiego, można użyć funkcji spread() z pakietu dplyr.

+

Funkcja spread(dataset,key,value) przyjmuje trzy agrumenty:

+
    +
  • dataset - zbiór danych w formacie wąskim,

  • +
  • key - kolumna (klucz) odpowiadająca kolumnie, która ma zostać rozłożona,

  • +
  • value - kolumna, w której znajdują się wartości wypełniające nowe kolumny.

  • +
+
szeroka <- spread(sleep, group, extra)
+colnames(szeroka) = c("ID","group 1","group 2")
+kable_styling(kable(head(szeroka)), position = "center")
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ID + +group 1 + +group 2 +
+1 + +0.7 + +1.9 +
+2 + +-1.6 + +0.8 +
+3 + +-0.2 + +1.1 +
+4 + +-1.2 + +0.1 +
+5 + +-0.1 + +-0.1 +
+6 + +3.4 + +4.4 +
+

Drugą opcją na uzyskanie tego samego rezultatu jest użycie funkcji pivot_wider z pakietu tidyverse.

+

Funkcja przyjmuje dwa argumenty pivot_wider(names_from = name, values_from = value):

+
    +
  • name - nazwa kolumny, która ma zostać rozłożona,

  • +
  • value - nazwa kolumny, w której znajdują się wartości.

  • +
+
sleep %>%
+    pivot_wider(names_from = group, values_from = extra)
+
+
+

6.3 Scalanie kilku kolumn w jedną (szeroka -> wąska)

+

Można wrócić z postaci szerokiej do wąskiej. W tym celu należy użyć funkcji gather() z pakietu tidyr.

+

Funkcja gather(dataset, key, value, other) przyjmuje również trzy argumenty:

+
    +
  • dataset - zbiór danych w formacie szerokim,

  • +
  • key - nazwy kolumn z kluczami,

  • +
  • value - nazwy kolumn z wartościami,

  • +
  • other - kolumny dataset, które mają być zawarte w nowej tabeli.

  • +
+

Aby wrócić do postaci wąskiej nałóżmy funkcję gather na wygenerowaną wcześniej tabele szeroka.

+
kable_styling(kable(head(szeroka %>% 
+  gather(group, extra, -ID))),position = "center")
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ID + +group + +extra +
+1 + +1 + +0.7 +
+2 + +1 + +-1.6 +
+3 + +1 + +-0.2 +
+4 + +1 + +-1.2 +
+5 + +1 + +-0.1 +
+6 + +1 + +3.4 +
+

Drugą funkcją, która umożliwia przejście z szerokiej reprezentacji danych do wąskiej jest funkcja pivot_longer z pakietu tidyverse.

+

Funkcja pivot_longer(col_names, names_to = name, values_to = value) przyjmuje trzy argumenty

+
    +
  • col_names - ciąg nazw kolumn, które chcemy złączyć,

  • +
  • name - nazwa nowo powstałej kolumny,

  • +
  • value - nazwa kolumny, w której pojawią się wartości.

  • +
+
kable_styling(kable(head(szeroka %>% 
+  pivot_longer(c("1", "2"), names_to = "group", values_to = "extra"))), 
+  position = "center")
+
+
+

6.4 Łączenie tabel danych

+

Mamy dwie tabele danych tab1 z małymi literami oraz tab2 z wielkimi literami:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Table 6.1: tab1 = x +
+indeks + +litery +
+1 + +a +
+2 + +b +
+3 + +c +
+4 + +d +
+5 + +e +
+6 + +f +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Table 6.1: tab2 = y +
+indeks + +LITERY +
+4 + +E +
+5 + +F +
+6 + +G +
+7 + +H +
+8 + +I +
+9 + +J +
+

gdzie x = tab1, a y = tab2.

+

Aby połączyć dwie tabele danych na podstawie wskazanych kolumn lub kolumn o wspólnej nazwie można użyć przykładowych funkcji.

+
+

6.4.1 merge()

+

Dostępna w bazowym R. Domyślnie funkcja ta łączy tabele względem nazw kolumn, które są wspólne.

+
tabela <- merge(x = tab1, y = tab2)
+kable(tabela)
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+indeks + +litery + +LITERY +
+4 + +d + +E +
+5 + +e + +F +
+6 + +f + +G +
+

Jeśli chcemy być pewni, że tabele zostaną połączone po odpowiedniej kolumnie, możemy przekazać nazwę tej kolumny w argumencie. W tym przypadku:

+
merge(tab1, tab2, by = "indeks") # INNER JOIN
+

Jeśli jest więcej kolumn, po których chcemy połączyć tabele, wystarczy przekazać w argumencie by wektor z nazwami tych kolumn.

+

Gdy nazwy kolumn po których chcemy złączyć tabele różnią się, należy wykorzystać argument by.*. Załóżmy, że kolumna tabeli tab1 - indeks zmieniła nazwę na index, zatem:

+
merge(tab1, tab2, by.x = "index", by.y = "indeks")
+

Wartości kolumn indeks w tab1 oraz tab2 różnią się. Dlatego korzystając z funkcji bez dodatkowych argumentów tracimy dane.

+

Aby zapobiec traceniu danych z poszczególnych tabel należy skorzystać z argumentu all, brakujące wartości zostaną uzupełnione NA:

+
merge(tab1, tab2, all.x = TRUE) # LEFT JOIN
+merge(tab1, tab2, all.y = TRUE) # RIGHT JOIN
+merge(tab1, tab2, all = TRUE)   # OUTER JOIN
+

Dostajemy wtedy kolejno:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Table 6.2: all.x = TRUE +
+indeks + +litery + +LITERY +
+1 + +a + +NA +
+2 + +b + +NA +
+3 + +c + +NA +
+4 + +d + +E +
+5 + +e + +F +
+6 + +f + +G +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Table 6.2: all.y = TRUE +
+indeks + +litery + +LITERY +
+4 + +d + +E +
+5 + +e + +F +
+6 + +f + +G +
+7 + +NA + +H +
+8 + +NA + +I +
+9 + +NA + +J +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Table 6.2: all = TRUE +
+indeks + +litery + +LITERY +
+1 + +a + +NA +
+2 + +b + +NA +
+3 + +c + +NA +
+4 + +d + +E +
+5 + +e + +F +
+6 + +f + +G +
+7 + +NA + +H +
+8 + +NA + +I +
+9 + +NA + +J +
+

Bez sprecyzowania argumentu sort wiersze wyniku merge() zostaną posortowane leksykograficznie po wspólnych kolumnach. Gdy sort = FALSE wiersze będą w nieokreślonej kolejności.

+

Kolumny złączonej tabeli to najpierw kolumny wspólne, następnie pozostałe z x a na końcu pozostałe z y, co widać na przykładach.

+
+
+

6.4.2 join()

+

Funkcja z paczki dplyr. Tabele x i y powinny zwykle pochodzić z tego samego źródła danych, ale jeśli copy = TRUE, y zostanie automatycznie skopiowany do tego samego źródła co x.

+

Są cztery typy join zmieniających:

+
    +
  • left_join() - zwraca wszystkie wiersze z x i wszystkie kolumny z x i y. Wiersze w x bez dopasowania w y będą miały wartości NA w nowych kolumnach. Jeśli istnieje wiele dopasowań między x a y, zwracane są wszystkie kombinacje dopasowań
  • +
+
tabela <- left_join(tab1, tab2)
+kable(tabela)
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+indeks + +litery + +LITERY +
+1 + +a + +NA +
+2 + +b + +NA +
+3 + +c + +NA +
+4 + +d + +E +
+5 + +e + +F +
+6 + +f + +G +
+6 + +z + +G +
+
    +
  • right_join() - analogicznie do left_join(), ale zwraca wszystkie wiersze z y, a wiersze bez dopasowania w x będą miały wartości NA

  • +
  • inner_join() - zwraca wszystkie wiersze z x, w których znajdują się pasujące wartości w y, oraz wszystkie kolumny z x i y. Jeśli istnieje wiele dopasowań między x a y, zwracane są wszystkie kombinacje dopasowań.

  • +
+
tabela <- inner_join(tab1, tab2)
+kable(tabela)
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+indeks + +litery + +LITERY +
+4 + +d + +E +
+5 + +e + +F +
+6 + +f + +G +
+6 + +z + +G +
+
    +
  • full_join() - zwraca wszystkie wiersze i wszystkie kolumny zarówno z x, jak i y. Jeśli nie ma pasujących wartości, zwraca NA dla brakujących.
  • +
+
tabela <- full_join(tab1, tab2)
+kable(tabela)
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+indeks + +litery + +LITERY +
+1 + +a + +NA +
+2 + +b + +NA +
+3 + +c + +NA +
+4 + +d + +E +
+5 + +e + +F +
+6 + +f + +G +
+6 + +z + +G +
+7 + +NA + +H +
+8 + +NA + +I +
+9 + +NA + +J +
+

Argument by przyjmuje wektor nazw zmiennych do połączenia. Jeśli by = NULL funkcja *_join() domyślnie połączy tabele dopasowując wartości ze wszystkich kolumn o wspólnych nazwach w obu tabelach.

+
+
+
+

6.5 Operacje na napisach i datach

+

Większość poniższych funkcji pochodzi z pakietu stringi.

+
+

6.5.1 Operacje na napisach

+
    +
  1. Wyznaczanie długości napisów. +Funkcja stri_lenght() zwraca długości poszczególnych napisów w danym wektorze, a stri_isempty() sprawdza, które napisy są puste -> ’’.

  2. +
  3. Łączenie i powielanie napisów. +Funkcja używana do łączenia kilku wektorów napisów w inny wektor napisów lub nawet w jeden napis, jest stri_paste() i jej warianty.

  4. +
+

Przykład:

+
  x <- LETTERS[1:3]
+    y <- letters[1:3] 
+    z <- '!'
+    stri_paste(x, y, z)
+
## [1] "Aa!" "Bb!" "Cc!"
+
    +
  1. Przycinanie i wypełnianie.
  2. +
+

Funkcja stri_wrap() wstawia znaki nowego wiersza (n), by napis po wyświetleniu np. przy funkcji cat() miał szerokość nie większą, niż podana, jeżeli to możliwe.

+

W przypadku przetwarzania tekstów pochodzących np. z formularzy na stronach internetowych może zachodzić potrzeba usunięcia tzw. białych znaków, np. spacji z początku lub końca napisu. Możemy to zrobić przy użyciu funkcji stri_trim(). Operacja w pewnym sensie odwrotną do tej można wykonać przy użyciu funkcji stri_pad().

+

Przykład:

+
stri_trim('            Mama i tata\n')
+
## [1] "Mama i tata"
+
    +
  1. Formatowanie napisów na podstawie innych obiektów. +Najprostszym sposobem na uzyskanie napisowej reprezentacji danego obiektu jest użycie funkcji as.character().
  2. +
+

Przykład:

+
as.character(list(1L, mean, NULL, pi, FALSE))
+
## [1] "1"                                       "function (x, ...) \nUseMethod(\"mean\")"
+## [3] "NULL"                                    "3.14159265358979"                       
+## [5] "FALSE"
+
x <-data.frame(a=c(TRUE, FALSE, FALSE), b=as.integer(c(1, 2, 3)))
+as.character(x)
+
## [1] "c(TRUE, FALSE, FALSE)" "1:3"
+
    +
  1. Zmiana pojedynczych znaków. +Zmiana poszczególnych znaków na inne przydaje się między innymi na etapie wstępnego przygotowania danych w celu ujednolicenia tekstowych identyfikatorów obiektów, możemy np. zmieniać wielkości wszystkich liter w napisach.
  2. +
+

Przykład:

+
stri_trans_toupper('chcemy duże litery')
+
## [1] "CHCEMY DUŻE LITERY"
+
stri_trans_tolower('ChCemY MałE LiTErY')
+
## [1] "chcemy małe litery"
+
stri_trans_char('zastępowanie znaków', 'ąćęłńóśżź', 'acelnoszz')
+
## [1] "zastepowanie znakow"
+
stri_trans_general('żółć', 'Latin-ASCII')
+
## [1] "zolc"
+
    +
  1. Wyznaczanie podnapisów. +Funkcja stri_sub() zwraca podnapis składający się ze znaków leżących na określonych pozycjach danego napisu.
  2. +
+

Przykład:

+
x <- 'Lasy, pola, pastwiska, koszą traktorem'
+        stri_sub(x, 7)  
+
## [1] "pola, pastwiska, koszą traktorem"
+
+
+

6.5.2 Operacje na datach

+
    +
  1. Funkcją zwracającą aktualną datę systemową jest Sys.Date(), a Sys.time() aktualny czas systemowy wraz z datą.
  2. +
+

Przykład:

+
(data <- Sys.Date())
+
## [1] "2023-10-12"
+
(czas <- Sys.time())
+
## [1] "2023-10-12 00:39:40 CEST"
+
    +
  1. Operacje arytmetyczne na datach – dodawanie, odejmowanie i porównywanie.
  2. +
+

Przykład:

+
data
+
## [1] "2023-10-12"
+
data-365
+
## [1] "2022-10-12"
+
data+365
+
## [1] "2024-10-11"
+
(d <- data-as.Date('2021-01-01'))
+
## Time difference of 1014 days
+
    +
  1. Do konwersji do napisu może służyć przeciążona wersja metody format(), której wywołanie jest tożsame z wywołaniem funkcji strftime() (ang. string-format-time).
  2. +
+

Przykład:

+
strftime(czas, '%Y-%m-%d %H:%M:%S %Z')  
+
## [1] "2023-10-12 00:39:40 CEST"
+
    +
  1. Do znajdowania “najstarszej” i “najmłodszej” daty używamy funkcji max() oraz min().

  2. +
  3. Do pracy ze strefami czasowymi możemy używać poniższych funkcji:

  4. +
+
    +
  1. force_tz() ustawienie strefy czasowej,
  2. +
  3. with_tz() sprawdzenie daty w innej strefie czasowej.
  4. +
+ +
+
+
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + diff --git a/docs/_book/czysty-i-wydajny-kod-w-r.html b/docs/_book/czysty-i-wydajny-kod-w-r.html new file mode 100644 index 0000000..b33fbba --- /dev/null +++ b/docs/_book/czysty-i-wydajny-kod-w-r.html @@ -0,0 +1,512 @@ + + + + + + + Chapter 8 Czysty i wydajny kod w R | Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+
+ + +
+
+ +
+
+

Chapter 8 Czysty i wydajny kod w R

+
+

8.1 Czysty kod

+

Na początku zajmiemy się szeroko pojętą czystością kodu. Aby dany kod mógł aspirować do takiego miana, musi przede wszystkim spełniać dwa podstawowe warunki:

+
    +
  1. Być łatwym do zrozumienia
  2. +
+

Aby kod był łatwy do zrozumienia musi być przede wszystkim czytelny. Niewątpliwie pomoże w\(~\)tym odpowiednie nazwanie zmiennych, zadbanie o to, żeby wszystkie użyte funkcje i obiekty miały swoją określoną rolę oraz by relacje między nimi były zrozumiałe.

+
    +
  1. Być łatwym do zmiany
  2. +
+

Tworząc kod powinniśmy myśleć o tym, że będzie on w przyszłości wykorzystywany. Aby to ułatwić, musi być napisany w taki sposób, żeby można było nanieść drobne poprawki lub zmienić dane bez konieczności zmieniania całego kodu.

+

Jeśli te dwa warunki nie są spełnione, istnieje obawa, że wprowadzenie nawet najmniejszych zmian całkowicie zniszczy kod.

+
+

8.1.1 Co jeśli w kodzie jest ,,bałagan’’?

+

Nieuporządkowany i nieklarowny kod może sprawić w przyszłości wiele kłopotów, takich jak na przykład:

+
    +
  1. Zmarnowanie czasu
  2. +
+

Jeśli my lub ktokolwiek inny będzie chciał w przyszłości wykorzystać taki kod z pewnością straci mnóstwo czasu na próby jego przeczytania i zrozumienia. Gdy już mu się to uda, może napotkać kolejny problem w postaci trudności z wprowadzeniem jakichkolwiek zmian.

+
    +
  1. Ograniczenie lub nawet brak możliwości rozwoju
  2. +
+

Złe napisanie kodu może spowodować, że po jego jedynym użyciu stanie się bezwartościowy. Nie będzie sensu wprowadzać w nim jakichkolwiek zmian (gdyż będzie to zbyt pracochłonne), ani w żaden sposób rozwinąć by mógł posłużyć do przyszłych projektów (gdyż nawet najmniejsze zmiany mogą ,,zepsuć’’ istniejący kod).

+
    +
  1. Podatność na wystąpienie błędów
  2. +
+

W nieczytelnym i napisanym w sposób niezrozumiały kodzie, łatwo przemycić błędy, które na pierwszy rzut oka są niewidoczne, ale wychodzą na jaw później.

+
+
+

8.1.2 Opis zmiennych

+
+
+

8.1.3 Opis intencji

+

Aby tworzyć czysty kod musimy pamiętać o kilku zasadach. Jedną z nich jest odpowiednie nazywanie zmiennych. Nie powinniśmy używać do tego skrótów, czy przypadkowych znaków. Idealna nazwa od razu wskazuje na to, czym jest dany obiekt oraz co oznacza. Przedstawia zamiary, jakie mamy do nazywanego obiektu.

+
+
+

8.1.4 Unikanie błędnych informacji

+

Równie ważne jest, aby w nazwach nie znajdywały się błędy lub informacje, które mogą wprowadzić potencjalnego czytelnika w błąd. Mówimy tu np. o: +- nazwaniu kilku obiektów zbyt podobnie, +- użyciu do nazwania listy (np. osób) słowa \(\mathtt{List}\), choć w rzeczywistości ta ,,lista’’ osób może być wektorem, +- użyciu trudno rozróżnialnych znaków (takich jak np. 0 i O), +- nazwaniu wszystkich obiektów za pomocą jednej litery i cyfry (np. \(x_1,x_2,...,x_n\)).

+
+

8.1.4.1 Kilka wskazówek

+

Jakie powinny być idealne nazwy obiektów w R? Oto kilka wskazówek: +- zrozumiałe dla osób, dla których jest przeznaczony kod, +- utrzymane w jednym stylu,
+- łatwe do zrozumienia i napisania,
+- nazwa obiektu powinna być rzeczownikiem, który wskazuje na to, z czym mamy do czynienia,
+- nazwa funkcji powinna być czasownikiem wskazującym na to, co robi dana funkcja.

+
+
+
+

8.1.5 Funkcje

+

W tym rozdziale dowiemy się jak pisać ,,dobre’’ funkcje. Tutaj również musimy pamiętać o kilku zasadach.
+Funkcje powinny: +- być możliwie jak najkrótsze,
+- odpowiadać za jedno pojedyncze zadanie, +- być na jednym poziomie abstrakcji, +- mieć maksymalnie 3 parametry.

+

To znaczy, że nie jest wskazane, aby tworzyć jedną wielką funkcję, która np. wylicza kilkanaście rzeczy, aby na końcu wygenerować jeden wynik. Zamiast tego lepiej stworzyć kilka mniejszych funkcji, które będą się odwoływały do poprzednich. Dzięki temu nasz kod będzie bardziej przejrzysty oraz w prosty sposób będzie można sprawdzić, czy pojedyncze funkcje działają poprawnie.

+

Co więcej, nie ma sensu tworzyć funkcji, która zwraca nam już oprawioną tabelę z wynikami. Lepiej, gdy zwraca surowe wyniki, a tworzeniem tabeli zajmuje się kolejna funkcja.

+

Przykładowa, poprawnie napisana funkcja:

+
calculate_conf_interval = function(sample, alpha) {
+  len = length(sample)
+  successes = length(sample[sample == 1])
+  mi = successes / n
+  se = sqrt(mi * (1 - mi) / len)
+  quantile = qt(1 - alpha / 2, len - 1)
+  left = mi - quantile * se
+  right = mi + quantile * se
+  return(c(left, right))
+}
+

Przykładowa funkcja, napisana w ,,nieładny’’ sposób:

+
func= function(x,y,temp1,temp2){
+  n =length(x)
+  s <-length(x[x==1])
+  m = s/n
+  sgm = sqrt(mi *(1- m)/n)
+  q<-qt(1 - y /2,len-1)
+  tmp = (s + 0.5 * q ^ 2) /(n + q ^ 2)
+  se = sqrt(tmp *(1 - tmp)/ (n+ q^2))
+  l<- tmp- q* se
+  r = tmp + q*se
+return(c(l,r))}
+

Główne problemy: +- czasem przypisanie jest za pomocą =, czasem <-,
+- brak spacji po przecinkach,
+- brak spacji pomiędzy +, -, *, /, itd,
+- niepoprawnie umiejscowione nawiasy {, }.
+- nazwa funkcji nie opisuje, co robi ta funkcja,
+- zmienne mają nic nieznaczące i jednoliterowe nazwy,
+- nazwa zmiennej tmp także nie mówi, czym ona jest,
+- dwa nieużywane parametry funkcji.

+
+
+

8.1.6 Komentarze

+

Zazwyczaj komentarze do kodu nie są potrzebne, a wręcz zbędne. Dzieje się tak, ponieważ dobrze napisany kod powinien sam się tłumaczyć, tzn. być na tyle zrozumiałym, żeby dodatkowe komentarze nie były potrzebne.

+

Jeśli jednak w kodzie jest bałagan, dodatkowe komentarze mogą wręcz wprowadzić dodatkowy chaos.

+

Od tej reguły są jednak pewne wyjątki. Jeśli używamy niezbyt oczywistych implementacji lub ,,sztuczek programistycznych’’ warto wspomnieć w komentarzu, co się w danej chwili dzieje. Wyjątkiem są też komentarze informujące o tym, co trzeba jeszcze zrobić lub o potrzebie poprawienia jakiejś części kodu.

+
+
+

8.1.7 Obiekt a struktura danych

+

W kontekście pisania czystego i wydajnego kodu, należy wziąć pod uwagę rozróżnienie pomiędzy klasami a strukturami danych. Te pierwsze zawierają atrybuty i funkcje, a instancje klasy nazywamy obiektem. Zastosowanie klas pozwala na stworzenie interfejsu definującego pewne dane. Struktury danych służą natomiast do reprezentacji danych dowolnego typu a nie ich opisu.

+
+
+
+

8.2 Styl kodu i narzędzia pomagające w utrzymaniu czystego kodu

+

Dobry styl kodowania jest porównywany do prawidłowego stosowania interpunkcji. Jest możliwe nie stosowanie się do jej zasad, jednak przestrzeganie ich pozwala, aby w zapisie panował ład i porządek. +W R dominują dwa style, które pomagają utrzymać dobry układ kodu. Jednym jest tidyverse style, a\(~\)drugim, wywodzącym się z poprzedniego, Google style. Istnieją przewodniki, które ułatwiają stosowanie się do zasad panujących w tych stylach. Style ustosunkowują się m.in. do stawiania spacji po przecinkach, przed operatorami matematycznymi oraz po nich, a także podkreślników w nazwach.
+Dodatkowo można zainstalować pakiety, które będą pomagać w utrzymaniu schludnego kodu: cleanr, stylerr, lintr.

+ +
+
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + diff --git "a/docs/_book/dane-w-formacie-w\304\205skim-i-szerokim.html" "b/docs/_book/dane-w-formacie-w\304\205skim-i-szerokim.html" new file mode 100644 index 0000000..37c5596 --- /dev/null +++ "b/docs/_book/dane-w-formacie-w\304\205skim-i-szerokim.html" @@ -0,0 +1,744 @@ + + + + + + + Chapter 7 Dane w formacie wąskim i szerokim | Dane w formacie wąskim i szerokim. Łączenie tabel danych (join). Operacje na napisach i datach. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+ + +
+
+ +
+
+

Chapter 7 Dane w formacie wąskim i szerokim

+

Dane najczęściej są przedstawiane w postaci tabelarycznej. Jednak mogą być w tej tabeli różnie sformatowane. Wyróżnia się między innymi szeroką reprezentacje danych i wąską reprezentacje danych. W zależności od tego, co chcemy z nimi zrobić czasami trzeba przejść z jednej postaci do drugiej. Aby przetransformować dane korzysta się z funkcji z pakietów dplyr i tidyverse.

+

O postaci szerokiej mówimy, gdy pojedyncza zmienna jest rozdzielona pomiędzy kilka kolumn. Różnicę najłatwiej jest pokazać na przykładzie. W tym celu wykorzystamy wbudowany zbiór danych sleep zawierający informacje o wpływie dwóch leków nasennych na ilość przespanych godzin. Kolumna extra zawiera informacje o ilości dodatkowo przespanych godzin.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+extra + +group + +ID +
+0.7 + +1 + +1 +
+-1.6 + +1 + +2 +
+-0.2 + +1 + +3 +
+-1.2 + +1 + +4 +
+-0.1 + +1 + +5 +
+3.4 + +1 + +6 +
+

Dane są przedstawione w postaci wąskiej, każda zmienna jest przedstawiona w oddzielnej kolumnie. Teraz ‘rozbijmy’ kolumnę group na group 1 i group 2.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ID + +group 1 + +group 2 +
+1 + +0.7 + +1.9 +
+2 + +-1.6 + +0.8 +
+3 + +-0.2 + +1.1 +
+4 + +-1.2 + +0.1 +
+5 + +-0.1 + +-0.1 +
+6 + +3.4 + +4.4 +
+7 + +3.7 + +5.5 +
+8 + +0.8 + +1.6 +
+9 + +0.0 + +4.6 +
+10 + +2.0 + +3.4 +
+

Można zaobserwować, że wartości z kolumny extra zostały wpisane w poszczególne komórki, a kolumna group została podzielona na dwie oddzielne kolumny group 1 i group 2. Tak sformatowane dane nazywamy szeroką reprezentacją danych.

+
+
+

7.1 Rozdzielanie na kolumny (wąska -> szeroka)

+

Aby przejść z wąskiego formatu przedstawiania danych do szerokiego, można użyć funkcji spread() z pakietu dplyr.

+

Funkcja spread(dataset,key,value) przyjmuje trzy agrumenty:

+
    +
  • dataset - zbiór danych w formacie wąskim,

  • +
  • key - kolumna (klucz) odpowiadająca kolumnie, która ma zostać rozłożona,

  • +
  • value - kolumna, w której znajdują się wartości wypełniające nowe kolumny.

  • +
+
szeroka <- spread(sleep, group, extra)
+colnames(szeroka) = c("ID","group 1","group 2")
+kable_styling(kable(head(szeroka)), position = "center")
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ID + +group 1 + +group 2 +
+1 + +0.7 + +1.9 +
+2 + +-1.6 + +0.8 +
+3 + +-0.2 + +1.1 +
+4 + +-1.2 + +0.1 +
+5 + +-0.1 + +-0.1 +
+6 + +3.4 + +4.4 +
+

Drugą opcją na uzyskanie tego samego rezultatu jest użycie funkcji pivot_wider z pakietu tidyverse.

+

Funkcja przyjmuje dwa argumenty pivot_wider(names_from = name, values_from = value):

+
    +
  • name - nazwa kolumny, która ma zostać rozłożona,

  • +
  • value - nazwa kolumny, w której znajdują się wartości.

  • +
+
sleep %>%
+    pivot_wider(names_from = group, values_from = extra)
+
+
+
+

7.2 Scalanie kilku kolumn w jedną (szeroka -> wąska)

+

Można wrócić z postaci szerokiej do wąskiej. W tym celu należy użyć funkcji gather() z pakietu tidyr.

+

Funkcja gather(dataset, key, value, other) przyjmuje również trzy argumenty:

+
    +
  • dataset - zbiór danych w formacie szerokim,

  • +
  • key - nazwy kolumn z kluczami,

  • +
  • value - nazwy kolumn z wartościami,

  • +
  • other - kolumny dataset, które mają być zawarte w nowej tabeli.

  • +
+

Aby wrócić do postaci wąskiej nałóżmy funkcję gather na wygenerowaną wcześniej tabele szeroka.

+
kable_styling(kable(head(szeroka %>% 
+  gather(group, extra, -ID))),position = "center")
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ID + +group + +extra +
+1 + +1 + +0.7 +
+2 + +1 + +-1.6 +
+3 + +1 + +-0.2 +
+4 + +1 + +-1.2 +
+5 + +1 + +-0.1 +
+6 + +1 + +3.4 +
+

Drugą funkcją, która umożliwia przejście z szerokiej reprezentacji danych do wąskiej jest funkcja pivot_longer z pakietu tidyverse.

+

Funkcja pivot_longer(col_names, names_to = name, values_to = value) przyjmuje trzy argumenty

+
    +
  • col_names - ciąg nazw kolumn, które chcemy złączyć,

  • +
  • name - nazwa nowo powstałej kolumny,

  • +
  • value - nazwa kolumny, w której pojawią się wartości.

  • +
+
kable_styling(kable(head(szeroka %>% 
+  pivot_longer(c("1", "2"), names_to = "group", values_to = "extra"))), 
+  position = "center")
+
+
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + diff --git a/docs/_book/eksploracyjna-analiza-danych.html b/docs/_book/eksploracyjna-analiza-danych.html new file mode 100644 index 0000000..cc64cf2 --- /dev/null +++ b/docs/_book/eksploracyjna-analiza-danych.html @@ -0,0 +1,468 @@ + + + + + + + Chapter 4 Eksploracyjna analiza danych | Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+
+ + +
+
+ +
+
+

Chapter 4 Eksploracyjna analiza danych

+

Badanie eksploracyjne danych (ang. exploratory data analysis) dotyczy opisu, wizualizacji i badania zebranych danych bez potrzeby zakładania z góry hipotez badawczych. Badania ekploracyjne obejmują również wstępne sprawdzenie danych w celu skontrolowania założeń modeli statystycznych lub występowania błędów w danych (np. braków odpowiedzi).

+
+

4.1 Dane tabelaryczne

+

Dane tabelaryczne to dane, które mają postać tabeli. Tabela to struktura danych, która składa się z wierszy i kolumn. Każdy wiersz odpowiada pewnej obserwacji, której cechy zostały zapisane w kolejnych kolumnach.

+
+
+

4.2 Typy zmiennych

+

Zmienne, które opisują kolejne obserwacje możemy podzielić na:

+
    +
  • zmienne jakościowe (niemierzalne) +
      +
    • porządkowe - np. klasyfikacja wzrostu (niski, średni, wysoki)
    • +
    • nominalne - np. kolor oczu, płeć, grupa krwi
    • +
  • +
  • zmienne ilościowe (mierzalne) +
      +
    • dyskretne - np. liczba dzieci, liczba gospodarstw domowych, wiek (w rozumieniu ilości skończonych lat)
    • +
    • ciągłe - np. wzrost, masa, wiek (w rozumieniu ilości dni między datą urodzin a datą badania)
    • +
    • proporcjonalne - np. masa, długość, temperatura wyrażona w Kelwinach lub stopniach Rankine’a (przyjmujemy istnienie zera i możemy twierdzić, że jedno ciało jest dwukrotnie gorętsze od drugiego)
    • +
    • interwałowe - np. temperatura wyrażona w stopniach Celsjusza lub Fahrenheita (możemy twierdzić, że coś jest o 20 °C cieplejsze od czegoś innego, ale nie możemy stwierdzić ilokrotnie cieplejsze jest ciało o temperaturze 40 °C od ciała o temperaturze –10 °C), data kalendarzowa (możemy mówić o stałej różnicy pomiędzy kolejnymi dniami)
    • +
  • +
+
+
+

4.3 Miary

+

Zapoznając się z danymi chcielibyśmy sprawdzić wokół jakiej wartości są skupione oraz jak bardzo są zmienne wartości danej cechy.

+

Miary lokacji (miary tendencji centralnej) pomagają nam umiejscowić dane na osi. Przykładami takich miar są:

+
    +
  • średnia - najczęściej arytmetyczna określona jako \(\overline{x} = \frac{1}{n}\sum\limits_{i=1}^n x_i\).
  • +
  • dominanta (moda) - ozn. \(Mo\) - dla zmiennych o rozkładzie dyskretnym, wartość o największym prawdopodobieństwie wystąpienia lub wartość najczęściej występująca w próbie. Dla zmiennej losowej o rozkładzie ciągłym jest to argument, dla którego funkcja gęstości prawdopodobieństwa ma wartość największą.
  • +
  • mediana - ozn. \(Me\) - wartość cechy w szeregu uporządkowanym, powyżej i poniżej której znajduje się jednakowa liczba obserwacji.
  • +
  • kwantyle rzędu \(p\) - wartość cechy w szeregu uporządkowanym, poniżej której znajduje się \(p \cdot 100\%\) liczby obserwacji, a powyżej której znajduje się \((1 - p) \cdot 100\%\) liczby obserwacji.
  • +
+

Natomiast miary rozrzutu dostarczają informacji jak bardzo zróżnicowane są obserwacje pod względem badanej cechy. Przykładami takich miar są:

+
    +
  • wariancja - stopień rozrzutu badanej cechy wokół wartości oczekiwanej. Im większa wariancja, tym rozrzut zmiennej jest większy. Nieobciążony estymator wariancji wyraża się wzorem: \(s^2 = \frac{1}{n}\sum\limits_{i=1}^n\left(x_i - \overline{x}\right)^2\)
  • +
  • odchylenie standardowe - mówi nam o przeciętnym odchyleniu wartości zmiennej losowej od jej wartości oczekiwanej. Im odchylenie standardowe jest większe, tym większe zróżnicowanie wartości badanej cechy. Odchylenie standardowe z próby obliczamy jako pierwiastek z wariancji z próby, tzn. \(s = \sqrt{s^2}\).
  • +
  • rozstęp międzykwartylowy - różnica między trzecim a pierwszym kwartylem. Ponieważ pomiędzy tymi kwartylami znajduje się z definicji 50% wszystkich obserwacji (położonych centralnie w rozkładzie), dlatego im większa szerokość tego rozstępu, tym większe zróżnicowanie cechy.
  • +
+

Wyróżniamy także miary asymetrii. Miary asymetrii mówią nam, czy większa część populacji klasuje się powyżej, czy poniżej przeciętnego poziomu badanej cechy. Asymetrię rozkładu można zbadać porównując średnią, dominantę i medianę.

+
    +
  • W przypadku rozkładu symetrycznego wszystkie te parametry są równe.
  • +
  • Jeśli zachodzi nierówność \(Mo < Me < \mathbb{E} X\), to mamy do czynienia z prawostronną asymetrycznością rozkładu. Tzn. dużo małych wartości i bardzo mało dużych.
  • +
  • Jeśli zachodzi nierówność \(\mathbb{E} X < Me < Mo\), to mamy do czynienia z lewostronną asymetrycznością rozkładu. Tzn. mało małych i bardzo dużo dużych.
  • +
+
+
+

4.4 R - podsumowanie kolumn

+

Podstawowymi funkcjami, które pomagają nam zapoznać się z danymi są funkcje:

+
    +
  • \(\texttt{head}\) - zwraca pierwszą część wektora, macierzy, tabeli lub ramki danych. Domyślnie 6 pierwszych elementów.
  • +
  • \(\texttt{nrow}\) - zwraca liczbę wierszy macierzy, tabeli lub ramki danych.
  • +
  • \(\texttt{ncol}\) - zwraca liczbę kolumn macierzy, tabeli lub ramki danych.
  • +
+

Natomiast podstawowymi funkcjami, które podsumowują kolejne kolumny są funkcje:

+
    +
  • \(\texttt{str}\) - zwraca strukturę danego obiektu. Wyświetla np. klasę obiektu, liczbę wierszy i kolumn, a także nazwę danej kolumny, typ wartości w niej zawartych, jak i kilka początkowych wartości.

  • +
  • \(\texttt{summary}\) - zwraca podsumowanie każdej kolumny. Dla zmiennych ciagłych wyznacza wartości tj.:

    +
      +
    • wartość najmniejsza i największa
    • +
    • średnia i mediana
    • +
    • 1 (0.25) i 3 (0.75) kwartyl
    • +
    • liczba wartości brakujących (NA)
    • +
    +

    Natomiast w przypadku zmiennych dyskretnych wyznacza liczbę obserwacji, które przyjmują daną wartość zmiennej.

  • +
  • \(\texttt{glimpse}\) - funkcja z pakietu \(\texttt{tidyverse}\) podobna do \(\texttt{str}\), ale stara się pokazać jak najwięcej danych. Wyświetla np. liczbę wierszy i kolumn, a także nazwę danej kolumny, typ wartości w niej zawartych oraz jak najwięcej wartości z tej kolumny.

  • +
+ +
+
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + diff --git a/docs/notatka_lab3/csv_example.gif b/docs/_book/img/csv_example.gif similarity index 100% rename from docs/notatka_lab3/csv_example.gif rename to docs/_book/img/csv_example.gif diff --git a/docs/notatka_lab3/excel_example.png b/docs/_book/img/excel_example.png similarity index 100% rename from docs/notatka_lab3/excel_example.png rename to docs/_book/img/excel_example.png diff --git a/docs/notatka_lab3/json_example.jpeg b/docs/_book/img/json_example.jpeg similarity index 100% rename from docs/notatka_lab3/json_example.jpeg rename to docs/_book/img/json_example.jpeg diff --git a/docs/notatka_lab3/libreoffice_example.png b/docs/_book/img/libreoffice_example.png similarity index 100% rename from docs/notatka_lab3/libreoffice_example.png rename to docs/_book/img/libreoffice_example.png diff --git a/docs/przyklad3.png b/docs/_book/img/przyklad3.png similarity index 100% rename from docs/przyklad3.png rename to docs/_book/img/przyklad3.png diff --git a/docs/notatka_lab3/xml_example.gif b/docs/_book/img/xml_example.gif similarity index 100% rename from docs/notatka_lab3/xml_example.gif rename to docs/_book/img/xml_example.gif diff --git a/docs/_book/index.html b/docs/_book/index.html new file mode 100644 index 0000000..212536c --- /dev/null +++ b/docs/_book/index.html @@ -0,0 +1,423 @@ + + + + + + + Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+
+ + +
+
+ +
+ +
+

Chapter 1 Wstęp

+

Autorzy poszczególnych rozdziałów:

+
    +
  • Podstawy języka R: Michał Dylewicz, Marcela Kamchen, Anna Krasoń, Katarzyna Kulon, Arkadiusz Soból (z wyjątkiem podrozdziału Funkcje).
  • +
  • Wczytywanie danych: Marta Kałużna, Sebastian Jachimek, Joanna Grunwald, Wojciech Wojnar.
  • +
  • Eksploracyjna analiza danych: Magdalena Mazur, Agata Rogowska, Zuzanna Różak, Aleksandra Siepiela. [Także podrozdział Funkcje pierwszego rozdziału.]
  • +
  • Podstawy kontroli wersji z Gitem: Magdalena Mazur, Agata Rogowska, Zuzanna Różak, Aleksandra Siepiela.
  • +
  • Przetwarzanie danych tabelarycznych: Weronika Domaszewska, Ewelina Grzmocińska, Gracjan Hrynczyszyn, Dominik Jaźwiecki, Michał Ociepa.
  • +
  • Czyste dane: Kacper Ambroży, Dominika Szewc, Radosław Szudra, Helena Wołoch.
  • +
  • Wizualizacja danych z pakietem ggplot2: Katarzyna Frankiewicz, Maciej Grabias, Jakub Michałowski
  • +
  • Czysty i wydajny kod w R: Paulina Bannert, Natalia Bercz, Piotr Mrozik, Dariusz Sudół, Monika Wyźnikiewicz
  • +
  • Interaktywna wizualizacja danych z pakietem shiny: interfejs użytkownika: Stanisław Banaszek, Mateusz Drobina, Dominik Mika, Adrian Płoszczyca, Jakub Sobkowiak
  • +
  • Interaktywna wizualizacja danych z pakietem shiny: strona serwerowa: Wojciech Leszkowicz, Małgorzata Stawińska, Tomasz Szmyd, Maciej Tadej.
  • +
+

Dodatkowe rozdziały: +- Podstawy kontroli wersji przy pomocy Gita: Magdalena Mazur, Agata Rogowska, Zuzanna Różak, Aleksandra Siepiela. +- Programowanie obiektowe w R: klasy S3: Agata Cieślik. +- Moduły w aplikacjach shiny: Krystyna Grzesiak.

+ +
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + diff --git "a/docs/_book/interaktywna-wizualizacja-danych-z-pakietem-shiny-interfejs-u\305\274ytkownika.html" "b/docs/_book/interaktywna-wizualizacja-danych-z-pakietem-shiny-interfejs-u\305\274ytkownika.html" new file mode 100644 index 0000000..436f1bd --- /dev/null +++ "b/docs/_book/interaktywna-wizualizacja-danych-z-pakietem-shiny-interfejs-u\305\274ytkownika.html" @@ -0,0 +1,598 @@ + + + + + + + Chapter 9 Interaktywna wizualizacja danych z pakietem shiny: interfejs użytkownika | Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+
+ + +
+
+ +
+
+

Chapter 9 Interaktywna wizualizacja danych z pakietem shiny: interfejs użytkownika

+
+

9.1 Wstęp

+

Shiny jest pakietem R pozwalającym na tworzenie interaktywnych aplikacji webowych w łatwy i przystępny sposób. Aplikacja w shiny zbudowana jest z dwóch następujcych elementów:

+
    +
  • ui - user interface, czyli obiekt, w którym zawarty jest wygląd aplikacji,

  • +
  • server - funkcja organizująca działanie aplikacji.

  • +
+

Do uruchomienia aplikacji służy funkcja shinyApp(ui, server). Stworzenie dobrej i czytelnej aplikacji może znacznie ułatwić analizowanie danych.

+

W tej notatce zajmiemy się omówieniem elementów oraz podstawowych schematów budowy UI.

+
library(shiny)
+library(shinyWidgets)
+library(shinydashboard)
+
+
+

9.2 Tworzenie UI

+

Do budowania podstawowego interfejsu w shiny będziemy korzystać z funkcji fluidPage, w której tworzymy cały UI. Wszystkie informacje o rodzajach wprowadznych danych, strukturze wyświetlanych danych oraz szeroko rozumianej estetyce aplikacji będą zawarte wewnątrz tej funkcji.

+
ui <- fluidPage(
+ # coś
+)
+
+
+

9.3 Układ strony

+

Tym co jest bardzo ważne w UI jest oczywiście wygląd, a dokładniej mówiąc przejrzystość i czytelność, dlatego chcielibyśmy uporządkować wyświetlane elementy tak, aby umożliwić użytkownikowi intuicyjne korzystanie z aplikacji. Pakiet shiny oferuje wiele narzędzi pozwalających na zorganizowanie układu interfejsu zgodnie z naszymi oczekiwaniami.

+

Przydadzą nam się do tego następujące funkcje:

+
    +
  • titlePanel - funkcja tworząca panel tytułowy, w której podajemy tytuł aplikacji,

  • +
  • sidebarLayout - funkcja organizująca wygląd strony jako mniejszy panel boczny po lewej stronie oraz większy panel po prawej stronie,

  • +
  • sidebarPanel - funkcja, którą możemy umieścić w poprzedniej funkcji, aby uporządkować panel, w którym będziemy np. wprowadzać dane,

  • +
  • mainPanel - funkcja, w której umieszczamy treści, które chcemy, aby znalazły się w panelu głównym,

  • +
  • tabsetPanel - funkcja umożliwiająca organizowanie paska zakładek. Aby utworzyć zakładki w jej ciele używamy funkcji tabPanel, w której umieszczamy dowolne treści, np. wykresy lub tabele.

  • +
+

Oprócz tego możemy bardziej modyfikować wygląd aplikacji dzięki funkcjom fluidRow i column pozwalającym na uporządkowanie obiektów odpowiednio w wierszach oraz kolumnach.

+
+
+

9.4 Elementy wejścia i wyjścia

+

Układ strony należy oczywiście podporządkować temu jaką funkcję ma pełnić aplikacja, a także temu jaki rodzaj interakcji ma mieć z nią docelowo użytkownik. Interakcje użytkownika z aplikacją można intuicyjnie podzielić na to co zostaje do aplikacji wprowadzone (input) oraz to co ostatecznie w związku z tym aplikacja zwraca (output). Każdy input i output jest w kodzie identyfikowany dzięki nadanej mu przez nas nazwie. Wewnątrz fluidPage możemy zawrzeć różne rodzaje inputów i outputów w zależności od rodzaju wprowadzanych/wyświetlanych danych.

+
+

9.4.0.1 Przykładowe elementy wejścia

+
    +
  • textInput - funkcja tworząca pole, w którym użytkownik może wprowadzić dowolny tekst,
  • +
+
ui <- fluidPage(
+  # Okienko do wpisywania tekstu
+  textInput("nazwa_inputu_1", "Tekst wyświetlany w aplikacji")
+)
+
    +
  • numericInput - funkcja tworząca pole, w którym użytkownik może wprowadzić wartość liczbową,
  • +
+
ui <- fluidPage(
+  # Okienko do wpisywania liczb
+  numericInput("nazwa_inputu_2", "Tekst wyświetlany w aplikacji",
+              # Wartość domyślna
+              value = 10)
+)
+
    +
  • selectInput - funkcja tworząca listę, z której użytkownik może dokonać wyboru - domyślnie parametr multiple umożliwia wybór jednej pozycji z listy,
  • +
+
ui <- fluidPage(
+  # Możliwość wybrania z listy
+   selectInput("nazwa_inputu_3", "Tekst wyświetlany w aplikacji", 
+               # Lista możliwości do wyboru
+               choices = c("Wybór_1", "Wybór_2"))
+)
+
    +
  • sliderInput - funkcja tworząca suwak umożliwiający użytkownikowi wybór zakresu interesujących go wartości,
  • +
+
ui <- fluidPage(
+  # Suwak do wyboru wartości
+  sliderInput("nazwa_inputu_4", "Tekst wyświetlany w aplikacji",
+              # Wartość domyślna
+              value = 1,
+              # Wartość minimalna
+              min = 0,
+              # Wartość maksymalna
+              max = 10)
+)
+
    +
  • dateRangeInput - funkcja tworząca pole wyboru zakresu interesujących dat.
  • +
+
ui <- fluidPage(
+  # Pole wyboru zakresu dat
+  dateRangeInput("nazwa_inputu_5", "Tekst wyświetlany w aplikacji",
+                 # Data początkowa
+                 start = "2001-01-01",
+                 # Data końcowa
+                 end   = "2010-12-31")
+)
+
+
+

9.4.0.2 Przykładowe elementy wyjścia

+

Używanie funkcji wyświetlających outputy jest bardzo proste, ponieważ w UI decydujemy jedynie gdzie i jak wyswietlić output, który jest obiektem utworzonym wewnątrz funkcji server na podstawie wprowadzonego przez użytkownika inputu.

+
    +
  • textOutput - funkcja wyświetlająca tekst,
  • +
+
ui <- fluidPage(
+  # Wyświetla tekst, który stworzyliśmy w serwerze pod daną nazwą
+  textOutput("nazwa_outputu_1")
+)
+
    +
  • tableOutput - podstawowa funkcja wyświetlająca tabelę,
  • +
+
ui <- fluidPage(
+  # Wyświetla tabelę stworzoną w serwerze pod daną nazwą
+  tableOutput("nazwa_outputu_2")
+)
+
    +
  • DTOutput - funkcja wyświetlająca interaktywną ramkę danych z użyciem pakietu data.table,
  • +
+
ui <- fluidPage(
+  # Interaktywna ramka danych z użyciem data.table
+  DT::DTOutput("nazwa_outputu_3")
+)
+
    +
  • plotOutput - funkcja wyświetlająca wykres.
  • +
+
ui <- fluidPage(
+  # Wyświetla wykres stworzony w serwerze
+  plotOutput("nazwa_outputu_4",
+             # Szerokość wykresu
+             width = "100%", 
+             # Wysokość wykresu
+             height = "400px")
+)
+
+
+
+

9.5 Przykład użycia

+

Oczywiście powyższe kody były jedynie fragmentami większej całości. Poniżej możemy zobaczyć przykładowy kod obrazujący strukturę budowy interfejsu. Rzeczą, o której należy pamiętać jest oddzielanie funkcji przecinkami.

+
ui <- fluidPage( 
+  # Tytuł
+  titlePanel("Tytuł"),
+  
+  # To co będzie wyświetlone z boku interfejsu
+  sidebarLayout(
+    
+    # Panel boczny
+    sidebarPanel(
+      
+      # Pierwszy input - wybór
+      selectInput("nazwa_inputu_1", "Tekst wyświetlany w aplikacji", 
+                  choices = c("Wybór_1", "Wybór_2")),
+      # Drugi input - suwak
+      sliderInput("nazwa_inputu_2", "Tekst wyświetlany w aplikacji",
+                  value = 1, min = 0, max = 10) 
+    ),
+  
+  # Główny panel
+  mainPanel( 
+    
+    # Tworzymy zakładki
+    tabsetPanel( 
+      
+      # Pierwsza zakładka - wykres
+      tabPanel("Tytuł wykresu", plotOutput("nazwa_outputu_1")),
+      # Druga zakładka - ramka danych
+      tabPanel("Tytuł ramki", DT::DTOutput("nazwa_outputu_2"))
+      )
+    )
+  )
+)
+

Dodatkowo warto zdawać sobie sprawę, że po wprowadzeniu danych przez użytkownika outputy aktualizują się automatycznie, dlatego często przydatne jest programowanie reaktywne z funkcją observeEvent oraz użycie actionButton, który pozwala na wykonanie danego działania dopiero po kliknięciu odpowiedniego przycisku przez użytkownika.

+
+
+

9.6 Wygląd aplikacji

+

Ostatecznie chcielibyśmy, aby aplikacja wyglądała bardziej estetycznie. Możemy do tego użyć kilku narzędzi. Po pierwsze możemy zmienić motyw naszej aplikacji.

+

Z pomocą przychodzi nam funkcja shinythemes::themeSelector(), którą musimy umieścić w naszym UI. Wtedy w naszej aplikacji pojawia się pole z możliwością wyboru motywu.

+

Gdy już wybierzemy ulubiony motyw zamieniamy poprzednią funkcję w UI na theme = shinythemes::shinytheme('NASZ_MOTYW') i gotowe!

+

Poza tym Shiny umożliwia całkowitą customizację wyglądu aplikacji przy użyciu HTML, CSS oraz JavaScript.

+

Ostatnim narzędziem, o którym warto pamiętać, jest shinyWidgetsGallery(). Jest to bardzo użyteczna aplikacja stworzona w bibliotece shinyWidgets, dzięki której możemy między innymi zobaczyć w praktyce działanie różnego typu inputów oraz kod umożliwiający użycie ich w aplikacji.

+
+

9.6.1 Uwaga

+

W tej notatce omówiliśmy podstawowe elementy pozwalające na zbudowanie interfejsu w shiny ale chcielibyśmy też dodać, że w poszukiwaniu bardziej zaawansowanych rozwiązań warto odwiedzić stronę https://shiny.rstudio.com/, gdzie można znaleźć dokumentację pakietu shiny, wiele przykładów oraz nieomówionych tu funkcji.

+ +
+
+
+

9.7 Wstęp

+

Shiny to biblioteka w R pozwalająca na budowanie interaktywnych aplikacji w prosty i szybki sposób. Aplikacja Shiny składa się z dwóch części, opisywanych w dwóch osobnych plikach: interfejs użytkownika (UI), czyli jak aplikacja będzie wyglądać u użytkownika oraz sposób przetwarzania danych (serwer). W tej pracy zajmiemy się stroną serwerową Shiny.

+
+
+

9.8 Serwer Shiny

+

Aplikacje Shiny zazwyczaj budujemy w sytuacjach, w których mamy dane, chcemy obliczyć pewne rzeczy i narysować odpowiednie wykresy. Użytkownik widzi efekt końcowy, czyli to jak zaprogramowaliśmy gdzie ma się wyświetlać wynik, natomiast w części serwerowej opisujemy jak ten wynik ma być obliczony. Jest to więc część zależna od pliku UI.

+

Musimy więc w kodzie serwera zamieścić obiekty opisane w UI. Zauważmy, że tworzymy kod serwera jako funkcję od dwóch parametrów: input, output. W środku serwera definiujemy zależności pomiędzy inputami i outputami.

+

Jedną z zalet Shiny jest interaktywność. Dzięki temu użytkownik może na bieżąco zmieniać parametry i generować nowe wykresy. Jednak generowanie kodu na nowo przy każdej zmianie danych nie zawsze jest pożądane. Ważnym pojęciem przy pisaniu strony serwerowej jest reaktywność (żródło infografiki: Shiny Cheat Sheet).

+
+ +

reaktywnosc

+
+

Jeśli zmienna jest reaktywna, to znaczy że jakakolwiek jej zmiana powoduje ponowne uruchomienie funkcji z nią powiązanych. Do budowania reaktywnych wyrażeń używamy funkcji reactive(). Taka zmienna jest liczona tylko raz i wyrażenia z nią związane używają tej wartości aż do momentu aktualizacji wybranego przez użytkownika. Z pojęciem reaktywności wiąże się kilka ważnych funkcji:

+
    +
  • reactiveValues(...), które tworzy listą reaktywnych zmiennych,
  • +
  • isolate(expr) - zapobiega zależności od reaktywnych zmiennych,
  • +
  • render*() - funkcje tworzące obiekty do wyświetlenia, które zmieniają się wraz z reaktywnymi zmiennymi,
  • +
  • observeEvent(...) - gdy nie chcemy aby model od razu się zaktualizował przy zmianie danych, a przy jakiejś określonej akcji,
  • +
  • reactive() - tworzy reaktywne wyrażenia
  • +
  • eventReactive - tworzy reaktywne wyrażenia, które nie zależą od wszystkich reaktywnych zmiennych, a zależą jedynie od akcji wymienionych w pierwszym argumencie.
  • +
+ +
+
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + diff --git "a/docs/_book/katarzyna-frankiewicz-maciej-grabias-jakub-micha\305\202owski.html" "b/docs/_book/katarzyna-frankiewicz-maciej-grabias-jakub-micha\305\202owski.html" new file mode 100644 index 0000000..491ad06 --- /dev/null +++ "b/docs/_book/katarzyna-frankiewicz-maciej-grabias-jakub-micha\305\202owski.html" @@ -0,0 +1,556 @@ + + + + + + + Chapter 7 Katarzyna Frankiewicz, Maciej Grabias, Jakub Michałowski | Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+
+ + +
+
+ +
+
+

Chapter 7 Katarzyna Frankiewicz, Maciej Grabias, Jakub Michałowski

+
+

7.1 Wprowadzenie

+

Jednym z ważnych elementów przekazywania ciekawych informacji oraz ich analizy jest przedstawienie graficzne interesujących nas danych. W R istnieje kilka sposobów na wizualizację danych. Jednym z nich jest korzytanie z narzędzi oferowanych przez pakiet ggplot2. Bibiloteka ggplot2 oprócz zwykłych funkcji plotowania, implementuje także gramatykę grafiki, co pozwala na wykonanie prawie każdego rodzaju (statystycznej) wizualizacji danych.

+
+

7.1.1 Gramatyka grafiki

+

Powyżej wspomnieliśmy o gramatyce grafiki. Dla dokładniejszego uporządkowania wiedzy przypomnijmy, że gramatyka grafiki daje nam możliwość zadawania odpowiednich parametórw dla wszystkich linii, słów, strzałek, itp., które połączone tworzą wykres. Dodatkowo możemy m.in. zmieniać układ współrzędnych, czy korygować położenie każdego obiektu znajdującego się na wykresie. Możliwości jakie oferuje nam gramatyka grafiki będą przedstawione dokładniej w dalszej części notatki.

+
+
+
+

7.2 Podstawy tworzenia wykresów w ggplot2

+

Na początku, aby móc tworzyć wizualizacje, musimy załadować pakiet oraz bibilotekę ggplot2. Warto zwrócić uwagę, że ggplot2 posiada również szereg wbudowanych zestawów danych. Aby pokazać możliwości jakie oferuje nam ggplot, przeprowadzimy symulację danych mpg dostępnych w R.

+
library(ggplot2)
+head(mpg)
+
## # A tibble: 6 × 11
+##   manufacturer model displ  year   cyl trans      drv     cty   hwy fl    class  
+##   <chr>        <chr> <dbl> <int> <int> <chr>      <chr> <int> <int> <chr> <chr>  
+## 1 audi         a4      1.8  1999     4 auto(l5)   f        18    29 p     compact
+## 2 audi         a4      1.8  1999     4 manual(m5) f        21    29 p     compact
+## 3 audi         a4      2    2008     4 manual(m6) f        20    31 p     compact
+## 4 audi         a4      2    2008     4 auto(av)   f        21    30 p     compact
+## 5 audi         a4      2.8  1999     6 auto(l5)   f        16    26 p     compact
+## 6 audi         a4      2.8  1999     6 manual(m5) f        18    26 p     compact
+

Składnia wykresów w ggplot polega na warstwowym budowaniu wykresów.

+
    +
  1. Zaczynamy od doboru danych, jakie chcielibyśmy zwizualizaować.
  2. +
  3. Określamy sposób mapowania zmiennych do aspektów wizualnych.
  4. +
  5. Definiujemy styl wykresu.
  6. +
  7. Dodajemy dodatkowe warstwy personalizujące wykres, tzn. dodajemy tytuł, etykiety, itp. (opcjonalnie)
  8. +
+

Uwaga!

+

Do dodawania nowych warstw do wykresu używamy symbolu “+” .

+
# Budujemy podstawę wykresu, określając z jakich danych będziemy korzytsać
+ggplot(mpg)
+

+
# Mapujemy dane ( na osiach pojawiły się odpowiednie podziałki)
+ggplot(mpg , aes( x = displ, y = hwy))
+

+
# Określamy styl wykresu, dzięki czemu dostemy wykres odpwiednich zależności
+ggplot(mpg , aes( x = displ, y = hwy))+
+  geom_point()
+

+
# Personalizujemy wykres poprzez dodanie tytułu oraz określenie motywu wykresu
+ggplot(mpg , aes( x = displ, y = hwy))+
+  geom_point()+
+  ggtitle("disp vs hwy")+
+  theme_bw()
+

+
+
+

7.3 Mapowanie

+

Mapowanie danych jest estetyką, która mówi ggplot’owi, jakie zmienne powinny znajdować się na osi x oraz y. Dodatkowo możemy wpływać na cechy wizualne danych, takie jak kolor ( color = ), kształt ( shape = ), czy przezroczystość ( alpha = ). Wszystkie estetyki dla podziałki wykresu są określone w wywołaniu funkcji aes().

+

Uwaga!

+

Każda warstwa geom może mieć swoją własną specyfikację aes. Możemy zdecydować, czy na wykresie geom_point punkty mają być zaznaczone jako koła, czy kwadraty.

+
# Punkty na wykresie będą kwadratami
+ggplot(mpg, aes(x = displ, y = hwy)) +
+  geom_point(shape = 0)+
+  ggtitle("displ vs hwy")+
+  theme( axis.title = element_text(size = 1))+
+  theme_bw()
+

+
# Punkty na wykresie to czerwone kropki
+ggplot(mpg, aes(x = displ, y = hwy)) +
+  geom_point(color = "red")+
+  ggtitle("displ vs hwy")+
+  theme_bw()
+

+
+
+

7.4 Geometria wykresu

+

Za pomocą ggplot2 możemy stworzyć prawie każdy rodzaj wykresu. W tym celu musimy zadać typ wykresu jaki nas interesuje. Ggplot2 daje możliwość stworzenia wykresu:

+
    +
  • punktowego ( geom_point)
  • +
  • liniowego ( geom_line)
  • +
  • słupkowego ( geom_bar)
  • +
  • skrzypcowego ( geom_violin)
  • +
  • histogramu ( geom_histogram)
  • +
  • boxplota ( geom_boxplot)
  • +
+

oraz wielu innych, które powstają m.in. przez zastosowanie funcji: geom_area, geom_density, geom_dotplot, geom_qq, geom_smooth.

+

Uwaga!

+
    +
  • Wykresy słupkowe i histogramy grupują dane, a następnie prezentują liczbę elementów znajdujących się w poszczególnych grupach
  • +
  • Na wykresach liniowych model jest dopasowywany do danych, a nastęonie wykreślane są przewidywania wyznaczone przez model
  • +
  • Wykresy pudełkowe obliczają kompleksowe podsumowanie rozkładu wartości
  • +
+

Poniżej przedstawione są dwa przykładowe typy wykresów. Pierwszy narysowany przy użyciu funkcji geom_smooth, która służy do rysowania wygładzonych linii, np. dla prostych trendów. Drugi wykres powstał przy zastosowaniu funkcji geom_histogram. +

+

W pakiecie ggplot2 bardzo prosto możemy łączyć ze sobą różne geometrie na jednym wykresie. Wystarczy wstawić znak “+” pomiędzy odpowiednimi funkcjami.

+
ggplot(mpg , aes( x = displ, y = hwy))+
+  geom_point()+
+  geom_smooth()+
+  ggtitle("Multiple geomteries")+
+  theme_bw()
+

+
+
+

7.5 Funkcje pomagające poprawić czytelność wykresu

+
+

7.5.1 Systemy współrzędnych

+

Domyślnym systemem współrzędnych dla ggplot2 jest kartezjański układ współrzędnych. W zależności od danych na jakich działamy, może się okazać, że pokazanie danych w innym układzie współrzędnych, wpłynie na lepszy odbiór informacji z wykresu.

+

Funkcjami, które odpowiadają za przekształcenie układu współrzędnych są m.in.

+
    +
  • coord_flip która zamienia osie x i y
  • +
  • coord_polar wykres jest pokazany we współrzędnych polarnych
  • +
  • coord_fixed nadal jesteśmy w kartezjańskim układzie współrzędnych, ale możemy zmienić proporcję między jednostkami na osi x i y
  • +
+
+
+

7.5.2 Dopasowanie położenia

+

Każda geometria w ggplot2 ma ustawione domyślne położenie różnych elementów na wykresie względem siebie. Różne opcje ustawienia położenia są dobrze widoczne na wykresach słupkowych. Zacznijmy od stworzenia zwykłego wykresu słupkowego, bez żadnych dodatkowych funkcji.

+

+

Jeżeli teraz do mapowania dodamy opcję fill = dvr, to każdy prostokąt będzie reprezentował kombinację wartości class oraz dvr.

+

+

Takie przedstawienie danych nie dla każdego może być czytelne, dlatego możemy skorzystać z opcji position, która przyjmuje m.in. argumenty: “dodge” i “fill”.

+

+
+
+

7.5.3 Zarządzanie osiami współrzędnych

+

Jedną z możliwości jaką oferuje nam pakiet ggplot2 jest prosta zmiana skali na osiach wykresu. Podstawowymi funkcjami, które to umożliwiają są:

+
    +
  • scale_x_log10 (zamiast x możemy podać także y) wtedy skala x-ów będzie zlogarytmowana
  • +
  • scale_x_reverse powoduje odwrotny kierunek na osi x
  • +
  • scale_x_sqrt() skala x-ów będzie spierwiastkowana
  • +
  • scale_fill_manual pozwala nam ręcznie wprowadzić oczekiwane przez nas wartości, m.in. możemy zmienić nazwy obiektów na skali, czy podać zakres wartości do uwzględnienia w mapowaniu
  • +
  • xlim(5,40) powoduje ograniczenie podziałki na osi x od 5 do 40 (analogicznie z ylim)
  • +
+

W ggplot2 z łatwością także dodamy etykiety tekstowe oraz adnotacje. Do wykresu możemy dodać tytuł oraz nazwy osi korzystając m.in. z funkcji labs().

+
ggplot(mpg, aes(x = displ, y = hwy, color = class)) +
+  geom_point() +
+  labs(title = "Fuel Efficiency by Engine Power",
+       x = "Engine power",
+       y = "Fuel Efficiency",
+       color = "Car Type")
+

+
+
+

7.5.4 Motywy

+

Theme to dobry sposób na dostosowanie odpowiedniego tytułu, etykiet, czcionek, tła, legendy, czy lini siatki na wykresie. Możemy skorzystać z jednego z dostępnych motywów, takich jak theme_bw(), czy theme_minimal(). Istnieje możliwość zastosowania wielu dostępnych opcji tak, aby odpowiednie elementy wykresu wyglądały tak, jak chcemy. Podstawowymi funkcjami, jakie warto znać są m.in. legend.position, dzięki której możemy ustalić pozycję legendy wykresu, axis.text, która umożliwia nam ustawienie czcionki na wykresie oraz ustalenie jej wielkości czy koloru. Przydatną funkcją pochodzącą z rodziny theme jest ‘theme(axis.text.x = element_text(angle = 90))’, która obraca nazwy znajdujące się na osi x, dzięki, czemu stają się one czytelniejsze.

+
+
+
+

7.6 Panele

+

Ostatnim z podstawowych funkcji jakie oferuje pakiet ggplot2 jest facets.

+

Panele to sposoby grupowania wykresu danych w wiele różnych części ze względu na zadaną zmienną. Możemy korzystać z funkcji:

+
    +
  • facet_wrap(), która ustawia panele w prostokątnym układzie
  • +
  • facet_grid(), która ustawia panele w kolumny lub w wiersze (zależnie jaką opcję wybierzemy)
  • +
+
ggplot(mpg, aes(x = displ, y = hwy)) +
+  geom_point() +
+  facet_grid(~ class)
+

+

Uwaga!

+

Aby zadać względem, której zmiennej chcemy grupować, w funkcji ‘facet_’ po znaku “~”, podajemy nazwę tej zmiennej.

+

Kiedy korzystamy z funkcji tworzącej panele, automatycznie wszytskie wykresy będą pokazane w układzie współrzędnych dopasowanym do wszytkich okienek. Istnieje jednak możliwość dopasowania układu współrzędnych do każdego panelu osobno. W tym celu możemy wykorzystać funcję ‘scale = “free”’.

+ +
+
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + diff --git a/docs/_book/libs/anchor-sections-1.1.0/anchor-sections-hash.css b/docs/_book/libs/anchor-sections-1.1.0/anchor-sections-hash.css new file mode 100644 index 0000000..b563ec9 --- /dev/null +++ b/docs/_book/libs/anchor-sections-1.1.0/anchor-sections-hash.css @@ -0,0 +1,2 @@ +/* Styles for section anchors */ +a.anchor-section::before {content: '#';font-size: 80%;} diff --git a/docs/_book/libs/anchor-sections-1.1.0/anchor-sections.css b/docs/_book/libs/anchor-sections-1.1.0/anchor-sections.css new file mode 100644 index 0000000..041905f --- /dev/null +++ b/docs/_book/libs/anchor-sections-1.1.0/anchor-sections.css @@ -0,0 +1,4 @@ +/* Styles for section anchors */ +a.anchor-section {margin-left: 10px; visibility: hidden; color: inherit;} +.hasAnchor:hover a.anchor-section {visibility: visible;} +ul > li > .anchor-section {display: none;} diff --git a/docs/_book/libs/anchor-sections-1.1.0/anchor-sections.js b/docs/_book/libs/anchor-sections-1.1.0/anchor-sections.js new file mode 100644 index 0000000..fee005d --- /dev/null +++ b/docs/_book/libs/anchor-sections-1.1.0/anchor-sections.js @@ -0,0 +1,11 @@ +document.addEventListener('DOMContentLoaded', function () { + // If section divs is used, we need to put the anchor in the child header + const headers = document.querySelectorAll("div.hasAnchor.section[class*='level'] > :first-child") + + headers.forEach(function (x) { + // Add to the header node + if (!x.classList.contains('hasAnchor')) x.classList.add('hasAnchor') + // Remove from the section or div created by Pandoc + x.parentElement.classList.remove('hasAnchor') + }) +}) diff --git a/docs/_book/libs/gitbook-2.6.7/css/fontawesome/fontawesome-webfont.ttf b/docs/_book/libs/gitbook-2.6.7/css/fontawesome/fontawesome-webfont.ttf new file mode 100644 index 0000000..35acda2 Binary files /dev/null and b/docs/_book/libs/gitbook-2.6.7/css/fontawesome/fontawesome-webfont.ttf differ diff --git a/docs/_book/libs/gitbook-2.6.7/css/plugin-bookdown.css b/docs/_book/libs/gitbook-2.6.7/css/plugin-bookdown.css new file mode 100644 index 0000000..ab7c20e --- /dev/null +++ b/docs/_book/libs/gitbook-2.6.7/css/plugin-bookdown.css @@ -0,0 +1,105 @@ +.book .book-header h1 { + padding-left: 20px; + padding-right: 20px; +} +.book .book-header.fixed { + position: fixed; + right: 0; + top: 0; + left: 0; + border-bottom: 1px solid rgba(0,0,0,.07); +} +span.search-highlight { + background-color: #ffff88; +} +@media (min-width: 600px) { + .book.with-summary .book-header.fixed { + left: 300px; + } +} +@media (max-width: 1240px) { + .book .book-body.fixed { + top: 50px; + } + .book .book-body.fixed .body-inner { + top: auto; + } +} +@media (max-width: 600px) { + .book.with-summary .book-header.fixed { + left: calc(100% - 60px); + min-width: 300px; + } + .book.with-summary .book-body { + transform: none; + left: calc(100% - 60px); + min-width: 300px; + } + .book .book-body.fixed { + top: 0; + } +} + +.book .book-body.fixed .body-inner { + top: 50px; +} +.book .book-body .page-wrapper .page-inner section.normal sub, .book .book-body .page-wrapper .page-inner section.normal sup { + font-size: 85%; +} + +@media print { + .book .book-summary, .book .book-body .book-header, .fa { + display: none !important; + } + .book .book-body.fixed { + left: 0px; + } + .book .book-body,.book .book-body .body-inner, .book.with-summary { + overflow: visible !important; + } +} +.kable_wrapper { + border-spacing: 20px 0; + border-collapse: separate; + border: none; + margin: auto; +} +.kable_wrapper > tbody > tr > td { + vertical-align: top; +} +.book .book-body .page-wrapper .page-inner section.normal table tr.header { + border-top-width: 2px; +} +.book .book-body .page-wrapper .page-inner section.normal table tr:last-child td { + border-bottom-width: 2px; +} +.book .book-body .page-wrapper .page-inner section.normal table td, .book .book-body .page-wrapper .page-inner section.normal table th { + border-left: none; + border-right: none; +} +.book .book-body .page-wrapper .page-inner section.normal table.kable_wrapper > tbody > tr, .book .book-body .page-wrapper .page-inner section.normal table.kable_wrapper > tbody > tr > td { + border-top: none; +} +.book .book-body .page-wrapper .page-inner section.normal table.kable_wrapper > tbody > tr:last-child > td { + border-bottom: none; +} + +div.theorem, div.lemma, div.corollary, div.proposition, div.conjecture { + font-style: italic; +} +span.theorem, span.lemma, span.corollary, span.proposition, span.conjecture { + font-style: normal; +} +div.proof>*:last-child:after { + content: "\25a2"; + float: right; +} +.header-section-number { + padding-right: .5em; +} +#header .multi-author { + margin: 0.5em 0 -0.5em 0; +} +#header .date { + margin-top: 1.5em; +} diff --git a/docs/_book/libs/gitbook-2.6.7/css/plugin-clipboard.css b/docs/_book/libs/gitbook-2.6.7/css/plugin-clipboard.css new file mode 100644 index 0000000..6844a70 --- /dev/null +++ b/docs/_book/libs/gitbook-2.6.7/css/plugin-clipboard.css @@ -0,0 +1,18 @@ +div.sourceCode { + position: relative; +} + +.copy-to-clipboard-button { + position: absolute; + right: 0; + top: 0; + visibility: hidden; +} + +.copy-to-clipboard-button:focus { + outline: 0; +} + +div.sourceCode:hover > .copy-to-clipboard-button { + visibility: visible; +} diff --git a/docs/_book/libs/gitbook-2.6.7/css/plugin-fontsettings.css b/docs/_book/libs/gitbook-2.6.7/css/plugin-fontsettings.css new file mode 100644 index 0000000..3fa6f35 --- /dev/null +++ b/docs/_book/libs/gitbook-2.6.7/css/plugin-fontsettings.css @@ -0,0 +1,303 @@ +/* + * Theme 1 + */ +.color-theme-1 .dropdown-menu { + background-color: #111111; + border-color: #7e888b; +} +.color-theme-1 .dropdown-menu .dropdown-caret .caret-inner { + border-bottom: 9px solid #111111; +} +.color-theme-1 .dropdown-menu .buttons { + border-color: #7e888b; +} +.color-theme-1 .dropdown-menu .button { + color: #afa790; +} +.color-theme-1 .dropdown-menu .button:hover { + color: #73553c; +} +/* + * Theme 2 + */ +.color-theme-2 .dropdown-menu { + background-color: #2d3143; + border-color: #272a3a; +} +.color-theme-2 .dropdown-menu .dropdown-caret .caret-inner { + border-bottom: 9px solid #2d3143; +} +.color-theme-2 .dropdown-menu .buttons { + border-color: #272a3a; +} +.color-theme-2 .dropdown-menu .button { + color: #62677f; +} +.color-theme-2 .dropdown-menu .button:hover { + color: #f4f4f5; +} +.book .book-header .font-settings .font-enlarge { + line-height: 30px; + font-size: 1.4em; +} +.book .book-header .font-settings .font-reduce { + line-height: 30px; + font-size: 1em; +} + +/* sidebar transition background */ +div.book.color-theme-1 { + background: #f3eacb; +} +.book.color-theme-1 .book-body { + color: #704214; + background: #f3eacb; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section { + background: #f3eacb; +} + +/* sidebar transition background */ +div.book.color-theme-2 { + background: #1c1f2b; +} + +.book.color-theme-2 .book-body { + color: #bdcadb; + background: #1c1f2b; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section { + background: #1c1f2b; +} +.book.font-size-0 .book-body .page-inner section { + font-size: 1.2rem; +} +.book.font-size-1 .book-body .page-inner section { + font-size: 1.4rem; +} +.book.font-size-2 .book-body .page-inner section { + font-size: 1.6rem; +} +.book.font-size-3 .book-body .page-inner section { + font-size: 2.2rem; +} +.book.font-size-4 .book-body .page-inner section { + font-size: 4rem; +} +.book.font-family-0 { + font-family: Georgia, serif; +} +.book.font-family-1 { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal { + color: #704214; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal a { + color: inherit; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h1, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h2, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h3, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h4, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h5, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h6 { + color: inherit; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h1, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h2 { + border-color: inherit; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h6 { + color: inherit; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal hr { + background-color: inherit; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal blockquote { + border-color: #c4b29f; + opacity: 0.9; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code { + background: #fdf6e3; + color: #657b83; + border-color: #f8df9c; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal .highlight { + background-color: inherit; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table th, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table td { + border-color: #f5d06c; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table tr { + color: inherit; + background-color: #fdf6e3; + border-color: #444444; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n) { + background-color: #fbeecb; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal { + color: #bdcadb; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal a { + color: #3eb1d0; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h1, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h2, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h3, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h4, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h5, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h6 { + color: #fffffa; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h1, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h2 { + border-color: #373b4e; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h6 { + color: #373b4e; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal hr { + background-color: #373b4e; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal blockquote { + border-color: #373b4e; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code { + color: #9dbed8; + background: #2d3143; + border-color: #2d3143; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal .highlight { + background-color: #282a39; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table th, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table td { + border-color: #3b3f54; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table tr { + color: #b6c2d2; + background-color: #2d3143; + border-color: #3b3f54; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n) { + background-color: #35394b; +} +.book.color-theme-1 .book-header { + color: #afa790; + background: transparent; +} +.book.color-theme-1 .book-header .btn { + color: #afa790; +} +.book.color-theme-1 .book-header .btn:hover { + color: #73553c; + background: none; +} +.book.color-theme-1 .book-header h1 { + color: #704214; +} +.book.color-theme-2 .book-header { + color: #7e888b; + background: transparent; +} +.book.color-theme-2 .book-header .btn { + color: #3b3f54; +} +.book.color-theme-2 .book-header .btn:hover { + color: #fffff5; + background: none; +} +.book.color-theme-2 .book-header h1 { + color: #bdcadb; +} +.book.color-theme-1 .book-body .navigation { + color: #afa790; +} +.book.color-theme-1 .book-body .navigation:hover { + color: #73553c; +} +.book.color-theme-2 .book-body .navigation { + color: #383f52; +} +.book.color-theme-2 .book-body .navigation:hover { + color: #fffff5; +} +/* + * Theme 1 + */ +.book.color-theme-1 .book-summary { + color: #afa790; + background: #111111; + border-right: 1px solid rgba(0, 0, 0, 0.07); +} +.book.color-theme-1 .book-summary .book-search { + background: transparent; +} +.book.color-theme-1 .book-summary .book-search input, +.book.color-theme-1 .book-summary .book-search input:focus { + border: 1px solid transparent; +} +.book.color-theme-1 .book-summary ul.summary li.divider { + background: #7e888b; + box-shadow: none; +} +.book.color-theme-1 .book-summary ul.summary li i.fa-check { + color: #33cc33; +} +.book.color-theme-1 .book-summary ul.summary li.done > a { + color: #877f6a; +} +.book.color-theme-1 .book-summary ul.summary li a, +.book.color-theme-1 .book-summary ul.summary li span { + color: #877f6a; + background: transparent; + font-weight: normal; +} +.book.color-theme-1 .book-summary ul.summary li.active > a, +.book.color-theme-1 .book-summary ul.summary li a:hover { + color: #704214; + background: transparent; + font-weight: normal; +} +/* + * Theme 2 + */ +.book.color-theme-2 .book-summary { + color: #bcc1d2; + background: #2d3143; + border-right: none; +} +.book.color-theme-2 .book-summary .book-search { + background: transparent; +} +.book.color-theme-2 .book-summary .book-search input, +.book.color-theme-2 .book-summary .book-search input:focus { + border: 1px solid transparent; +} +.book.color-theme-2 .book-summary ul.summary li.divider { + background: #272a3a; + box-shadow: none; +} +.book.color-theme-2 .book-summary ul.summary li i.fa-check { + color: #33cc33; +} +.book.color-theme-2 .book-summary ul.summary li.done > a { + color: #62687f; +} +.book.color-theme-2 .book-summary ul.summary li a, +.book.color-theme-2 .book-summary ul.summary li span { + color: #c1c6d7; + background: transparent; + font-weight: 600; +} +.book.color-theme-2 .book-summary ul.summary li.active > a, +.book.color-theme-2 .book-summary ul.summary li a:hover { + color: #f4f4f5; + background: #252737; + font-weight: 600; +} diff --git a/docs/_book/libs/gitbook-2.6.7/css/plugin-highlight.css b/docs/_book/libs/gitbook-2.6.7/css/plugin-highlight.css new file mode 100644 index 0000000..2aabd3d --- /dev/null +++ b/docs/_book/libs/gitbook-2.6.7/css/plugin-highlight.css @@ -0,0 +1,426 @@ +.book .book-body .page-wrapper .page-inner section.normal pre, +.book .book-body .page-wrapper .page-inner section.normal code { + /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ + /* Tomorrow Comment */ + /* Tomorrow Red */ + /* Tomorrow Orange */ + /* Tomorrow Yellow */ + /* Tomorrow Green */ + /* Tomorrow Aqua */ + /* Tomorrow Blue */ + /* Tomorrow Purple */ +} +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-comment, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-comment, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-title { + color: #8e908c; +} +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-variable, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-variable, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-attribute, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-attribute, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-tag, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-tag, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-regexp, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-regexp, +.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-constant, +.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-constant, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-tag .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-tag .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-pi, +.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-pi, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-doctype, +.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-doctype, +.book .book-body .page-wrapper .page-inner section.normal pre .html .hljs-doctype, +.book .book-body .page-wrapper .page-inner section.normal code .html .hljs-doctype, +.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-id, +.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-id, +.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-class, +.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-class, +.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-pseudo, +.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-pseudo { + color: #c82829; +} +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-number, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-number, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-preprocessor, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-preprocessor, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-pragma, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-pragma, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-built_in, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-built_in, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-literal, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-literal, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-params, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-params, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-constant, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-constant { + color: #f5871f; +} +.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-class .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-class .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-rules .hljs-attribute, +.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-rules .hljs-attribute { + color: #eab700; +} +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-string, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-string, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-value, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-value, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-inheritance, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-inheritance, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-header, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-header, +.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-symbol, +.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-symbol, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-cdata, +.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-cdata { + color: #718c00; +} +.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-hexcolor, +.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-hexcolor { + color: #3e999f; +} +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-function, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-function, +.book .book-body .page-wrapper .page-inner section.normal pre .python .hljs-decorator, +.book .book-body .page-wrapper .page-inner section.normal code .python .hljs-decorator, +.book .book-body .page-wrapper .page-inner section.normal pre .python .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal code .python .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-function .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-function .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-title .hljs-keyword, +.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-title .hljs-keyword, +.book .book-body .page-wrapper .page-inner section.normal pre .perl .hljs-sub, +.book .book-body .page-wrapper .page-inner section.normal code .perl .hljs-sub, +.book .book-body .page-wrapper .page-inner section.normal pre .javascript .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal code .javascript .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal pre .coffeescript .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal code .coffeescript .hljs-title { + color: #4271ae; +} +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-keyword, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-keyword, +.book .book-body .page-wrapper .page-inner section.normal pre .javascript .hljs-function, +.book .book-body .page-wrapper .page-inner section.normal code .javascript .hljs-function { + color: #8959a8; +} +.book .book-body .page-wrapper .page-inner section.normal pre .hljs, +.book .book-body .page-wrapper .page-inner section.normal code .hljs { + display: block; + background: white; + color: #4d4d4c; + padding: 0.5em; +} +.book .book-body .page-wrapper .page-inner section.normal pre .coffeescript .javascript, +.book .book-body .page-wrapper .page-inner section.normal code .coffeescript .javascript, +.book .book-body .page-wrapper .page-inner section.normal pre .javascript .xml, +.book .book-body .page-wrapper .page-inner section.normal code .javascript .xml, +.book .book-body .page-wrapper .page-inner section.normal pre .tex .hljs-formula, +.book .book-body .page-wrapper .page-inner section.normal code .tex .hljs-formula, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .javascript, +.book .book-body .page-wrapper .page-inner section.normal code .xml .javascript, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .vbscript, +.book .book-body .page-wrapper .page-inner section.normal code .xml .vbscript, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .css, +.book .book-body .page-wrapper .page-inner section.normal code .xml .css, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-cdata, +.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-cdata { + opacity: 0.5; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code { + /* + +Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull + +*/ + /* Solarized Green */ + /* Solarized Cyan */ + /* Solarized Blue */ + /* Solarized Yellow */ + /* Solarized Orange */ + /* Solarized Red */ + /* Solarized Violet */ +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs { + display: block; + padding: 0.5em; + background: #fdf6e3; + color: #657b83; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-comment, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-comment, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-template_comment, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-template_comment, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .diff .hljs-header, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .diff .hljs-header, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-doctype, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-doctype, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-pi, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-pi, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .lisp .hljs-string, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .lisp .hljs-string, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-javadoc, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-javadoc { + color: #93a1a1; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-keyword, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-keyword, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-winutils, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-winutils, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .method, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .method, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-addition, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-addition, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-tag, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .css .hljs-tag, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-request, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-request, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-status, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-status, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .nginx .hljs-title, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .nginx .hljs-title { + color: #859900; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-number, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-number, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-command, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-command, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-string, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-string, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-tag .hljs-value, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-tag .hljs-value, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-rules .hljs-value, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-rules .hljs-value, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-phpdoc, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-phpdoc, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .tex .hljs-formula, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .tex .hljs-formula, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-regexp, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-regexp, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-hexcolor, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-hexcolor, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-link_url, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-link_url { + color: #2aa198; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-title, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-title, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-localvars, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-localvars, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-chunk, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-chunk, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-decorator, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-decorator, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-built_in, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-built_in, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-identifier, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-identifier, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .vhdl .hljs-literal, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .vhdl .hljs-literal, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-id, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-id, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-function, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .css .hljs-function { + color: #268bd2; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-attribute, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-attribute, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-variable, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-variable, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .lisp .hljs-body, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .lisp .hljs-body, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .smalltalk .hljs-number, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .smalltalk .hljs-number, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-constant, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-constant, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-class .hljs-title, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-class .hljs-title, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-parent, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-parent, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .haskell .hljs-type, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .haskell .hljs-type, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-link_reference, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-link_reference { + color: #b58900; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-preprocessor, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-preprocessor, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-preprocessor .hljs-keyword, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-preprocessor .hljs-keyword, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-pragma, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-pragma, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-shebang, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-shebang, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-symbol, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-symbol, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-symbol .hljs-string, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-symbol .hljs-string, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .diff .hljs-change, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .diff .hljs-change, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-special, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-special, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-attr_selector, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-attr_selector, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-subst, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-subst, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-cdata, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-cdata, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .clojure .hljs-title, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .clojure .hljs-title, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-pseudo, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .css .hljs-pseudo, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-header, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-header { + color: #cb4b16; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-deletion, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-deletion, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-important, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-important { + color: #dc322f; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-link_label, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-link_label { + color: #6c71c4; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .tex .hljs-formula, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .tex .hljs-formula { + background: #eee8d5; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code { + /* Tomorrow Night Bright Theme */ + /* Original theme - https://github.com/chriskempson/tomorrow-theme */ + /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ + /* Tomorrow Comment */ + /* Tomorrow Red */ + /* Tomorrow Orange */ + /* Tomorrow Yellow */ + /* Tomorrow Green */ + /* Tomorrow Aqua */ + /* Tomorrow Blue */ + /* Tomorrow Purple */ +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-comment, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-comment, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-title { + color: #969896; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-variable, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-variable, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-attribute, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-attribute, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-tag, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-tag, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-regexp, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-regexp, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-constant, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-constant, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-tag .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-tag .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-pi, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-pi, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-doctype, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-doctype, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .html .hljs-doctype, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .html .hljs-doctype, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-id, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-id, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-class, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-class, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-pseudo, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-pseudo { + color: #d54e53; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-number, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-number, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-preprocessor, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-preprocessor, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-pragma, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-pragma, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-built_in, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-built_in, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-literal, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-literal, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-params, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-params, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-constant, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-constant { + color: #e78c45; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-class .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-class .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-rules .hljs-attribute, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-rules .hljs-attribute { + color: #e7c547; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-string, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-string, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-value, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-value, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-inheritance, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-inheritance, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-header, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-header, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-symbol, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-symbol, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-cdata, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-cdata { + color: #b9ca4a; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-hexcolor, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-hexcolor { + color: #70c0b1; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-function, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-function, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .python .hljs-decorator, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .python .hljs-decorator, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .python .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .python .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-function .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-function .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-title .hljs-keyword, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-title .hljs-keyword, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .perl .hljs-sub, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .perl .hljs-sub, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .javascript .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .javascript .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .coffeescript .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .coffeescript .hljs-title { + color: #7aa6da; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-keyword, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-keyword, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .javascript .hljs-function, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .javascript .hljs-function { + color: #c397d8; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs { + display: block; + background: black; + color: #eaeaea; + padding: 0.5em; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .coffeescript .javascript, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .coffeescript .javascript, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .javascript .xml, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .javascript .xml, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .tex .hljs-formula, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .tex .hljs-formula, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .javascript, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .javascript, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .vbscript, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .vbscript, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .css, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .css, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-cdata, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-cdata { + opacity: 0.5; +} diff --git a/docs/_book/libs/gitbook-2.6.7/css/plugin-search.css b/docs/_book/libs/gitbook-2.6.7/css/plugin-search.css new file mode 100644 index 0000000..c85e557 --- /dev/null +++ b/docs/_book/libs/gitbook-2.6.7/css/plugin-search.css @@ -0,0 +1,31 @@ +.book .book-summary .book-search { + padding: 6px; + background: transparent; + position: absolute; + top: -50px; + left: 0px; + right: 0px; + transition: top 0.5s ease; +} +.book .book-summary .book-search input, +.book .book-summary .book-search input:focus, +.book .book-summary .book-search input:hover { + width: 100%; + background: transparent; + border: 1px solid #ccc; + box-shadow: none; + outline: none; + line-height: 22px; + padding: 7px 4px; + color: inherit; + box-sizing: border-box; +} +.book.with-search .book-summary .book-search { + top: 0px; +} +.book.with-search .book-summary ul.summary { + top: 50px; +} +.with-search .summary li[data-level] a[href*=".html#"] { + display: none; +} diff --git a/docs/_book/libs/gitbook-2.6.7/css/plugin-table.css b/docs/_book/libs/gitbook-2.6.7/css/plugin-table.css new file mode 100644 index 0000000..7fba1b9 --- /dev/null +++ b/docs/_book/libs/gitbook-2.6.7/css/plugin-table.css @@ -0,0 +1 @@ +.book .book-body .page-wrapper .page-inner section.normal table{display:table;width:100%;border-collapse:collapse;border-spacing:0;overflow:auto}.book .book-body .page-wrapper .page-inner section.normal table td,.book .book-body .page-wrapper .page-inner section.normal table th{padding:6px 13px;border:1px solid #ddd}.book .book-body .page-wrapper .page-inner section.normal table tr{background-color:#fff;border-top:1px solid #ccc}.book .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n){background-color:#f8f8f8}.book .book-body .page-wrapper .page-inner section.normal table th{font-weight:700} diff --git a/docs/_book/libs/gitbook-2.6.7/css/style.css b/docs/_book/libs/gitbook-2.6.7/css/style.css new file mode 100644 index 0000000..cba69b2 --- /dev/null +++ b/docs/_book/libs/gitbook-2.6.7/css/style.css @@ -0,0 +1,13 @@ +/*! normalize.css v2.1.0 | MIT License | git.io/normalize */img,legend{border:0}*{-webkit-font-smoothing:antialiased}sub,sup{position:relative}.book .book-body .page-wrapper .page-inner section.normal hr:after,.book-langs-index .inner .languages:after,.buttons:after,.dropdown-menu .buttons:after{clear:both}body,html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}.hidden,[hidden]{display:none}audio:not([controls]){display:none;height:0}html{font-family:sans-serif}body,figure{margin:0}a:focus{outline:dotted thin}a:active,a:hover{outline:0}h1{font-size:2em;margin:.67em 0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:"\201C" "\201D" "\2018" "\2019"}small{font-size:80%}sub,sup{font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}svg:not(:root){overflow:hidden}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{padding:0}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}button,input{line-height:normal}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button{margin-right:10px;}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}/*! + * Preboot v2 + * + * Open sourced under MIT license by @mdo. + * Some variables and mixins from Bootstrap (Apache 2 license). + */.link-inherit,.link-inherit:focus,.link-inherit:hover{color:inherit}/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('./fontawesome/fontawesome-webfont.ttf?v=4.7.0') format('truetype');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} +.book .book-header,.book .book-summary{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}.book-langs-index{width:100%;height:100%;padding:40px 0;margin:0;overflow:auto}@media (max-width:600px){.book-langs-index{padding:0}}.book-langs-index .inner{max-width:600px;width:100%;margin:0 auto;padding:30px;background:#fff;border-radius:3px}.book-langs-index .inner h3{margin:0}.book-langs-index .inner .languages{list-style:none;padding:20px 30px;margin-top:20px;border-top:1px solid #eee}.book-langs-index .inner .languages:after,.book-langs-index .inner .languages:before{content:" ";display:table;line-height:0}.book-langs-index .inner .languages li{width:50%;float:left;padding:10px 5px;font-size:16px}@media (max-width:600px){.book-langs-index .inner .languages li{width:100%;max-width:100%}}.book .book-header{overflow:visible;height:50px;padding:0 8px;z-index:2;font-size:.85em;color:#7e888b;background:0 0}.book .book-header .btn{display:block;height:50px;padding:0 15px;border-bottom:none;color:#ccc;text-transform:uppercase;line-height:50px;-webkit-box-shadow:none!important;box-shadow:none!important;position:relative;font-size:14px}.book .book-header .btn:hover{position:relative;text-decoration:none;color:#444;background:0 0}.book .book-header h1{margin:0;font-size:20px;font-weight:200;text-align:center;line-height:50px;opacity:0;padding-left:200px;padding-right:200px;-webkit-transition:opacity .2s ease;-moz-transition:opacity .2s ease;-o-transition:opacity .2s ease;transition:opacity .2s ease;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.book .book-header h1 a,.book .book-header h1 a:hover{color:inherit;text-decoration:none}@media (max-width:1000px){.book .book-header h1{display:none}}.book .book-header h1 i{display:none}.book .book-header:hover h1{opacity:1}.book.is-loading .book-header h1 i{display:inline-block}.book.is-loading .book-header h1 a{display:none}.dropdown{position:relative}.dropdown-menu{position:absolute;top:100%;left:0;z-index:100;display:none;float:left;min-width:160px;padding:0;margin:2px 0 0;list-style:none;font-size:14px;background-color:#fafafa;border:1px solid rgba(0,0,0,.07);border-radius:1px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175);background-clip:padding-box}.dropdown-menu.open{display:block}.dropdown-menu.dropdown-left{left:auto;right:4%}.dropdown-menu.dropdown-left .dropdown-caret{right:14px;left:auto}.dropdown-menu .dropdown-caret{position:absolute;top:-8px;left:14px;width:18px;height:10px;float:left;overflow:hidden}.dropdown-menu .dropdown-caret .caret-inner,.dropdown-menu .dropdown-caret .caret-outer{display:inline-block;top:0;border-left:9px solid transparent;border-right:9px solid transparent;position:absolute}.dropdown-menu .dropdown-caret .caret-outer{border-bottom:9px solid rgba(0,0,0,.1);height:auto;left:0;width:auto;margin-left:-1px}.dropdown-menu .dropdown-caret .caret-inner{margin-top:-1px;top:1px;border-bottom:9px solid #fafafa}.dropdown-menu .buttons{border-bottom:1px solid rgba(0,0,0,.07)}.dropdown-menu .buttons:after,.dropdown-menu .buttons:before{content:" ";display:table;line-height:0}.dropdown-menu .buttons:last-child{border-bottom:none}.dropdown-menu .buttons .button{border:0;background-color:transparent;color:#a6a6a6;width:100%;text-align:center;float:left;line-height:1.42857143;padding:8px 4px}.alert,.dropdown-menu .buttons .button:hover{color:#444}.dropdown-menu .buttons .button:focus,.dropdown-menu .buttons .button:hover{outline:0}.dropdown-menu .buttons .button.size-2{width:50%}.dropdown-menu .buttons .button.size-3{width:33%}.alert{padding:15px;margin-bottom:20px;background:#eee;border-bottom:5px solid #ddd}.alert-success{background:#dff0d8;border-color:#d6e9c6;color:#3c763d}.alert-info{background:#d9edf7;border-color:#bce8f1;color:#31708f}.alert-danger{background:#f2dede;border-color:#ebccd1;color:#a94442}.alert-warning{background:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.book .book-summary{position:absolute;top:0;left:-300px;bottom:0;z-index:1;width:300px;color:#364149;background:#fafafa;border-right:1px solid rgba(0,0,0,.07);-webkit-transition:left 250ms ease;-moz-transition:left 250ms ease;-o-transition:left 250ms ease;transition:left 250ms ease}.book .book-summary ul.summary{position:absolute;top:0;left:0;right:0;bottom:0;overflow-y:auto;list-style:none;margin:0;padding:0;-webkit-transition:top .5s ease;-moz-transition:top .5s ease;-o-transition:top .5s ease;transition:top .5s ease}.book .book-summary ul.summary li{list-style:none}.book .book-summary ul.summary li.divider{height:1px;margin:7px 0;overflow:hidden;background:rgba(0,0,0,.07)}.book .book-summary ul.summary li i.fa-check{display:none;position:absolute;right:9px;top:16px;font-size:9px;color:#3c3}.book .book-summary ul.summary li.done>a{color:#364149;font-weight:400}.book .book-summary ul.summary li.done>a i{display:inline}.book .book-summary ul.summary li a,.book .book-summary ul.summary li span{display:block;padding:10px 15px;border-bottom:none;color:#364149;background:0 0;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;position:relative}.book .book-summary ul.summary li span{cursor:not-allowed;opacity:.3;filter:alpha(opacity=30)}.book .book-summary ul.summary li a:hover,.book .book-summary ul.summary li.active>a{color:#008cff;background:0 0;text-decoration:none}.book .book-summary ul.summary li ul{padding-left:20px}@media (max-width:600px){.book .book-summary{width:calc(100% - 60px);bottom:0;left:-100%}}.book.with-summary .book-summary{left:0}.book.without-animation .book-summary{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;transition:none!important}.book{position:relative;width:100%;height:100%}.book .book-body,.book .book-body .body-inner{position:absolute;top:0;left:0;overflow-y:auto;bottom:0;right:0}.book .book-body{color:#000;background:#fff;-webkit-transition:left 250ms ease;-moz-transition:left 250ms ease;-o-transition:left 250ms ease;transition:left 250ms ease}.book .book-body .page-wrapper{position:relative;outline:0}.book .book-body .page-wrapper .page-inner{max-width:800px;margin:0 auto;padding:20px 0 40px}.book .book-body .page-wrapper .page-inner section{margin:0;padding:5px 15px;background:#fff;border-radius:2px;line-height:1.7;font-size:1.6rem}.book .book-body .page-wrapper .page-inner .btn-group .btn{border-radius:0;background:#eee;border:0}@media (max-width:1240px){.book .book-body{-webkit-transition:-webkit-transform 250ms ease;-moz-transition:-moz-transform 250ms ease;-o-transition:-o-transform 250ms ease;transition:transform 250ms ease;padding-bottom:20px}.book .book-body .body-inner{position:static;min-height:calc(100% - 50px)}}@media (min-width:600px){.book.with-summary .book-body{left:300px}}@media (max-width:600px){.book.with-summary{overflow:hidden}.book.with-summary .book-body{-webkit-transform:translate(calc(100% - 60px),0);-moz-transform:translate(calc(100% - 60px),0);-ms-transform:translate(calc(100% - 60px),0);-o-transform:translate(calc(100% - 60px),0);transform:translate(calc(100% - 60px),0)}}.book.without-animation .book-body{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;transition:none!important}.buttons:after,.buttons:before{content:" ";display:table;line-height:0}.button{border:0;background:#eee;color:#666;width:100%;text-align:center;float:left;line-height:1.42857143;padding:8px 4px}.button:hover{color:#444}.button:focus,.button:hover{outline:0}.button.size-2{width:50%}.button.size-3{width:33%}.book .book-body .page-wrapper .page-inner section{display:none}.book .book-body .page-wrapper .page-inner section.normal{display:block;word-wrap:break-word;overflow:hidden;color:#333;line-height:1.7;text-size-adjust:100%;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%}.book .book-body .page-wrapper .page-inner section.normal *{box-sizing:border-box;-webkit-box-sizing:border-box;}.book .book-body .page-wrapper .page-inner section.normal>:first-child{margin-top:0!important}.book .book-body .page-wrapper .page-inner section.normal>:last-child{margin-bottom:0!important}.book .book-body .page-wrapper .page-inner section.normal blockquote,.book .book-body .page-wrapper .page-inner section.normal code,.book .book-body .page-wrapper .page-inner section.normal figure,.book .book-body .page-wrapper .page-inner section.normal img,.book .book-body .page-wrapper .page-inner section.normal pre,.book .book-body .page-wrapper .page-inner section.normal table,.book .book-body .page-wrapper .page-inner section.normal tr{page-break-inside:avoid}.book .book-body .page-wrapper .page-inner section.normal h2,.book .book-body .page-wrapper .page-inner section.normal h3,.book .book-body .page-wrapper .page-inner section.normal h4,.book .book-body .page-wrapper .page-inner section.normal h5,.book .book-body .page-wrapper .page-inner section.normal p{orphans:3;widows:3}.book .book-body .page-wrapper .page-inner section.normal h1,.book .book-body .page-wrapper .page-inner section.normal h2,.book .book-body .page-wrapper .page-inner section.normal h3,.book .book-body .page-wrapper .page-inner section.normal h4,.book .book-body .page-wrapper .page-inner section.normal h5{page-break-after:avoid}.book .book-body .page-wrapper .page-inner section.normal b,.book .book-body .page-wrapper .page-inner section.normal strong{font-weight:700}.book .book-body .page-wrapper .page-inner section.normal em{font-style:italic}.book .book-body .page-wrapper .page-inner section.normal blockquote,.book .book-body .page-wrapper .page-inner section.normal dl,.book .book-body .page-wrapper .page-inner section.normal ol,.book .book-body .page-wrapper .page-inner section.normal p,.book .book-body .page-wrapper .page-inner section.normal table,.book .book-body .page-wrapper .page-inner section.normal ul{margin-top:0;margin-bottom:.85em}.book .book-body .page-wrapper .page-inner section.normal a{color:#4183c4;text-decoration:none;background:0 0}.book .book-body .page-wrapper .page-inner section.normal a:active,.book .book-body .page-wrapper .page-inner section.normal a:focus,.book .book-body .page-wrapper .page-inner section.normal a:hover{outline:0;text-decoration:underline}.book .book-body .page-wrapper .page-inner section.normal img{border:0;max-width:100%}.book .book-body .page-wrapper .page-inner section.normal hr{height:4px;padding:0;margin:1.7em 0;overflow:hidden;background-color:#e7e7e7;border:none}.book .book-body .page-wrapper .page-inner section.normal hr:after,.book .book-body .page-wrapper .page-inner section.normal hr:before{display:table;content:" "}.book .book-body .page-wrapper .page-inner section.normal h1,.book .book-body .page-wrapper .page-inner section.normal h2,.book .book-body .page-wrapper .page-inner section.normal h3,.book .book-body .page-wrapper .page-inner section.normal h4,.book .book-body .page-wrapper .page-inner section.normal h5,.book .book-body .page-wrapper .page-inner section.normal h6{margin-top:1.275em;margin-bottom:.85em;}.book .book-body .page-wrapper .page-inner section.normal h1{font-size:2em}.book .book-body .page-wrapper .page-inner section.normal h2{font-size:1.75em}.book .book-body .page-wrapper .page-inner section.normal h3{font-size:1.5em}.book .book-body .page-wrapper .page-inner section.normal h4{font-size:1.25em}.book .book-body .page-wrapper .page-inner section.normal h5{font-size:1em}.book .book-body .page-wrapper .page-inner section.normal h6{font-size:1em;color:#777}.book .book-body .page-wrapper .page-inner section.normal code,.book .book-body .page-wrapper .page-inner section.normal pre{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;direction:ltr;border:none;color:inherit}.book .book-body .page-wrapper .page-inner section.normal pre{overflow:auto;word-wrap:normal;margin:0 0 1.275em;padding:.85em 1em;background:#f7f7f7}.book .book-body .page-wrapper .page-inner section.normal pre>code{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;font-size:.85em;white-space:pre;background:0 0}.book .book-body .page-wrapper .page-inner section.normal pre>code:after,.book .book-body .page-wrapper .page-inner section.normal pre>code:before{content:normal}.book .book-body .page-wrapper .page-inner section.normal code{padding:.2em;margin:0;font-size:.85em;background-color:#f7f7f7}.book .book-body .page-wrapper .page-inner section.normal code:after,.book .book-body .page-wrapper .page-inner section.normal code:before{letter-spacing:-.2em;content:"\00a0"}.book .book-body .page-wrapper .page-inner section.normal ol,.book .book-body .page-wrapper .page-inner section.normal ul{padding:0 0 0 2em;margin:0 0 .85em}.book .book-body .page-wrapper .page-inner section.normal ol ol,.book .book-body .page-wrapper .page-inner section.normal ol ul,.book .book-body .page-wrapper .page-inner section.normal ul ol,.book .book-body .page-wrapper .page-inner section.normal ul ul{margin-top:0;margin-bottom:0}.book .book-body .page-wrapper .page-inner section.normal ol ol{list-style-type:lower-roman}.book .book-body .page-wrapper .page-inner section.normal blockquote{margin:0 0 .85em;padding:0 15px;opacity:0.75;border-left:4px solid #dcdcdc}.book .book-body .page-wrapper .page-inner section.normal blockquote:first-child{margin-top:0}.book .book-body .page-wrapper .page-inner section.normal blockquote:last-child{margin-bottom:0}.book .book-body .page-wrapper .page-inner section.normal dl{padding:0}.book .book-body .page-wrapper .page-inner section.normal dl dt{padding:0;margin-top:.85em;font-style:italic;font-weight:700}.book .book-body .page-wrapper .page-inner section.normal dl dd{padding:0 .85em;margin-bottom:.85em}.book .book-body .page-wrapper .page-inner section.normal dd{margin-left:0}.book .book-body .page-wrapper .page-inner section.normal .glossary-term{cursor:help;text-decoration:underline}.book .book-body .navigation{position:absolute;top:50px;bottom:0;margin:0;max-width:150px;min-width:90px;display:flex;justify-content:center;align-content:center;flex-direction:column;font-size:40px;color:#ccc;text-align:center;-webkit-transition:all 350ms ease;-moz-transition:all 350ms ease;-o-transition:all 350ms ease;transition:all 350ms ease}.book .book-body .navigation:hover{text-decoration:none;color:#444}.book .book-body .navigation.navigation-next{right:0}.book .book-body .navigation.navigation-prev{left:0}@media (max-width:1240px){.book .book-body .navigation{position:static;top:auto;max-width:50%;width:50%;display:inline-block;float:left}.book .book-body .navigation.navigation-unique{max-width:100%;width:100%}}.book .book-body .page-wrapper .page-inner section.glossary{margin-bottom:40px}.book .book-body .page-wrapper .page-inner section.glossary h2 a,.book .book-body .page-wrapper .page-inner section.glossary h2 a:hover{color:inherit;text-decoration:none}.book .book-body .page-wrapper .page-inner section.glossary .glossary-index{list-style:none;margin:0;padding:0}.book .book-body .page-wrapper .page-inner section.glossary .glossary-index li{display:inline;margin:0 8px;white-space:nowrap}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-overflow-scrolling:auto;-webkit-tap-highlight-color:transparent;-webkit-text-size-adjust:none;-webkit-touch-callout:none}a{text-decoration:none}body,html{height:100%}html{font-size:62.5%}body{text-rendering:optimizeLegibility;font-smoothing:antialiased;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;letter-spacing:.2px;text-size-adjust:100%} +.book .book-summary ul.summary li a span {display:inline;padding:initial;overflow:visible;cursor:auto;opacity:1;} +/* show arrow before summary tag as in bootstrap */ +details > summary {display:list-item;cursor:pointer;} diff --git a/docs/_book/libs/gitbook-2.6.7/js/app.min.js b/docs/_book/libs/gitbook-2.6.7/js/app.min.js new file mode 100644 index 0000000..643f1f9 --- /dev/null +++ b/docs/_book/libs/gitbook-2.6.7/js/app.min.js @@ -0,0 +1 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o"'`]/g,reHasEscapedHtml=RegExp(reEscapedHtml.source),reHasUnescapedHtml=RegExp(reUnescapedHtml.source);var reEscape=/<%-([\s\S]+?)%>/g,reEvaluate=/<%([\s\S]+?)%>/g,reInterpolate=/<%=([\s\S]+?)%>/g;var reIsDeepProp=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/,reIsPlainProp=/^\w*$/,rePropName=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g;var reRegExpChars=/^[:!,]|[\\^$.*+?()[\]{}|\/]|(^[0-9a-fA-Fnrtuvx])|([\n\r\u2028\u2029])/g,reHasRegExpChars=RegExp(reRegExpChars.source);var reComboMark=/[\u0300-\u036f\ufe20-\ufe23]/g;var reEscapeChar=/\\(\\)?/g;var reEsTemplate=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;var reFlags=/\w*$/;var reHasHexPrefix=/^0[xX]/;var reIsHostCtor=/^\[object .+?Constructor\]$/;var reIsUint=/^\d+$/;var reLatin1=/[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g;var reNoMatch=/($^)/;var reUnescapedString=/['\n\r\u2028\u2029\\]/g;var reWords=function(){var upper="[A-Z\\xc0-\\xd6\\xd8-\\xde]",lower="[a-z\\xdf-\\xf6\\xf8-\\xff]+";return RegExp(upper+"+(?="+upper+lower+")|"+upper+"?"+lower+"|"+upper+"+|[0-9]+","g")}();var contextProps=["Array","ArrayBuffer","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Math","Number","Object","RegExp","Set","String","_","clearTimeout","isFinite","parseFloat","parseInt","setTimeout","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap"];var templateCounter=-1;var typedArrayTags={};typedArrayTags[float32Tag]=typedArrayTags[float64Tag]=typedArrayTags[int8Tag]=typedArrayTags[int16Tag]=typedArrayTags[int32Tag]=typedArrayTags[uint8Tag]=typedArrayTags[uint8ClampedTag]=typedArrayTags[uint16Tag]=typedArrayTags[uint32Tag]=true;typedArrayTags[argsTag]=typedArrayTags[arrayTag]=typedArrayTags[arrayBufferTag]=typedArrayTags[boolTag]=typedArrayTags[dateTag]=typedArrayTags[errorTag]=typedArrayTags[funcTag]=typedArrayTags[mapTag]=typedArrayTags[numberTag]=typedArrayTags[objectTag]=typedArrayTags[regexpTag]=typedArrayTags[setTag]=typedArrayTags[stringTag]=typedArrayTags[weakMapTag]=false;var cloneableTags={};cloneableTags[argsTag]=cloneableTags[arrayTag]=cloneableTags[arrayBufferTag]=cloneableTags[boolTag]=cloneableTags[dateTag]=cloneableTags[float32Tag]=cloneableTags[float64Tag]=cloneableTags[int8Tag]=cloneableTags[int16Tag]=cloneableTags[int32Tag]=cloneableTags[numberTag]=cloneableTags[objectTag]=cloneableTags[regexpTag]=cloneableTags[stringTag]=cloneableTags[uint8Tag]=cloneableTags[uint8ClampedTag]=cloneableTags[uint16Tag]=cloneableTags[uint32Tag]=true;cloneableTags[errorTag]=cloneableTags[funcTag]=cloneableTags[mapTag]=cloneableTags[setTag]=cloneableTags[weakMapTag]=false;var deburredLetters={"À":"A","Á":"A","Â":"A","Ã":"A","Ä":"A","Å":"A","à":"a","á":"a","â":"a","ã":"a","ä":"a","å":"a","Ç":"C","ç":"c","Ð":"D","ð":"d","È":"E","É":"E","Ê":"E","Ë":"E","è":"e","é":"e","ê":"e","ë":"e","Ì":"I","Í":"I","Î":"I","Ï":"I","ì":"i","í":"i","î":"i","ï":"i","Ñ":"N","ñ":"n","Ò":"O","Ó":"O","Ô":"O","Õ":"O","Ö":"O","Ø":"O","ò":"o","ó":"o","ô":"o","õ":"o","ö":"o","ø":"o","Ù":"U","Ú":"U","Û":"U","Ü":"U","ù":"u","ú":"u","û":"u","ü":"u","Ý":"Y","ý":"y","ÿ":"y","Æ":"Ae","æ":"ae","Þ":"Th","þ":"th","ß":"ss"};var htmlEscapes={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"};var htmlUnescapes={"&":"&","<":"<",">":">",""":'"',"'":"'","`":"`"};var objectTypes={function:true,object:true};var regexpEscapes={0:"x30",1:"x31",2:"x32",3:"x33",4:"x34",5:"x35",6:"x36",7:"x37",8:"x38",9:"x39",A:"x41",B:"x42",C:"x43",D:"x44",E:"x45",F:"x46",a:"x61",b:"x62",c:"x63",d:"x64",e:"x65",f:"x66",n:"x6e",r:"x72",t:"x74",u:"x75",v:"x76",x:"x78"};var stringEscapes={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"};var freeExports=objectTypes[typeof exports]&&exports&&!exports.nodeType&&exports;var freeModule=objectTypes[typeof module]&&module&&!module.nodeType&&module;var freeGlobal=freeExports&&freeModule&&typeof global=="object"&&global&&global.Object&&global;var freeSelf=objectTypes[typeof self]&&self&&self.Object&&self;var freeWindow=objectTypes[typeof window]&&window&&window.Object&&window;var moduleExports=freeModule&&freeModule.exports===freeExports&&freeExports;var root=freeGlobal||freeWindow!==(this&&this.window)&&freeWindow||freeSelf||this;function baseCompareAscending(value,other){if(value!==other){var valIsNull=value===null,valIsUndef=value===undefined,valIsReflexive=value===value;var othIsNull=other===null,othIsUndef=other===undefined,othIsReflexive=other===other;if(value>other&&!othIsNull||!valIsReflexive||valIsNull&&!othIsUndef&&othIsReflexive||valIsUndef&&othIsReflexive){return 1}if(value-1){}return index}function charsRightIndex(string,chars){var index=string.length;while(index--&&chars.indexOf(string.charAt(index))>-1){}return index}function compareAscending(object,other){return baseCompareAscending(object.criteria,other.criteria)||object.index-other.index}function compareMultiple(object,other,orders){var index=-1,objCriteria=object.criteria,othCriteria=other.criteria,length=objCriteria.length,ordersLength=orders.length;while(++index=ordersLength){return result}var order=orders[index];return result*(order==="asc"||order===true?1:-1)}}return object.index-other.index}function deburrLetter(letter){return deburredLetters[letter]}function escapeHtmlChar(chr){return htmlEscapes[chr]}function escapeRegExpChar(chr,leadingChar,whitespaceChar){if(leadingChar){chr=regexpEscapes[chr]}else if(whitespaceChar){chr=stringEscapes[chr]}return"\\"+chr}function escapeStringChar(chr){return"\\"+stringEscapes[chr]}function indexOfNaN(array,fromIndex,fromRight){var length=array.length,index=fromIndex+(fromRight?0:-1);while(fromRight?index--:++index=9&&charCode<=13)||charCode==32||charCode==160||charCode==5760||charCode==6158||charCode>=8192&&(charCode<=8202||charCode==8232||charCode==8233||charCode==8239||charCode==8287||charCode==12288||charCode==65279)}function replaceHolders(array,placeholder){var index=-1,length=array.length,resIndex=-1,result=[];while(++index>>1;var MAX_SAFE_INTEGER=9007199254740991;var metaMap=WeakMap&&new WeakMap;var realNames={};function lodash(value){if(isObjectLike(value)&&!isArray(value)&&!(value instanceof LazyWrapper)){if(value instanceof LodashWrapper){return value}if(hasOwnProperty.call(value,"__chain__")&&hasOwnProperty.call(value,"__wrapped__")){return wrapperClone(value)}}return new LodashWrapper(value)}function baseLodash(){}function LodashWrapper(value,chainAll,actions){this.__wrapped__=value;this.__actions__=actions||[];this.__chain__=!!chainAll}var support=lodash.support={};lodash.templateSettings={escape:reEscape,evaluate:reEvaluate,interpolate:reInterpolate,variable:"",imports:{_:lodash}};function LazyWrapper(value){this.__wrapped__=value;this.__actions__=[];this.__dir__=1;this.__filtered__=false;this.__iteratees__=[];this.__takeCount__=POSITIVE_INFINITY;this.__views__=[]}function lazyClone(){var result=new LazyWrapper(this.__wrapped__);result.__actions__=arrayCopy(this.__actions__);result.__dir__=this.__dir__;result.__filtered__=this.__filtered__;result.__iteratees__=arrayCopy(this.__iteratees__);result.__takeCount__=this.__takeCount__;result.__views__=arrayCopy(this.__views__);return result}function lazyReverse(){if(this.__filtered__){var result=new LazyWrapper(this);result.__dir__=-1;result.__filtered__=true}else{result=this.clone();result.__dir__*=-1}return result}function lazyValue(){var array=this.__wrapped__.value(),dir=this.__dir__,isArr=isArray(array),isRight=dir<0,arrLength=isArr?array.length:0,view=getView(0,arrLength,this.__views__),start=view.start,end=view.end,length=end-start,index=isRight?end:start-1,iteratees=this.__iteratees__,iterLength=iteratees.length,resIndex=0,takeCount=nativeMin(length,this.__takeCount__);if(!isArr||arrLength=LARGE_ARRAY_SIZE?createCache(values):null,valuesLength=values.length;if(cache){indexOf=cacheIndexOf;isCommon=false;values=cache}outer:while(++indexlength?0:length+start}end=end===undefined||end>length?length:+end||0;if(end<0){end+=length}length=start>end?0:end>>>0;start>>>=0;while(startlength?0:length+start}end=end===undefined||end>length?length:+end||0;if(end<0){end+=length}length=start>end?0:end-start>>>0;start>>>=0;var result=Array(length);while(++index=LARGE_ARRAY_SIZE,seen=isLarge?createCache():null,result=[];if(seen){indexOf=cacheIndexOf;isCommon=false}else{isLarge=false;seen=iteratee?[]:result}outer:while(++index>>1,computed=array[mid];if((retHighest?computed<=value:computed2?sources[length-2]:undefined,guard=length>2?sources[2]:undefined,thisArg=length>1?sources[length-1]:undefined;if(typeof customizer=="function"){customizer=bindCallback(customizer,thisArg,5);length-=2}else{customizer=typeof thisArg=="function"?thisArg:undefined;length-=customizer?1:0}if(guard&&isIterateeCall(sources[0],sources[1],guard)){customizer=length<3?undefined:customizer;length=1}while(++index-1?collection[index]:undefined}return baseFind(collection,predicate,eachFunc)}}function createFindIndex(fromRight){return function(array,predicate,thisArg){if(!(array&&array.length)){return-1}predicate=getCallback(predicate,thisArg,3);return baseFindIndex(array,predicate,fromRight)}}function createFindKey(objectFunc){return function(object,predicate,thisArg){predicate=getCallback(predicate,thisArg,3);return baseFind(object,predicate,objectFunc,true)}}function createFlow(fromRight){return function(){var wrapper,length=arguments.length,index=fromRight?length:-1,leftIndex=0,funcs=Array(length);while(fromRight?index--:++index=LARGE_ARRAY_SIZE){return wrapper.plant(value).value()}var index=0,result=length?funcs[index].apply(this,args):value;while(++index=length||!nativeIsFinite(length)){return""}var padLength=length-strLength;chars=chars==null?" ":chars+"";return repeat(chars,nativeCeil(padLength/chars.length)).slice(0,padLength)}function createPartialWrapper(func,bitmask,thisArg,partials){var isBind=bitmask&BIND_FLAG,Ctor=createCtorWrapper(func);function wrapper(){var argsIndex=-1,argsLength=arguments.length,leftIndex=-1,leftLength=partials.length,args=Array(leftLength+argsLength);while(++leftIndexarrLength)){return false}while(++index-1&&value%1==0&&value-1&&value%1==0&&value<=MAX_SAFE_INTEGER}function isStrictComparable(value){return value===value&&!isObject(value)}function mergeData(data,source){var bitmask=data[1],srcBitmask=source[1],newBitmask=bitmask|srcBitmask,isCommon=newBitmask0){if(++count>=HOT_COUNT){return key}}else{count=0}return baseSetData(key,value)}}();function shimKeys(object){var props=keysIn(object),propsLength=props.length,length=propsLength&&object.length;var allowIndexes=!!length&&isLength(length)&&(isArray(object)||isArguments(object));var index=-1,result=[];while(++index=120?createCache(othIndex&&value):null}var array=arrays[0],index=-1,length=array?array.length:0,seen=caches[0];outer:while(++index-1){splice.call(array,fromIndex,1)}}return array}var pullAt=restParam(function(array,indexes){indexes=baseFlatten(indexes);var result=baseAt(array,indexes);basePullAt(array,indexes.sort(baseCompareAscending));return result});function remove(array,predicate,thisArg){var result=[];if(!(array&&array.length)){return result}var index=-1,indexes=[],length=array.length;predicate=getCallback(predicate,thisArg,3);while(++index2?arrays[length-2]:undefined,thisArg=length>1?arrays[length-1]:undefined;if(length>2&&typeof iteratee=="function"){length-=2}else{iteratee=length>1&&typeof thisArg=="function"?(--length,thisArg):undefined;thisArg=undefined}arrays.length=length;return unzipWith(arrays,iteratee,thisArg)});function chain(value){var result=lodash(value);result.__chain__=true;return result}function tap(value,interceptor,thisArg){interceptor.call(thisArg,value);return value}function thru(value,interceptor,thisArg){return interceptor.call(thisArg,value)}function wrapperChain(){return chain(this)}function wrapperCommit(){return new LodashWrapper(this.value(),this.__chain__)}var wrapperConcat=restParam(function(values){values=baseFlatten(values);return this.thru(function(array){return arrayConcat(isArray(array)?array:[toObject(array)],values)})});function wrapperPlant(value){var result,parent=this;while(parent instanceof baseLodash){var clone=wrapperClone(parent);if(result){previous.__wrapped__=clone}else{result=clone}var previous=clone;parent=parent.__wrapped__}previous.__wrapped__=value;return result}function wrapperReverse(){var value=this.__wrapped__;var interceptor=function(value){return wrapped&&wrapped.__dir__<0?value:value.reverse()};if(value instanceof LazyWrapper){var wrapped=value;if(this.__actions__.length){wrapped=new LazyWrapper(this)}wrapped=wrapped.reverse();wrapped.__actions__.push({func:thru,args:[interceptor],thisArg:undefined});return new LodashWrapper(wrapped,this.__chain__)}return this.thru(interceptor)}function wrapperToString(){return this.value()+""}function wrapperValue(){return baseWrapperValue(this.__wrapped__,this.__actions__)}var at=restParam(function(collection,props){return baseAt(collection,baseFlatten(props))});var countBy=createAggregator(function(result,value,key){hasOwnProperty.call(result,key)?++result[key]:result[key]=1});function every(collection,predicate,thisArg){var func=isArray(collection)?arrayEvery:baseEvery;if(thisArg&&isIterateeCall(collection,predicate,thisArg)){predicate=undefined}if(typeof predicate!="function"||thisArg!==undefined){predicate=getCallback(predicate,thisArg,3)}return func(collection,predicate)}function filter(collection,predicate,thisArg){var func=isArray(collection)?arrayFilter:baseFilter;predicate=getCallback(predicate,thisArg,3);return func(collection,predicate)}var find=createFind(baseEach);var findLast=createFind(baseEachRight,true);function findWhere(collection,source){return find(collection,baseMatches(source))}var forEach=createForEach(arrayEach,baseEach);var forEachRight=createForEach(arrayEachRight,baseEachRight);var groupBy=createAggregator(function(result,value,key){if(hasOwnProperty.call(result,key)){result[key].push(value)}else{result[key]=[value]}});function includes(collection,target,fromIndex,guard){var length=collection?getLength(collection):0;if(!isLength(length)){collection=values(collection);length=collection.length}if(typeof fromIndex!="number"||guard&&isIterateeCall(target,fromIndex,guard)){fromIndex=0}else{fromIndex=fromIndex<0?nativeMax(length+fromIndex,0):fromIndex||0}return typeof collection=="string"||!isArray(collection)&&isString(collection)?fromIndex<=length&&collection.indexOf(target,fromIndex)>-1:!!length&&getIndexOf(collection,target,fromIndex)>-1}var indexBy=createAggregator(function(result,value,key){result[key]=value});var invoke=restParam(function(collection,path,args){var index=-1,isFunc=typeof path=="function",isProp=isKey(path),result=isArrayLike(collection)?Array(collection.length):[];baseEach(collection,function(value){var func=isFunc?path:isProp&&value!=null?value[path]:undefined;result[++index]=func?func.apply(value,args):invokePath(value,path,args)});return result});function map(collection,iteratee,thisArg){var func=isArray(collection)?arrayMap:baseMap;iteratee=getCallback(iteratee,thisArg,3);return func(collection,iteratee)}var partition=createAggregator(function(result,value,key){result[key?0:1].push(value)},function(){return[[],[]]});function pluck(collection,path){return map(collection,property(path))}var reduce=createReduce(arrayReduce,baseEach);var reduceRight=createReduce(arrayReduceRight,baseEachRight);function reject(collection,predicate,thisArg){var func=isArray(collection)?arrayFilter:baseFilter;predicate=getCallback(predicate,thisArg,3);return func(collection,function(value,index,collection){return!predicate(value,index,collection)})}function sample(collection,n,guard){if(guard?isIterateeCall(collection,n,guard):n==null){collection=toIterable(collection);var length=collection.length;return length>0?collection[baseRandom(0,length-1)]:undefined}var index=-1,result=toArray(collection),length=result.length,lastIndex=length-1;n=nativeMin(n<0?0:+n||0,length);while(++index0){result=func.apply(this,arguments)}if(n<=1){func=undefined}return result}}var bind=restParam(function(func,thisArg,partials){var bitmask=BIND_FLAG;if(partials.length){var holders=replaceHolders(partials,bind.placeholder);bitmask|=PARTIAL_FLAG}return createWrapper(func,bitmask,thisArg,partials,holders)});var bindAll=restParam(function(object,methodNames){methodNames=methodNames.length?baseFlatten(methodNames):functions(object);var index=-1,length=methodNames.length;while(++indexwait){complete(trailingCall,maxTimeoutId)}else{timeoutId=setTimeout(delayed,remaining)}}function maxDelayed(){complete(trailing,timeoutId)}function debounced(){args=arguments;stamp=now();thisArg=this;trailingCall=trailing&&(timeoutId||!leading);if(maxWait===false){var leadingCall=leading&&!timeoutId}else{if(!maxTimeoutId&&!leading){lastCalled=stamp}var remaining=maxWait-(stamp-lastCalled),isCalled=remaining<=0||remaining>maxWait;if(isCalled){if(maxTimeoutId){maxTimeoutId=clearTimeout(maxTimeoutId)}lastCalled=stamp;result=func.apply(thisArg,args)}else if(!maxTimeoutId){maxTimeoutId=setTimeout(maxDelayed,remaining)}}if(isCalled&&timeoutId){timeoutId=clearTimeout(timeoutId)}else if(!timeoutId&&wait!==maxWait){timeoutId=setTimeout(delayed,wait)}if(leadingCall){isCalled=true;result=func.apply(thisArg,args)}if(isCalled&&!timeoutId&&!maxTimeoutId){args=thisArg=undefined}return result}debounced.cancel=cancel;return debounced}var defer=restParam(function(func,args){return baseDelay(func,1,args)});var delay=restParam(function(func,wait,args){return baseDelay(func,wait,args)});var flow=createFlow();var flowRight=createFlow(true);function memoize(func,resolver){if(typeof func!="function"||resolver&&typeof resolver!="function"){throw new TypeError(FUNC_ERROR_TEXT)}var memoized=function(){var args=arguments,key=resolver?resolver.apply(this,args):args[0],cache=memoized.cache;if(cache.has(key)){return cache.get(key)}var result=func.apply(this,args);memoized.cache=cache.set(key,result);return result};memoized.cache=new memoize.Cache;return memoized}var modArgs=restParam(function(func,transforms){transforms=baseFlatten(transforms);if(typeof func!="function"||!arrayEvery(transforms,baseIsFunction)){throw new TypeError(FUNC_ERROR_TEXT)}var length=transforms.length;return restParam(function(args){var index=nativeMin(args.length,length);while(index--){args[index]=transforms[index](args[index])}return func.apply(this,args)})});function negate(predicate){if(typeof predicate!="function"){throw new TypeError(FUNC_ERROR_TEXT)}return function(){return!predicate.apply(this,arguments)}}function once(func){return before(2,func)}var partial=createPartial(PARTIAL_FLAG);var partialRight=createPartial(PARTIAL_RIGHT_FLAG);var rearg=restParam(function(func,indexes){return createWrapper(func,REARG_FLAG,undefined,undefined,undefined,baseFlatten(indexes))});function restParam(func,start){if(typeof func!="function"){throw new TypeError(FUNC_ERROR_TEXT)}start=nativeMax(start===undefined?func.length-1:+start||0,0);return function(){var args=arguments,index=-1,length=nativeMax(args.length-start,0),rest=Array(length);while(++indexother}function gte(value,other){return value>=other}function isArguments(value){return isObjectLike(value)&&isArrayLike(value)&&hasOwnProperty.call(value,"callee")&&!propertyIsEnumerable.call(value,"callee")}var isArray=nativeIsArray||function(value){return isObjectLike(value)&&isLength(value.length)&&objToString.call(value)==arrayTag};function isBoolean(value){return value===true||value===false||isObjectLike(value)&&objToString.call(value)==boolTag}function isDate(value){return isObjectLike(value)&&objToString.call(value)==dateTag}function isElement(value){return!!value&&value.nodeType===1&&isObjectLike(value)&&!isPlainObject(value)}function isEmpty(value){if(value==null){return true}if(isArrayLike(value)&&(isArray(value)||isString(value)||isArguments(value)||isObjectLike(value)&&isFunction(value.splice))){return!value.length}return!keys(value).length}function isEqual(value,other,customizer,thisArg){customizer=typeof customizer=="function"?bindCallback(customizer,thisArg,3):undefined;var result=customizer?customizer(value,other):undefined;return result===undefined?baseIsEqual(value,other,customizer):!!result}function isError(value){return isObjectLike(value)&&typeof value.message=="string"&&objToString.call(value)==errorTag}function isFinite(value){return typeof value=="number"&&nativeIsFinite(value)}function isFunction(value){return isObject(value)&&objToString.call(value)==funcTag}function isObject(value){var type=typeof value;return!!value&&(type=="object"||type=="function")}function isMatch(object,source,customizer,thisArg){customizer=typeof customizer=="function"?bindCallback(customizer,thisArg,3):undefined;return baseIsMatch(object,getMatchData(source),customizer)}function isNaN(value){return isNumber(value)&&value!=+value}function isNative(value){if(value==null){return false}if(isFunction(value)){return reIsNative.test(fnToString.call(value))}return isObjectLike(value)&&reIsHostCtor.test(value)}function isNull(value){return value===null}function isNumber(value){return typeof value=="number"||isObjectLike(value)&&objToString.call(value)==numberTag}function isPlainObject(value){var Ctor;if(!(isObjectLike(value)&&objToString.call(value)==objectTag&&!isArguments(value))||!hasOwnProperty.call(value,"constructor")&&(Ctor=value.constructor,typeof Ctor=="function"&&!(Ctor instanceof Ctor))){return false}var result;baseForIn(value,function(subValue,key){result=key});return result===undefined||hasOwnProperty.call(value,result)}function isRegExp(value){return isObject(value)&&objToString.call(value)==regexpTag}function isString(value){return typeof value=="string"||isObjectLike(value)&&objToString.call(value)==stringTag}function isTypedArray(value){return isObjectLike(value)&&isLength(value.length)&&!!typedArrayTags[objToString.call(value)]}function isUndefined(value){return value===undefined}function lt(value,other){return value0;while(++index=nativeMin(start,end)&&value=0&&string.indexOf(target,position)==position}function escape(string){string=baseToString(string);return string&&reHasUnescapedHtml.test(string)?string.replace(reUnescapedHtml,escapeHtmlChar):string}function escapeRegExp(string){string=baseToString(string);return string&&reHasRegExpChars.test(string)?string.replace(reRegExpChars,escapeRegExpChar):string||"(?:)"}var kebabCase=createCompounder(function(result,word,index){return result+(index?"-":"")+word.toLowerCase()});function pad(string,length,chars){string=baseToString(string);length=+length;var strLength=string.length;if(strLength>=length||!nativeIsFinite(length)){return string}var mid=(length-strLength)/2,leftLength=nativeFloor(mid),rightLength=nativeCeil(mid);chars=createPadding("",rightLength,chars);return chars.slice(0,leftLength)+string+chars}var padLeft=createPadDir();var padRight=createPadDir(true);function parseInt(string,radix,guard){if(guard?isIterateeCall(string,radix,guard):radix==null){radix=0}else if(radix){radix=+radix}string=trim(string);return nativeParseInt(string,radix||(reHasHexPrefix.test(string)?16:10))}function repeat(string,n){var result="";string=baseToString(string);n=+n;if(n<1||!string||!nativeIsFinite(n)){return result}do{if(n%2){result+=string}n=nativeFloor(n/2);string+=string}while(n);return result}var snakeCase=createCompounder(function(result,word,index){return result+(index?"_":"")+word.toLowerCase()});var startCase=createCompounder(function(result,word,index){return result+(index?" ":"")+(word.charAt(0).toUpperCase()+word.slice(1))});function startsWith(string,target,position){string=baseToString(string);position=position==null?0:nativeMin(position<0?0:+position||0,string.length);return string.lastIndexOf(target,position)==position}function template(string,options,otherOptions){var settings=lodash.templateSettings;if(otherOptions&&isIterateeCall(string,options,otherOptions)){options=otherOptions=undefined}string=baseToString(string);options=assignWith(baseAssign({},otherOptions||options),settings,assignOwnDefaults);var imports=assignWith(baseAssign({},options.imports),settings.imports,assignOwnDefaults),importsKeys=keys(imports),importsValues=baseValues(imports,importsKeys);var isEscaping,isEvaluating,index=0,interpolate=options.interpolate||reNoMatch,source="__p += '";var reDelimiters=RegExp((options.escape||reNoMatch).source+"|"+interpolate.source+"|"+(interpolate===reInterpolate?reEsTemplate:reNoMatch).source+"|"+(options.evaluate||reNoMatch).source+"|$","g");var sourceURL="//# sourceURL="+("sourceURL"in options?options.sourceURL:"lodash.templateSources["+ ++templateCounter+"]")+"\n";string.replace(reDelimiters,function(match,escapeValue,interpolateValue,esTemplateValue,evaluateValue,offset){interpolateValue||(interpolateValue=esTemplateValue);source+=string.slice(index,offset).replace(reUnescapedString,escapeStringChar);if(escapeValue){isEscaping=true;source+="' +\n__e("+escapeValue+") +\n'"}if(evaluateValue){isEvaluating=true;source+="';\n"+evaluateValue+";\n__p += '"}if(interpolateValue){source+="' +\n((__t = ("+interpolateValue+")) == null ? '' : __t) +\n'"}index=offset+match.length;return match});source+="';\n";var variable=options.variable;if(!variable){source="with (obj) {\n"+source+"\n}\n"}source=(isEvaluating?source.replace(reEmptyStringLeading,""):source).replace(reEmptyStringMiddle,"$1").replace(reEmptyStringTrailing,"$1;");source="function("+(variable||"obj")+") {\n"+(variable?"":"obj || (obj = {});\n")+"var __t, __p = ''"+(isEscaping?", __e = _.escape":"")+(isEvaluating?", __j = Array.prototype.join;\n"+"function print() { __p += __j.call(arguments, '') }\n":";\n")+source+"return __p\n}";var result=attempt(function(){return Function(importsKeys,sourceURL+"return "+source).apply(undefined,importsValues)});result.source=source;if(isError(result)){throw result}return result}function trim(string,chars,guard){var value=string;string=baseToString(string);if(!string){return string}if(guard?isIterateeCall(value,chars,guard):chars==null){return string.slice(trimmedLeftIndex(string),trimmedRightIndex(string)+1)}chars=chars+"";return string.slice(charsLeftIndex(string,chars),charsRightIndex(string,chars)+1)}function trimLeft(string,chars,guard){var value=string;string=baseToString(string);if(!string){return string}if(guard?isIterateeCall(value,chars,guard):chars==null){return string.slice(trimmedLeftIndex(string))}return string.slice(charsLeftIndex(string,chars+""))}function trimRight(string,chars,guard){var value=string;string=baseToString(string);if(!string){return string}if(guard?isIterateeCall(value,chars,guard):chars==null){return string.slice(0,trimmedRightIndex(string)+1)}return string.slice(0,charsRightIndex(string,chars+"")+1)}function trunc(string,options,guard){if(guard&&isIterateeCall(string,options,guard)){options=undefined}var length=DEFAULT_TRUNC_LENGTH,omission=DEFAULT_TRUNC_OMISSION;if(options!=null){if(isObject(options)){var separator="separator"in options?options.separator:separator;length="length"in options?+options.length||0:length;omission="omission"in options?baseToString(options.omission):omission}else{length=+options||0}}string=baseToString(string);if(length>=string.length){return string}var end=length-omission.length;if(end<1){return omission}var result=string.slice(0,end);if(separator==null){return result+omission}if(isRegExp(separator)){if(string.slice(end).search(separator)){var match,newEnd,substring=string.slice(0,end);if(!separator.global){separator=RegExp(separator.source,(reFlags.exec(separator)||"")+"g")}separator.lastIndex=0;while(match=separator.exec(substring)){newEnd=match.index}result=result.slice(0,newEnd==null?end:newEnd)}}else if(string.indexOf(separator,end)!=end){var index=result.lastIndexOf(separator);if(index>-1){result=result.slice(0,index)}}return result+omission}function unescape(string){string=baseToString(string);return string&&reHasEscapedHtml.test(string)?string.replace(reEscapedHtml,unescapeHtmlChar):string}function words(string,pattern,guard){if(guard&&isIterateeCall(string,pattern,guard)){pattern=undefined}string=baseToString(string);return string.match(pattern||reWords)||[]}var attempt=restParam(function(func,args){try{return func.apply(undefined,args)}catch(e){return isError(e)?e:new Error(e)}});function callback(func,thisArg,guard){if(guard&&isIterateeCall(func,thisArg,guard)){thisArg=undefined}return isObjectLike(func)?matches(func):baseCallback(func,thisArg)}function constant(value){return function(){return value}}function identity(value){return value}function matches(source){return baseMatches(baseClone(source,true))}function matchesProperty(path,srcValue){return baseMatchesProperty(path,baseClone(srcValue,true))}var method=restParam(function(path,args){return function(object){return invokePath(object,path,args)}});var methodOf=restParam(function(object,args){return function(path){return invokePath(object,path,args)}});function mixin(object,source,options){if(options==null){var isObj=isObject(source),props=isObj?keys(source):undefined,methodNames=props&&props.length?baseFunctions(source,props):undefined;if(!(methodNames?methodNames.length:isObj)){methodNames=false;options=source;source=object;object=this}}if(!methodNames){methodNames=baseFunctions(source,keys(source))}var chain=true,index=-1,isFunc=isFunction(object),length=methodNames.length;if(options===false){chain=false}else if(isObject(options)&&"chain"in options){chain=options.chain}while(++index0||end<0)){return new LazyWrapper(result)}if(start<0){result=result.takeRight(-start)}else if(start){result=result.drop(start)}if(end!==undefined){end=+end||0;result=end<0?result.dropRight(-end):result.take(end-start)}return result};LazyWrapper.prototype.takeRightWhile=function(predicate,thisArg){return this.reverse().takeWhile(predicate,thisArg).reverse()};LazyWrapper.prototype.toArray=function(){return this.take(POSITIVE_INFINITY)};baseForOwn(LazyWrapper.prototype,function(func,methodName){var checkIteratee=/^(?:filter|map|reject)|While$/.test(methodName),retUnwrapped=/^(?:first|last)$/.test(methodName),lodashFunc=lodash[retUnwrapped?"take"+(methodName=="last"?"Right":""):methodName];if(!lodashFunc){return}lodash.prototype[methodName]=function(){var args=retUnwrapped?[1]:arguments,chainAll=this.__chain__,value=this.__wrapped__,isHybrid=!!this.__actions__.length,isLazy=value instanceof LazyWrapper,iteratee=args[0],useLazy=isLazy||isArray(value);if(useLazy&&checkIteratee&&typeof iteratee=="function"&&iteratee.length!=1){isLazy=useLazy=false}var interceptor=function(value){return retUnwrapped&&chainAll?lodashFunc(value,1)[0]:lodashFunc.apply(undefined,arrayPush([value],args))};var action={func:thru,args:[interceptor],thisArg:undefined},onlyLazy=isLazy&&!isHybrid;if(retUnwrapped&&!chainAll){if(onlyLazy){value=value.clone();value.__actions__.push(action);return func.call(value)}return lodashFunc.call(undefined,this.value())[0]}if(!retUnwrapped&&useLazy){value=onlyLazy?value:new LazyWrapper(this);var result=func.apply(value,args);result.__actions__.push(action);return new LodashWrapper(result,chainAll)}return this.thru(interceptor)}});arrayEach(["join","pop","push","replace","shift","sort","splice","split","unshift"],function(methodName){var func=(/^(?:replace|split)$/.test(methodName)?stringProto:arrayProto)[methodName],chainName=/^(?:push|sort|unshift)$/.test(methodName)?"tap":"thru",retUnwrapped=/^(?:join|pop|replace|shift)$/.test(methodName);lodash.prototype[methodName]=function(){var args=arguments;if(retUnwrapped&&!this.__chain__){return func.apply(this.value(),args)}return this[chainName](function(value){return func.apply(value,args)})}});baseForOwn(LazyWrapper.prototype,function(func,methodName){var lodashFunc=lodash[methodName];if(lodashFunc){var key=lodashFunc.name,names=realNames[key]||(realNames[key]=[]);names.push({name:methodName,func:lodashFunc})}});realNames[createHybridWrapper(undefined,BIND_KEY_FLAG).name]=[{name:"wrapper",func:undefined}];LazyWrapper.prototype.clone=lazyClone;LazyWrapper.prototype.reverse=lazyReverse;LazyWrapper.prototype.value=lazyValue;lodash.prototype.chain=wrapperChain;lodash.prototype.commit=wrapperCommit;lodash.prototype.concat=wrapperConcat;lodash.prototype.plant=wrapperPlant;lodash.prototype.reverse=wrapperReverse;lodash.prototype.toString=wrapperToString;lodash.prototype.run=lodash.prototype.toJSON=lodash.prototype.valueOf=lodash.prototype.value=wrapperValue;lodash.prototype.collect=lodash.prototype.map;lodash.prototype.head=lodash.prototype.first;lodash.prototype.select=lodash.prototype.filter;lodash.prototype.tail=lodash.prototype.rest;return lodash}var _=runInContext();if(typeof define=="function"&&typeof define.amd=="object"&&define.amd){root._=_;define(function(){return _})}else if(freeExports&&freeModule){if(moduleExports){(freeModule.exports=_)._=_}else{freeExports._=_}}else{root._=_}}).call(this)}).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{}],3:[function(require,module,exports){(function(window,document,undefined){var _MAP={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",18:"alt",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"ins",46:"del",91:"meta",93:"meta",224:"meta"};var _KEYCODE_MAP={106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"};var _SHIFT_MAP={"~":"`","!":"1","@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\"};var _SPECIAL_ALIASES={option:"alt",command:"meta",return:"enter",escape:"esc",plus:"+",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"};var _REVERSE_MAP;for(var i=1;i<20;++i){_MAP[111+i]="f"+i}for(i=0;i<=9;++i){_MAP[i+96]=i}function _addEvent(object,type,callback){if(object.addEventListener){object.addEventListener(type,callback,false);return}object.attachEvent("on"+type,callback)}function _characterFromEvent(e){if(e.type=="keypress"){var character=String.fromCharCode(e.which);if(!e.shiftKey){character=character.toLowerCase()}return character}if(_MAP[e.which]){return _MAP[e.which]}if(_KEYCODE_MAP[e.which]){return _KEYCODE_MAP[e.which]}return String.fromCharCode(e.which).toLowerCase()}function _modifiersMatch(modifiers1,modifiers2){return modifiers1.sort().join(",")===modifiers2.sort().join(",")}function _eventModifiers(e){var modifiers=[];if(e.shiftKey){modifiers.push("shift")}if(e.altKey){modifiers.push("alt")}if(e.ctrlKey){modifiers.push("ctrl")}if(e.metaKey){modifiers.push("meta")}return modifiers}function _preventDefault(e){if(e.preventDefault){e.preventDefault();return}e.returnValue=false}function _stopPropagation(e){if(e.stopPropagation){e.stopPropagation();return}e.cancelBubble=true}function _isModifier(key){return key=="shift"||key=="ctrl"||key=="alt"||key=="meta"}function _getReverseMap(){if(!_REVERSE_MAP){_REVERSE_MAP={};for(var key in _MAP){if(key>95&&key<112){continue}if(_MAP.hasOwnProperty(key)){_REVERSE_MAP[_MAP[key]]=key}}}return _REVERSE_MAP}function _pickBestAction(key,modifiers,action){if(!action){action=_getReverseMap()[key]?"keydown":"keypress"}if(action=="keypress"&&modifiers.length){action="keydown"}return action}function _keysFromString(combination){if(combination==="+"){return["+"]}combination=combination.replace(/\+{2}/g,"+plus");return combination.split("+")}function _getKeyInfo(combination,action){var keys;var key;var i;var modifiers=[];keys=_keysFromString(combination);for(i=0;i1){_bindSequence(combination,sequence,callback,action);return}info=_getKeyInfo(combination,action);self._callbacks[info.key]=self._callbacks[info.key]||[];_getMatches(info.key,info.modifiers,{type:info.action},sequenceName,combination,level);self._callbacks[info.key][sequenceName?"unshift":"push"]({callback:callback,modifiers:info.modifiers,action:info.action,seq:sequenceName,level:level,combo:combination})}self._bindMultiple=function(combinations,callback,action){for(var i=0;i-1){return false}if(_belongsTo(element,self.target)){return false}return element.tagName=="INPUT"||element.tagName=="SELECT"||element.tagName=="TEXTAREA"||element.isContentEditable};Mousetrap.prototype.handleKey=function(){var self=this;return self._handleKey.apply(self,arguments)};Mousetrap.init=function(){var documentMousetrap=Mousetrap(document);for(var method in documentMousetrap){if(method.charAt(0)!=="_"){Mousetrap[method]=function(method){return function(){return documentMousetrap[method].apply(documentMousetrap,arguments)}}(method)}}};Mousetrap.init();window.Mousetrap=Mousetrap;if(typeof module!=="undefined"&&module.exports){module.exports=Mousetrap}if(typeof define==="function"&&define.amd){define(function(){return Mousetrap})}})(window,document)},{}],4:[function(require,module,exports){(function(process){function normalizeArray(parts,allowAboveRoot){var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up--;up){parts.unshift("..")}}return parts}var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;var splitPath=function(filename){return splitPathRe.exec(filename).slice(1)};exports.resolve=function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:process.cwd();if(typeof path!=="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){continue}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=path.charAt(0)==="/"}resolvedPath=normalizeArray(filter(resolvedPath.split("/"),function(p){return!!p}),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."};exports.normalize=function(path){var isAbsolute=exports.isAbsolute(path),trailingSlash=substr(path,-1)==="/";path=normalizeArray(filter(path.split("/"),function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path};exports.isAbsolute=function(path){return path.charAt(0)==="/"};exports.join=function(){var paths=Array.prototype.slice.call(arguments,0);return exports.normalize(filter(paths,function(p,index){if(typeof p!=="string"){throw new TypeError("Arguments to path.join must be strings")}return p}).join("/"))};exports.relative=function(from,to){from=exports.resolve(from).substr(1);to=exports.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i1){for(var i=1;i= 0x80 (not a basic code point)","invalid-input":"Invalid input"},baseMinusTMin=base-tMin,floor=Math.floor,stringFromCharCode=String.fromCharCode,key;function error(type){throw RangeError(errors[type])}function map(array,fn){var length=array.length;var result=[];while(length--){result[length]=fn(array[length])}return result}function mapDomain(string,fn){var parts=string.split("@");var result="";if(parts.length>1){result=parts[0]+"@";string=parts[1]}string=string.replace(regexSeparators,".");var labels=string.split(".");var encoded=map(labels,fn).join(".");return result+encoded}function ucs2decode(string){var output=[],counter=0,length=string.length,value,extra;while(counter=55296&&value<=56319&&counter65535){value-=65536;output+=stringFromCharCode(value>>>10&1023|55296);value=56320|value&1023}output+=stringFromCharCode(value);return output}).join("")}function basicToDigit(codePoint){if(codePoint-48<10){return codePoint-22}if(codePoint-65<26){return codePoint-65}if(codePoint-97<26){return codePoint-97}return base}function digitToBasic(digit,flag){return digit+22+75*(digit<26)-((flag!=0)<<5)}function adapt(delta,numPoints,firstTime){var k=0;delta=firstTime?floor(delta/damp):delta>>1;delta+=floor(delta/numPoints);for(;delta>baseMinusTMin*tMax>>1;k+=base){delta=floor(delta/baseMinusTMin)}return floor(k+(baseMinusTMin+1)*delta/(delta+skew))}function decode(input){var output=[],inputLength=input.length,out,i=0,n=initialN,bias=initialBias,basic,j,index,oldi,w,k,digit,t,baseMinusT;basic=input.lastIndexOf(delimiter);if(basic<0){basic=0}for(j=0;j=128){error("not-basic")}output.push(input.charCodeAt(j))}for(index=basic>0?basic+1:0;index=inputLength){error("invalid-input")}digit=basicToDigit(input.charCodeAt(index++));if(digit>=base||digit>floor((maxInt-i)/w)){error("overflow")}i+=digit*w;t=k<=bias?tMin:k>=bias+tMax?tMax:k-bias;if(digitfloor(maxInt/baseMinusT)){error("overflow")}w*=baseMinusT}out=output.length+1;bias=adapt(i-oldi,out,oldi==0);if(floor(i/out)>maxInt-n){error("overflow")}n+=floor(i/out);i%=out;output.splice(i++,0,n)}return ucs2encode(output)}function encode(input){var n,delta,handledCPCount,basicLength,bias,j,m,q,k,t,currentValue,output=[],inputLength,handledCPCountPlusOne,baseMinusT,qMinusT;input=ucs2decode(input);inputLength=input.length;n=initialN;delta=0;bias=initialBias;for(j=0;j=n&¤tValuefloor((maxInt-delta)/handledCPCountPlusOne)){error("overflow")}delta+=(m-n)*handledCPCountPlusOne;n=m;for(j=0;jmaxInt){error("overflow")}if(currentValue==n){for(q=delta,k=base;;k+=base){t=k<=bias?tMin:k>=bias+tMax?tMax:k-bias;if(q0&&len>maxKeys){len=maxKeys}for(var i=0;i=0){kstr=x.substr(0,idx);vstr=x.substr(idx+1)}else{kstr=x;vstr=""}k=decodeURIComponent(kstr);v=decodeURIComponent(vstr);if(!hasOwnProperty(obj,k)){obj[k]=v}else if(isArray(obj[k])){obj[k].push(v)}else{obj[k]=[obj[k],v]}}return obj};var isArray=Array.isArray||function(xs){return Object.prototype.toString.call(xs)==="[object Array]"}},{}],8:[function(require,module,exports){"use strict";var stringifyPrimitive=function(v){switch(typeof v){case"string":return v;case"boolean":return v?"true":"false";case"number":return isFinite(v)?v:"";default:return""}};module.exports=function(obj,sep,eq,name){sep=sep||"&";eq=eq||"=";if(obj===null){obj=undefined}if(typeof obj==="object"){return map(objectKeys(obj),function(k){var ks=encodeURIComponent(stringifyPrimitive(k))+eq;if(isArray(obj[k])){return map(obj[k],function(v){return ks+encodeURIComponent(stringifyPrimitive(v))}).join(sep)}else{return ks+encodeURIComponent(stringifyPrimitive(obj[k]))}}).join(sep)}if(!name)return"";return encodeURIComponent(stringifyPrimitive(name))+eq+encodeURIComponent(stringifyPrimitive(obj))};var isArray=Array.isArray||function(xs){return Object.prototype.toString.call(xs)==="[object Array]"};function map(xs,f){if(xs.map)return xs.map(f);var res=[];for(var i=0;i",'"',"`"," ","\r","\n","\t"],unwise=["{","}","|","\\","^","`"].concat(delims),autoEscape=["'"].concat(unwise),nonHostChars=["%","/","?",";","#"].concat(autoEscape),hostEndingChars=["/","?","#"],hostnameMaxLen=255,hostnamePartPattern=/^[a-z0-9A-Z_-]{0,63}$/,hostnamePartStart=/^([a-z0-9A-Z_-]{0,63})(.*)$/,unsafeProtocol={javascript:true,"javascript:":true},hostlessProtocol={javascript:true,"javascript:":true},slashedProtocol={http:true,https:true,ftp:true,gopher:true,file:true,"http:":true,"https:":true,"ftp:":true,"gopher:":true,"file:":true},querystring=require("querystring");function urlParse(url,parseQueryString,slashesDenoteHost){if(url&&isObject(url)&&url instanceof Url)return url;var u=new Url;u.parse(url,parseQueryString,slashesDenoteHost);return u}Url.prototype.parse=function(url,parseQueryString,slashesDenoteHost){if(!isString(url)){throw new TypeError("Parameter 'url' must be a string, not "+typeof url)}var rest=url;rest=rest.trim();var proto=protocolPattern.exec(rest);if(proto){proto=proto[0];var lowerProto=proto.toLowerCase();this.protocol=lowerProto;rest=rest.substr(proto.length)}if(slashesDenoteHost||proto||rest.match(/^\/\/[^@\/]+@[^@\/]+/)){var slashes=rest.substr(0,2)==="//";if(slashes&&!(proto&&hostlessProtocol[proto])){rest=rest.substr(2);this.slashes=true}}if(!hostlessProtocol[proto]&&(slashes||proto&&!slashedProtocol[proto])){var hostEnd=-1;for(var i=0;i127){newpart+="x"}else{newpart+=part[j]}}if(!newpart.match(hostnamePartPattern)){var validParts=hostparts.slice(0,i);var notHost=hostparts.slice(i+1);var bit=part.match(hostnamePartStart);if(bit){validParts.push(bit[1]);notHost.unshift(bit[2])}if(notHost.length){rest="/"+notHost.join(".")+rest}this.hostname=validParts.join(".");break}}}}if(this.hostname.length>hostnameMaxLen){this.hostname=""}else{this.hostname=this.hostname.toLowerCase()}if(!ipv6Hostname){var domainArray=this.hostname.split(".");var newOut=[];for(var i=0;i0?result.host.split("@"):false;if(authInHost){result.auth=authInHost.shift();result.host=result.hostname=authInHost.shift()}}result.search=relative.search;result.query=relative.query;if(!isNull(result.pathname)||!isNull(result.search)){result.path=(result.pathname?result.pathname:"")+(result.search?result.search:"")}result.href=result.format();return result}if(!srcPath.length){result.pathname=null;if(result.search){result.path="/"+result.search}else{result.path=null}result.href=result.format();return result}var last=srcPath.slice(-1)[0];var hasTrailingSlash=(result.host||relative.host)&&(last==="."||last==="..")||last==="";var up=0;for(var i=srcPath.length;i>=0;i--){last=srcPath[i];if(last=="."){srcPath.splice(i,1)}else if(last===".."){srcPath.splice(i,1);up++}else if(up){srcPath.splice(i,1);up--}}if(!mustEndAbs&&!removeAllDots){for(;up--;up){srcPath.unshift("..")}}if(mustEndAbs&&srcPath[0]!==""&&(!srcPath[0]||srcPath[0].charAt(0)!=="/")){srcPath.unshift("")}if(hasTrailingSlash&&srcPath.join("/").substr(-1)!=="/"){srcPath.push("")}var isAbsolute=srcPath[0]===""||srcPath[0]&&srcPath[0].charAt(0)==="/";if(psychotic){result.hostname=result.host=isAbsolute?"":srcPath.length?srcPath.shift():"";var authInHost=result.host&&result.host.indexOf("@")>0?result.host.split("@"):false;if(authInHost){result.auth=authInHost.shift();result.host=result.hostname=authInHost.shift()}}mustEndAbs=mustEndAbs||result.host&&srcPath.length;if(mustEndAbs&&!isAbsolute){srcPath.unshift("")}if(!srcPath.length){result.pathname=null;result.path=null}else{result.pathname=srcPath.join("/")}if(!isNull(result.pathname)||!isNull(result.search)){result.path=(result.pathname?result.pathname:"")+(result.search?result.search:"")}result.auth=relative.auth||result.auth;result.slashes=result.slashes||relative.slashes;result.href=result.format();return result};Url.prototype.parseHost=function(){var host=this.host;var port=portPattern.exec(host);if(port){port=port[0];if(port!==":"){this.port=port.substr(1)}host=host.substr(0,host.length-port.length)}if(host)this.hostname=host};function isString(arg){return typeof arg==="string"}function isObject(arg){return typeof arg==="object"&&arg!==null}function isNull(arg){return arg===null}function isNullOrUndefined(arg){return arg==null}},{punycode:6,querystring:9}],11:[function(require,module,exports){var $=require("jquery");function toggleDropdown(e){var $dropdown=$(e.currentTarget).parent().find(".dropdown-menu");$dropdown.toggleClass("open");e.stopPropagation();e.preventDefault()}function closeDropdown(e){$(".dropdown-menu").removeClass("open")}function init(){$(document).on("click",".toggle-dropdown",toggleDropdown);$(document).on("click",".dropdown-menu",function(e){e.stopPropagation()});$(document).on("click",closeDropdown)}module.exports={init:init}},{jquery:1}],12:[function(require,module,exports){var $=require("jquery");module.exports=$({})},{jquery:1}],13:[function(require,module,exports){var $=require("jquery");var _=require("lodash");var storage=require("./storage");var dropdown=require("./dropdown");var events=require("./events");var state=require("./state");var keyboard=require("./keyboard");var navigation=require("./navigation");var sidebar=require("./sidebar");var toolbar=require("./toolbar");function start(config){sidebar.init();keyboard.init();dropdown.init();navigation.init();toolbar.createButton({index:0,icon:"fa fa-align-justify",label:"Toggle Sidebar",onClick:function(e){e.preventDefault();sidebar.toggle()}});events.trigger("start",config);navigation.notify()}var gitbook={start:start,events:events,state:state,toolbar:toolbar,sidebar:sidebar,storage:storage,keyboard:keyboard};var MODULES={gitbook:gitbook,jquery:$,lodash:_};window.gitbook=gitbook;window.$=$;window.jQuery=$;gitbook.require=function(mods,fn){mods=_.map(mods,function(mod){mod=mod.toLowerCase();if(!MODULES[mod]){throw new Error("GitBook module "+mod+" doesn't exist")}return MODULES[mod]});fn.apply(null,mods)};module.exports={}},{"./dropdown":11,"./events":12,"./keyboard":14,"./navigation":16,"./sidebar":18,"./state":19,"./storage":20,"./toolbar":21,jquery:1,lodash:2}],14:[function(require,module,exports){var Mousetrap=require("mousetrap");var navigation=require("./navigation");var sidebar=require("./sidebar");function bindShortcut(keys,fn){Mousetrap.bind(keys,function(e){fn();return false})}function init(){bindShortcut(["right"],function(e){navigation.goNext()});bindShortcut(["left"],function(e){navigation.goPrev()});bindShortcut(["s"],function(e){sidebar.toggle()})}module.exports={init:init,bind:bindShortcut}},{"./navigation":16,"./sidebar":18,mousetrap:3}],15:[function(require,module,exports){var state=require("./state");function showLoading(p){state.$book.addClass("is-loading");p.always(function(){state.$book.removeClass("is-loading")});return p}module.exports={show:showLoading}},{"./state":19}],16:[function(require,module,exports){var $=require("jquery");var url=require("url");var events=require("./events");var state=require("./state");var loading=require("./loading");var usePushState=typeof history.pushState!=="undefined";function handleNavigation(relativeUrl,push){var uri=url.resolve(window.location.pathname,relativeUrl);notifyPageChange();location.href=relativeUrl;return}function updateNavigationPosition(){var bodyInnerWidth,pageWrapperWidth;bodyInnerWidth=parseInt($(".body-inner").css("width"),10);pageWrapperWidth=parseInt($(".page-wrapper").css("width"),10);$(".navigation-next").css("margin-right",bodyInnerWidth-pageWrapperWidth+"px")}function notifyPageChange(){events.trigger("page.change")}function preparePage(notify){var $bookBody=$(".book-body");var $bookInner=$bookBody.find(".body-inner");var $pageWrapper=$bookInner.find(".page-wrapper");updateNavigationPosition();$bookInner.scrollTop(0);$bookBody.scrollTop(0);if(notify!==false)notifyPageChange()}function isLeftClickEvent(e){return e.button===0}function isModifiedEvent(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function handlePagination(e){if(isModifiedEvent(e)||!isLeftClickEvent(e)){return}e.stopPropagation();e.preventDefault();var url=$(this).attr("href");if(url)handleNavigation(url,true)}function goNext(){var url=$(".navigation-next").attr("href");if(url)handleNavigation(url,true)}function goPrev(){var url=$(".navigation-prev").attr("href");if(url)handleNavigation(url,true)}function init(){$.ajaxSetup({});if(location.protocol!=="file:"){history.replaceState({path:window.location.href},"")}window.onpopstate=function(event){if(event.state===null){return}return handleNavigation(event.state.path,false)};$(document).on("click",".navigation-prev",handlePagination);$(document).on("click",".navigation-next",handlePagination);$(document).on("click",".summary [data-path] a",handlePagination);$(window).resize(updateNavigationPosition);preparePage(false)}module.exports={init:init,goNext:goNext,goPrev:goPrev,notify:notifyPageChange}},{"./events":12,"./loading":15,"./state":19,jquery:1,url:10}],17:[function(require,module,exports){module.exports={isMobile:function(){return document.body.clientWidth<=600}}},{}],18:[function(require,module,exports){var $=require("jquery");var _=require("lodash");var storage=require("./storage");var platform=require("./platform");var state=require("./state");function toggleSidebar(_state,animation){if(state!=null&&isOpen()==_state)return;if(animation==null)animation=true;state.$book.toggleClass("without-animation",!animation);state.$book.toggleClass("with-summary",_state);storage.set("sidebar",isOpen())}function isOpen(){return state.$book.hasClass("with-summary")}function init(){if(platform.isMobile()){toggleSidebar(false,false)}else{toggleSidebar(storage.get("sidebar",true),false)}$(document).on("click",".book-summary li.chapter a",function(e){if(platform.isMobile())toggleSidebar(false,false)})}function filterSummary(paths){var $summary=$(".book-summary");$summary.find("li").each(function(){var path=$(this).data("path");var st=paths==null||_.contains(paths,path);$(this).toggle(st);if(st)$(this).parents("li").show()})}module.exports={init:init,isOpen:isOpen,toggle:toggleSidebar,filter:filterSummary}},{"./platform":17,"./state":19,"./storage":20,jquery:1,lodash:2}],19:[function(require,module,exports){var $=require("jquery");var url=require("url");var path=require("path");var state={};state.update=function(dom){var $book=$(dom.find(".book"));state.$book=$book;state.level=$book.data("level");state.basePath=$book.data("basepath");state.innerLanguage=$book.data("innerlanguage");state.revision=$book.data("revision");state.filepath=$book.data("filepath");state.chapterTitle=$book.data("chapter-title");state.root=url.resolve(location.protocol+"//"+location.host,path.dirname(path.resolve(location.pathname.replace(/\/$/,"/index.html"),state.basePath))).replace(/\/?$/,"/");state.bookRoot=state.innerLanguage?url.resolve(state.root,".."):state.root};state.update($);module.exports=state},{jquery:1,path:4,url:10}],20:[function(require,module,exports){var baseKey="";module.exports={setBaseKey:function(key){baseKey=key},set:function(key,value){key=baseKey+":"+key;try{sessionStorage[key]=JSON.stringify(value)}catch(e){}},get:function(key,def){key=baseKey+":"+key;if(sessionStorage[key]===undefined)return def;try{var v=JSON.parse(sessionStorage[key]);return v==null?def:v}catch(err){return sessionStorage[key]||def}},remove:function(key){key=baseKey+":"+key;sessionStorage.removeItem(key)}}},{}],21:[function(require,module,exports){var $=require("jquery");var _=require("lodash");var events=require("./events");var buttons=[];function insertAt(parent,selector,index,element){var lastIndex=parent.children(selector).length;if(index<0){index=Math.max(0,lastIndex+1+index)}parent.append(element);if(index",{class:"dropdown-menu",html:''});if(_.isString(dropdown)){$menu.append(dropdown)}else{var groups=_.map(dropdown,function(group){if(_.isArray(group))return group;else return[group]});_.each(groups,function(group){var $group=$("
",{class:"buttons"});var sizeClass="size-"+group.length;_.each(group,function(btn){btn=_.defaults(btn||{},{text:"",className:"",onClick:defaultOnClick});var $btn=$("'; + var clipboard; + + gitbook.events.bind("page.change", function() { + + if (!ClipboardJS.isSupported()) return; + + // the page.change event is thrown twice: before and after the page changes + if (clipboard) { + // clipboard is already defined but we are on the same page + if (clipboard._prevPage === window.location.pathname) return; + // clipboard is already defined and url path change + // we can deduct that we are before page changes + clipboard.destroy(); // destroy the previous events listeners + clipboard = undefined; // reset the clipboard object + return; + } + + $(copyButton).prependTo("div.sourceCode"); + + clipboard = new ClipboardJS(".copy-to-clipboard-button", { + text: function(trigger) { + return trigger.parentNode.textContent; + } + }); + + clipboard._prevPage = window.location.pathname + + }); + +}); diff --git a/docs/_book/libs/gitbook-2.6.7/js/plugin-fontsettings.js b/docs/_book/libs/gitbook-2.6.7/js/plugin-fontsettings.js new file mode 100644 index 0000000..a70f0fb --- /dev/null +++ b/docs/_book/libs/gitbook-2.6.7/js/plugin-fontsettings.js @@ -0,0 +1,152 @@ +gitbook.require(["gitbook", "lodash", "jQuery"], function(gitbook, _, $) { + var fontState; + + var THEMES = { + "white": 0, + "sepia": 1, + "night": 2 + }; + + var FAMILY = { + "serif": 0, + "sans": 1 + }; + + // Save current font settings + function saveFontSettings() { + gitbook.storage.set("fontState", fontState); + update(); + } + + // Increase font size + function enlargeFontSize(e) { + e.preventDefault(); + if (fontState.size >= 4) return; + + fontState.size++; + saveFontSettings(); + }; + + // Decrease font size + function reduceFontSize(e) { + e.preventDefault(); + if (fontState.size <= 0) return; + + fontState.size--; + saveFontSettings(); + }; + + // Change font family + function changeFontFamily(index, e) { + e.preventDefault(); + + fontState.family = index; + saveFontSettings(); + }; + + // Change type of color + function changeColorTheme(index, e) { + e.preventDefault(); + + var $book = $(".book"); + + if (fontState.theme !== 0) + $book.removeClass("color-theme-"+fontState.theme); + + fontState.theme = index; + if (fontState.theme !== 0) + $book.addClass("color-theme-"+fontState.theme); + + saveFontSettings(); + }; + + function update() { + var $book = gitbook.state.$book; + + $(".font-settings .font-family-list li").removeClass("active"); + $(".font-settings .font-family-list li:nth-child("+(fontState.family+1)+")").addClass("active"); + + $book[0].className = $book[0].className.replace(/\bfont-\S+/g, ''); + $book.addClass("font-size-"+fontState.size); + $book.addClass("font-family-"+fontState.family); + + if(fontState.theme !== 0) { + $book[0].className = $book[0].className.replace(/\bcolor-theme-\S+/g, ''); + $book.addClass("color-theme-"+fontState.theme); + } + }; + + function init(config) { + var $bookBody, $book; + + //Find DOM elements. + $book = gitbook.state.$book; + $bookBody = $book.find(".book-body"); + + // Instantiate font state object + fontState = gitbook.storage.get("fontState", { + size: config.size || 2, + family: FAMILY[config.family || "sans"], + theme: THEMES[config.theme || "white"] + }); + + update(); + }; + + + gitbook.events.bind("start", function(e, config) { + var opts = config.fontsettings; + if (!opts) return; + + // Create buttons in toolbar + gitbook.toolbar.createButton({ + icon: 'fa fa-font', + label: 'Font Settings', + className: 'font-settings', + dropdown: [ + [ + { + text: 'A', + className: 'font-reduce', + onClick: reduceFontSize + }, + { + text: 'A', + className: 'font-enlarge', + onClick: enlargeFontSize + } + ], + [ + { + text: 'Serif', + onClick: _.partial(changeFontFamily, 0) + }, + { + text: 'Sans', + onClick: _.partial(changeFontFamily, 1) + } + ], + [ + { + text: 'White', + onClick: _.partial(changeColorTheme, 0) + }, + { + text: 'Sepia', + onClick: _.partial(changeColorTheme, 1) + }, + { + text: 'Night', + onClick: _.partial(changeColorTheme, 2) + } + ] + ] + }); + + + // Init current settings + init(opts); + }); +}); + + diff --git a/docs/_book/libs/gitbook-2.6.7/js/plugin-search.js b/docs/_book/libs/gitbook-2.6.7/js/plugin-search.js new file mode 100644 index 0000000..747fcce --- /dev/null +++ b/docs/_book/libs/gitbook-2.6.7/js/plugin-search.js @@ -0,0 +1,270 @@ +gitbook.require(["gitbook", "lodash", "jQuery"], function(gitbook, _, $) { + var index = null; + var fuse = null; + var _search = {engine: 'lunr', opts: {}}; + var $searchInput, $searchLabel, $searchForm; + var $highlighted = [], hi, hiOpts = { className: 'search-highlight' }; + var collapse = false, toc_visible = []; + + function init(config) { + // Instantiate search settings + _search = gitbook.storage.get("search", { + engine: config.search.engine || 'lunr', + opts: config.search.options || {}, + }); + }; + + // Save current search settings + function saveSearchSettings() { + gitbook.storage.set("search", _search); + } + + // Use a specific index + function loadIndex(data) { + // [Yihui] In bookdown, I use a character matrix to store the chapter + // content, and the index is dynamically built on the client side. + // Gitbook prebuilds the index data instead: https://github.com/GitbookIO/plugin-search + // We can certainly do that via R packages V8 and jsonlite, but let's + // see how slow it really is before improving it. On the other hand, + // lunr cannot handle non-English text very well, e.g. the default + // tokenizer cannot deal with Chinese text, so we may want to replace + // lunr with a dumb simple text matching approach. + if (_search.engine === 'lunr') { + index = lunr(function () { + this.ref('url'); + this.field('title', { boost: 10 }); + this.field('body'); + }); + data.map(function(item) { + index.add({ + url: item[0], + title: item[1], + body: item[2] + }); + }); + return; + } + fuse = new Fuse(data.map((_data => { + return { + url: _data[0], + title: _data[1], + body: _data[2] + }; + })), Object.assign( + { + includeScore: true, + threshold: 0.1, + ignoreLocation: true, + keys: ["title", "body"] + }, + _search.opts + )); + } + + // Fetch the search index + function fetchIndex() { + return $.getJSON(gitbook.state.basePath+"/search_index.json") + .then(loadIndex); // [Yihui] we need to use this object later + } + + // Search for a term and return results + function search(q) { + let results = []; + switch (_search.engine) { + case 'fuse': + if (!fuse) return; + results = fuse.search(q).map(function(result) { + var parts = result.item.url.split('#'); + return { + path: parts[0], + hash: parts[1] + }; + }); + break; + case 'lunr': + default: + if (!index) return; + results = _.chain(index.search(q)).map(function(result) { + var parts = result.ref.split("#"); + return { + path: parts[0], + hash: parts[1] + }; + }) + .value(); + } + + // [Yihui] Highlight the search keyword on current page + $highlighted = $('.page-inner') + .unhighlight(hiOpts).highlight(q, hiOpts).find('span.search-highlight'); + scrollToHighlighted(0); + + return results; + } + + // [Yihui] Scroll the chapter body to the i-th highlighted string + function scrollToHighlighted(d) { + var n = $highlighted.length; + hi = hi === undefined ? 0 : hi + d; + // navignate to the previous/next page in the search results if reached the top/bottom + var b = hi < 0; + if (d !== 0 && (b || hi >= n)) { + var path = currentPath(), n2 = toc_visible.length; + if (n2 === 0) return; + for (var i = b ? 0 : n2; (b && i < n2) || (!b && i >= 0); i += b ? 1 : -1) { + if (toc_visible.eq(i).data('path') === path) break; + } + i += b ? -1 : 1; + if (i < 0) i = n2 - 1; + if (i >= n2) i = 0; + var lnk = toc_visible.eq(i).find('a[href$=".html"]'); + if (lnk.length) lnk[0].click(); + return; + } + if (n === 0) return; + var $p = $highlighted.eq(hi); + $p[0].scrollIntoView(); + $highlighted.css('background-color', ''); + // an orange background color on the current item and removed later + $p.css('background-color', 'orange'); + setTimeout(function() { + $p.css('background-color', ''); + }, 2000); + } + + function currentPath() { + var href = window.location.pathname; + href = href.substr(href.lastIndexOf('/') + 1); + return href === '' ? 'index.html' : href; + } + + // Create search form + function createForm(value) { + if ($searchForm) $searchForm.remove(); + if ($searchLabel) $searchLabel.remove(); + if ($searchInput) $searchInput.remove(); + + $searchForm = $('
', { + 'class': 'book-search', + 'role': 'search' + }); + + $searchLabel = $('",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0 + + + + + + Chapter 12 Moduły w aplikacjach shiny | Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+
+ + +
+
+ +
+
+

Chapter 12 Moduły w aplikacjach shiny

+
+

12.1 Czym jest moduł Shiny

+

Modułem Shiny nazywamy odrębny kawałek aplikacji Shiny. Moduł nie może być wywołany niezależnie od reszty aplikacji. Traktuje się go jako część większej aplikacji lub większego modułu Shiny (moduł może składać się z modułów).

+
+

12.1.1 Dlaczego warto używać modułów Shiny?

+
    +
  • Uproszczenie kodu - moduły pozwalają nam na uporządkowanie złożonego kodu w przypadku dużych i skomplikowanych aplikacji

  • +
  • Własna przestrzeń nazw - w aplikacjach shiny ID obiektów z inputów i outputów pochodzą ze wspólnej przestrzeni nazw. To znaczy, że ID każdego z obiektów w całej aplikacji musi być unikalne. Jako że moduł jest osobną funkcją wywołaną w aplikacji, posiada własną przestrzeń nazw. Wystarczy zatem, że ID obiektów są unikalne wewnątrz modułu.

  • +
  • Recykling - ponieważ moduł Shiny jest niezależną funkcją, może być użyty zarówno wiele razy w jednej aplikacji, jak i w wielu różnych aplikacjach. Dzięki temu można z łatwością przechowywać gotowe fragmenty aplikacji w eRowych pakietach i wykorzystywać je w razie potrzeby.

  • +
+
+
+
+

12.2 Budowa modułu Shiny.

+
    +
  • kawałek UI - funkcja odpowiadająca za User Interface w module Shiny

  • +
  • kawałek serwera - funkcja zawierająca fragment serwera, który jest wykorzystywany w UI

  • +
+
+

12.2.1 Jak używać modułów Shiny?

+

Rozważmy aplikację składającą się z dwóch paneli - każdy z wykresem i danymi dla dwóch rozkładów, otrzymaną za pomocą poniższego kodu:

+
library(shiny)
+library(ggplot2)
+
+ui <- fluidPage(
+  tabsetPanel(
+    #generujemy panel dla rozkładu normalnego
+    tabPanel(title = "Rozkład normalny",
+             tabsetPanel(
+               tabPanel(
+                 title = "Wykres",
+                 numericInput(inputId = "normal_n",
+                              label = "Podaj wielkość próby", 
+                              value = 1000),
+                 plotOutput("normal_plot")
+               ),
+               tabPanel(
+                 title = "Dane",
+                 tableOutput("normal_data")
+               )
+             )
+    ),
+    #generujemy panel dla rozkładu wykładniczego
+    tabPanel(title = "Rozkład wykładniczy",
+             tabsetPanel(
+               tabPanel(
+                 title = "wykres",
+                 numericInput(inputId = "exp_n",
+                              label = "Podaj wielkość próby", 
+                              value = 1000),
+                 plotOutput("exp_plot")
+               ),
+               tabPanel(
+                 title = "Dane",
+                 tableOutput("exp_data")
+               )
+             )
+    )
+  )
+)
+
+server <- function(input, output, session) {
+  #generujemy dane
+  normal_data <- reactive({
+    set.seed(17)
+    data.frame(id = 1:input[["normal_n"]],
+               sample = rnorm(input[["normal_n"]]))
+  })
+  exp_data <- reactive({
+    set.seed(17)
+    data.frame(id = 1:input[["exp_n"]],
+               sample = rnorm(input[["exp_n"]]))
+  })
+  #generujemy tabele
+  output[["normal_data"]] <- renderTable({
+    normal_data()
+  })
+  output[["exp_data"]] <- renderTable({
+    exp_data()
+  })
+  #generuemy wykresy
+  output[["normal_plot"]] <- renderPlot({
+    ggplot(normal_data(), aes(x = sample)) +
+      geom_density()
+  })
+  output[["exp_plot"]] <- renderPlot({
+    ggplot(exp_data(), aes(x = sample)) +
+      geom_density() +
+      xlim(0, 5)
+  })
+}
+shinyApp(ui, server)
+

Aplikacja wygląda następująco:

+

W naszej przestrzeni wykorzystaliśmy nazwy:

+
    +
  • inputy - normal_n, exp_n

  • +
  • outputy - normal_plot, normal_data, exp_plot, exp_data

  • +
+

Co daje razem 6 obiektów. W aplikacji UI zajmuje 36 linijek kodu, a server 29, razem 65 linijek.

+

Zrefaktoryzuemy kod powyższej aplikacji przy użyciu modułów Shiny. Za powtarzające się elementy (tj. panele z wykresem i danymi) będą odpowiedzialne następujące funkcje module_UI oraz module_SERVER (odpowiedniki UI oraz servera dla odrębnego fragmentu aplikacji).

+
module_UI <- function(id) {
+  ns <- NS(id)
+  tagList(
+    tabsetPanel(
+      tabPanel(
+        title = "Wykres",
+        numericInput(inputId = ns("n"),
+                     label = "Podaj wielkość próby", 
+                     value = 1000),
+        plotOutput(ns("plot"))
+      ),
+      tabPanel(title = "Dane",
+               tableOutput(outputId = ns("data"))
+      )
+    )
+  )
+}
+

Na szczególną uwagę w powyższym kodzie zasługuje linijka

+
ns <- NS(id)
+

Za pomocą funkcji NS() tworzymy osobną przestrzeń nazw ID.

+
module_SERVER <- function(id) {
+  moduleServer(id, function(input, output, session) {
+    #generujemy dane
+    data <- reactive({
+      set.seed(17)
+      data.frame(id = 1:input[["n"]],
+                 sample = rnorm(input[["n"]]))
+    })
+    #generujemy wykres
+    output[["plot"]] <- renderPlot({
+      ggplot(data(), aes(x = sample)) +
+        geom_density()
+    })
+    #generujemy tabelę
+    output[["data"]] <- renderTable({
+      data()
+    })
+  })
+}
+

Ostatecznie nasza aplikacja używająca pomocniczego modułu wygląda następująco

+
library(shiny)
+
+ui <- fluidPage(
+  titlePanel("Przykładowe ciągłe rozkłady prawdopodobieństwa"),
+  tabsetPanel(
+    #generujemy panel dla rozkładu normalnego
+    tabPanel(title = "Rozkład normalny",
+             module_UI("norm") 
+    ),
+    #generujemy panel dla rozkładu wykładniczego
+    tabPanel(title = "Rozkład wykładniczy",
+             module_UI("exp") 
+    )
+  )
+)
+
+server <- function(input, output, session) {
+  module_SERVER("norm")
+  module_SERVER("exp")
+}
+
+shinyApp(ui, server)
+

Powyższy kod jest czytelniejszy, krótszy, a także rozwiązuje problem wielu zmiennych.

+ +
+
+
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + diff --git a/docs/_book/operacje-na-napisach-i-datach.html b/docs/_book/operacje-na-napisach-i-datach.html new file mode 100644 index 0000000..d8bfabd --- /dev/null +++ b/docs/_book/operacje-na-napisach-i-datach.html @@ -0,0 +1,412 @@ + + + + + + + Chapter 9 Operacje na napisach i datach | Dane w formacie wąskim i szerokim. Łączenie tabel danych (join). Operacje na napisach i datach. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+ + +
+
+ +
+
+

Chapter 9 Operacje na napisach i datach

+

Większość poniższych funkcji pochodzi z pakietu stringi.

+
+

9.1 Operacje na napisach

+
    +
  1. Wyznaczanie długości napisów. +Funkcja stri_lenght() zwraca długości poszczególnych napisów w danym wektorze, a stri_isempty() sprawdza, które napisy są puste -> ’’.

  2. +
  3. Łączenie i powielanie napisów. +Funkcja używana do łączenia kilku wektorów napisów w inny wektor napisów lub nawet w jeden napis, jest stri_paste() i jej warianty.

  4. +
+

Przykład:

+
  x <- LETTERS[1:3]
+    y <- letters[1:3] 
+    z <- '!'
+    stri_paste(x, y, z)
+
## [1] "Aa!" "Bb!" "Cc!"
+
    +
  1. Przycinanie i wypełnianie.
  2. +
+

Funkcja stri_wrap() wstawia znaki nowego wiersza (n), by napis po wyświetleniu np. przy funkcji cat() miał szerokość nie większą, niż podana, jeżeli to możliwe.

+

W przypadku przetwarzania tekstów pochodzących np. z formularzy na stronach internetowych może zachodzić potrzeba usunięcia tzw. białych znaków, np. spacji z początku lub końca napisu. Możemy to zrobić przy użyciu funkcji stri_trim(). Operacja w pewnym sensie odwrotną do tej można wykonać przy użyciu funkcji stri_pad().

+

Przykład:

+
stri_trim('            Mama i tata\n')
+
## [1] "Mama i tata"
+
    +
  1. Formatowanie napisów na podstawie innych obiektów. +Najprostszym sposobem na uzyskanie napisowej reprezentacji danego obiektu jest użycie funkcji as.character().
  2. +
+

Przykład:

+
as.character(list(1L, mean, NULL, pi, FALSE))
+
## [1] "1"                                       "function (x, ...) \nUseMethod(\"mean\")"
+## [3] "NULL"                                    "3.14159265358979"                       
+## [5] "FALSE"
+
x <-data.frame(a=c(TRUE, FALSE, FALSE), b=as.integer(c(1, 2, 3)))
+as.character(x)
+
## [1] "c(TRUE, FALSE, FALSE)" "1:3"
+
    +
  1. Zmiana pojedynczych znaków. +Zmiana poszczególnych znaków na inne przydaje się między innymi na etapie wstępnego przygotowania danych w celu ujednolicenia tekstowych identyfikatorów obiektów, możemy np. zmieniać wielkości wszystkich liter w napisach.
  2. +
+

Przykład:

+
stri_trans_toupper('chcemy duże litery')
+
## [1] "CHCEMY DUŻE LITERY"
+
stri_trans_tolower('ChCemY MałE LiTErY')
+
## [1] "chcemy małe litery"
+
stri_trans_char('zastępowanie znaków', 'ąćęłńóśżź', 'acelnoszz')
+
## [1] "zastepowanie znakow"
+
stri_trans_general('żółć', 'Latin-ASCII')
+
## [1] "zolc"
+
    +
  1. Wyznaczanie podnapisów. +Funkcja stri_sub() zwraca podnapis składający się ze znaków leżących na określonych pozycjach danego napisu.
  2. +
+

Przykład:

+
x <- 'Lasy, pola, pastwiska, koszą traktorem'
+        stri_sub(x, 7)  
+
## [1] "pola, pastwiska, koszą traktorem"
+
+
+

9.2 Operacje na datach

+
    +
  1. Funkcją zwracającą aktualną datę systemową jest Sys.Date(), a Sys.time() aktualny czas systemowy wraz z datą.
  2. +
+

Przykład:

+
(data <- Sys.Date())
+
## [1] "2023-10-12"
+
(czas <- Sys.time())
+
## [1] "2023-10-12 00:09:02 CEST"
+
    +
  1. Operacje arytmetyczne na datach – dodawanie, odejmowanie i porównywanie.
  2. +
+

Przykład:

+
data
+
## [1] "2023-10-12"
+
data-365
+
## [1] "2022-10-12"
+
data+365
+
## [1] "2024-10-11"
+
(d <- data-as.Date('2021-01-01'))
+
## Time difference of 1014 days
+
    +
  1. Do konwersji do napisu może służyć przeciążona wersja metody format(), której wywołanie jest tożsame z wywołaniem funkcji strftime() (ang. string-format-time).
  2. +
+

Przykład:

+
strftime(czas, '%Y-%m-%d %H:%M:%S %Z')  
+
## [1] "2023-10-12 00:09:02 CEST"
+
    +
  1. Do znajdowania “najstarszej” i “najmłodszej” daty używamy funkcji max() oraz min().

  2. +
  3. Do pracy ze strefami czasowymi możemy używać poniższych funkcji:

  4. +
+
    +
  1. force_tz() ustawienie strefy czasowej,
  2. +
  3. with_tz() sprawdzenie daty w innej strefie czasowej.
  4. +
+ +
+
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + diff --git a/docs/_book/podstawowe-typy-danych.html b/docs/_book/podstawowe-typy-danych.html new file mode 100644 index 0000000..f0bde0b --- /dev/null +++ b/docs/_book/podstawowe-typy-danych.html @@ -0,0 +1,872 @@ + + + + + + + Chapter 2 Podstawowe typy danych | Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+ + +
+
+ +
+
+

Chapter 2 Podstawowe typy danych

+

Język R posiada kilka typów danych, które pokrótce postaramy sie omówić poniżej. Pokażemy ich budowe jak i operacje na nich, przytaczając stosowne przyklady.

+
+

2.1 Liczby

+

Liczby całkowite i rzeczywiste (tutaj separator dziesiętny to kropka). Możemy używać również notacji naukowej. Operacje na liczbach to podstawowe działania matematyczne jak i trochę rozszerzone, ukazane niżej wraz z specjalnymi liczbami.

+
5; 5.5; 5.5e-2; 
+
## [1] 5
+
## [1] 5.5
+
## [1] 0.055
+

Tutaj liczby specjalne,

+
NaN # not a number
+
## [1] NaN
+
Inf # nieskończoność
+
## [1] Inf
+
-Inf # - nieskończoność
+
## [1] -Inf
+

oraz kilka działań na liczbach

+
1 + 1 # podobnie '-' to odejmowanie
+
## [1] 2
+
4/2 # dzielenie, a '*' to mnożenie
+
## [1] 2
+
5 %/% 3 # dzielenie całkowite
+
## [1] 1
+
5 %% 3 # reszta z dzielenia
+
## [1] 2
+
2^3 # potęgowanie
+
## [1] 8
+
2**3 # też potęgowanie
+
## [1] 8
+
sqrt(4) #pierwiastkowanie
+
## [1] 2
+
abs(-1) # wartość bezwzględna
+
## [1] 1
+
+
+

2.2 Łańcuchy znaków

+

Łańcuch znaków to po prostu napi. Napis jest otoczony przez ” lub ’. W napisie możemy umieszczać dowolne znaki, pamiętając że są też znaki specjalne (rozpoczynające się od \ i mające specjalne funkcje). Na napisach istnieje wiele operacji (np. \(\verb+paste()+,\) czyli sklejenie dwóch napisów), lecz je zobaczymy w notatce o napisach.

+
"napis"
+
## [1] "napis"
+
'to też'
+
## [1] "to też"
+
"'a tutaj nawet z bonusem'"
+
## [1] "'a tutaj nawet z bonusem'"
+
# ""a"" to już wbrew intuicji nie jest napis
+cat("i znak \n specjalny, wstawiający nową linie") # cat() wyświetla napis w sposób niesformatowany
+
## i znak 
+##  specjalny, wstawiający nową linie
+
+
+

2.3 Wartości logiczne

+

Logiczna Prawda (\(\verb+TRUE+\) lub \(\verb+T+\)) oraz logiczny Fałsz (\(\verb+FALSE+\) lub \(\verb+F+\)). Na tych obiektach możemy wykonywać operacje logiczne oraz algebraiczne.

+
TRUE & TRUE # operator 'i'
+
## [1] TRUE
+
TRUE | FALSE # operator 'lub'
+
## [1] TRUE
+
1 == 1 # testowanie równości
+
## [1] TRUE
+
1 != 2 # testowanie nierówności
+
## [1] TRUE
+
2*TRUE # TRUE ma wartość 1
+
## [1] 2
+
2*FALSE # FALSE ma wartość 0
+
## [1] 0
+
T ; `T` <- FALSE; T # używając `` możemy zmienić wartość logiczną wyrażenia
+
## [1] FALSE
+
## [1] FALSE
+
+
+

2.4 Wektory

+

Wektor to w R uporządkowany zbiór elementów. Elementy te muszą mieć ten sam typ, także jeśli do wektora trafią elementy z różnym typem (poza NA), to nastąpi konwersja elementów do jednego typu. Proste wektory tworzymy przez polecenie \(\verb+c()+\) i elementy wypisujemy w nawiasie po przecinku. Dodatkowo, element wektora jest traktowany jako jednoelementowy wektor. Wektory liczbowe jak i inne możemy tworzyć za pomocą wbudowanych funkcji do tego przeznaczonych.

+
v <- c(1, 2, 3) #przypisanie wektora do zmiennej
+0:10 # wektor liczbowy
+
##  [1]  0  1  2  3  4  5  6  7  8  9 10
+
seq(from = 0, to = 10, by = 1) # to samo, ale za pomocą seq(), czyli sequance
+
##  [1]  0  1  2  3  4  5  6  7  8  9 10
+
seq(0, 1, length.out = 4) # równe odstępy w 4 liczbowym wektorze
+
## [1] 0.0000000 0.3333333 0.6666667 1.0000000
+
length(v) # zwraca długość vectora
+
## [1] 3
+
# vector(mode, lenght) tworzy wektor dlugosci lenght, a wyrazy tego wektora maja klase mode
+vector("integer", 10) # wektor liczb calkowitych
+
##  [1] 0 0 0 0 0 0 0 0 0 0
+
vector("numeric", 10) # wektor liczb rzeczywistych
+
##  [1] 0 0 0 0 0 0 0 0 0 0
+
vector("character", 10) # wektor slów
+
##  [1] "" "" "" "" "" "" "" "" "" ""
+
rep(v, each = 2) # każdy element v zostanie powtórzony 2 razy
+
## [1] 1 1 2 2 3 3
+
rep(v, times = 2) # v zostanie powtórzony 2 razy
+
## [1] 1 2 3 1 2 3
+
# mały mix tj. tutaj element v traktujemy jako wektor jednoelementowy
+# i powtarzamy times razy
+rep(v, times = 1:3) 
+
## [1] 1 2 2 3 3 3
+
x <- c("a", "A") # wektor napisowy
+v <- "a" # to też
+toupper(x) # zmieni stringi w argumencie na wielkie litery
+
## [1] "A" "A"
+
tolower(x) # zmieni stringi w argumencie na male litery
+
## [1] "a" "a"
+
+
+

2.5 Indeksowanie

+

W R wektory są indeksowane od 1 (a nie od 0 jak w wielu językach programowania!). Aby odwołać się do konkretnego elementu wektora korzystamy z nawiasów kwadratowych \(\verb+[]+.\)

+
letters[3]
+
## [1] "c"
+

Można wybrać więcej niż jeden element, wpisując w nawiasach kwadratowych wektor indeksów.

+
letters[1:10]
+
##  [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j"
+
letters[c(1, 15)]
+
## [1] "a" "o"
+
letters[seq(1, 20, by = 2)]
+
##  [1] "a" "c" "e" "g" "i" "k" "m" "o" "q" "s"
+

Jeśli przed wektorem indeksów widnieje znak minus, R zwróci wszystkie elementy wektora z wyjątkiem tych w nawiasie kwadratowym.

+
letters[-(1:10)] # niezbędny nawias wokół 1:10
+
##  [1] "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
+
letters[-c(1, 15)]
+
##  [1] "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
+
letters[-seq(1, 20, by = 2)]
+
##  [1] "b" "d" "f" "h" "j" "l" "n" "p" "r" "t" "u" "v" "w" "x" "y" "z"
+

Pod wybrane indeksy można przypisać nowe wartości.

+
new_letters <- letters
+new_letters[1:5] <- LETTERS[1:5]
+new_letters
+
##  [1] "A" "B" "C" "D" "E" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x"
+## [25] "y" "z"
+

Albo pod każdy wybrany indeks nową wspólną wartość.

+
new_letters[1:5] <- "x"
+new_letters
+
##  [1] "x" "x" "x" "x" "x" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x"
+## [25] "y" "z"
+

Tworząc wektor funkcją \(\verb+c()+,\) możemy nazwać każdy z jego elementów.

+
str_vec_nam <- c("a" = "A", "b" = "B", "c" = "C")
+str_vec_nam
+
##   a   b   c 
+## "A" "B" "C"
+

Może być to użyteczne przy odwoływaniu się do konkretnego elementu wektora, nie trzeba wtedy znać numeru jego indeksu.

+
str_vec_nam["a"]
+
##   a 
+## "A"
+
str_vec_nam[c("a", "c")]
+
##   a   c 
+## "A" "C"
+
str_vec_nam[c("c", "a")]
+
##   c   a 
+## "C" "A"
+

Wektory możemy również indeksować za pomocą wektorów logicznych. Działa to wtedy jak wybieranie tych elementów wektora, które spełniają ustalony warunek.

+
x_ind <- new_letters == "x"
+x_ind
+
##  [1]  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
+## [17] FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE
+
new_letters[x_ind]
+
## [1] "x" "x" "x" "x" "x" "x"
+
seq_vec <- seq(0, 1, length.out = 10)
+seq_vec[seq_vec < 0.5]
+
## [1] 0.0000000 0.1111111 0.2222222 0.3333333 0.4444444
+

Można oczywiście rozbudowywać wyrażenia logiczne, np. następująco:

+
seq_vec[seq_vec < 0.3 | seq_vec > 0.8]
+
## [1] 0.0000000 0.1111111 0.2222222 0.8888889 1.0000000
+
seq_vec[seq_vec > 0.3 & seq_vec < 0.8]
+
## [1] 0.3333333 0.4444444 0.5555556 0.6666667 0.7777778
+
+
+

2.6 Operacje na wektorach

+

W R domyślnym i naturalnym zachowaniem funkcji na wektorach jest działanie element po elemencie

+
1:10 + seq(0, 1, length.out = 10)
+
##  [1]  1.000000  2.111111  3.222222  4.333333  5.444444  6.555556  7.666667  8.777778  9.888889
+## [10] 11.000000
+
c(2,4,6,8)^(1:4)
+
## [1]    2   16  216 4096
+

W przypadku gdy wektory, na których wykonujemy obliczenia mają różne długości zachodzi recykling, tj. R samoistnie przedłuża krótszy wektor replikując go odpowiednią liczbę razy. Widzimy, że obie poniższe linie kodu dają taki sam efekt.

+
1:10 + 1:2
+
##  [1]  2  4  4  6  6  8  8 10 10 12
+
1:10 + rep(1:2, 5)
+
##  [1]  2  4  4  6  6  8  8 10 10 12
+

Gdy długość dłuższego wektora nie jest wielokrotnością krótszego, recykling także zadziała, jednak R zgłosi warning.

+
1:10 + 1:3
+
## Warning in 1:10 + 1:3: długość dłuszego obiektu nie jest wielokrotnością długości krótszego obiektu
+
##  [1]  2  4  6  5  7  9  8 10 12 11
+
1:10 + 1:3 + 1:2 + 1:5
+
## Warning in 1:10 + 1:3: długość dłuszego obiektu nie jest wielokrotnością długości krótszego obiektu
+
##  [1]  4  8 10 11 13 12 11 15 17 18
+

Na wektorach możemy wykonywać oczywiście inne funkcje poza podstawowymi operacjami arytmetycznymi. Jedną z opcji jest posortowanie wektora.

+
num_vec <- c(3,6,1,9,8,-3,0,102,-5)
+sort(num_vec) # sortowanie rosnące
+
## [1]  -5  -3   0   1   3   6   8   9 102
+
sort(num_vec, decreasing = TRUE) # sortowanie malejące
+
## [1] 102   9   8   6   3   1   0  -3  -5
+

Odwrócić kolejnośc elementów wektora można następująco.

+
rev(num_vec)
+
## [1]  -5 102   0  -3   8   9   1   6   3
+

Oto kilka kolejnych funkcji.

+
sum(num_vec) # suma elementów wektora
+
## [1] 121
+
prod(num_vec) # iloczyn elementów wektora
+
## [1] 0
+
mean(num_vec) # średnia elementów wektora
+
## [1] 13.44444
+

Przy operacjach jak powyższe należy jednak uważać na wektory zawierające “NA”.

+
vec_with_NA <- c(3,6,1,NA)
+sum(vec_with_NA)
+
## [1] NA
+

Aby zsumować wartości z pominięciem “NA” należy dopisać dodatkowy argument funkcji.

+
sum(vec_with_NA, na.rm = TRUE)
+
## [1] 10
+

Analogicznie dla iloczynu i średniej elementów.

+
prod(vec_with_NA)
+
## [1] NA
+
prod(vec_with_NA, na.rm = TRUE)
+
## [1] 18
+
mean(vec_with_NA)
+
## [1] NA
+
mean(vec_with_NA, na.rm = TRUE)
+
## [1] 3.333333
+Lista jest podobna do wektora tj. jest pewnym ciągiem obiektów, tyle że jej elementy mogą mieć różne typy. +
l <- list(1:5)
+#lista z elementami bedacymi liczbami
+
## [[1]]
+## [1] 1 2 3 4 5
+
l2 <- list(zwierze='dog', imie='Max',czyLubiInnePsy = TRUE)
+#lista z elementami bedacymi stringami lub wartosciami logicznymi
+
## $zwierze
+## [1] "dog"
+## 
+## $imie
+## [1] "Max"
+## 
+## $czyLubiInnePsy
+## [1] TRUE
+

Kolejnę różnica pomiedzy wektorem a listą jest możliwość odwoływania się do elementów listy za pomocą nazwy tego elementu i operatora $. Np:

+
# odwolanie do elementu bedacego za pomoca [], 
+# wynikiem takiej operacji jest lista zawierajaca wektor
+l[1]
+
## [[1]]
+## [1] 1 2 3 4 5
+
# aby odwolac sie do konkretnego elementu uzwywamy [[]], na przyklad operacja l[[1]][2] 
+# zwroci drugi element wektora z listy
+ l[[1]][2]
+
## [1] 2
+
# nadpisywanie elementu listy wektorem
+l[[1]] <- c("a", "b", "c")
+# odwolanie do elementu za pomoca nazwy elementu
+l2$zwierze
+
## [1] "dog"
+
l2$imie
+
## [1] "Max"
+
l2$czyLubiInnePsy
+
## [1] TRUE
+

Listy można łączyć oraz modyfikować. Funkcja \(\verb+lapply()+\) to funkcja, która pozwala na wykonanie pewnego konkretnego działania na KAŻDYM elemencie z listy. Na przykład, możemy każdy element chcieć zapisać tylko dużymi literami:

+
lapply(l2,toupper)
+
## $zwierze
+## [1] "DOG"
+## 
+## $imie
+## [1] "MAX"
+## 
+## $czyLubiInnePsy
+## [1] "TRUE"
+

Aby połączyć dwie listy, należy użyć \(\verb+c()+,\) robiąc z dwóch list wektor i przypisując go do nowej zmiennej.

+
l3 <- c(l,l2)
+
## [[1]]
+## [1] "a" "b" "c"
+## 
+## $zwierze
+## [1] "dog"
+## 
+## $imie
+## [1] "Max"
+## 
+## $czyLubiInnePsy
+## [1] TRUE
+

Macierz to obiekt dwuwymiarowy. Składa się z elementów tego samego typu. Tworzy się ją funkcją \(\verb+matrix()+,\) do której podajemy wartości macierzy (zwykle w postaci wektora), liczbę wierszy i kolumn.

+
matrix(data = 1:10, nrow = 2, ncol = 5)
+
##      [,1] [,2] [,3] [,4] [,5]
+## [1,]    1    3    5    7    9
+## [2,]    2    4    6    8   10
+

Widzimy, że R domyślnie wypełnia macierz po kolumnach. Aby wypełnić ją po wierszach ustalamy parametr \(\verb+byrow = TRUE+\)

+
m <- matrix(data = 1:10, nrow = 2, ncol = 5, byrow = TRUE)
+m
+
##      [,1] [,2] [,3] [,4] [,5]
+## [1,]    1    2    3    4    5
+## [2,]    6    7    8    9   10
+

Elementy macierzy wybiera się za pomocą dwóch indeksów - indeksu wiersza i indeksu kolumny umieszczonych w nawiasach kwadaratowych i rozdzielonych przecinkiem.

+
m[2,3]
+
## [1] 8
+

Można również wybrać konkretne wiersze lub kolumny.

+
m[1:2,3:4] # wybiera wiersze 1 i 2 oraz kolumny 3 i 4
+
##      [,1] [,2]
+## [1,]    3    4
+## [2,]    8    9
+
m[2,c(1,4,5)] # wybiera wiersz 2 oraz kolumny 1,4 i 5
+
## [1]  6  9 10
+

Nie podanie indeksu przed przecinkiem oznacza, że chcemy otrzymać wszystkie wiersze. Analogicznie nie podanie indeksu po przecinku oznacza, że chcemy otrzymać wszystkie kolumny.

+
m[,c(1,3)] 
+
##      [,1] [,2]
+## [1,]    1    3
+## [2,]    6    8
+
m[2,]
+
## [1]  6  7  8  9 10
+

Macierze, podobnie jak wektory, możemy także indeksować warunkami logicznymi.

+
# zwraca elementy (w tym wypadku element) z pierwszej kolumny,
+# które są większe od 2
+m[m[,1] > 2, 1]  
+
## [1] 6
+

Można także indeksować macierz inną macierzą o dwóch kolumnach. Zwrócone zostaną wtedy elementy o indeksach będących wierszami tej macierzy.

+
matrix_ind<- matrix(c(1, 2, 2, 3, 2, 4), byrow = TRUE, nrow = 3, ncol = 2)
+m[matrix_ind]  
+
## [1] 2 8 9
+

Na macierzach o tych samych wymiarach możemy wykonywać operacje arytmetyczne. Trzeba zwrócić uwagę, że są one wykonywane element po elemencie (z matematycznego punktu widzenia jest to oczekiwane przy dodawaniu, ale nieoczekiwane przy mnożeniu macierzy).

+
m1 <- matrix(1:4,2,2)
+m1
+
##      [,1] [,2]
+## [1,]    1    3
+## [2,]    2    4
+
m2 <- matrix(2:5,2,2)
+m2
+
##      [,1] [,2]
+## [1,]    2    4
+## [2,]    3    5
+
m1 + m2
+
##      [,1] [,2]
+## [1,]    3    7
+## [2,]    5    9
+
m1 * m2
+
##      [,1] [,2]
+## [1,]    2   12
+## [2,]    6   20
+

Aby wykonać matematyczne mnożenie macierzy należy użyć operatora \(\verb+%*%+.\)

+
m1 %*% m2
+
##      [,1] [,2]
+## [1,]   11   19
+## [2,]   16   28
+Jest to obiekt przechowujący dane w postaci tabeli dwuwymiarowej, którą tworzą wektory o dowolnym typie. Z ramki danych można korzystać jak z macierzy dwuwymiarowej (poprzez korzystanie z \(\verb+[,]+\)), jak i z listy (poprzez korzystanie z $). +
imie <- c("Max", "Reksio","Rex","Luna") #utworzymy ramke z 2 wektorow 
+wiek <- c(2,8,3,11)
+ramka <- data.frame(imie,wiek) 
+ #ramke tworzymy za pomoca polecenia data.frame()
+
##     imie wiek
+## 1    Max    2
+## 2 Reksio    8
+## 3    Rex    3
+## 4   Luna   11
+
#wyswietlanie nazw kolumn
+names(ramka)
+
## [1] "imie" "wiek"
+
#odnoszenie sie do elementu znajdujacego sie w 2. rzedzie i 1. kolumnie
+ramka[2,1]
+
## [1] "Reksio"
+
#pobieranie paru wierszy na raz za pomoca wektora
+ramka[c(1, 2), ]
+
##     imie wiek
+## 1    Max    2
+## 2 Reksio    8
+
#pobieranie wszystkich kolumn dla 1. wiersza
+ramka[1,]
+
##   imie wiek
+## 1  Max    2
+
#pobieranie wszystkich wierszy dla 1. kolumny
+ramka[,1]
+
## [1] "Max"    "Reksio" "Rex"    "Luna"
+
# pierwsza kolumna bez drugiego wiersza
+ramka[-2, 1]
+
## [1] "Max"  "Rex"  "Luna"
+
#pobieranie kolumn/wierszy po nazwie
+ramka$wiek
+
## [1]  2  8  3 11
+
# inny sposób indeksowanie po nazwie
+ramka[, "wiek"]
+
## [1]  2  8  3 11
+
    +
  • Indeksowanie na podstawie zawartości ramki danych
    +Dane z ramki mogą być przez nas “filtrowane” za pomocą []. Na przykład
  • +
+
# psy poniżej 9 roku życia
+ramka[ramka$wiek < 9, ]
+
##     imie wiek
+## 1    Max    2
+## 2 Reksio    8
+## 3    Rex    3
+
#dane tylko dla Reksia
+ramka[ramka$imie == "Reksio", ]
+
##     imie wiek
+## 2 Reksio    8
+
# analogicznie dla wektorów
+wiek[wiek < 9]
+
## [1] 2 8 3
+

Tworząc ramkę danych należy pamiętać o tym, aby wektory danych służące za kolumny były tej samej długości.

+
#zamiana nazw kolumn
+names(ramka) <- c("imie_psa", "wiek_psa")
+
##   imie_psa wiek_psa
+## 1      Max        2
+## 2   Reksio        8
+## 3      Rex        3
+## 4     Luna       11
+

Ramki danych możemy powiększać o dodatkowe wiersze i kolumny, ale typy (dla wierszy) i rozmiary muszą sie zgadzać z typami i rozmiarem ramki danych. Rozpatrzmy poniższy przykład, aby pokazać, jak dodać wiersz i kolumnę za pomocą funkcji \(\verb+cbind()+\) oraz \(\verb+rbind()+\).

+
#dodawanie nowego wiersza
+dodajemy_wiersz <- data.frame(imie_psa ="Quentin", wiek_psa=9)
+#funkcja rbind "skleja" wierszowo argument pierwszy (u nas ramka) z drugim
+ramka <- rbind(ramka,dodajemy_wiersz)
+#dodawanie nowej kolumny 
+czyLubiInnePsy <- c(TRUE,TRUE, FALSE, TRUE, FALSE)
+#funkcja cbind "skleja" kolumnowo argument pierwszy (u nas ramka) z drugim
+ramka <- cbind(ramka,czyLubiInnePsy)
+
##   imie_psa wiek_psa czyLubiInnePsy
+## 1      Max        2           TRUE
+## 2   Reksio        8           TRUE
+## 3      Rex        3          FALSE
+## 4     Luna       11           TRUE
+## 5  Quentin        9          FALSE
+

Możemy rownież dodawać wiersze za pomocą indeksowania, to znaczy przypisywania wartości do konkretnych indeksów ramki:

+
#jako 6. wiersz "wkladamy" nowy wektor
+ramka[6,] <- c("Fanta",0.5,TRUE)
+
##   imie_psa wiek_psa czyLubiInnePsy
+## 1      Max        2           TRUE
+## 2   Reksio        8           TRUE
+## 3      Rex        3          FALSE
+## 4     Luna       11           TRUE
+## 5  Quentin        9          FALSE
+## 6    Fanta      0.5           TRUE
+
# jako 4.kolumne "wkladamy" nowy wektor
+ramka[,4] <- c("Mateusz","Romek","Renata","Leon","Quennie","Filip")
+# nazywamy kolumne 4.
+names(ramka)[4] <- "opiekun_psa"
+
##   imie_psa wiek_psa czyLubiInnePsy opiekun_psa
+## 1      Max        2           TRUE     Mateusz
+## 2   Reksio        8           TRUE       Romek
+## 3      Rex        3          FALSE      Renata
+## 4     Luna       11           TRUE        Leon
+## 5  Quentin        9          FALSE     Quennie
+## 6    Fanta      0.5           TRUE       Filip
+

Analizując nową dla nas ramkę danych, użyteczne okazują się funkcje pozwalające na poznanie właściwości ramki danych. Oto pare z nich:

+
# wymiary ramki (6 wierszy,4 kolumny) mozna sprawdzic za pomoca funkcji dim()
+dim(ramka)
+
## [1] 6 4
+
# aby zobaczyc skrocony opis typow danych zawartych w ramce uzywana jest funkcja str()
+str(ramka)
+
## 'data.frame':    6 obs. of  4 variables:
+##  $ imie_psa      : chr  "Max" "Reksio" "Rex" "Luna" ...
+##  $ wiek_psa      : chr  "2" "8" "3" "11" ...
+##  $ czyLubiInnePsy: chr  "TRUE" "TRUE" "FALSE" "TRUE" ...
+##  $ opiekun_psa   : chr  "Mateusz" "Romek" "Renata" "Leon" ...
+
# aby "podejrzec" pierwsze wiersze ramki danych, wraz naglowkami kolumn uzywana jest funkcja head()
+head(ramka)
+
##   imie_psa wiek_psa czyLubiInnePsy opiekun_psa
+## 1      Max        2           TRUE     Mateusz
+## 2   Reksio        8           TRUE       Romek
+## 3      Rex        3          FALSE      Renata
+## 4     Luna       11           TRUE        Leon
+## 5  Quentin        9          FALSE     Quennie
+## 6    Fanta      0.5           TRUE       Filip
+
# wysietlanie pierwszych n wierszy
+head(ramka,n=2)
+
##   imie_psa wiek_psa czyLubiInnePsy opiekun_psa
+## 1      Max        2           TRUE     Mateusz
+## 2   Reksio        8           TRUE       Romek
+
# wyswietlanie ostatnich n wierszy za pomoca funkcji tail()
+tail(ramka,n=2)
+
##   imie_psa wiek_psa czyLubiInnePsy opiekun_psa
+## 5  Quentin        9          FALSE     Quennie
+## 6    Fanta      0.5           TRUE       Filip
+

Pętli oraz instrukcji warunkowych używamy, kiedy chcemy uniknąć powielania kodu i chcemy zachować jego przejrzystość. Ułatwia to wprowadzanie potencjalnych zmian. Instrukcje opisujące co powinno się zdarzyć należy umieścić w nawiasach \(\verb+{ }+\). Jeśli chcemy wykonać tylko jedną linijke kodu, możemy je opuścić.

+

Umożliwia warunkowe wykonanie kawałka kodu - jeśli warunek zawarty w \(\verb+if+\) jest spełniony, to R przejdzie do zawartej instrukcji. W przeciwnym wypadku wykona polecenie zawarte w \(\verb+else+\), a jeśli go nie ma , to przejdzie do kolejnych pętli. Część \(\verb+else+\) nie jest wymagana, w tym wypadku z góry wiadomo ile razy kod zostanie wykonany.

+

Składnia wygląda następująco:

+
if(warunek)
+{
+  instrukcja_1
+}
+

i jest analogiczna do

+
if(warunek) instrukcja_1
+

Możemy także zapisać

+
if(warunek)
+{
+  instrukcja_1
+  instrukcja_2 
+} else
+  {
+    instrukcja_3
+  }
+

Powiedzmy, że rozpatrujemy liczbe z rozkładu normalnego i sprawdzamy jakiego jest znaku.

+
x_norm <- rnorm(1)
+
+if (x_norm < 0)
+{
+  cat("Liczba", x_norm, "jest ujemna")
+} else
+{
+  cat("Liczba ", x_norm, "jest dodatnia")
+}
+
## Liczba  0.9265532 jest dodatnia
+

Możemy chcieć wykonać różne operacje na tak wylosowanej liczbie. Przykładowo, jeśli będzie ujemna, to zmienić znak, zaokrąglić i zreplikować w wektorze

+
if (x_norm < 0)
+{
+  x_norm <- abs(x_norm)
+  x_wek <- rep(round(x_norm, 2), times = 5)
+} else 
+{
+  x_wek <- "X"
+}
+

i otrzymać X (X oznacza, że wylosowana liczba była dodatnia, a z nią nic nie robimy).

+

Pętla \(\verb+while+\) działa tak długo, dopóki warunek jest spełniony - tzn. do kiedy nie dostaniemy \(\verb+FALSE+\). Warunek należy opisać tak, żeby w pewnym momencie został spełniony - inaczej pętla będzie działać w “nieskończoność”. Często używa sie jej do szukania losowych liczb o pewnych właściwościach.

+

Składnia tej pętli jest następująca:

+
while(warunek)
+{
+  instrukcja_1
+  instrukcja_2 
+}
+

Tutaj przykład wykorzystania, gdy chcemy losować liczby z przedziału [1, 100], dopóki różnica między dwoma kolejnymi nie będzie parzysta

+
i <- 2
+los <- c()
+los[1] <- 0
+roznica <- 1
+while(roznica%%2 != 0)
+{
+  los <- c(los, sample(1:100, 1, replace = TRUE))
+  roznica <- los[i]-los[i-1]
+  i = i+1
+}
+

W ten sposób dostajemy wylosowane liczby: 0, 70, z różnicą między ostatnimi równą 70.

+

Pętla \(\verb+for+\) wygląda następująco:

+
for(iterator in warunek)
+{
+  instrukcja_1
+  instrukcja_2 
+}
+

Ta pętla wykonuje instrukcje określoną ilość razy - tyle ile elementów \(\verb+iterator+\) w zbiorze \(\verb+warunek+\). W warunku możemy mieć liste albo wektor. Po każdym wykonaniu pętli, zmienna \(\verb+iterator+\) przeskakuje do kolejnego elementu warunku.

+

Jeśli chcemy wykonać tylko 1 instrukcje, można zapisać

+
for(iterator in warunek) instrukcja_1
+

Przykładowo, jeśli chcemy elementy ze zbioru [1, 10] podnieść do potęgi, możemy użyć pętli \(\verb+for+\).

+
wynik <-c()
+for (i in 1:10) wynik <- c(wynik, i*i)
+wynik
+
##  [1]   1   4   9  16  25  36  49  64  81 100
+

Możemy także napisać pętle zagnieżdżone, przykładowo do obliczenia wartości w macierzach. W tym wypadku wartością każdego elementu macierzy (3x3) jest iloczyn jego indeksów, co daje następujący wynik

+
macierz <- matrix(nrow=3, ncol=3)
+
+for(i in 1:dim(macierz)[1]) 
+  {
+    for(j in 1:dim(macierz)[2]) 
+      {
+        macierz[i,j] = i*j
+      }
+}
+
+macierz
+
##      [,1] [,2] [,3]
+## [1,]    1    2    3
+## [2,]    2    4    6
+## [3,]    3    6    9
+

Teraz zajmiemy się rodziną funkcji \(\verb+apply+\). Należą do niej takie funkcję jak \(\verb+apply, tapply, sapply, lapply, vapply+\). Wszystkie one pozwalają na wykonanie pewnej operacji na szeregu podzbiorów danych. Operacja, która ma być wykonana określana jest przez argument \(\verb+FUN+\). Funkcje z tej rodziny przyjmują elementy listy \(\verb+(lapply()+)\), elementy wektora \(\verb+(sapply())+\), macierze \(\verb+(apply())+\) oraz podgrup wskazanych przez jedną lub kilka zmiennych \(\verb+(by()+\) i \(\verb+tapply())+\).

+

Zacznijmy od funkcji \(\verb+lapply()+\). Wykonuje funkcję \(\verb+FUN+\) dla wszystkich elementów wektora \(\verb+x+\). Przydatna funkcja zastępująca pętlę \(\verb+for+\). Domyślnie wynikiem działania jest lista, lecz jeżeli w wyniku chcielibyśmy otrzymać wektor, to jednym z rozwiązań jest zamiana listy na wektor funkcją \(\verb+unlist()+\). Oto przykładowe działanie funkcji \(\verb+lapply()+\):

+
x=c(1,2,3,4,5,6,7,8,9,10)
+func=function(x){return(x**3-3*x)}
+lapply(x,func)
+
## [[1]]
+## [1] -2
+## 
+## [[2]]
+## [1] 2
+## 
+## [[3]]
+## [1] 18
+## 
+## [[4]]
+## [1] 52
+## 
+## [[5]]
+## [1] 110
+## 
+## [[6]]
+## [1] 198
+## 
+## [[7]]
+## [1] 322
+## 
+## [[8]]
+## [1] 488
+## 
+## [[9]]
+## [1] 702
+## 
+## [[10]]
+## [1] 970
+

Funkcja \(\verb+sapply+\) jest bardziej przyjazną użytkownikowi wersją \(\verb+lapply+\) zwracającą wektor lub macierz i może przyjmować więcej argumentów, np. \(\verb+sapply(x, f, simplify = FALSE, USE.NAMES = FALSE)+\) zwraca ten sam wynik co \(\verb+lapply(x, f)+\).

+

Funkcja \(\verb+vapply+\) jest podobna do \(\verb+sapply+\), ale ma z góry określony typ zwracanych wartości, a może być również bezpieczniejszy w użyciu, a czasem nawet szybszy.

+

Teraz weźmiemy pod lupe \(\verb+tapply()+\), która to wykonuje funkcję \(\verb+FUN+\) dla podzbiorów wektora \(\verb+x+\) określonego +przez poziomy zmiennej czynnikowej \(\verb+index+\). Przydatna funkcja, gdy chcemy policzyć pewną statystykę w podgrupach, np. +odchylenie standardowe w z wagami. W tym przypadku \(\verb+x+\) będzie wektorem z wagami, \(\verb+index+\) wektorem z płcią a \(\verb+FUN+\) będzie +funkcją sd).

+
x=c(98,67,65,82,55,60,72,81,48,88)
+index=c('M','M','K','M','K','M','M','M','K','M')
+tapply(x,index,sd)
+
##         K         M 
+##  8.544004 12.944938
+

A teraz bardziej zaawansowana werssa funkcji \(\verb+tapply()+\) z tą różnicą, że \(\verb+x+\) może być macierzą lub listą, \(\verb+index+\) może być listą, a wynik tej funkcji jest specyficznie wyświetlany. Jeżeli \(\verb+index+\) jest listą zmiennych czynnikowych, to wartość funkcji \(\verb+FUN+\) będzie wyznaczona dla każdego przecięcia czynników tych zmiennych. Wynik +funkcji \(\verb+by()+\) jest klasy \(\verb+by+\), ale po usunięciu informacji o klasie, np. poprzez użycie funkcji \(\verb+unclass()+\) otrzymujemy zwykłą +macierz. Argument \(\verb+x+\) może być listą lub macierzą, dzięki czemu +do funkcji \(\verb+FUN+\) przekazać można kilka zmiennych – elementów/kolumn listy/macierzy \(\verb+x+\).

+
m1=seq(1:9)
+x=c('a','b','c','a','b','c','a','b','c')
+by(m1,x,mean)
+
## x: a
+## [1] 4
+## ----------------------------------------------------------------------------- 
+## x: b
+## [1] 5
+## ----------------------------------------------------------------------------- 
+## x: c
+## [1] 6
+

Z kolei \(\verb+mapply()+\) to wielowymiarowy odpowiednik funkcji \(\verb+sapply()+\). Argumentami tej funkcji jest funkcja \(\verb+fun+\) oraz kilka (dwa lub więcej) wektorów o tej samej długości. Wynikiem jest wektor, w którym na pozycji \(\verb+i+\)-tej jest wynik funkcji \(\verb+fun+\) wywołanej z \(\verb+i+\)-tych elementów wektorów będących argumentami.

+
a=function(x,y){return(x**y)}
+mapply(a,x=seq(1,101,by=10),y=seq(1:11))
+
##  [1] 1.000000e+00 1.210000e+02 9.261000e+03 9.235210e+05 1.158562e+08 1.759629e+10 3.142743e+12
+##  [8] 6.457535e+14 1.500946e+17 3.894161e+19 1.115668e+22
+ +
+
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + diff --git "a/docs/_book/podstawy-j\304\231zyka-r.html" "b/docs/_book/podstawy-j\304\231zyka-r.html" new file mode 100644 index 0000000..2244fa0 --- /dev/null +++ "b/docs/_book/podstawy-j\304\231zyka-r.html" @@ -0,0 +1,1071 @@ + + + + + + + Chapter 2 Podstawy języka R | Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+
+ + +
+
+ +
+
+

Chapter 2 Podstawy języka R

+

Język R posiada kilka typów danych, które pokrótce postaramy sie omówić poniżej. Pokażemy ich budowe jak i operacje na nich, przytaczając stosowne przyklady.

+
+

2.1 Liczby

+

Liczby całkowite i rzeczywiste (tutaj separator dziesiętny to kropka). Możemy używać również notacji naukowej. Operacje na liczbach to podstawowe działania matematyczne jak i trochę rozszerzone, ukazane niżej wraz z specjalnymi liczbami.

+
5; 5.5; 5.5e-2; 
+
## [1] 5
+
## [1] 5.5
+
## [1] 0.055
+

Tutaj liczby specjalne,

+
NaN # not a number
+
## [1] NaN
+
Inf # nieskończoność
+
## [1] Inf
+
-Inf # - nieskończoność
+
## [1] -Inf
+

oraz kilka działań na liczbach

+
1 + 1 # podobnie '-' to odejmowanie
+
## [1] 2
+
4/2 # dzielenie, a '*' to mnożenie
+
## [1] 2
+
5 %/% 3 # dzielenie całkowite
+
## [1] 1
+
5 %% 3 # reszta z dzielenia
+
## [1] 2
+
2^3 # potęgowanie
+
## [1] 8
+
2**3 # też potęgowanie
+
## [1] 8
+
sqrt(4) #pierwiastkowanie
+
## [1] 2
+
abs(-1) # wartość bezwzględna
+
## [1] 1
+
+
+

2.2 Łańcuchy znaków

+

Łańcuch znaków to po prostu napi. Napis jest otoczony przez ” lub ’. W napisie możemy umieszczać dowolne znaki, pamiętając że są też znaki specjalne (rozpoczynające się od \ i mające specjalne funkcje). Na napisach istnieje wiele operacji (np. \(\verb+paste()+,\) czyli sklejenie dwóch napisów), lecz je zobaczymy w notatce o napisach.

+
"napis"
+
## [1] "napis"
+
'to też'
+
## [1] "to też"
+
"'a tutaj nawet z bonusem'"
+
## [1] "'a tutaj nawet z bonusem'"
+
# ""a"" to już wbrew intuicji nie jest napis
+cat("i znak \n specjalny, wstawiający nową linie") # cat() wyświetla napis w sposób niesformatowany
+
## i znak 
+##  specjalny, wstawiający nową linie
+
+
+

2.3 Wartości logiczne

+

Logiczna Prawda (\(\verb+TRUE+\) lub \(\verb+T+\)) oraz logiczny Fałsz (\(\verb+FALSE+\) lub \(\verb+F+\)). Na tych obiektach możemy wykonywać operacje logiczne oraz algebraiczne.

+
TRUE & TRUE # operator 'i'
+
## [1] TRUE
+
TRUE | FALSE # operator 'lub'
+
## [1] TRUE
+
1 == 1 # testowanie równości
+
## [1] TRUE
+
1 != 2 # testowanie nierówności
+
## [1] TRUE
+
2*TRUE # TRUE ma wartość 1
+
## [1] 2
+
2*FALSE # FALSE ma wartość 0
+
## [1] 0
+
T ; `T` <- FALSE; T # używając `` możemy zmienić wartość logiczną wyrażenia
+
## [1] FALSE
+
## [1] FALSE
+
+
+

2.4 Wektory

+

Wektor to w R uporządkowany zbiór elementów. Elementy te muszą mieć ten sam typ, także jeśli do wektora trafią elementy z różnym typem (poza NA), to nastąpi konwersja elementów do jednego typu. Proste wektory tworzymy przez polecenie \(\verb+c()+\) i elementy wypisujemy w nawiasie po przecinku. Dodatkowo, element wektora jest traktowany jako jednoelementowy wektor. Wektory liczbowe jak i inne możemy tworzyć za pomocą wbudowanych funkcji do tego przeznaczonych.

+
v <- c(1, 2, 3) #przypisanie wektora do zmiennej
+0:10 # wektor liczbowy
+
##  [1]  0  1  2  3  4  5  6  7  8  9 10
+
seq(from = 0, to = 10, by = 1) # to samo, ale za pomocą seq(), czyli sequance
+
##  [1]  0  1  2  3  4  5  6  7  8  9 10
+
seq(0, 1, length.out = 4) # równe odstępy w 4 liczbowym wektorze
+
## [1] 0.0000000 0.3333333 0.6666667 1.0000000
+
length(v) # zwraca długość vectora
+
## [1] 3
+
# vector(mode, lenght) tworzy wektor dlugosci lenght, a wyrazy tego wektora maja klase mode
+vector("integer", 10) # wektor liczb calkowitych
+
##  [1] 0 0 0 0 0 0 0 0 0 0
+
vector("numeric", 10) # wektor liczb rzeczywistych
+
##  [1] 0 0 0 0 0 0 0 0 0 0
+
vector("character", 10) # wektor slów
+
##  [1] "" "" "" "" "" "" "" "" "" ""
+
rep(v, each = 2) # każdy element v zostanie powtórzony 2 razy
+
## [1] 1 1 2 2 3 3
+
rep(v, times = 2) # v zostanie powtórzony 2 razy
+
## [1] 1 2 3 1 2 3
+
# mały mix tj. tutaj element v traktujemy jako wektor jednoelementowy
+# i powtarzamy times razy
+rep(v, times = 1:3) 
+
## [1] 1 2 2 3 3 3
+
x <- c("a", "A") # wektor napisowy
+v <- "a" # to też
+toupper(x) # zmieni stringi w argumencie na wielkie litery
+
## [1] "A" "A"
+
tolower(x) # zmieni stringi w argumencie na male litery
+
## [1] "a" "a"
+
+
+

2.5 Indeksowanie

+

W R wektory są indeksowane od 1 (a nie od 0 jak w wielu językach programowania!). Aby odwołać się do konkretnego elementu wektora korzystamy z nawiasów kwadratowych \(\verb+[]+.\)

+
letters[3]
+
## [1] "c"
+

Można wybrać więcej niż jeden element, wpisując w nawiasach kwadratowych wektor indeksów.

+
letters[1:10]
+
##  [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j"
+
letters[c(1, 15)]
+
## [1] "a" "o"
+
letters[seq(1, 20, by = 2)]
+
##  [1] "a" "c" "e" "g" "i" "k" "m" "o" "q" "s"
+

Jeśli przed wektorem indeksów widnieje znak minus, R zwróci wszystkie elementy wektora z wyjątkiem tych w nawiasie kwadratowym.

+
letters[-(1:10)] # niezbędny nawias wokół 1:10
+
##  [1] "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
+
letters[-c(1, 15)]
+
##  [1] "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
+
letters[-seq(1, 20, by = 2)]
+
##  [1] "b" "d" "f" "h" "j" "l" "n" "p" "r" "t" "u" "v" "w" "x" "y" "z"
+

Pod wybrane indeksy można przypisać nowe wartości.

+
new_letters <- letters
+new_letters[1:5] <- LETTERS[1:5]
+new_letters
+
##  [1] "A" "B" "C" "D" "E" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y"
+## [26] "z"
+

Albo pod każdy wybrany indeks nową wspólną wartość.

+
new_letters[1:5] <- "x"
+new_letters
+
##  [1] "x" "x" "x" "x" "x" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y"
+## [26] "z"
+

Tworząc wektor funkcją \(\verb+c()+,\) możemy nazwać każdy z jego elementów.

+
str_vec_nam <- c("a" = "A", "b" = "B", "c" = "C")
+str_vec_nam
+
##   a   b   c 
+## "A" "B" "C"
+

Może być to użyteczne przy odwoływaniu się do konkretnego elementu wektora, nie trzeba wtedy znać numeru jego indeksu.

+
str_vec_nam["a"]
+
##   a 
+## "A"
+
str_vec_nam[c("a", "c")]
+
##   a   c 
+## "A" "C"
+
str_vec_nam[c("c", "a")]
+
##   c   a 
+## "C" "A"
+

Wektory możemy również indeksować za pomocą wektorów logicznych. Działa to wtedy jak wybieranie tych elementów wektora, które spełniają ustalony warunek.

+
x_ind <- new_letters == "x"
+x_ind
+
##  [1]  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
+## [17] FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE
+
new_letters[x_ind]
+
## [1] "x" "x" "x" "x" "x" "x"
+
seq_vec <- seq(0, 1, length.out = 10)
+seq_vec[seq_vec < 0.5]
+
## [1] 0.0000000 0.1111111 0.2222222 0.3333333 0.4444444
+

Można oczywiście rozbudowywać wyrażenia logiczne, np. następująco:

+
seq_vec[seq_vec < 0.3 | seq_vec > 0.8]
+
## [1] 0.0000000 0.1111111 0.2222222 0.8888889 1.0000000
+
seq_vec[seq_vec > 0.3 & seq_vec < 0.8]
+
## [1] 0.3333333 0.4444444 0.5555556 0.6666667 0.7777778
+
+
+

2.6 Operacje na wektorach

+

W R domyślnym i naturalnym zachowaniem funkcji na wektorach jest działanie element po elemencie

+
1:10 + seq(0, 1, length.out = 10)
+
##  [1]  1.000000  2.111111  3.222222  4.333333  5.444444  6.555556  7.666667  8.777778  9.888889 11.000000
+
c(2,4,6,8)^(1:4)
+
## [1]    2   16  216 4096
+

W przypadku gdy wektory, na których wykonujemy obliczenia mają różne długości zachodzi recykling, tj. R samoistnie przedłuża krótszy wektor replikując go odpowiednią liczbę razy. Widzimy, że obie poniższe linie kodu dają taki sam efekt.

+
1:10 + 1:2
+
##  [1]  2  4  4  6  6  8  8 10 10 12
+
1:10 + rep(1:2, 5)
+
##  [1]  2  4  4  6  6  8  8 10 10 12
+

Gdy długość dłuższego wektora nie jest wielokrotnością krótszego, recykling także zadziała, jednak R zgłosi warning.

+
1:10 + 1:3
+
## Warning in 1:10 + 1:3: długość dłuszego obiektu nie jest wielokrotnością długości krótszego obiektu
+
##  [1]  2  4  6  5  7  9  8 10 12 11
+
1:10 + 1:3 + 1:2 + 1:5
+
## Warning in 1:10 + 1:3: długość dłuszego obiektu nie jest wielokrotnością długości krótszego obiektu
+
##  [1]  4  8 10 11 13 12 11 15 17 18
+

Na wektorach możemy wykonywać oczywiście inne funkcje poza podstawowymi operacjami arytmetycznymi. Jedną z opcji jest posortowanie wektora.

+
num_vec <- c(3,6,1,9,8,-3,0,102,-5)
+sort(num_vec) # sortowanie rosnące
+
## [1]  -5  -3   0   1   3   6   8   9 102
+
sort(num_vec, decreasing = TRUE) # sortowanie malejące
+
## [1] 102   9   8   6   3   1   0  -3  -5
+

Odwrócić kolejnośc elementów wektora można następująco.

+
rev(num_vec)
+
## [1]  -5 102   0  -3   8   9   1   6   3
+

Oto kilka kolejnych funkcji.

+
sum(num_vec) # suma elementów wektora
+
## [1] 121
+
prod(num_vec) # iloczyn elementów wektora
+
## [1] 0
+
mean(num_vec) # średnia elementów wektora
+
## [1] 13.44444
+

Przy operacjach jak powyższe należy jednak uważać na wektory zawierające “NA”.

+
vec_with_NA <- c(3,6,1,NA)
+sum(vec_with_NA)
+
## [1] NA
+

Aby zsumować wartości z pominięciem “NA” należy dopisać dodatkowy argument funkcji.

+
sum(vec_with_NA, na.rm = TRUE)
+
## [1] 10
+

Analogicznie dla iloczynu i średniej elementów.

+
prod(vec_with_NA)
+
## [1] NA
+
prod(vec_with_NA, na.rm = TRUE)
+
## [1] 18
+
mean(vec_with_NA)
+
## [1] NA
+
mean(vec_with_NA, na.rm = TRUE)
+
## [1] 3.333333
+Lista jest podobna do wektora tj. jest pewnym ciągiem obiektów, tyle że jej elementy mogą mieć różne typy. +
l <- list(1:5)
+#lista z elementami bedacymi liczbami
+
## [[1]]
+## [1] 1 2 3 4 5
+
l2 <- list(zwierze='dog', imie='Max',czyLubiInnePsy = TRUE)
+#lista z elementami bedacymi stringami lub wartosciami logicznymi
+
## $zwierze
+## [1] "dog"
+## 
+## $imie
+## [1] "Max"
+## 
+## $czyLubiInnePsy
+## [1] TRUE
+

Kolejnę różnica pomiedzy wektorem a listą jest możliwość odwoływania się do elementów listy za pomocą nazwy tego elementu i operatora $. Np:

+
# odwolanie do elementu bedacego za pomoca [], 
+# wynikiem takiej operacji jest lista zawierajaca wektor
+l[1]
+
## [[1]]
+## [1] 1 2 3 4 5
+
# aby odwolac sie do konkretnego elementu uzwywamy [[]], na przyklad operacja l[[1]][2] 
+# zwroci drugi element wektora z listy
+ l[[1]][2]
+
## [1] 2
+
# nadpisywanie elementu listy wektorem
+l[[1]] <- c("a", "b", "c")
+# odwolanie do elementu za pomoca nazwy elementu
+l2$zwierze
+
## [1] "dog"
+
l2$imie
+
## [1] "Max"
+
l2$czyLubiInnePsy
+
## [1] TRUE
+

Listy można łączyć oraz modyfikować. Funkcja \(\verb+lapply()+\) to funkcja, która pozwala na wykonanie pewnego konkretnego działania na KAŻDYM elemencie z listy. Na przykład, możemy każdy element chcieć zapisać tylko dużymi literami:

+
lapply(l2,toupper)
+
## $zwierze
+## [1] "DOG"
+## 
+## $imie
+## [1] "MAX"
+## 
+## $czyLubiInnePsy
+## [1] "TRUE"
+

Aby połączyć dwie listy, należy użyć \(\verb+c()+,\) robiąc z dwóch list wektor i przypisując go do nowej zmiennej.

+
l3 <- c(l,l2)
+
## [[1]]
+## [1] "a" "b" "c"
+## 
+## $zwierze
+## [1] "dog"
+## 
+## $imie
+## [1] "Max"
+## 
+## $czyLubiInnePsy
+## [1] TRUE
+

Macierz to obiekt dwuwymiarowy. Składa się z elementów tego samego typu. Tworzy się ją funkcją \(\verb+matrix()+,\) do której podajemy wartości macierzy (zwykle w postaci wektora), liczbę wierszy i kolumn.

+
matrix(data = 1:10, nrow = 2, ncol = 5)
+
##      [,1] [,2] [,3] [,4] [,5]
+## [1,]    1    3    5    7    9
+## [2,]    2    4    6    8   10
+

Widzimy, że R domyślnie wypełnia macierz po kolumnach. Aby wypełnić ją po wierszach ustalamy parametr \(\verb+byrow = TRUE+\)

+
m <- matrix(data = 1:10, nrow = 2, ncol = 5, byrow = TRUE)
+m
+
##      [,1] [,2] [,3] [,4] [,5]
+## [1,]    1    2    3    4    5
+## [2,]    6    7    8    9   10
+

Elementy macierzy wybiera się za pomocą dwóch indeksów - indeksu wiersza i indeksu kolumny umieszczonych w nawiasach kwadaratowych i rozdzielonych przecinkiem.

+
m[2,3]
+
## [1] 8
+

Można również wybrać konkretne wiersze lub kolumny.

+
m[1:2,3:4] # wybiera wiersze 1 i 2 oraz kolumny 3 i 4
+
##      [,1] [,2]
+## [1,]    3    4
+## [2,]    8    9
+
m[2,c(1,4,5)] # wybiera wiersz 2 oraz kolumny 1,4 i 5
+
## [1]  6  9 10
+

Nie podanie indeksu przed przecinkiem oznacza, że chcemy otrzymać wszystkie wiersze. Analogicznie nie podanie indeksu po przecinku oznacza, że chcemy otrzymać wszystkie kolumny.

+
m[,c(1,3)] 
+
##      [,1] [,2]
+## [1,]    1    3
+## [2,]    6    8
+
m[2,]
+
## [1]  6  7  8  9 10
+

Macierze, podobnie jak wektory, możemy także indeksować warunkami logicznymi.

+
# zwraca elementy (w tym wypadku element) z pierwszej kolumny,
+# które są większe od 2
+m[m[,1] > 2, 1]  
+
## [1] 6
+

Można także indeksować macierz inną macierzą o dwóch kolumnach. Zwrócone zostaną wtedy elementy o indeksach będących wierszami tej macierzy.

+
matrix_ind<- matrix(c(1, 2, 2, 3, 2, 4), byrow = TRUE, nrow = 3, ncol = 2)
+m[matrix_ind]  
+
## [1] 2 8 9
+

Na macierzach o tych samych wymiarach możemy wykonywać operacje arytmetyczne. Trzeba zwrócić uwagę, że są one wykonywane element po elemencie (z matematycznego punktu widzenia jest to oczekiwane przy dodawaniu, ale nieoczekiwane przy mnożeniu macierzy).

+
m1 <- matrix(1:4,2,2)
+m1
+
##      [,1] [,2]
+## [1,]    1    3
+## [2,]    2    4
+
m2 <- matrix(2:5,2,2)
+m2
+
##      [,1] [,2]
+## [1,]    2    4
+## [2,]    3    5
+
m1 + m2
+
##      [,1] [,2]
+## [1,]    3    7
+## [2,]    5    9
+
m1 * m2
+
##      [,1] [,2]
+## [1,]    2   12
+## [2,]    6   20
+

Aby wykonać matematyczne mnożenie macierzy należy użyć operatora \(\verb+%*%+.\)

+
m1 %*% m2
+
##      [,1] [,2]
+## [1,]   11   19
+## [2,]   16   28
+Jest to obiekt przechowujący dane w postaci tabeli dwuwymiarowej, którą tworzą wektory o dowolnym typie. Z ramki danych można korzystać jak z macierzy dwuwymiarowej (poprzez korzystanie z \(\verb+[,]+\)), jak i z listy (poprzez korzystanie z $). +
imie <- c("Max", "Reksio","Rex","Luna") #utworzymy ramke z 2 wektorow 
+wiek <- c(2,8,3,11)
+ramka <- data.frame(imie,wiek) 
+ #ramke tworzymy za pomoca polecenia data.frame()
+
##     imie wiek
+## 1    Max    2
+## 2 Reksio    8
+## 3    Rex    3
+## 4   Luna   11
+
#wyswietlanie nazw kolumn
+names(ramka)
+
## [1] "imie" "wiek"
+
#odnoszenie sie do elementu znajdujacego sie w 2. rzedzie i 1. kolumnie
+ramka[2,1]
+
## [1] "Reksio"
+
#pobieranie paru wierszy na raz za pomoca wektora
+ramka[c(1, 2), ]
+
##     imie wiek
+## 1    Max    2
+## 2 Reksio    8
+
#pobieranie wszystkich kolumn dla 1. wiersza
+ramka[1,]
+
##   imie wiek
+## 1  Max    2
+
#pobieranie wszystkich wierszy dla 1. kolumny
+ramka[,1]
+
## [1] "Max"    "Reksio" "Rex"    "Luna"
+
# pierwsza kolumna bez drugiego wiersza
+ramka[-2, 1]
+
## [1] "Max"  "Rex"  "Luna"
+
#pobieranie kolumn/wierszy po nazwie
+ramka$wiek
+
## [1]  2  8  3 11
+
# inny sposób indeksowanie po nazwie
+ramka[, "wiek"]
+
## [1]  2  8  3 11
+
    +
  • Indeksowanie na podstawie zawartości ramki danych
    +Dane z ramki mogą być przez nas “filtrowane” za pomocą []. Na przykład
  • +
+
# psy poniżej 9 roku życia
+ramka[ramka$wiek < 9, ]
+
##     imie wiek
+## 1    Max    2
+## 2 Reksio    8
+## 3    Rex    3
+
#dane tylko dla Reksia
+ramka[ramka$imie == "Reksio", ]
+
##     imie wiek
+## 2 Reksio    8
+
# analogicznie dla wektorów
+wiek[wiek < 9]
+
## [1] 2 8 3
+

Tworząc ramkę danych należy pamiętać o tym, aby wektory danych służące za kolumny były tej samej długości.

+
#zamiana nazw kolumn
+names(ramka) <- c("imie_psa", "wiek_psa")
+
##   imie_psa wiek_psa
+## 1      Max        2
+## 2   Reksio        8
+## 3      Rex        3
+## 4     Luna       11
+

Ramki danych możemy powiększać o dodatkowe wiersze i kolumny, ale typy (dla wierszy) i rozmiary muszą sie zgadzać z typami i rozmiarem ramki danych. Rozpatrzmy poniższy przykład, aby pokazać, jak dodać wiersz i kolumnę za pomocą funkcji \(\verb+cbind()+\) oraz \(\verb+rbind()+\).

+
#dodawanie nowego wiersza
+dodajemy_wiersz <- data.frame(imie_psa ="Quentin", wiek_psa=9)
+#funkcja rbind "skleja" wierszowo argument pierwszy (u nas ramka) z drugim
+ramka <- rbind(ramka,dodajemy_wiersz)
+#dodawanie nowej kolumny 
+czyLubiInnePsy <- c(TRUE,TRUE, FALSE, TRUE, FALSE)
+#funkcja cbind "skleja" kolumnowo argument pierwszy (u nas ramka) z drugim
+ramka <- cbind(ramka,czyLubiInnePsy)
+
##   imie_psa wiek_psa czyLubiInnePsy
+## 1      Max        2           TRUE
+## 2   Reksio        8           TRUE
+## 3      Rex        3          FALSE
+## 4     Luna       11           TRUE
+## 5  Quentin        9          FALSE
+

Możemy rownież dodawać wiersze za pomocą indeksowania, to znaczy przypisywania wartości do konkretnych indeksów ramki:

+
#jako 6. wiersz "wkladamy" nowy wektor
+ramka[6,] <- c("Fanta",0.5,TRUE)
+
##   imie_psa wiek_psa czyLubiInnePsy
+## 1      Max        2           TRUE
+## 2   Reksio        8           TRUE
+## 3      Rex        3          FALSE
+## 4     Luna       11           TRUE
+## 5  Quentin        9          FALSE
+## 6    Fanta      0.5           TRUE
+
# jako 4.kolumne "wkladamy" nowy wektor
+ramka[,4] <- c("Mateusz","Romek","Renata","Leon","Quennie","Filip")
+# nazywamy kolumne 4.
+names(ramka)[4] <- "opiekun_psa"
+
##   imie_psa wiek_psa czyLubiInnePsy opiekun_psa
+## 1      Max        2           TRUE     Mateusz
+## 2   Reksio        8           TRUE       Romek
+## 3      Rex        3          FALSE      Renata
+## 4     Luna       11           TRUE        Leon
+## 5  Quentin        9          FALSE     Quennie
+## 6    Fanta      0.5           TRUE       Filip
+

Analizując nową dla nas ramkę danych, użyteczne okazują się funkcje pozwalające na poznanie właściwości ramki danych. Oto pare z nich:

+
# wymiary ramki (6 wierszy,4 kolumny) mozna sprawdzic za pomoca funkcji dim()
+dim(ramka)
+
## [1] 6 4
+
# aby zobaczyc skrocony opis typow danych zawartych w ramce uzywana jest funkcja str()
+str(ramka)
+
## 'data.frame':    6 obs. of  4 variables:
+##  $ imie_psa      : chr  "Max" "Reksio" "Rex" "Luna" ...
+##  $ wiek_psa      : chr  "2" "8" "3" "11" ...
+##  $ czyLubiInnePsy: chr  "TRUE" "TRUE" "FALSE" "TRUE" ...
+##  $ opiekun_psa   : chr  "Mateusz" "Romek" "Renata" "Leon" ...
+
# aby "podejrzec" pierwsze wiersze ramki danych, wraz naglowkami kolumn uzywana jest funkcja head()
+head(ramka)
+
##   imie_psa wiek_psa czyLubiInnePsy opiekun_psa
+## 1      Max        2           TRUE     Mateusz
+## 2   Reksio        8           TRUE       Romek
+## 3      Rex        3          FALSE      Renata
+## 4     Luna       11           TRUE        Leon
+## 5  Quentin        9          FALSE     Quennie
+## 6    Fanta      0.5           TRUE       Filip
+
# wysietlanie pierwszych n wierszy
+head(ramka,n=2)
+
##   imie_psa wiek_psa czyLubiInnePsy opiekun_psa
+## 1      Max        2           TRUE     Mateusz
+## 2   Reksio        8           TRUE       Romek
+
# wyswietlanie ostatnich n wierszy za pomoca funkcji tail()
+tail(ramka,n=2)
+
##   imie_psa wiek_psa czyLubiInnePsy opiekun_psa
+## 5  Quentin        9          FALSE     Quennie
+## 6    Fanta      0.5           TRUE       Filip
+

Pętli oraz instrukcji warunkowych używamy, kiedy chcemy uniknąć powielania kodu i chcemy zachować jego przejrzystość. Ułatwia to wprowadzanie potencjalnych zmian. Instrukcje opisujące co powinno się zdarzyć należy umieścić w nawiasach \(\verb+{ }+\). Jeśli chcemy wykonać tylko jedną linijke kodu, możemy je opuścić.

+

Umożliwia warunkowe wykonanie kawałka kodu - jeśli warunek zawarty w \(\verb+if+\) jest spełniony, to R przejdzie do zawartej instrukcji. W przeciwnym wypadku wykona polecenie zawarte w \(\verb+else+\), a jeśli go nie ma , to przejdzie do kolejnych pętli. Część \(\verb+else+\) nie jest wymagana, w tym wypadku z góry wiadomo ile razy kod zostanie wykonany.

+

Składnia wygląda następująco:

+
if(warunek)
+{
+  instrukcja_1
+}
+

i jest analogiczna do

+
if(warunek) instrukcja_1
+

Możemy także zapisać

+
if(warunek)
+{
+  instrukcja_1
+  instrukcja_2 
+} else
+  {
+    instrukcja_3
+  }
+

Powiedzmy, że rozpatrujemy liczbe z rozkładu normalnego i sprawdzamy jakiego jest znaku.

+
x_norm <- rnorm(1)
+
+if (x_norm < 0)
+{
+  cat("Liczba", x_norm, "jest ujemna")
+} else
+{
+  cat("Liczba ", x_norm, "jest dodatnia")
+}
+
## Liczba  0.2630452 jest dodatnia
+

Możemy chcieć wykonać różne operacje na tak wylosowanej liczbie. Przykładowo, jeśli będzie ujemna, to zmienić znak, zaokrąglić i zreplikować w wektorze

+
if (x_norm < 0)
+{
+  x_norm <- abs(x_norm)
+  x_wek <- rep(round(x_norm, 2), times = 5)
+} else 
+{
+  x_wek <- "X"
+}
+

i otrzymać X (X oznacza, że wylosowana liczba była dodatnia, a z nią nic nie robimy).

+

Pętla \(\verb+while+\) działa tak długo, dopóki warunek jest spełniony - tzn. do kiedy nie dostaniemy \(\verb+FALSE+\). Warunek należy opisać tak, żeby w pewnym momencie został spełniony - inaczej pętla będzie działać w “nieskończoność”. Często używa sie jej do szukania losowych liczb o pewnych właściwościach.

+

Składnia tej pętli jest następująca:

+
while(warunek)
+{
+  instrukcja_1
+  instrukcja_2 
+}
+

Tutaj przykład wykorzystania, gdy chcemy losować liczby z przedziału [1, 100], dopóki różnica między dwoma kolejnymi nie będzie parzysta

+
i <- 2
+los <- c()
+los[1] <- 0
+roznica <- 1
+while(roznica%%2 != 0)
+{
+  los <- c(los, sample(1:100, 1, replace = TRUE))
+  roznica <- los[i]-los[i-1]
+  i = i+1
+}
+

W ten sposób dostajemy wylosowane liczby: 0, 39, 53, z różnicą między ostatnimi równą 14.

+

Pętla \(\verb+for+\) wygląda następująco:

+
for(iterator in warunek)
+{
+  instrukcja_1
+  instrukcja_2 
+}
+

Ta pętla wykonuje instrukcje określoną ilość razy - tyle ile elementów \(\verb+iterator+\) w zbiorze \(\verb+warunek+\). W warunku możemy mieć liste albo wektor. Po każdym wykonaniu pętli, zmienna \(\verb+iterator+\) przeskakuje do kolejnego elementu warunku.

+

Jeśli chcemy wykonać tylko 1 instrukcje, można zapisać

+
for(iterator in warunek) instrukcja_1
+

Przykładowo, jeśli chcemy elementy ze zbioru [1, 10] podnieść do potęgi, możemy użyć pętli \(\verb+for+\).

+
wynik <-c()
+for (i in 1:10) wynik <- c(wynik, i*i)
+wynik
+
##  [1]   1   4   9  16  25  36  49  64  81 100
+

Możemy także napisać pętle zagnieżdżone, przykładowo do obliczenia wartości w macierzach. W tym wypadku wartością każdego elementu macierzy (3x3) jest iloczyn jego indeksów, co daje następujący wynik

+
macierz <- matrix(nrow=3, ncol=3)
+
+for(i in 1:dim(macierz)[1]) 
+  {
+    for(j in 1:dim(macierz)[2]) 
+      {
+        macierz[i,j] = i*j
+      }
+}
+
+macierz
+
##      [,1] [,2] [,3]
+## [1,]    1    2    3
+## [2,]    2    4    6
+## [3,]    3    6    9
+

Teraz zajmiemy się rodziną funkcji \(\verb+apply+\). Należą do niej takie funkcję jak \(\verb+apply, tapply, sapply, lapply, vapply+\). Wszystkie one pozwalają na wykonanie pewnej operacji na szeregu podzbiorów danych. Operacja, która ma być wykonana określana jest przez argument \(\verb+FUN+\). Funkcje z tej rodziny przyjmują elementy listy \(\verb+(lapply()+)\), elementy wektora \(\verb+(sapply())+\), macierze \(\verb+(apply())+\) oraz podgrup wskazanych przez jedną lub kilka zmiennych \(\verb+(by()+\) i \(\verb+tapply())+\).

+

Zacznijmy od funkcji \(\verb+lapply()+\). Wykonuje funkcję \(\verb+FUN+\) dla wszystkich elementów wektora \(\verb+x+\). Przydatna funkcja zastępująca pętlę \(\verb+for+\). Domyślnie wynikiem działania jest lista, lecz jeżeli w wyniku chcielibyśmy otrzymać wektor, to jednym z rozwiązań jest zamiana listy na wektor funkcją \(\verb+unlist()+\). Oto przykładowe działanie funkcji \(\verb+lapply()+\):

+
x=c(1,2,3,4,5,6,7,8,9,10)
+func=function(x){return(x**3-3*x)}
+lapply(x,func)
+
## [[1]]
+## [1] -2
+## 
+## [[2]]
+## [1] 2
+## 
+## [[3]]
+## [1] 18
+## 
+## [[4]]
+## [1] 52
+## 
+## [[5]]
+## [1] 110
+## 
+## [[6]]
+## [1] 198
+## 
+## [[7]]
+## [1] 322
+## 
+## [[8]]
+## [1] 488
+## 
+## [[9]]
+## [1] 702
+## 
+## [[10]]
+## [1] 970
+

Funkcja \(\verb+sapply+\) jest bardziej przyjazną użytkownikowi wersją \(\verb+lapply+\) zwracającą wektor lub macierz i może przyjmować więcej argumentów, np. \(\verb+sapply(x, f, simplify = FALSE, USE.NAMES = FALSE)+\) zwraca ten sam wynik co \(\verb+lapply(x, f)+\).

+

Funkcja \(\verb+vapply+\) jest podobna do \(\verb+sapply+\), ale ma z góry określony typ zwracanych wartości, a może być również bezpieczniejszy w użyciu, a czasem nawet szybszy.

+

Teraz weźmiemy pod lupe \(\verb+tapply()+\), która to wykonuje funkcję \(\verb+FUN+\) dla podzbiorów wektora \(\verb+x+\) określonego +przez poziomy zmiennej czynnikowej \(\verb+index+\). Przydatna funkcja, gdy chcemy policzyć pewną statystykę w podgrupach, np. +odchylenie standardowe w z wagami. W tym przypadku \(\verb+x+\) będzie wektorem z wagami, \(\verb+index+\) wektorem z płcią a \(\verb+FUN+\) będzie +funkcją sd).

+
x=c(98,67,65,82,55,60,72,81,48,88)
+index=c('M','M','K','M','K','M','M','M','K','M')
+tapply(x,index,sd)
+
##         K         M 
+##  8.544004 12.944938
+

A teraz bardziej zaawansowana werssa funkcji \(\verb+tapply()+\) z tą różnicą, że \(\verb+x+\) może być macierzą lub listą, \(\verb+index+\) może być listą, a wynik tej funkcji jest specyficznie wyświetlany. Jeżeli \(\verb+index+\) jest listą zmiennych czynnikowych, to wartość funkcji \(\verb+FUN+\) będzie wyznaczona dla każdego przecięcia czynników tych zmiennych. Wynik +funkcji \(\verb+by()+\) jest klasy \(\verb+by+\), ale po usunięciu informacji o klasie, np. poprzez użycie funkcji \(\verb+unclass()+\) otrzymujemy zwykłą +macierz. Argument \(\verb+x+\) może być listą lub macierzą, dzięki czemu +do funkcji \(\verb+FUN+\) przekazać można kilka zmiennych – elementów/kolumn listy/macierzy \(\verb+x+\).

+
m1=seq(1:9)
+x=c('a','b','c','a','b','c','a','b','c')
+by(m1,x,mean)
+
## x: a
+## [1] 4
+## ------------------------------------------------------------------------------ 
+## x: b
+## [1] 5
+## ------------------------------------------------------------------------------ 
+## x: c
+## [1] 6
+

Z kolei \(\verb+mapply()+\) to wielowymiarowy odpowiednik funkcji \(\verb+sapply()+\). Argumentami tej funkcji jest funkcja \(\verb+fun+\) oraz kilka (dwa lub więcej) wektorów o tej samej długości. Wynikiem jest wektor, w którym na pozycji \(\verb+i+\)-tej jest wynik funkcji \(\verb+fun+\) wywołanej z \(\verb+i+\)-tych elementów wektorów będących argumentami.

+
a=function(x,y){return(x**y)}
+mapply(a,x=seq(1,101,by=10),y=seq(1:11))
+
##  [1] 1.000000e+00 1.210000e+02 9.261000e+03 9.235210e+05 1.158562e+08 1.759629e+10 3.142743e+12
+##  [8] 6.457535e+14 1.500946e+17 3.894161e+19 1.115668e+22
+
+
+

2.7 R - funkcje

+

Funkcje przydają się do zamknięcia w nich operacji, które się często powtarzają w naszym kodzie lub dla jego lepszej czytelności. Podstawowa składnia funkcji w R wygląda tak:

+
nazwa_funkcja <-  function(argument 1, argument 2, …){
+  ciało funkcji
+  return(wartość lub obiekt zwracany)
+}
+

Napiszmy funkcję, która będzie mnożyła dowolny wektor przez podaną liczbę, a następnie zsumuje elementy wektora:

+
funkcja1 <- function(wektor, liczba){
+  rezultat <- wektor * liczba
+  rezultat <- sum(rezultat)
+  return(rezultat)
+}
+

Możemy także pominąc \(\texttt{return}\) i zdefiniować funkcje:

+
funkcja2 <- function(wektor, liczba){
+  rezultat <- wektor * liczba
+  rezultat <- sum(rezultat)
+  rezultat
+}
+

Obie funkcje \(\texttt{funkcja1}\) i \(\texttt{funkcja2}\) robią to samo. Wykonajmy nasze funkcje dla dwóch zdefiniowanych zmiennych:

+
v <- 1:5
+n <- 2
+
+funkcja1(v, n)
+
## [1] 30
+
funkcja2(v, n)
+
## [1] 30
+

Oczywiście do wykonania funkcji potrzebne jest zdefiniowanie obu argumentów. Jak ich nie dodamy wyświetli się błąd, że argument drugi zaginął i nie mamy zdefiniowanej jego wartości domyślnej. Zdefiniujmy zatem domyślną wartość argumentu \(\texttt{liczba}\) jako \(\texttt{NULL}\) i dopiszmy do naszej funkcji kod, który gdy ten argument będzie miał wartość domyślną zwróci tylko sumę elementów wektora:

+
funkcja3 <- function(wektor, liczba = NULL){
+  if(is.null(liczba)){
+    rezultat <- sum(wektor)
+  } else{
+    rezultat <- wektor * liczba
+    rezultat <- sum(rezultat)
+  }
+  rezultat
+}
+

Wykonajmy funckję \(\texttt{funkcja3}\) na wcześniej zdefiniowanym wektorze \(\texttt{v}\):

+
funkcja3(v)
+
## [1] 15
+

Oprócz zdefiniowania wartości domyślnej argumentu poprzez trzy kropki możemy również dopuścić parametry dodatkowe. Zdefiniujmy funkcję z parametrami dodatkowymi:

+
funkcja4 <- function(wektor, liczba = NULL, ...){
+  if(is.null(liczba)){
+    rezultat <- sum(wektor, ...)
+  } else{
+    rezultat <- wektor * liczba
+    rezultat <- sum(rezultat, ...)
+  }
+  rezultat
+}
+

Wykonajmy funckję \(\texttt{funkcja4}\) usuwając wartości brakujące z nowo zdefiniowanego wektora:

+
v <- c(NA, 1, NA, 2:4, NA, 5)
+
+v
+
## [1] NA  1 NA  2  3  4 NA  5
+
funkcja4(v, na.rm = TRUE)
+
## [1] 15
+

Funkcje są bardzo przydatne, gdy mamy do napisania długi skrypt. Pozwalają na podzielenie głównej części kodu na mniejsze kawałeczki, które kolejnemu użytkownikowi skryptu lub nam będzie łatwiej modyfikować.

+ +
+
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + diff --git a/docs/_book/podstawy-kontroli-wersji-przy-pomocy-gita.html b/docs/_book/podstawy-kontroli-wersji-przy-pomocy-gita.html new file mode 100644 index 0000000..862a3f9 --- /dev/null +++ b/docs/_book/podstawy-kontroli-wersji-przy-pomocy-gita.html @@ -0,0 +1,571 @@ + + + + + + + Chapter 10 Podstawy kontroli wersji przy pomocy Gita | Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+
+ + +
+
+ +
+
+

Chapter 10 Podstawy kontroli wersji przy pomocy Gita

+
+

10.1 Podstawowe informacje

+

System kontroli wersji to narzędzie, które zarządza zmianami wprowadzanymi w plikach i katalogach w projekcie. Istnieje wiele systemów kontroli wersji. Przykładem takiego systemu jest Git. Jego mocne strony to:

+
    +
  • Nic, co jest zapisane w Git, nigdy nie jest tracone, więc zawsze możesz wrócić, aby zobaczyć, które wyniki zostały wygenerowane przez które wersje twoich programów.
  • +
  • Git automatycznie powiadamia Cię, gdy Twoja praca koliduje z pracą innej osoby, więc jest trudniej (choć nie jest to niemożliwe) o przypadkowe nadpisanie pracy.
  • +
  • Git może synchronizować pracę wykonywaną przez różne osoby na różnych komputerach.
  • +
+

Kontrola wersji nie dotyczy tylko oprogramowania: książki, artykuły, zestawy parametrów i wszystko, co zmienia się w czasie lub wymaga udostępnienia, może i powinno być przechowywane i udostępniane za pomocą czegoś takiego jak Git.

+

Każdy z projektów Git składa się z dwóch części: plików i katalogów, które tworzysz i edytujesz bezpośrednio, oraz dodatkowych informacji, które Git rejestruje o historii projektu. Połączenie tych dwóch rzeczy nazywa się repozytorium.

+

Git przechowuje wszystkie dodatkowe informacje w katalogu o nazwie \(\texttt{.git}\) znajdującym się w katalogu głównym repozytorium.

+
+
+

10.2 Podstawowe komendy

+

Używając Gita zapewne często będziemy chcieli sprawdzić stan swojego repozytorium. Aby to zrobić, użyjemy polecenie \(\texttt{git status}\).

+
    +
  • \(\texttt{git status}\) - wyświetla listę plików, które zostały zmodyfikowane od czasu ostatniego zapisania zmian
  • +
+

Git ma obszar przejściowy, w którym przechowuje pliki ze zmianami, które chcemy zapisać, a które nie zostały jeszcze zapisane.

+
    +
  • \(\texttt{git status}\) - pokazuje, które pliki znajdują się w tym obszarze przejściowy i które mają zmiany, które nie zostały jeszcze zatwierdzone

  • +
  • \(\texttt{git diff}\) - pokaże wszystkie zmiany w twoim repozytorium (porównując obecną postać plików z ostatnio zapisaną)

  • +
  • \(\texttt{git diff directory}\) - pokaże zmiany w plikach w jakimś katalogu (porównując obecną postać plików z ostatnio zapisaną)

  • +
  • \(\texttt{git diff filename}\) - pokaże zmiany w danym pliku (porównując obecną postać z ostatnio zapisaną)

  • +
+

Git różnice między dwiema wersjami pliku wyświetla w poniższy sposób: +diff --git a/report.txt b/report.txt index e713b17..4c0742a 100644 --- a/report.txt +++ b/report.txt @@ -1,4 +1,5 @@ -# Seasonal Dental Surgeries 2017-18 +# Seasonal Dental Surgeries (2017) 2017-18 +# TODO: write new summary +gdzie:

+
    +
  • \(\texttt{a/report.txt, b/report.txt}\) to pierwsza i druga wersja pliku,

  • +
  • linia druga wypisuje klucze do wewnętrznej bazy danych zmian Gita,

  • +
  • \(\texttt{--- a/report.txt, +++ b/report.txt}\) oznacza, że usuwane linie oznaczone są przedrostkiem \(\texttt{-}\), dodawane linie oznaczone są przedrostkiem \(\texttt{+}\),

  • +
  • linia zaczynająca się od \(\texttt{@@}\) mówi, gdzie wprowadzane są zmiany. Pary liczb to numer lini ,,startowej’’ i liczba linii,

  • +
  • kolejne linie są listą zmian, które zostały wprowadzone.

  • +
  • \(\texttt{git add filename}\) - dodaje plik do obszaru przejściowego

  • +
  • \(\texttt{git diff -r HEAD}\) - porówna pliki z repozytorium z plikami z obszaru przejściowego

  • +
  • \(\texttt{git diff -r HEAD path/to/file}\) - porówna konkretny plik z repozytorium z plikiem z obszaru przejściowego

  • +
  • \(\texttt{nano filename}\) - otwiera plik w edytorze tekstowym \(\texttt{nano}\)

    +
      +
    • poruszanie się strzałkami
    • +
  • +
  • \(\texttt{Backspace}\) - usuń znak

  • +
  • \(\texttt{Ctrl-K}\): usuń linię

  • +
  • \(\texttt{Ctrl-U}\): cofnij usunięcie linii

  • +
  • \(\texttt{Ctrl-O}\): zapisz plik

  • +
  • \(\texttt{Ctrl-X}\): wyjdź z edytora

  • +
  • \(\texttt{git commit -m "comment"}\) - zapisuje zmiany w obszarze przejściowym z jednowierszowym komunikatem o wprowadzonych zmianach

  • +
  • \(\texttt{git commit --amend - m "new message"}\) - zmienia ostatni komunikat

  • +
  • \(\texttt{git log}\) - wyświetlenie historii projektu (od najnowszych zmian). Wyświetlany zostaje unikatowy identyfikator dla zatwierdzenia oraz informacje na temat tego kto dokonał zmiany, kiedy i jaki komunikat napisał dokonując zmiany.

  • +
  • \(\texttt{spacja}\) - przejcie w dół o stronę

  • +
  • \(\texttt{q}\) - wyjście

  • +
  • \(\texttt{git log path}\) - wyświetlenie historii danego pliku lub katalogu

  • +
+
+
+

10.3 Repozytoria

+

   Informacje dotyczące zatwiedzonych zmian przechowywane są poprzez trzypoziomową strukturę. Każde zatwierdzenie (tzw. commit) zwiera komunikat o zatwierdzeniu i informacje o autorze i czasie, w którym zatwierdzenie zmian zostało wykonane. Każdy commit ma również swoje drzewo, które śledzi, gdzie w repozytorium dokonano zmian. Dla każdego pliku w drzewie istnieje tzw. blob (binary large object). Każdy blob zawiera skompresowaną migawkę zawartości pliku, z chwili w której nastąpił commit.

+

Czym jest hash?

+

Każde zatwierdzenie zmian w repozytorium ma unikalny identyfikator zwany hashem. Jest on zapisywany jako 40-znakowy ciąg szesnastkowy. Zazwyczaj jednak wystarczy podać pierwsze 6 lub 8 znaków hasha, by odnaleźć konkretne zatwierdzenie (commit). Identyfikatory jakimi są hashe umożliwiają Gitowi wydajne udostępnianie danych pomiędzy repozytoriami.

+

Jak wyświetlić konkretny commit?

+

By wyświetlić szczegóły dotyczące konkretnego commitu należy użyć komendy git show z pierwszymi 6 znakami hasha danego commmitu np.: git show Oda2f7.

+

Czym jest odpowiednik ścieżki względnej w Git?

+

Innym sposobem identyfikacji zatwierdzenia jest użycie odpowiednika ściezki względnej. By wyświetlić zatem ostatni commit możemy użyć komendy git show z etykietą HEAD. Jeśli natomiast zamiast HEAD wpiszemy HEAD~1 wyświetlony zostanie przedostatni commit, polecenie git show HEAD~2 zwróci nam natomiast jeszcze wcześniejszy commit itp.

+
+
+

10.4 Podstawowe komendy

+
    +
  • git log - wyświetla całą historię danego pliku lub projektu. W Gicie możemy jednak sprawdzić bardziej szczegółowe informacje. Dzięki poleceniu git annotate file możemy sprawdzić kto i kiedy dokonał ostatniej zmiany w każdej linijce pliku.

  • +
  • git diff ID1..ID2 - umożliwia sprawdzenie zmian pomiędzy dwoma commitami, których identyfikatory to odpowiednio ID1 i ID2.

  • +
  • git add - polecenie umożliwiające dodanie nowego pliku. Po wykonaniu tego polecenia Git zaczyna śledzić dodany plik.

  • +
  • git clean -n - pokazuje listę plików, które są w repozytorium, ale których historia nie jest śledzona przez Gita.

  • +
  • git clean -f - usuwa pliki, które są w repozytorium i których historii nie śledzi Git. Z używaniem tego polecenia należy uważać, ponieważ usuwa ono pliki z pamięci na stałe i nie da się ich już odzyskać.

  • +
  • git config - -list - wyświetla ustawienia Gita.

  • +
  • git config - -system - wyświetla ustawienia każdego użytkownika na danym komputerze.

  • +
  • git config - -global - wyświetla ustawienia każdego projektu.

  • +
  • git config - -local - wyświetla ustawienia poszczególnego projektu.

  • +
+

Każdy poziom zastępuje poziom nad nim, więc ustawienia lokalne (na projekt) mają pierwszeństwo przed ustawieniami globalnymi (na użytkownika), które z kolei mają pierwszeństwo przed ustawieniami systemowymi (dla wszystkich użytkowników na komputerze).

+
    +
  • git config - -global setting value - zmienia konfigurację odpowiedniej wartości dla wszystkich projektów na danym komputerze. Jako setting należy wpisać to co chcemy zmienić (np. user.name, user.email itp.), a jako value to co chcemy ustawić.
  • +
+
+
+

10.5 Cofanie zmian

+

Teraz dowiemy się jak cofnąć wprowadzone zmiany.

+
    +
  • \(\texttt{git reset HEAD}\)- usuwa ostatnio dodany plik ze śledzenia,

  • +
  • \(\texttt{git checkout -- filename}\) - odrzuci zmiany, które nie zostały jeszcze dodane do śledzenia,

  • +
  • \(\texttt{git reset HEAD path/to/file}\) - odrzuci ostatnie zmiany w pliku, który został juz dodany do śledzenia,

  • +
  • \(\texttt{git checkout 2242bd filename}\)- zamienia aktualna wersje pliku, na tę o hashu ‘2242bd’.

  • +
+

Do ostatniej komendy przydatne może być wykonanie poniższzego polecenia, aby sprawdzić hashe plików.

+
    +
  • \(\texttt{git log - 3 filename}\)- pokaże 3 ostatnie commity dotyczące wskazanego pliku.
  • +
+

Poniższe dwie komendy pokazują, jak cofać zmiany na więcej niż jednym pliku.

+
    +
  • \(\texttt{git reset HEAD data}\)- usuwa ze śledzenia wszystkie pliki z katalogu data. Jeżeli nie podamy nazwy katalogu( wtedy wystarczy samo \(\texttt{git reset}\)) wszystkie pliki zostaną usunięte.

  • +
  • \(\texttt{git checkout -- data}\)- wszystkie pliki w katalagu data zostaną cofnięte do poprzednich wersji.

  • +
+
+
+

10.6 Gałęzie

+

Jeśli nie używasz kontroli wersji, typowym przepływem pracy jest tworzenie różnych podkatalogów do przechowywania różnych wersji projektu w różnych stanach, na przykład deweloperskich i końcowych. Oczywiście zawsze kończy się to ostateczną aktualizacją i ostateczną aktualizacją-poprawioną. Problem polega na tym, że trudno jest to rozwiązać, jeśli masz odpowiednią wersję każdego pliku w odpowiednim podkatalogu i ryzykujesz utratę pracy. + 

+Jednym z powodów, dla których Git jest popularny, jest jego obsługa tworzenia gałęzi (branchy), co pozwala na posiadanie wielu wersji Twojej pracy i pozwala na systematyczne śledzenie każdej wersji. + 

+Każda gałąź jest jak wszechświat równoległy: zmiany, które wprowadzasz w jednej gałęzi, nie wpływają na inne gałęzie (dopóki nie połączysz ich z powrotem). + 

+Domyślnie kazde repozytorium Gita ma branch zwany master. + 

+Podstawowe komendy związanie z działaniem na branchach (gałęziach):  

+
    +
  • \(\texttt{git branch}\) - pokazuje wszystkie branche w repozytorium (branch, w którym obecnie się znajdujesz będziesz wylistowany z \(*\)).  

  • +
  • \(\texttt{git diff branch1..branch2}\) - wyświetla różnice między dwoma branchami  

  • +
+

Ciekawostka:

+
    +
  • \(\texttt{git diff branch1..branch2}\) - -\(\texttt{shortstat}\) - wyświetla konkretną liczbę plików które się różnią między dwoma branchami 

  • +
  • \(\texttt{git checkout branch1}\) - pozwala przełączyć się na branch1 

  • +
  • \(\texttt{git checkout -b branch-name}\) - pozwala utworzyć nowego brancha o nazwie branch-name 

    +Rozgałęzianie pozwala tworzyć równoległe wszechświaty. Scalanie (merging) to sposób, w jaki łączysz je z powrotem. Kiedy łączysz jedną gałąź (nazwijmy ją źródłową) z inną (nazwijmy ją docelową), Git włącza zmiany wprowadzone w gałęzi źródłowej do gałęzi docelowej. Jeśli te zmiany nie nakładają się, wynikiem jest nowe zatwierdzenie w gałęzi docelowej, które zawiera wszystko z gałęzi źródłowej. Do mergowania dwóch gałęzi używamy polecenia:  

  • +
  • \(\texttt{git merge source destination}\) - mergowanie dwóch branchy w jeden 

  • +
+

Czasami zmiany w dwóch gałęziach będą ze sobą kolidować: na przykład poprawki błędów mogą dotyczyć tych samych wierszy kodu lub analizy w dwóch różnych gałęziach mogą dołączać nowe (i różne) rekordy do pliku danych podsumowania. W takim przypadku ty decydujesz o sprzeczności zmian. 

+Jeżeli podczas mergowania występuje konflikt Git informuje Cię, że wystapił problem a \(\texttt{git status}\) poinformuje Cię, które pliki wmagają rozwiązania konfliktów. 

+Git pozostawia na danym pliku znaczniki, aby poinformować Cię o konkretnym miejscu konfliktu. Znaczniki te wyglądają następująco: +<<<<<<< destination-branch-name ...changes from the destination branch... ======= ...changes from the source branch... >>>>>>> source-branch-name
+Aby rozwiązać konflikt edytuj plik, usuwając znaczniki i wprowadź wszelkie zmiany potrzbne do rozwiązania kofilktu, a następnie zrób commit tych zmian. 

+
+
+

10.7 Tworzenie własnych repozytoriów

+

Przejdźmy do kolejnego zagadnienia związanego z pracą w Gicie.  

+Do tej pory wszystkie poznane funkcje Gita dotyczyły działań na repozytoriach już istniejących. Aby stworzyć własne repozytorium w bieżącym katalogu roboczym wystarczy komenda: 

+
    +
  • \(\texttt{git init project-name}\)  
  • +
+

Warto wspomnieć, że chociaż Git pozwala tworzyć zagnieżdżone repozytoria nie powinieneś tego robić. Aktualizacja takich repozytoriów bardzo szybko staje się bardzo skomplikowana, ponieważ musisz powiedzieć Gitowi, w którym z dwóch katalogów .git ma być przechowywana aktualizacja. 

+Nie tworzymy repozytorium w innym już istniejącym!  

+Poniżej kilka ważnych komend: 

+
    +
  • \(\texttt{git init}\) - inicjalizacja repozytorium w bieżącym katalogu  

  • +
  • \(\texttt{git init /path/to/project}\) - inicjalizacja repozytorium we wskazanym ścieżką katalogu  

  • +
  • \(\texttt{git clone URL}\) - tworzenie kopii istniejącego pod wskazanym adresem URL repozytorium

  • +
  • \(\texttt{git clone /existing/project newprojectname}\) - tworzenie kopii istniejącego repozytroium o zadanej nazwie - newprojectname  

  • +
  • \(\texttt{git remote}\) - wyświetla informację o fizycznej lokalizacji na serwerze Gita, z której zostało sklonowane repo  

  • +
  • \(\texttt{git remote -v}\) - wyświetla informację o URL serwerze Gita, z którego zostało sklonowane repo  

  • +
  • \(\texttt{git remote add remote-name URL}\) - pozawala na dodanie własnego remota z podanego URL

    +
      +
    • \(\texttt{git remote rm remote-name}\) - usuwanie istniejącego remota  
    • +
  • +
  • \(\texttt{git pull remote branch}\) - pobieranie zmian w branchu w lokalnym repozytorium i mergowanie ich z bieżacym brnachem w lokalnym repozytorium  

  • +
+

Uwaga!  

+

Git powstrzymuje Cię przed pobieraniem ze zdalnego repozytorium zmian, które mogą nadpisać niezapisane lokalnie zmiany. Wystarczy zrobić commit tych zmian lub cofnąć je, a następnie spullować repo ponownie.  

+
    +
  • \(\texttt{git push remote-name branch-name}\) - pushuje zmiany wprowadzone lokalnie na danym branchu do zdalnego repozytorium  
  • +
+ +
+
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + diff --git a/docs/_book/programowanie-obiektowe-w-r-klasy-s3.html b/docs/_book/programowanie-obiektowe-w-r-klasy-s3.html new file mode 100644 index 0000000..ce959c2 --- /dev/null +++ b/docs/_book/programowanie-obiektowe-w-r-klasy-s3.html @@ -0,0 +1,784 @@ + + + + + + + Chapter 11 Programowanie obiektowe w R: klasy S3 | Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+
+ + +
+
+ +
+
+

Chapter 11 Programowanie obiektowe w R: klasy S3

+

Programując w R jesteśmy oswojeni z myśleniem kategoriami funkcji - przekształceń nakładanych na macierze lub ramki danych. Jest to naturalne ze względu na zastosowanie R głównie w statystyce i pochodnych jej dziedzin.

+

Tymczasem programowanie obiektowe, choć często niepotrzebne do przeprowadzenia analiz lub symulacji, może się okazać użyteczne przy tworzeniu większego projektu, w szczególności projektu współtworzonego przez więcej osób. Zdefiniowanie klas i metod nadaje projektowi strukturę, co sprawia, że jego rozbudowa przebiega mniej chaotycznie.

+

Ponadto, znajomość podstaw systemów programowania obiektowego w R umożliwia nam lepsze zrozumienie działania bazowych funkcji i obiektów R oraz ewentualne ich rozbudowywanie.

+
+

11.1 Systemy programowania obiektowego w R

+

W przeciwieństwie do wielu popularnych języków programowania, R nie ma jednego ujednoliconego systemu programowania obiektowego - jest ich wiele, przy czym różnią się nie tylko składnią, ale też funkcjonalnościami.

+

Pierwsze wersje pierwowzoru języka R - języka S nie posiadały żadnego systemu obiektowego. Wraz z trzecią wersją S wprowadzono pierwszy z nich: S3. Następnie, kiedy ten okazał się niewystarczający dla potrzeb użytkowników - S4. Oba systemy finalnie znalazły się w base języka R. Z czasem, w miarę wzrastania potrzeb, powstawały kolejne alternatywne systemy klas, które funkcjonowały równolegle i równoprawnie.

+

Do dzisiaj nie wyróżniamy systemu “oficjalnego” czy preferowanego - każdy z kilku pozostałych w powszechnym użyciu ma swoje zastosowania, w których niekorzystnym lub niewygodnym jest zastąpienie go innym. W tej notatce przyjrzymy się przede wszystkim S3.

+
+
+

11.2 S3

+

S3 to system, z którym stykamy się najczęśniej. Wszystkie wbudowane klasy obiektów zostały zbudowane właśnie przy pomocy systemu S3. By sprawdzić, do jakiej klasy S3 należy obiekt, używamy funkcji class. +W codziennej pracy w R operujemy w wiekszości na obiektach zbudowanych w S3. Klasy S3 to m.in. factor,data.frame,matrix.

+
f <- factor(c("y","n","y","n","n"))
+class(f)
+
## [1] "factor"
+

Warto w tym miejscu podkreślić, że klasa zmiennej nie jest równoważna typowi zmiennej, np. macierz liczb jest klasy matrix, ale typu double.

+
m <- matrix(c(1,2,3,4),2,2)
+class(m)
+
## [1] "matrix" "array"
+
typeof(m)
+
## [1] "double"
+

Każdemu obiektowi mogą być (ale nie muszą) przypisane atrybuty. Atrybuty mozna rozumieć jako cechy lub parametry obiektu. W przypadku macierzy są to jej wymiary.

+
attributes(m)
+
## $dim
+## [1] 2 2
+
+

11.2.1 Klasy i atrybuty

+

W systemie S3 nie tworzymy definicji klasy, nie określamy również, jakie atrybuty obiekt danej klasy ma. Obiektowi możemy nadać klasę przy jego utworzeniu, z użyciem funkcji structure:

+
kanapka <-structure(c("szynka", "margaryna", "chleb"), class = "jedzenie")
+class(kanapka)
+
## [1] "jedzenie"
+

lub w dowolnym momencie po jego utworzeniu z użyciem class:

+
szarlotka <- c("jaja", "mąka", "masło", "cukier")
+szarlotka <- c(szarlotka, "jabłka")
+class(szarlotka) <- "jedzenie"
+class(szarlotka)
+
## [1] "jedzenie"
+

Każdemu obiektowi możemy również indywidualnie przypisać atrybuty, również na kilka sposobów, przy jego utworzeniu z użyciem structure:

+
hot_dog <- structure(c("parówka", "bułka", "ketchup"), class="jedzenie", kalorie = 300)
+attributes(hot_dog)
+
## $class
+## [1] "jedzenie"
+## 
+## $kalorie
+## [1] 300
+
class(hot_dog)
+
## [1] "jedzenie"
+

Lub w dowolnym momencie z użyciem funkcji attr:

+
attr(kanapka, "kalorie")=150
+attr(szarlotka, "kalorie")=265
+attributes(kanapka)
+
## $class
+## [1] "jedzenie"
+## 
+## $kalorie
+## [1] 150
+
attributes(szarlotka)
+
## $class
+## [1] "jedzenie"
+## 
+## $kalorie
+## [1] 265
+

Z użyciem funkcji attributes i attr można również “dostać się” do wartości atrybutów obiektu:

+
attr(szarlotka, "kalorie")
+
## [1] 265
+
attributes(szarlotka)$kalorie
+
## [1] 265
+

Tworzenie obiektów różnych klas S3 jest więc bardzo proste i nie wymaga (przynajmniej formalnie) predefiniowania klasy i atrybutów. System jest więc z jednej strony bardzo elastyczny, z drugiej - nieprecyzyjny. Niesie to za sobą pewne konsekwencje, np. formalnie nic nie stoi na przeszkodzie by zrobić coś takiego:

+
droga <- c("asfalt","pobocze", "lewy pas", "prawy pas")
+class(droga) <- "jedzenie"
+class(droga)
+
## [1] "jedzenie"
+

lub takiego…

+
średnia_bez_na <- function(...) mean(na.rm=TRUE,...)
+class(średnia_bez_na)<-"jedzenie"
+class(średnia_bez_na)
+
## [1] "jedzenie"
+

lub takiego:

+
attr(hot_dog, "kalorie") <- "przecież to prawie nie ma kalorii!"
+bilans_posilkow <- attr(hot_dog,"kalorie")+attr(szarlotka, "kalorie")+attr(kanapka, "kalorie")
+
## Error in attr(hot_dog, "kalorie") + attr(szarlotka, "kalorie"): argument nieliczbowy przekazany do operatora dwuargumentowego
+

Dlatego należy pamiętać, by klas i atrybutów nie przydzielać chaotycznie, zachować pewne reguły, mimo że formalnie nie są wymagane przy użyciu S3.

+
+
+

11.2.2 Funkcje generyczne i metody

+

Metody to funkcje działające na obiektach danej klasy. Z reguły są predefiniowane przy utworzeniu klasy wraz z polami. Inaczej jednak jest z systemem S3 w R. Nie definiujemy klasy - klasa jest tworzona przy pierwszym przypisaniu jej jakiemuś obiektowi. Metody tworzy się przy pomocy funkcji generycznych (generics).

+
+

11.2.2.1 Funkcje generyczne

+

By lepiej zrozumieć logikę stojącą za funkcjami generycznymi, spróbujmy spojrzeć na klasy i ich metody z nieco mniej standardowej perspektywy. Dla różnych klas możemy mieć analogiczne metody, zachowujące się nieco inaczej w zależności od specyfiki klasy, np. inaczej rozumiemy różnicę między dwoma datami a różnicę między dwoma liczbami - liczby odejmujemy od siebie bezpośrednio, podczas gdy w przypadku dat oczekujemy różnicy w dniach pomiędzy nimi - w tym celu nie wystarczy bezpośrednie odjęcie od siebie dwóch dat.

+

W systemie S3 metody nie są przypisane bezpośrednio klasie, są przypisane odpowiedniej funkcji generycznej. Funkcja generyczna określa nazwę metody wspólną dla wszystkich klas i umożliwia tworzenie wariantów metody dla różnych klas pod tą konkretną nazwą. Zanim przejdziemy do tworzenia funkcji generycznych oraz metod dla własnych klas przyjrzyjmy się działaniu już istniejących.

+

Jedną z funkcji generycznych jest funkcja summary - funkcja podsumowująca obiekt (np. summary(lm(X~Y))).

+
summary
+
## function (object, ...) 
+## UseMethod("summary")
+## <bytecode: 0x55bf1e71aba0>
+## <environment: namespace:base>
+

Użyjemy funkcji methods, by wylistować wszystkie dostępne metody dla danej funkcji generycznej.

+
methods(summary)
+
##   [1] summary,ANY-method                  summary,DBIObject-method           
+##   [3] summary,diagonalMatrix-method       summary,marrayInfo-method          
+##   [5] summary,marrayLayout-method         summary,marrayNorm-method          
+##   [7] summary,marrayRaw-method            summary,mle-method                 
+##   [9] summary,sparseMatrix-method         summary.aareg*                     
+##  [11] summary.allFit*                     summary.aov                        
+##  [13] summary.aovlist*                    summary.aspell*                    
+##  [15] summary.cch*                        summary.check_packages_in_dir*     
+##  [17] summary.connection                  summary.corAR1*                    
+##  [19] summary.corARMA*                    summary.corCAR1*                   
+##  [21] summary.corCompSymm*                summary.corExp*                    
+##  [23] summary.corGaus*                    summary.corIdent*                  
+##  [25] summary.corLin*                     summary.corNatural*                
+##  [27] summary.corRatio*                   summary.corSpher*                  
+##  [29] summary.corStruct*                  summary.corSymm*                   
+##  [31] summary.coxph*                      summary.coxph.penal*               
+##  [33] summary.data.frame                  summary.Date                       
+##  [35] summary.default                     summary.Duration*                  
+##  [37] summary.ecdf*                       summary.EList*                     
+##  [39] summary.EListRaw*                   summary.factor                     
+##  [41] summary.gam*                        summary.ggplot*                    
+##  [43] summary.glm                         summary.gls*                       
+##  [45] summary.haven_labelled*             summary.hcl_palettes*              
+##  [47] summary.infl*                       summary.Interval*                  
+##  [49] summary.lm                          summary.lme*                       
+##  [51] summary.lmList*                     summary.lmList4*                   
+##  [53] summary.loess*                      summary.loglm*                     
+##  [55] summary.MAList*                     summary.manova                     
+##  [57] summary.MArrayLM*                   summary.matrix                     
+##  [59] summary.merMod*                     summary.mlm*                       
+##  [61] summary.modelStruct*                summary.negbin*                    
+##  [63] summary.nls*                        summary.nlsList*                   
+##  [65] summary.packageStatus*              summary.pdBlocked*                 
+##  [67] summary.pdCompSymm*                 summary.pdDiag*                    
+##  [69] summary.pdIdent*                    summary.pdIdnot*                   
+##  [71] summary.pdLogChol*                  summary.pdMat*                     
+##  [73] summary.pdNatural*                  summary.pdSymm*                    
+##  [75] summary.pdTens*                     summary.Period*                    
+##  [77] summary.polr*                       summary.POSIXct                    
+##  [79] summary.POSIXlt                     summary.ppr*                       
+##  [81] summary.prcomp*                     summary.prcomplist*                
+##  [83] summary.princomp*                   summary.proc_time                  
+##  [85] summary.pyears*                     summary.ratetable*                 
+##  [87] summary.reStruct*                   summary.RGList*                    
+##  [89] summary.rlang_error*                summary.rlang_message*             
+##  [91] summary.rlang_trace*                summary.rlang_warning*             
+##  [93] summary.rlang:::list_of_conditions* summary.rlm*                       
+##  [95] summary.RUnitTestData*              summary.shingle*                   
+##  [97] summary.srcfile                     summary.srcref                     
+##  [99] summary.stepfun                     summary.stl*                       
+## [101] summary.summary.merMod*             summary.survexp*                   
+## [103] summary.survfit*                    summary.survfitms*                 
+## [105] summary.survreg*                    summary.table                      
+## [107] summary.TestResults*                summary.tmerge*                    
+## [109] summary.trellis*                    summary.tukeysmooth*               
+## [111] summary.varComb*                    summary.varConstPower*             
+## [113] summary.varConstProp*               summary.varExp*                    
+## [115] summary.varFixed*                   summary.varFunc*                   
+## [117] summary.varIdent*                   summary.varPower*                  
+## [119] summary.vctrs_sclr*                 summary.vctrs_vctr*                
+## [121] summary.warnings                    summary.XMLInternalDocument*       
+## see '?methods' for accessing help and source code
+

Każda z wypisanych nazw odpowiada wariantowi metody dla jednej klasy. Zwrócmy uwagę na specyficzną składnię nazw tych funkcji - człon po kropce odpowiada nazwie klasy, jakiej metoda dotyczy. Przyjrzyjmy się wariantom summary dla dwóch różnych klas: lm i matrix.

+
X <- matrix(rep(1,12), 6,2)
+Y <- c(2,2,3,2,2,2)
+model <- lm(Y~X)
+
+summary.lm(model)
+
## 
+## Call:
+## lm(formula = Y ~ X)
+## 
+## Residuals:
+##       1       2       3       4       5       6 
+## -0.1667 -0.1667  0.8333 -0.1667 -0.1667 -0.1667 
+## 
+## Coefficients: (2 not defined because of singularities)
+##             Estimate Std. Error t value Pr(>|t|)    
+## (Intercept)   2.1667     0.1667      13  4.8e-05 ***
+## X1                NA         NA      NA       NA    
+## X2                NA         NA      NA       NA    
+## ---
+## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
+## 
+## Residual standard error: 0.4082 on 5 degrees of freedom
+
summary.matrix(X)
+
##        V1          V2   
+##  Min.   :1   Min.   :1  
+##  1st Qu.:1   1st Qu.:1  
+##  Median :1   Median :1  
+##  Mean   :1   Mean   :1  
+##  3rd Qu.:1   3rd Qu.:1  
+##  Max.   :1   Max.   :1
+

Jednak, by użyć odpowiedniej funkcji dla obiektu, nie musimy specyfikować jego klasy - właśnie dzięki zdefiniowaniu funkcji generycznej. Bez względu na klasę obiektu uzywamy składni funkcja_generyczna(obiekt). Wywoływana jest wówczas funkcja generyczna, która na podstawie klasy lub typu obiektu dopasowuje wariant metody. Spójrzmy jak to wygląda na przykładzie summary:

+
summary(model)
+
## 
+## Call:
+## lm(formula = Y ~ X)
+## 
+## Residuals:
+##       1       2       3       4       5       6 
+## -0.1667 -0.1667  0.8333 -0.1667 -0.1667 -0.1667 
+## 
+## Coefficients: (2 not defined because of singularities)
+##             Estimate Std. Error t value Pr(>|t|)    
+## (Intercept)   2.1667     0.1667      13  4.8e-05 ***
+## X1                NA         NA      NA       NA    
+## X2                NA         NA      NA       NA    
+## ---
+## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
+## 
+## Residual standard error: 0.4082 on 5 degrees of freedom
+
summary(X)
+
##        V1          V2   
+##  Min.   :1   Min.   :1  
+##  1st Qu.:1   1st Qu.:1  
+##  Median :1   Median :1  
+##  Mean   :1   Mean   :1  
+##  3rd Qu.:1   3rd Qu.:1  
+##  Max.   :1   Max.   :1
+

Jak widać na przykładzie powyżej wywołanie funkcji generycznej na obiekcie spowodowało dopasowanie odpowiedniej dla klasy obiektu metody i dało identyczny efekt jak wywołanie bezpośrednio dedykowanej funkcji.

+
+
+

11.2.2.2 Tworzenie funkcji generycznych i metod

+

Nowe funkcje generyczne tworzy się według następującego schematu:

+
nazwa_metody <- function (x) {
+  UseMethod("nazwa_metody", x)
+}
+

Po utworzeniu funkcji generycznej możemy przystąpić do napisania metod dla konkretnych klas. Metody tworzymy jako funkcje nazwane według konwencji nazwa_metody.klasa.

+

Spróbujmy wg powyższego schematu utworzyć metodę dla utworzonej wcześniej klasy jedzenie. Niech nasza metoda nazywa się zjedz:

+
zjedz <- function(x){
+  UseMethod("zjedz",x)
+}
+

Mając gotową funkcję generyczną możemy przejść do określenia zachowania metody dla naszej klasy:

+
zjedz.jedzenie <- function(x){
+  cat("Mniam mniam\n")
+}
+zjedz(szarlotka)
+
## Mniam mniam
+
zjedz(kanapka)
+
## Mniam mniam
+
zjedz(hot_dog)
+
## Mniam mniam
+

Oraz, jeśli potrzebujemy, również dla innych istniejących klas, w tym również wbudowanych, np. matrix.

+
zjedz.matrix <- function(x){
+  warning("Przeciez to macierz! Tego sie nie je!\n")
+}
+zjedz(matrix(c(1,2,2,3),2,2))
+
## Warning in zjedz.matrix(matrix(c(1, 2, 2, 3), 2, 2)): Przeciez to macierz! Tego sie nie je!
+

W szczególności możemy określić zachowanie metody dla pseudoklasy default, czyli określić domyślne zachowanie metody. W kontekście naszego przykładu z klasą jedzenie moglibyśmy oczekiwać, że nasza metoda domyślnie nie będzie “jeść” żadnych obiektów, robiąc wyjątek wyłącznie dla jedzenia.

+
zjedz.default <- function(x,...){
+  warning("Tego sie nie je!\n")
+}
+zjedz(mean)
+
## Warning in zjedz.default(mean): Tego sie nie je!
+
zjedz(c(1,2,3,4))
+
## Warning in zjedz.default(c(1, 2, 3, 4)): Tego sie nie je!
+

Zauważmy, że istnieje tu pewna hierarchia:

+
zjedz(matrix(c(1,2,3,4,4,5),2,3))
+
## Warning in zjedz.matrix(matrix(c(1, 2, 3, 4, 4, 5), 2, 3)): Przeciez to macierz! Tego sie nie je!
+
zjedz(szarlotka)
+
## Mniam mniam
+

Funkcja generyczna wywołuje metodę dla pseudoklasy default wtedy, gdy dla danej klasy indywidualnie nie ma zdefiniowanej metody. W pierwszej kolejności szuka metody dedykowanej dla danej klasy, dlatego dla obiektów klas jedzenie i matrix zostały wywołane odpowiednio zjedz.jedzenie i zjedz.matrix, a nie zjedz.default.

+
+
+
+

11.2.3 Dziedziczenie

+

O dziedziczeniu mówimy, gdy jedna z klas przejmuje (dziedziczy) właściwości i cechy innej. W S3 dziedziczenie wprowadza się w zaskakująco oczywisty i prosty sposób.

+

Każdemu obiektowi można przypisać więcej niż jedną klasę, przypisując class(obiekt) nie jedną nazwę klasy, lecz ich wektor, np. dla utworzonego wcześniej obiektu szarlotka:

+
class(szarlotka) <- c("ciasto", "slodkosci", "jedzenie")
+class(szarlotka)
+
## [1] "ciasto"    "slodkosci" "jedzenie"
+

Kolejność klas w wektorze nie jest przypadkowa: zaczynamy od klasy “najmłodszej” (i najważniejszej, najbardziej specyficznej), a kończymy na “najstarszej” (najogólniejszej). Kolejność ta jest również obowiązująca przy dopasowywaniu metod przez funkcję generyczną: funkcja generyczna najpierw szuka metody dla pierwszej z klas w wektorze, następnie dla drugiej itd. Jeśli nie uda jej się znaleźć metody dla żadnej z klas, wywołuje metodę dla pseudoklasy default.

+
zjedz.ciasto <- function(x){
+  cat("Mniam mniam, pyszne ciacho!\n")
+}
+zjedz.slodkosci <- function(x){
+  cat("Słodkie, dobre, mniam.. \n")
+}
+zjedz(szarlotka)
+
## Mniam mniam, pyszne ciacho!
+

Zgodnie z oczekiwaniami funkcja generyczna wywołała metodę dla pierwszej z klas w wektorze, czyli klasy ciasto, ignorując metody dla klas slodkosci i jedzenie.

+

W większości przypadków jednak wprowadzamy nowe klasy nie po to, by zastępować istniejące metody dla klas ogólniejszych, lecz po to by wprowadzić pewne rozszerzenia, np. metody czy atrybuty, które nie mają sensu dla innych obiektów z klasy - rodzica.

+

Wprowadźmy metodę pokroj. Dla każdego obiektu klasy jedzenie bedzie ona dzialac identycznie - dzielić atrybut kalorie przez wskazane n i zwracać n równych “porcji” :

+
pokroj<-function(x,...){
+  UseMethod("pokroj",x)
+}
+pokroj.jedzenie <- function(x,n){
+  porcja<-attr(x, "kalorie")/n
+  rep(porcja,n)
+}
+pokroj(kanapka,4)
+
## [1] 37.5 37.5 37.5 37.5
+

Oprócz tego wprowadźmy metodę sensowną tylko dla klasy ciasto: posyp_cukrem_pudrem.

+
posyp_cukrem_pudrem<-function(x,...){
+  UseMethod("posyp_cukrem_pudrem",x)
+}
+posyp_cukrem_pudrem.ciasto <- function(x){
+  cat("Syp syp syp\n")
+}
+

Zauważmy, że przez wprowadzenie dodatkowych klas obiekt szarlotka nie traci funkcjonalności klasy jedzenie:

+
pokroj(szarlotka,5)
+
## [1] 53 53 53 53 53
+

Podczas gdy równocześnie możliwym stało się zdefiniowanie dla niego indywidualnych metod:

+
posyp_cukrem_pudrem(szarlotka)
+
## Syp syp syp
+
posyp_cukrem_pudrem(hot_dog)
+
## Error in UseMethod("posyp_cukrem_pudrem", x): niestosowalna metoda dla 'posyp_cukrem_pudrem' zastosowana do obiektu klasy "jedzenie"
+

Dziedziczenie może być bardzo użyteczne, ale należy je stosować z ostrożnością, mając w pamięci elastyczność klas i atrybutów w S3. Bardzo łatwo stracić orientację, w szczególności wprowadzając dziedziczenie po klasach wbudowanych lub po klasach zbudowanych przez kogoś innego - wówczas ciężko nam wziąć pod uwagę wszystkie istniejące dla danych klas metody. Posługując się dziedziczeniem i klasami w sposób rozrzutny i nieprzemyślany łatwo możemy doprowadzić do chaosu.

+
+

11.2.3.1 NextMethod

+

NextMethod jest używane w sytuacji, gdy wewnątrz metody klasy chcemy wywołać metodę klasy nadrzędnej (rodzica).

+
zjedz.ciasto<- function(x){
+  cat("Mniam mniam, pyszne ciacho!\n")
+  NextMethod()
+}
+
+zjedz(szarlotka)
+
## Mniam mniam, pyszne ciacho!
+## Słodkie, dobre, mniam..
+
zjedz.slodkosci <- function(x){
+  cat("Słodkie, dobre, mniam.. \n")
+  NextMethod()
+}
+zjedz(szarlotka)
+
## Mniam mniam, pyszne ciacho!
+## Słodkie, dobre, mniam.. 
+## Mniam mniam
+
+
+
+
+

11.3 S4

+

S3 jest użyteczny i do niektórych zastosowań wystarczający, ale nie posiada wielu własności znanych z systemów programowania w innych językach. Ponadto, ze względu na dużą swobodę w tworzeniu klas i metod, utrzymanie bardziej złożonych struktur i hierarchii może być uciążliwe i mało przejrzyste z użyciem S3.

+

Pierwszą alternatywą dla S3 był system S4. W S4 metody tworzone z użyciem tej samej logiki - przez funckje generyczne. W przeciwieństie do S3, system S4 wymaga zdefiniowania klasy, w szczególności jej pól (slotów) i dziedziczenia po innych klasach.

+

Klasę definiuje się z użyciem funkcji setClass:

+
# pierwszym argumentem funkcji jest nazwa klasy
+setClass("nazwa_klasy",
+  slots = c(
+    # tutaj definiowane są sloty i ich typ
+    slot_1 = "data.frame",
+    slot_2 = "list"
+  ),
+  prototype = c(
+    # tutaj definiowane są wartości domyślne (prototyp) dla slotów
+    slot_1 = data.frame(),
+    slot_2 = list()
+  )
+)
+

Funkcja setClass posiada również parametr contains, który odpowiada za dziedziczenie po innych klasach:

+
setClass("klasa_rodzic", 
+         slots=c(
+           macierz = "matrix"
+         ))
+setClass("klasa_dziecko", contains="klasa_rodzic")
+

Funkcje generyczne z użyciem których tworzymy metody S4 są rozróżniane od funkcji generycznych systemu S3. Mechanizm tworzenia metody jest więc bardzo podobny, ale używamy do tego celu dedykowanych dla S4 funkcji.

+
# tworzenie funkcji generycznej S4
+setGeneric("nazwa_metody", function(x, ...) standardGeneric("nazwa_metody"))
+
## [1] "nazwa_metody"
+
# tworzenie metody dla klasy
+setMethod("nazwa_metody", "nazwa_klasy", function(x,...){
+  # działanie metody na obiekcie klasy 
+})
+
+

11.3.1 Bonus: przykład wykorzystania systemu S4

+

S4, choć bardziej restrykcyjny niż S3, nadal daje dużo swobody w przypisywaniu klas, dziedziczeniu, w szczególności w manewrowaniu wbudowanymi klasami R.

+

Wykorzystaliśmy to w naszym raczkującym pakiecie autoeda do ominięcia problemu przypisania różnego zachowania funkcji w zależności od otrzymanego typu danych.

+

Celem było obliczenie tej samej funkcji (np. średniej) dla wszystkich kolumn danych, przy założeniu, że nasz zbiór danych jest średniej wielkości (kilkadziesiąt kolumn - zmiennych). Jeśli niemożliwe jest obliczenie funkcji dla danej kolumny (np. próbujemy obliczyć średnią z kolumny stringów), chcieliśmy uniknąć przerywania pracy funkcji i zwracać NA.

+

By osiągnąć powyższy rezultat zdecydowaliśmy się zdefiniować klasę funkcji - miar obliczanych na kolumnach jako klasę dziedziczącą po… klasie funkcji generycznych:

+
setClass("RankingMeasure",
+  slots = c(
+    name = "character",
+    description = "character"
+  ),
+  prototype = list(
+    name = NA_character_,
+    description = NA_character_
+  )
+)
+setClass("BuiltInMeasure",
+  contains = c("standardGeneric", "RankingMeasure")
+)
+

Następnie dla każdej potrzebnej nam funkcji utworzyliśmy odpowiadający jej obiekt - funkcję generyczną klasy BuiltInMeasure i zdefiniowaliśmy metody tej funkcji generycznej dla możliwych typów zmiennych, zwracając NA domyślnie i wynik liczbowy, gdzie to możliwe.

+ +
+
+
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + diff --git a/docs/_book/przetwarzanie-danych-tabelarycznych.html b/docs/_book/przetwarzanie-danych-tabelarycznych.html new file mode 100644 index 0000000..28377a2 --- /dev/null +++ b/docs/_book/przetwarzanie-danych-tabelarycznych.html @@ -0,0 +1,654 @@ + + + + + + + Chapter 5 Przetwarzanie danych tabelarycznych | Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+
+ + +
+
+ +
+
+

Chapter 5 Przetwarzanie danych tabelarycznych

+

Operacje na danych w R są związane głównie z filtrowaniem, dodawaniem i modyfikowaniem kolumn, grupowaniem oraz podsumowywaniem danych. Można je wykonywać za pomocą funkcji bazowego R lub narzędzi z zaimportowanych pakietów: tidyverse, data.table.

+

Załóżmy, że ramka danych jest przypisana do zmiennej \(dane\), a nazwy jej kolumn to: \(kol.1, kol.2, kol.3,...\) .

+
+

5.1 Wybieranie kolumn

+

Poniżej przedstawione są instrukcje pozwalające na wybieranie konkretnych kolumn z ramki danych w~zależności od metody. +Dla uproszczenia przyjmijmy, że wybieramy kolumny: \(kol.1, kol.2, kol.3\).

+
    +
  1. base
  2. +
+
    +
  • dane = dane[, c(“kol.1”, “kol.2”, “kol.3”)]
  • +
+
    +
  1. tidyverse
  2. +
+
    +
  • dane = select(dane, kol 1, kol 2, kol 3)
  • +
  • dane = dane %>% select(kol 1, kol 2, kol 3)
  • +
+
    +
  1. data.table
  2. +
+

Nazwy kolumn ramki danych zawierą znak “.” . Wprowadźmy zmienną pomocniczą \(kolumny\). Będzie ona zawierać nazwy kolumn, ale zastępując znak “.” znakiem ” “.

+
kolumny = c("kol 1", "kol 2", "kol 3")
+
    +
  • dane = dane[, kolumny]
  • +
  • dane = dane[, kolumny, with = FALSE] - dana metoda nie zadziała bez argumentu \(with~=~FALSE\), ponieważ szuka w ramce danych kolumn o nazwach zawartych w obiekcie \(kolumny\), a nie konkretnie podanych nazw
  • +
  • dane = dane[, colnames(dane) %in% kolumny, with = FALSE]
  • +
  • dane = dane[, ..kolumny]
  • +
  • dane = dane[, list(kol 1, kol 2, kol 3)]
  • +
  • dane = dane[, .(kol 1, kol 2, kol 3)]
  • +
+
+
+

5.2 Zmiana nazw kolumn

+

Teraz zostaną zaprezentowane sposoby na zmianę nazw kolumn ramki danych. Przyjmijmy, że nowe nazwy kolumn są postaci \(k1, k2, k3, ...\) .

+
    +
  1. base
  2. +
+
    +
  • colnames(dane) = c(“k1”, “k2”, “k3”)
  • +
+
    +
  1. tidyverse
  2. +
+
    +
  • dane = dane %>% rename(k1 = kol.1, k2 = kol.2, k3 = kol.3)
  • +
+
    +
  1. data.table
  2. +
+
    +
  • setnames(dane, c(“kol.1”, “kol.2”, “kol.3”), c(“k1”, “k2”, “k3”)) - zaleta: nie kopiuje ramki danych
  • +
+
+
+

5.3 Filtrowanie

+

Dany rozdział skupia się na sposobach filtrowania danych. Przydatne funkcje:

+
    +
  • unique(dane\(\$\)k1) - zwraca unikalne wartości kolumny \(k1\)
  • +
  • table(dane\(\$\)k1) - zlicza ilość wystąpienia każdej wartości w kolumnie \(k1\)
  • +
  • prop.table(table(dane\(\$\)k1)) - pokazuje procentowo ilość wystąpienia każdej wartości w kolumnie \(k1\) w\(~\)stosunku do wszystkich wartości
  • +
+

Przyjmnijmy, że wybieramy z kolumny \(k1\) określoną wartość \(abc\).

+
    +
  1. base
  2. +
+
    +
  • dane[dane\(\$\)k1 == “abc”, ]
  • +
+
    +
  1. tidyverse
  2. +
+
    +
  • dane %>% filter(k1 == “abc”) +
      +
    • można podać kilka warunków (po przecinku), będą one domyślnie rozdzielone spójnikiem \(i\)
    • +
    • aby połączyć warunki spójnikiem \(i\) można również użyć operatora \(\&\)
    • +
    • aby połączyć warunki spójnikiem \(lub\) należy użyć operatora \(|\)
    • +
  • +
+
    +
  1. data.table
  2. +
+
    +
  • dane[k1 == “abc”]
  • +
+
+
+

5.4 Usuwanie kolumn

+

Załóżmy, że usuwamy pierwszą kolumnę - \(k1\).

+
    +
  1. base
  2. +
+
    +
  • dane = dane[, -1] - gdzie \(1\) to numer usuwanej kolumny, a “-” oznacza usuwanie
  • +
+
    +
  1. tidyverse
  2. +
+
    +
  • dane = select(dane, -k1) - jak powyżej, “-” oznacza usuwanie, ale w tym przypadku stosujemy nazwę kolumny a nie jej numer
  • +
+
    +
  1. data.table
  2. +
+
    +
  • dane[, k1 := NULL] - operator \(:=\) (referencja) oznacza, że operacja jest wykonywana bez kopiowania ramki danych
  • +
  • dane = dane[, -1, with = FALSE]
  • +
+
+
+

5.5 Manipulacje na kolumnach

+

Przyjmijmy, że kolumna \(k2\) zawiera tylko liczby. Wartości ujemne zamieniamy na \(0\). W tym celu posłużymy się funkcją \(ifelse\): +\[ ifelse(warunek \ logiczny,\ wartość \ jeśli \ spełniony, \ wartość \ jeśli\ niespełniony).\] +1. base

+
    +
  • dane[[“k2”]] = ifelse(dane[[“k2”]] < 0, 0, dane[[“k2”]])
  • +
+
    +
  1. tidyverse
  2. +
+
    +
  • dane = dane %>% mutate(k2 = ifelse(k2 < 0, 0, k2)) +
      +
    • możemy modyfikować kilka kolumn jednocześnie, rozdzielając je przecinkiem
    • +
  • +
+
    +
  1. data.table
  2. +
+
    +
  • dane[, k2 := ifelse(k2 < 0, 0, k2)] - z użyciem referencji
  • +
  • dane[[“k2”]] = ifelse(dane[[“k2”]] < 0, 0, dane[[“k2”]]) - bez użycia referencji
  • +
+
+
+

5.6 Aplikowanie transformacji do każdej kolumny

+

W tym rozdziale będziemy operować na wszystkich kolumnach ramki danych. Wartości w nich zawarte mogą być typu \(factor\), które zamienimy na typ \(character\).

+
    +
  1. base
  2. +
+
    +
  • poprzez pętlę
  • +
+
      for (i in 1:ncol(dane)){
+        if (is.factor(dane[, i])){
+          dane[, i] = as.character(dane[, i])
+        }
+      }
+
    +
  • poprzez funkcję \(lapply\)
  • +
+
      lapply(dane, fun(x){
+        if(is.factor(x))
+          x = as.character(x)
+      })
+
    +
  1. tidyverse
  2. +
+
    +
  • przy użyciu funkcji \(mutate\_all\)
  • +
+
    dane = dane %>%
+      mutate_all(function(x){
+        if (is.factor(x)){
+          as.character(x)
+        } 
+        else{
+          x
+        }
+      })
+
    +
  1. data.table
  2. +
+
    +
  • przy użyciu funkcji lapply
  • +
+
    dane = dane[, lapply(.SD, function(x){
+      if (is.factor(x)){
+        as.character(x)
+      } 
+      else{
+        x
+      } 
+    })]
+
+
+

5.7 Grupowanie i podsumowanie

+

Załóżmy, że do wyznaczenia wszystkich unkialnych wartości ramki danych potrzebne są kolumny \(k1\), \(k2\) i \(k3\). Natomiast podsumowywana będzie kolumna \(k4\) - zostanie wyliczona średnia dla każdej unikalnej wartości.

+
    +
  1. base
  2. +
+
    +
  • przy użyciu funkcji \(aggregate\) - zastosowana zostanie formuła \(k4\) ~ \(k1 + k2 + k3\), która oznacza, że będzie podsumowywana zmienna \(k4\) w zależności od unikalnych zestawów wartości zmiennych \(k1\), \(k2\), \(k3\) +
      +
    • aggregate(k4 ~ k1 + k2 + k3, +data = dane, +FUN = function(x) mean(x, na.rm = TRUE)) - poprzez zastosowanie własnej funkcji

    • +
    • aggregate(k4 ~ k1 + k2 + k3, +data = dane, +FUN = mean, na.rm = TRUE) - poprzez zastosowanie istniejącej funkcji

    • +
  • +
+
    +
  1. tidyverse
  2. +
+
    +
  • dane %>% +group_by(k1, k2, k3) %>% +summarize(srednia = mean(k4, na.rm = TRUE), maksimum = max(k4, na.rm = TRUE)) +
      +
    • \(group\_by\) - grupuje po kolumnach \(k1\), \(k2\), \(k3\)
    • +
    • \(summarize\) - podsumowuje według podanych elementów (w tym przypadku wylicza średnią i maksimum z kolumny \(k4\))
    • +
  • +
+
    +
  1. data.table
  2. +
+
    +
  • dane[, list(średnia = mean(k4, na.rm = TRUE), +maksimum = max(k4, na.rm = TRUE)), +by = c(“k1”, “k2”, “k3”)]
  • +
+
+
+

5.8 Podsumowywanie wszystkich kolumn

+

W celu podsumowania kolumn zdefiniujemy poniższą funkcję, która zwróci ilość niepustych wartości.

+
  num_unique_noNA = function(input_vector){
+    sum(!is.na(unique(input_vector)))
+  }
+
    +
  1. base
  2. +
+
    +
  • apply(dane, 2, num_unique_noNA) - gdzie \(2\) oznacza, że wywołujemy podaną funkcję \(num\_unique\_noNA\) po kolumnach +
      +
    • lapply(dane, num_unique_noNA)
    • +
    • sapply(dane, num_unique_noNA)
    • +
  • +
+
    +
  1. tidyverse
  2. +
+
    +
  • summarise_all(dane, num_unique_noNA)
  • +
+
    +
  1. data.table
  2. +
+
    +
  • dane[, lapply(.SD, num_unique_noNA)]
  • +
+ +
+
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + diff --git a/docs/_book/reference-keys.txt b/docs/_book/reference-keys.txt new file mode 100644 index 0000000..66e6a81 --- /dev/null +++ b/docs/_book/reference-keys.txt @@ -0,0 +1,113 @@ +tab:unnamed-chunk-94 +tab:unnamed-chunk-101 +wstęp +podstawy-języka-r +liczby +łańcuchy-znaków +wartości-logiczne +wektory +indeksowanie +operacje-na-wektorach +r---funkcje +wczytywanie-danych-w-r +formaty-danych +csvdsv +xml +json +excel-xlsx +otwarte-wersje-programu-excel +pliki-tekstowe +base +readr +data.table +różnice +arkusze-kalkulacyjne-i-pliki-json +locale +ustawienie-locale-przez-użytkownika +ustawienie-locale-poprzez-readr +natywne-formaty-r +rds +rda +eksploracyjna-analiza-danych +dane-tabelaryczne +typy-zmiennych +miary +r---podsumowanie-kolumn +przetwarzanie-danych-tabelarycznych +wybieranie-kolumn +zmiana-nazw-kolumn +filtrowanie +usuwanie-kolumn +manipulacje-na-kolumnach +aplikowanie-transformacji-do-każdej-kolumny +grupowanie-i-podsumowanie +podsumowywanie-wszystkich-kolumn +czyste-dane +dane-w-formacie-wąskim-i-szerokim +rozdzielanie-na-kolumny-wąska---szeroka +scalanie-kilku-kolumn-w-jedną-szeroka---wąska +łączenie-tabel-danych +merge +join +operacje-na-napisach-i-datach +operacje-na-napisach +operacje-na-datach +katarzyna-frankiewicz-maciej-grabias-jakub-michałowski +wprowadzenie +gramatyka-grafiki +podstawy-tworzenia-wykresów-w-ggplot2 +mapowanie +geometria-wykresu +funkcje-pomagające-poprawić-czytelność-wykresu +systemy-współrzędnych +dopasowanie-położenia +zarządzanie-osiami-współrzędnych +motywy +panele +czysty-i-wydajny-kod-w-r +czysty-kod +co-jeśli-w-kodzie-jest-bałagan +opis-zmiennych +opis-intencji +unikanie-błędnych-informacji +kilka-wskazówek +funkcje +komentarze +obiekt-a-struktura-danych +styl-kodu-i-narzędzia-pomagające-w-utrzymaniu-czystego-kodu +interaktywna-wizualizacja-danych-z-pakietem-shiny-interfejs-użytkownika +wstęp-1 +tworzenie-ui +układ-strony +elementy-wejścia-i-wyjścia +przykładowe-elementy-wejścia +przykładowe-elementy-wyjścia +przykład-użycia +wygląd-aplikacji +uwaga +wstęp-2 +serwer-shiny +podstawy-kontroli-wersji-przy-pomocy-gita +podstawowe-informacje +podstawowe-komendy +repozytoria +podstawowe-komendy-1 +cofanie-zmian +gałęzie +tworzenie-własnych-repozytoriów +programowanie-obiektowe-w-r-klasy-s3 +systemy-programowania-obiektowego-w-r +s3 +klasy-i-atrybuty +funkcje-generyczne-i-metody +funkcje-generyczne +tworzenie-funkcji-generycznych-i-metod +dziedziczenie +nextmethod +s4 +bonus-przykład-wykorzystania-systemu-s4 +moduły-w-aplikacjach-shiny +czym-jest-moduł-shiny +dlaczego-warto-używać-modułów-shiny +budowa-modułu-shiny. +jak-używać-modułów-shiny diff --git a/docs/_book/search_index.json b/docs/_book/search_index.json new file mode 100644 index 0000000..84be323 --- /dev/null +++ b/docs/_book/search_index.json @@ -0,0 +1 @@ +[["index.html", "Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ Instytut Matematyczny, Uniwersytet Wrocławski Chapter 1 Wstęp", " Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ Instytut Matematyczny, Uniwersytet Wrocławski Mateusz Staniak 2023-10-12 Chapter 1 Wstęp Autorzy poszczególnych rozdziałów: Podstawy języka R: Michał Dylewicz, Marcela Kamchen, Anna Krasoń, Katarzyna Kulon, Arkadiusz Soból (z wyjątkiem podrozdziału Funkcje). Wczytywanie danych: Marta Kałużna, Sebastian Jachimek, Joanna Grunwald, Wojciech Wojnar. Eksploracyjna analiza danych: Magdalena Mazur, Agata Rogowska, Zuzanna Różak, Aleksandra Siepiela. [Także podrozdział Funkcje pierwszego rozdziału.] Podstawy kontroli wersji z Gitem: Magdalena Mazur, Agata Rogowska, Zuzanna Różak, Aleksandra Siepiela. Przetwarzanie danych tabelarycznych: Weronika Domaszewska, Ewelina Grzmocińska, Gracjan Hrynczyszyn, Dominik Jaźwiecki, Michał Ociepa. Czyste dane: Kacper Ambroży, Dominika Szewc, Radosław Szudra, Helena Wołoch. Wizualizacja danych z pakietem ggplot2: Katarzyna Frankiewicz, Maciej Grabias, Jakub Michałowski Czysty i wydajny kod w R: Paulina Bannert, Natalia Bercz, Piotr Mrozik, Dariusz Sudół, Monika Wyźnikiewicz Interaktywna wizualizacja danych z pakietem shiny: interfejs użytkownika: Stanisław Banaszek, Mateusz Drobina, Dominik Mika, Adrian Płoszczyca, Jakub Sobkowiak Interaktywna wizualizacja danych z pakietem shiny: strona serwerowa: Wojciech Leszkowicz, Małgorzata Stawińska, Tomasz Szmyd, Maciej Tadej. Dodatkowe rozdziały: - Podstawy kontroli wersji przy pomocy Gita: Magdalena Mazur, Agata Rogowska, Zuzanna Różak, Aleksandra Siepiela. - Programowanie obiektowe w R: klasy S3: Agata Cieślik. - Moduły w aplikacjach shiny: Krystyna Grzesiak. "],["podstawy-języka-r.html", "Chapter 2 Podstawy języka R 2.1 Liczby 2.2 Łańcuchy znaków 2.3 Wartości logiczne 2.4 Wektory 2.5 Indeksowanie 2.6 Operacje na wektorach 2.7 R - funkcje", " Chapter 2 Podstawy języka R Język R posiada kilka typów danych, które pokrótce postaramy sie omówić poniżej. Pokażemy ich budowe jak i operacje na nich, przytaczając stosowne przyklady. 2.1 Liczby Liczby całkowite i rzeczywiste (tutaj separator dziesiętny to kropka). Możemy używać również notacji naukowej. Operacje na liczbach to podstawowe działania matematyczne jak i trochę rozszerzone, ukazane niżej wraz z specjalnymi liczbami. 5; 5.5; 5.5e-2; ## [1] 5 ## [1] 5.5 ## [1] 0.055 Tutaj liczby specjalne, NaN # not a number ## [1] NaN Inf # nieskończoność ## [1] Inf -Inf # - nieskończoność ## [1] -Inf oraz kilka działań na liczbach 1 + 1 # podobnie '-' to odejmowanie ## [1] 2 4/2 # dzielenie, a '*' to mnożenie ## [1] 2 5 %/% 3 # dzielenie całkowite ## [1] 1 5 %% 3 # reszta z dzielenia ## [1] 2 2^3 # potęgowanie ## [1] 8 2**3 # też potęgowanie ## [1] 8 sqrt(4) #pierwiastkowanie ## [1] 2 abs(-1) # wartość bezwzględna ## [1] 1 2.2 Łańcuchy znaków Łańcuch znaków to po prostu napi. Napis jest otoczony przez ” lub ’. W napisie możemy umieszczać dowolne znaki, pamiętając że są też znaki specjalne (rozpoczynające się od \\ i mające specjalne funkcje). Na napisach istnieje wiele operacji (np. \\(\\verb+paste()+,\\) czyli sklejenie dwóch napisów), lecz je zobaczymy w notatce o napisach. "napis" ## [1] "napis" 'to też' ## [1] "to też" "'a tutaj nawet z bonusem'" ## [1] "'a tutaj nawet z bonusem'" # ""a"" to już wbrew intuicji nie jest napis cat("i znak \\n specjalny, wstawiający nową linie") # cat() wyświetla napis w sposób niesformatowany ## i znak ## specjalny, wstawiający nową linie 2.3 Wartości logiczne Logiczna Prawda (\\(\\verb+TRUE+\\) lub \\(\\verb+T+\\)) oraz logiczny Fałsz (\\(\\verb+FALSE+\\) lub \\(\\verb+F+\\)). Na tych obiektach możemy wykonywać operacje logiczne oraz algebraiczne. TRUE & TRUE # operator 'i' ## [1] TRUE TRUE | FALSE # operator 'lub' ## [1] TRUE 1 == 1 # testowanie równości ## [1] TRUE 1 != 2 # testowanie nierówności ## [1] TRUE 2*TRUE # TRUE ma wartość 1 ## [1] 2 2*FALSE # FALSE ma wartość 0 ## [1] 0 T ; `T` <- FALSE; T # używając `` możemy zmienić wartość logiczną wyrażenia ## [1] FALSE ## [1] FALSE 2.4 Wektory Wektor to w R uporządkowany zbiór elementów. Elementy te muszą mieć ten sam typ, także jeśli do wektora trafią elementy z różnym typem (poza NA), to nastąpi konwersja elementów do jednego typu. Proste wektory tworzymy przez polecenie \\(\\verb+c()+\\) i elementy wypisujemy w nawiasie po przecinku. Dodatkowo, element wektora jest traktowany jako jednoelementowy wektor. Wektory liczbowe jak i inne możemy tworzyć za pomocą wbudowanych funkcji do tego przeznaczonych. v <- c(1, 2, 3) #przypisanie wektora do zmiennej 0:10 # wektor liczbowy ## [1] 0 1 2 3 4 5 6 7 8 9 10 seq(from = 0, to = 10, by = 1) # to samo, ale za pomocą seq(), czyli sequance ## [1] 0 1 2 3 4 5 6 7 8 9 10 seq(0, 1, length.out = 4) # równe odstępy w 4 liczbowym wektorze ## [1] 0.0000000 0.3333333 0.6666667 1.0000000 length(v) # zwraca długość vectora ## [1] 3 # vector(mode, lenght) tworzy wektor dlugosci lenght, a wyrazy tego wektora maja klase mode vector("integer", 10) # wektor liczb calkowitych ## [1] 0 0 0 0 0 0 0 0 0 0 vector("numeric", 10) # wektor liczb rzeczywistych ## [1] 0 0 0 0 0 0 0 0 0 0 vector("character", 10) # wektor slów ## [1] "" "" "" "" "" "" "" "" "" "" rep(v, each = 2) # każdy element v zostanie powtórzony 2 razy ## [1] 1 1 2 2 3 3 rep(v, times = 2) # v zostanie powtórzony 2 razy ## [1] 1 2 3 1 2 3 # mały mix tj. tutaj element v traktujemy jako wektor jednoelementowy # i powtarzamy times razy rep(v, times = 1:3) ## [1] 1 2 2 3 3 3 x <- c("a", "A") # wektor napisowy v <- "a" # to też toupper(x) # zmieni stringi w argumencie na wielkie litery ## [1] "A" "A" tolower(x) # zmieni stringi w argumencie na male litery ## [1] "a" "a" 2.5 Indeksowanie W R wektory są indeksowane od 1 (a nie od 0 jak w wielu językach programowania!). Aby odwołać się do konkretnego elementu wektora korzystamy z nawiasów kwadratowych \\(\\verb+[]+.\\) letters[3] ## [1] "c" Można wybrać więcej niż jeden element, wpisując w nawiasach kwadratowych wektor indeksów. letters[1:10] ## [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" letters[c(1, 15)] ## [1] "a" "o" letters[seq(1, 20, by = 2)] ## [1] "a" "c" "e" "g" "i" "k" "m" "o" "q" "s" Jeśli przed wektorem indeksów widnieje znak minus, R zwróci wszystkie elementy wektora z wyjątkiem tych w nawiasie kwadratowym. letters[-(1:10)] # niezbędny nawias wokół 1:10 ## [1] "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z" letters[-c(1, 15)] ## [1] "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z" letters[-seq(1, 20, by = 2)] ## [1] "b" "d" "f" "h" "j" "l" "n" "p" "r" "t" "u" "v" "w" "x" "y" "z" Pod wybrane indeksy można przypisać nowe wartości. new_letters <- letters new_letters[1:5] <- LETTERS[1:5] new_letters ## [1] "A" "B" "C" "D" "E" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" ## [26] "z" Albo pod każdy wybrany indeks nową wspólną wartość. new_letters[1:5] <- "x" new_letters ## [1] "x" "x" "x" "x" "x" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" ## [26] "z" Tworząc wektor funkcją \\(\\verb+c()+,\\) możemy nazwać każdy z jego elementów. str_vec_nam <- c("a" = "A", "b" = "B", "c" = "C") str_vec_nam ## a b c ## "A" "B" "C" Może być to użyteczne przy odwoływaniu się do konkretnego elementu wektora, nie trzeba wtedy znać numeru jego indeksu. str_vec_nam["a"] ## a ## "A" str_vec_nam[c("a", "c")] ## a c ## "A" "C" str_vec_nam[c("c", "a")] ## c a ## "C" "A" Wektory możemy również indeksować za pomocą wektorów logicznych. Działa to wtedy jak wybieranie tych elementów wektora, które spełniają ustalony warunek. x_ind <- new_letters == "x" x_ind ## [1] TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE ## [17] FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE new_letters[x_ind] ## [1] "x" "x" "x" "x" "x" "x" seq_vec <- seq(0, 1, length.out = 10) seq_vec[seq_vec < 0.5] ## [1] 0.0000000 0.1111111 0.2222222 0.3333333 0.4444444 Można oczywiście rozbudowywać wyrażenia logiczne, np. następująco: seq_vec[seq_vec < 0.3 | seq_vec > 0.8] ## [1] 0.0000000 0.1111111 0.2222222 0.8888889 1.0000000 seq_vec[seq_vec > 0.3 & seq_vec < 0.8] ## [1] 0.3333333 0.4444444 0.5555556 0.6666667 0.7777778 2.6 Operacje na wektorach W R domyślnym i naturalnym zachowaniem funkcji na wektorach jest działanie element po elemencie 1:10 + seq(0, 1, length.out = 10) ## [1] 1.000000 2.111111 3.222222 4.333333 5.444444 6.555556 7.666667 8.777778 9.888889 11.000000 c(2,4,6,8)^(1:4) ## [1] 2 16 216 4096 W przypadku gdy wektory, na których wykonujemy obliczenia mają różne długości zachodzi recykling, tj. R samoistnie przedłuża krótszy wektor replikując go odpowiednią liczbę razy. Widzimy, że obie poniższe linie kodu dają taki sam efekt. 1:10 + 1:2 ## [1] 2 4 4 6 6 8 8 10 10 12 1:10 + rep(1:2, 5) ## [1] 2 4 4 6 6 8 8 10 10 12 Gdy długość dłuższego wektora nie jest wielokrotnością krótszego, recykling także zadziała, jednak R zgłosi warning. 1:10 + 1:3 ## Warning in 1:10 + 1:3: długość dłuszego obiektu nie jest wielokrotnością długości krótszego obiektu ## [1] 2 4 6 5 7 9 8 10 12 11 1:10 + 1:3 + 1:2 + 1:5 ## Warning in 1:10 + 1:3: długość dłuszego obiektu nie jest wielokrotnością długości krótszego obiektu ## [1] 4 8 10 11 13 12 11 15 17 18 Na wektorach możemy wykonywać oczywiście inne funkcje poza podstawowymi operacjami arytmetycznymi. Jedną z opcji jest posortowanie wektora. num_vec <- c(3,6,1,9,8,-3,0,102,-5) sort(num_vec) # sortowanie rosnące ## [1] -5 -3 0 1 3 6 8 9 102 sort(num_vec, decreasing = TRUE) # sortowanie malejące ## [1] 102 9 8 6 3 1 0 -3 -5 Odwrócić kolejnośc elementów wektora można następująco. rev(num_vec) ## [1] -5 102 0 -3 8 9 1 6 3 Oto kilka kolejnych funkcji. sum(num_vec) # suma elementów wektora ## [1] 121 prod(num_vec) # iloczyn elementów wektora ## [1] 0 mean(num_vec) # średnia elementów wektora ## [1] 13.44444 Przy operacjach jak powyższe należy jednak uważać na wektory zawierające “NA”. vec_with_NA <- c(3,6,1,NA) sum(vec_with_NA) ## [1] NA Aby zsumować wartości z pominięciem “NA” należy dopisać dodatkowy argument funkcji. sum(vec_with_NA, na.rm = TRUE) ## [1] 10 Analogicznie dla iloczynu i średniej elementów. prod(vec_with_NA) ## [1] NA prod(vec_with_NA, na.rm = TRUE) ## [1] 18 mean(vec_with_NA) ## [1] NA mean(vec_with_NA, na.rm = TRUE) ## [1] 3.333333 Lista jest podobna do wektora tj. jest pewnym ciągiem obiektów, tyle że jej elementy mogą mieć różne typy. l <- list(1:5) #lista z elementami bedacymi liczbami ## [[1]] ## [1] 1 2 3 4 5 l2 <- list(zwierze='dog', imie='Max',czyLubiInnePsy = TRUE) #lista z elementami bedacymi stringami lub wartosciami logicznymi ## $zwierze ## [1] "dog" ## ## $imie ## [1] "Max" ## ## $czyLubiInnePsy ## [1] TRUE Kolejnę różnica pomiedzy wektorem a listą jest możliwość odwoływania się do elementów listy za pomocą nazwy tego elementu i operatora $. Np: # odwolanie do elementu bedacego za pomoca [], # wynikiem takiej operacji jest lista zawierajaca wektor l[1] ## [[1]] ## [1] 1 2 3 4 5 # aby odwolac sie do konkretnego elementu uzwywamy [[]], na przyklad operacja l[[1]][2] # zwroci drugi element wektora z listy l[[1]][2] ## [1] 2 # nadpisywanie elementu listy wektorem l[[1]] <- c("a", "b", "c") # odwolanie do elementu za pomoca nazwy elementu l2$zwierze ## [1] "dog" l2$imie ## [1] "Max" l2$czyLubiInnePsy ## [1] TRUE Listy można łączyć oraz modyfikować. Funkcja \\(\\verb+lapply()+\\) to funkcja, która pozwala na wykonanie pewnego konkretnego działania na KAŻDYM elemencie z listy. Na przykład, możemy każdy element chcieć zapisać tylko dużymi literami: lapply(l2,toupper) ## $zwierze ## [1] "DOG" ## ## $imie ## [1] "MAX" ## ## $czyLubiInnePsy ## [1] "TRUE" Aby połączyć dwie listy, należy użyć \\(\\verb+c()+,\\) robiąc z dwóch list wektor i przypisując go do nowej zmiennej. l3 <- c(l,l2) ## [[1]] ## [1] "a" "b" "c" ## ## $zwierze ## [1] "dog" ## ## $imie ## [1] "Max" ## ## $czyLubiInnePsy ## [1] TRUE Macierz to obiekt dwuwymiarowy. Składa się z elementów tego samego typu. Tworzy się ją funkcją \\(\\verb+matrix()+,\\) do której podajemy wartości macierzy (zwykle w postaci wektora), liczbę wierszy i kolumn. matrix(data = 1:10, nrow = 2, ncol = 5) ## [,1] [,2] [,3] [,4] [,5] ## [1,] 1 3 5 7 9 ## [2,] 2 4 6 8 10 Widzimy, że R domyślnie wypełnia macierz po kolumnach. Aby wypełnić ją po wierszach ustalamy parametr \\(\\verb+byrow = TRUE+\\) m <- matrix(data = 1:10, nrow = 2, ncol = 5, byrow = TRUE) m ## [,1] [,2] [,3] [,4] [,5] ## [1,] 1 2 3 4 5 ## [2,] 6 7 8 9 10 Elementy macierzy wybiera się za pomocą dwóch indeksów - indeksu wiersza i indeksu kolumny umieszczonych w nawiasach kwadaratowych i rozdzielonych przecinkiem. m[2,3] ## [1] 8 Można również wybrać konkretne wiersze lub kolumny. m[1:2,3:4] # wybiera wiersze 1 i 2 oraz kolumny 3 i 4 ## [,1] [,2] ## [1,] 3 4 ## [2,] 8 9 m[2,c(1,4,5)] # wybiera wiersz 2 oraz kolumny 1,4 i 5 ## [1] 6 9 10 Nie podanie indeksu przed przecinkiem oznacza, że chcemy otrzymać wszystkie wiersze. Analogicznie nie podanie indeksu po przecinku oznacza, że chcemy otrzymać wszystkie kolumny. m[,c(1,3)] ## [,1] [,2] ## [1,] 1 3 ## [2,] 6 8 m[2,] ## [1] 6 7 8 9 10 Macierze, podobnie jak wektory, możemy także indeksować warunkami logicznymi. # zwraca elementy (w tym wypadku element) z pierwszej kolumny, # które są większe od 2 m[m[,1] > 2, 1] ## [1] 6 Można także indeksować macierz inną macierzą o dwóch kolumnach. Zwrócone zostaną wtedy elementy o indeksach będących wierszami tej macierzy. matrix_ind<- matrix(c(1, 2, 2, 3, 2, 4), byrow = TRUE, nrow = 3, ncol = 2) m[matrix_ind] ## [1] 2 8 9 Na macierzach o tych samych wymiarach możemy wykonywać operacje arytmetyczne. Trzeba zwrócić uwagę, że są one wykonywane element po elemencie (z matematycznego punktu widzenia jest to oczekiwane przy dodawaniu, ale nieoczekiwane przy mnożeniu macierzy). m1 <- matrix(1:4,2,2) m1 ## [,1] [,2] ## [1,] 1 3 ## [2,] 2 4 m2 <- matrix(2:5,2,2) m2 ## [,1] [,2] ## [1,] 2 4 ## [2,] 3 5 m1 + m2 ## [,1] [,2] ## [1,] 3 7 ## [2,] 5 9 m1 * m2 ## [,1] [,2] ## [1,] 2 12 ## [2,] 6 20 Aby wykonać matematyczne mnożenie macierzy należy użyć operatora \\(\\verb+%*%+.\\) m1 %*% m2 ## [,1] [,2] ## [1,] 11 19 ## [2,] 16 28 Jest to obiekt przechowujący dane w postaci tabeli dwuwymiarowej, którą tworzą wektory o dowolnym typie. Z ramki danych można korzystać jak z macierzy dwuwymiarowej (poprzez korzystanie z \\(\\verb+[,]+\\)), jak i z listy (poprzez korzystanie z $). imie <- c("Max", "Reksio","Rex","Luna") #utworzymy ramke z 2 wektorow wiek <- c(2,8,3,11) ramka <- data.frame(imie,wiek) #ramke tworzymy za pomoca polecenia data.frame() ## imie wiek ## 1 Max 2 ## 2 Reksio 8 ## 3 Rex 3 ## 4 Luna 11 #wyswietlanie nazw kolumn names(ramka) ## [1] "imie" "wiek" #odnoszenie sie do elementu znajdujacego sie w 2. rzedzie i 1. kolumnie ramka[2,1] ## [1] "Reksio" #pobieranie paru wierszy na raz za pomoca wektora ramka[c(1, 2), ] ## imie wiek ## 1 Max 2 ## 2 Reksio 8 #pobieranie wszystkich kolumn dla 1. wiersza ramka[1,] ## imie wiek ## 1 Max 2 #pobieranie wszystkich wierszy dla 1. kolumny ramka[,1] ## [1] "Max" "Reksio" "Rex" "Luna" # pierwsza kolumna bez drugiego wiersza ramka[-2, 1] ## [1] "Max" "Rex" "Luna" #pobieranie kolumn/wierszy po nazwie ramka$wiek ## [1] 2 8 3 11 # inny sposób indeksowanie po nazwie ramka[, "wiek"] ## [1] 2 8 3 11 Indeksowanie na podstawie zawartości ramki danych Dane z ramki mogą być przez nas “filtrowane” za pomocą []. Na przykład # psy poniżej 9 roku życia ramka[ramka$wiek < 9, ] ## imie wiek ## 1 Max 2 ## 2 Reksio 8 ## 3 Rex 3 #dane tylko dla Reksia ramka[ramka$imie == "Reksio", ] ## imie wiek ## 2 Reksio 8 # analogicznie dla wektorów wiek[wiek < 9] ## [1] 2 8 3 Tworząc ramkę danych należy pamiętać o tym, aby wektory danych służące za kolumny były tej samej długości. #zamiana nazw kolumn names(ramka) <- c("imie_psa", "wiek_psa") ## imie_psa wiek_psa ## 1 Max 2 ## 2 Reksio 8 ## 3 Rex 3 ## 4 Luna 11 Ramki danych możemy powiększać o dodatkowe wiersze i kolumny, ale typy (dla wierszy) i rozmiary muszą sie zgadzać z typami i rozmiarem ramki danych. Rozpatrzmy poniższy przykład, aby pokazać, jak dodać wiersz i kolumnę za pomocą funkcji \\(\\verb+cbind()+\\) oraz \\(\\verb+rbind()+\\). #dodawanie nowego wiersza dodajemy_wiersz <- data.frame(imie_psa ="Quentin", wiek_psa=9) #funkcja rbind "skleja" wierszowo argument pierwszy (u nas ramka) z drugim ramka <- rbind(ramka,dodajemy_wiersz) #dodawanie nowej kolumny czyLubiInnePsy <- c(TRUE,TRUE, FALSE, TRUE, FALSE) #funkcja cbind "skleja" kolumnowo argument pierwszy (u nas ramka) z drugim ramka <- cbind(ramka,czyLubiInnePsy) ## imie_psa wiek_psa czyLubiInnePsy ## 1 Max 2 TRUE ## 2 Reksio 8 TRUE ## 3 Rex 3 FALSE ## 4 Luna 11 TRUE ## 5 Quentin 9 FALSE Możemy rownież dodawać wiersze za pomocą indeksowania, to znaczy przypisywania wartości do konkretnych indeksów ramki: #jako 6. wiersz "wkladamy" nowy wektor ramka[6,] <- c("Fanta",0.5,TRUE) ## imie_psa wiek_psa czyLubiInnePsy ## 1 Max 2 TRUE ## 2 Reksio 8 TRUE ## 3 Rex 3 FALSE ## 4 Luna 11 TRUE ## 5 Quentin 9 FALSE ## 6 Fanta 0.5 TRUE # jako 4.kolumne "wkladamy" nowy wektor ramka[,4] <- c("Mateusz","Romek","Renata","Leon","Quennie","Filip") # nazywamy kolumne 4. names(ramka)[4] <- "opiekun_psa" ## imie_psa wiek_psa czyLubiInnePsy opiekun_psa ## 1 Max 2 TRUE Mateusz ## 2 Reksio 8 TRUE Romek ## 3 Rex 3 FALSE Renata ## 4 Luna 11 TRUE Leon ## 5 Quentin 9 FALSE Quennie ## 6 Fanta 0.5 TRUE Filip Analizując nową dla nas ramkę danych, użyteczne okazują się funkcje pozwalające na poznanie właściwości ramki danych. Oto pare z nich: # wymiary ramki (6 wierszy,4 kolumny) mozna sprawdzic za pomoca funkcji dim() dim(ramka) ## [1] 6 4 # aby zobaczyc skrocony opis typow danych zawartych w ramce uzywana jest funkcja str() str(ramka) ## 'data.frame': 6 obs. of 4 variables: ## $ imie_psa : chr "Max" "Reksio" "Rex" "Luna" ... ## $ wiek_psa : chr "2" "8" "3" "11" ... ## $ czyLubiInnePsy: chr "TRUE" "TRUE" "FALSE" "TRUE" ... ## $ opiekun_psa : chr "Mateusz" "Romek" "Renata" "Leon" ... # aby "podejrzec" pierwsze wiersze ramki danych, wraz naglowkami kolumn uzywana jest funkcja head() head(ramka) ## imie_psa wiek_psa czyLubiInnePsy opiekun_psa ## 1 Max 2 TRUE Mateusz ## 2 Reksio 8 TRUE Romek ## 3 Rex 3 FALSE Renata ## 4 Luna 11 TRUE Leon ## 5 Quentin 9 FALSE Quennie ## 6 Fanta 0.5 TRUE Filip # wysietlanie pierwszych n wierszy head(ramka,n=2) ## imie_psa wiek_psa czyLubiInnePsy opiekun_psa ## 1 Max 2 TRUE Mateusz ## 2 Reksio 8 TRUE Romek # wyswietlanie ostatnich n wierszy za pomoca funkcji tail() tail(ramka,n=2) ## imie_psa wiek_psa czyLubiInnePsy opiekun_psa ## 5 Quentin 9 FALSE Quennie ## 6 Fanta 0.5 TRUE Filip Pętli oraz instrukcji warunkowych używamy, kiedy chcemy uniknąć powielania kodu i chcemy zachować jego przejrzystość. Ułatwia to wprowadzanie potencjalnych zmian. Instrukcje opisujące co powinno się zdarzyć należy umieścić w nawiasach \\(\\verb+{ }+\\). Jeśli chcemy wykonać tylko jedną linijke kodu, możemy je opuścić. Umożliwia warunkowe wykonanie kawałka kodu - jeśli warunek zawarty w \\(\\verb+if+\\) jest spełniony, to R przejdzie do zawartej instrukcji. W przeciwnym wypadku wykona polecenie zawarte w \\(\\verb+else+\\), a jeśli go nie ma , to przejdzie do kolejnych pętli. Część \\(\\verb+else+\\) nie jest wymagana, w tym wypadku z góry wiadomo ile razy kod zostanie wykonany. Składnia wygląda następująco: if(warunek) { instrukcja_1 } i jest analogiczna do if(warunek) instrukcja_1 Możemy także zapisać if(warunek) { instrukcja_1 instrukcja_2 } else { instrukcja_3 } Powiedzmy, że rozpatrujemy liczbe z rozkładu normalnego i sprawdzamy jakiego jest znaku. x_norm <- rnorm(1) if (x_norm < 0) { cat("Liczba", x_norm, "jest ujemna") } else { cat("Liczba ", x_norm, "jest dodatnia") } ## Liczba 0.2630452 jest dodatnia Możemy chcieć wykonać różne operacje na tak wylosowanej liczbie. Przykładowo, jeśli będzie ujemna, to zmienić znak, zaokrąglić i zreplikować w wektorze if (x_norm < 0) { x_norm <- abs(x_norm) x_wek <- rep(round(x_norm, 2), times = 5) } else { x_wek <- "X" } i otrzymać X (X oznacza, że wylosowana liczba była dodatnia, a z nią nic nie robimy). Pętla \\(\\verb+while+\\) działa tak długo, dopóki warunek jest spełniony - tzn. do kiedy nie dostaniemy \\(\\verb+FALSE+\\). Warunek należy opisać tak, żeby w pewnym momencie został spełniony - inaczej pętla będzie działać w “nieskończoność”. Często używa sie jej do szukania losowych liczb o pewnych właściwościach. Składnia tej pętli jest następująca: while(warunek) { instrukcja_1 instrukcja_2 } Tutaj przykład wykorzystania, gdy chcemy losować liczby z przedziału [1, 100], dopóki różnica między dwoma kolejnymi nie będzie parzysta i <- 2 los <- c() los[1] <- 0 roznica <- 1 while(roznica%%2 != 0) { los <- c(los, sample(1:100, 1, replace = TRUE)) roznica <- los[i]-los[i-1] i = i+1 } W ten sposób dostajemy wylosowane liczby: 0, 39, 53, z różnicą między ostatnimi równą 14. Pętla \\(\\verb+for+\\) wygląda następująco: for(iterator in warunek) { instrukcja_1 instrukcja_2 } Ta pętla wykonuje instrukcje określoną ilość razy - tyle ile elementów \\(\\verb+iterator+\\) w zbiorze \\(\\verb+warunek+\\). W warunku możemy mieć liste albo wektor. Po każdym wykonaniu pętli, zmienna \\(\\verb+iterator+\\) przeskakuje do kolejnego elementu warunku. Jeśli chcemy wykonać tylko 1 instrukcje, można zapisać for(iterator in warunek) instrukcja_1 Przykładowo, jeśli chcemy elementy ze zbioru [1, 10] podnieść do potęgi, możemy użyć pętli \\(\\verb+for+\\). wynik <-c() for (i in 1:10) wynik <- c(wynik, i*i) wynik ## [1] 1 4 9 16 25 36 49 64 81 100 Możemy także napisać pętle zagnieżdżone, przykładowo do obliczenia wartości w macierzach. W tym wypadku wartością każdego elementu macierzy (3x3) jest iloczyn jego indeksów, co daje następujący wynik macierz <- matrix(nrow=3, ncol=3) for(i in 1:dim(macierz)[1]) { for(j in 1:dim(macierz)[2]) { macierz[i,j] = i*j } } macierz ## [,1] [,2] [,3] ## [1,] 1 2 3 ## [2,] 2 4 6 ## [3,] 3 6 9 Teraz zajmiemy się rodziną funkcji \\(\\verb+apply+\\). Należą do niej takie funkcję jak \\(\\verb+apply, tapply, sapply, lapply, vapply+\\). Wszystkie one pozwalają na wykonanie pewnej operacji na szeregu podzbiorów danych. Operacja, która ma być wykonana określana jest przez argument \\(\\verb+FUN+\\). Funkcje z tej rodziny przyjmują elementy listy \\(\\verb+(lapply()+)\\), elementy wektora \\(\\verb+(sapply())+\\), macierze \\(\\verb+(apply())+\\) oraz podgrup wskazanych przez jedną lub kilka zmiennych \\(\\verb+(by()+\\) i \\(\\verb+tapply())+\\). Zacznijmy od funkcji \\(\\verb+lapply()+\\). Wykonuje funkcję \\(\\verb+FUN+\\) dla wszystkich elementów wektora \\(\\verb+x+\\). Przydatna funkcja zastępująca pętlę \\(\\verb+for+\\). Domyślnie wynikiem działania jest lista, lecz jeżeli w wyniku chcielibyśmy otrzymać wektor, to jednym z rozwiązań jest zamiana listy na wektor funkcją \\(\\verb+unlist()+\\). Oto przykładowe działanie funkcji \\(\\verb+lapply()+\\): x=c(1,2,3,4,5,6,7,8,9,10) func=function(x){return(x**3-3*x)} lapply(x,func) ## [[1]] ## [1] -2 ## ## [[2]] ## [1] 2 ## ## [[3]] ## [1] 18 ## ## [[4]] ## [1] 52 ## ## [[5]] ## [1] 110 ## ## [[6]] ## [1] 198 ## ## [[7]] ## [1] 322 ## ## [[8]] ## [1] 488 ## ## [[9]] ## [1] 702 ## ## [[10]] ## [1] 970 Funkcja \\(\\verb+sapply+\\) jest bardziej przyjazną użytkownikowi wersją \\(\\verb+lapply+\\) zwracającą wektor lub macierz i może przyjmować więcej argumentów, np. \\(\\verb+sapply(x, f, simplify = FALSE, USE.NAMES = FALSE)+\\) zwraca ten sam wynik co \\(\\verb+lapply(x, f)+\\). Funkcja \\(\\verb+vapply+\\) jest podobna do \\(\\verb+sapply+\\), ale ma z góry określony typ zwracanych wartości, a może być również bezpieczniejszy w użyciu, a czasem nawet szybszy. Teraz weźmiemy pod lupe \\(\\verb+tapply()+\\), która to wykonuje funkcję \\(\\verb+FUN+\\) dla podzbiorów wektora \\(\\verb+x+\\) określonego przez poziomy zmiennej czynnikowej \\(\\verb+index+\\). Przydatna funkcja, gdy chcemy policzyć pewną statystykę w podgrupach, np. odchylenie standardowe w z wagami. W tym przypadku \\(\\verb+x+\\) będzie wektorem z wagami, \\(\\verb+index+\\) wektorem z płcią a \\(\\verb+FUN+\\) będzie funkcją sd). x=c(98,67,65,82,55,60,72,81,48,88) index=c('M','M','K','M','K','M','M','M','K','M') tapply(x,index,sd) ## K M ## 8.544004 12.944938 A teraz bardziej zaawansowana werssa funkcji \\(\\verb+tapply()+\\) z tą różnicą, że \\(\\verb+x+\\) może być macierzą lub listą, \\(\\verb+index+\\) może być listą, a wynik tej funkcji jest specyficznie wyświetlany. Jeżeli \\(\\verb+index+\\) jest listą zmiennych czynnikowych, to wartość funkcji \\(\\verb+FUN+\\) będzie wyznaczona dla każdego przecięcia czynników tych zmiennych. Wynik funkcji \\(\\verb+by()+\\) jest klasy \\(\\verb+by+\\), ale po usunięciu informacji o klasie, np. poprzez użycie funkcji \\(\\verb+unclass()+\\) otrzymujemy zwykłą macierz. Argument \\(\\verb+x+\\) może być listą lub macierzą, dzięki czemu do funkcji \\(\\verb+FUN+\\) przekazać można kilka zmiennych – elementów/kolumn listy/macierzy \\(\\verb+x+\\). m1=seq(1:9) x=c('a','b','c','a','b','c','a','b','c') by(m1,x,mean) ## x: a ## [1] 4 ## ------------------------------------------------------------------------------ ## x: b ## [1] 5 ## ------------------------------------------------------------------------------ ## x: c ## [1] 6 Z kolei \\(\\verb+mapply()+\\) to wielowymiarowy odpowiednik funkcji \\(\\verb+sapply()+\\). Argumentami tej funkcji jest funkcja \\(\\verb+fun+\\) oraz kilka (dwa lub więcej) wektorów o tej samej długości. Wynikiem jest wektor, w którym na pozycji \\(\\verb+i+\\)-tej jest wynik funkcji \\(\\verb+fun+\\) wywołanej z \\(\\verb+i+\\)-tych elementów wektorów będących argumentami. a=function(x,y){return(x**y)} mapply(a,x=seq(1,101,by=10),y=seq(1:11)) ## [1] 1.000000e+00 1.210000e+02 9.261000e+03 9.235210e+05 1.158562e+08 1.759629e+10 3.142743e+12 ## [8] 6.457535e+14 1.500946e+17 3.894161e+19 1.115668e+22 2.7 R - funkcje Funkcje przydają się do zamknięcia w nich operacji, które się często powtarzają w naszym kodzie lub dla jego lepszej czytelności. Podstawowa składnia funkcji w R wygląda tak: nazwa_funkcja <- function(argument 1, argument 2, …){ ciało funkcji return(wartość lub obiekt zwracany) } Napiszmy funkcję, która będzie mnożyła dowolny wektor przez podaną liczbę, a następnie zsumuje elementy wektora: funkcja1 <- function(wektor, liczba){ rezultat <- wektor * liczba rezultat <- sum(rezultat) return(rezultat) } Możemy także pominąc \\(\\texttt{return}\\) i zdefiniować funkcje: funkcja2 <- function(wektor, liczba){ rezultat <- wektor * liczba rezultat <- sum(rezultat) rezultat } Obie funkcje \\(\\texttt{funkcja1}\\) i \\(\\texttt{funkcja2}\\) robią to samo. Wykonajmy nasze funkcje dla dwóch zdefiniowanych zmiennych: v <- 1:5 n <- 2 funkcja1(v, n) ## [1] 30 funkcja2(v, n) ## [1] 30 Oczywiście do wykonania funkcji potrzebne jest zdefiniowanie obu argumentów. Jak ich nie dodamy wyświetli się błąd, że argument drugi zaginął i nie mamy zdefiniowanej jego wartości domyślnej. Zdefiniujmy zatem domyślną wartość argumentu \\(\\texttt{liczba}\\) jako \\(\\texttt{NULL}\\) i dopiszmy do naszej funkcji kod, który gdy ten argument będzie miał wartość domyślną zwróci tylko sumę elementów wektora: funkcja3 <- function(wektor, liczba = NULL){ if(is.null(liczba)){ rezultat <- sum(wektor) } else{ rezultat <- wektor * liczba rezultat <- sum(rezultat) } rezultat } Wykonajmy funckję \\(\\texttt{funkcja3}\\) na wcześniej zdefiniowanym wektorze \\(\\texttt{v}\\): funkcja3(v) ## [1] 15 Oprócz zdefiniowania wartości domyślnej argumentu poprzez trzy kropki możemy również dopuścić parametry dodatkowe. Zdefiniujmy funkcję z parametrami dodatkowymi: funkcja4 <- function(wektor, liczba = NULL, ...){ if(is.null(liczba)){ rezultat <- sum(wektor, ...) } else{ rezultat <- wektor * liczba rezultat <- sum(rezultat, ...) } rezultat } Wykonajmy funckję \\(\\texttt{funkcja4}\\) usuwając wartości brakujące z nowo zdefiniowanego wektora: v <- c(NA, 1, NA, 2:4, NA, 5) v ## [1] NA 1 NA 2 3 4 NA 5 funkcja4(v, na.rm = TRUE) ## [1] 15 Funkcje są bardzo przydatne, gdy mamy do napisania długi skrypt. Pozwalają na podzielenie głównej części kodu na mniejsze kawałeczki, które kolejnemu użytkownikowi skryptu lub nam będzie łatwiej modyfikować. "],["wczytywanie-danych-w-r.html", "Chapter 3 Wczytywanie danych w R 3.1 Formaty danych 3.2 Locale 3.3 Natywne formaty R", " Chapter 3 Wczytywanie danych w R 3.1 Formaty danych 3.1.1 CSV/DSV CSV (Comma Separated Values) to plik tekstowy, w którym wartości rozdzielane są przecinkami, a kolejne wiersze znakiem nowej linii. Plik CSV zazwyczaj przechowuje dane tabelaryczne. Nagłówki kolumn są często dołączane jako pierwszy wiersz (są to nazwy zmiennych), a każdy kolejny wiersz odpowiada jednej obserwacji (jednemu wierszowi w tabeli danych). CSV jest szczególnym przypadkiem formatu danych o nazwie Delimiter Seperated Values (DSV). Jest to plik tekstowy w którym pola w każdym wierszu oddzielone są dowolnym separatorem. Najczęściej spotykane separatory to: przecinek (CSV), tabulator (TSV), średnik. Przykładowy plik CSV 3.1.2 XML XML to skrót od nazwy Extensible Markup Language. Dane przechowywane w tym formacie mają zagnieżdżoną strukturę: znaczniki oznaczają nazwy zmiennych, a wewnątrz przechowywane są ich wartości. XML swoją strukturą przypomina plik HTML. Przykładowy plik XML 3.1.3 JSON JSON - JavaScript Object Notation - to format przydatny w przypadku pracy z danymi pochodzącymi z REST API, czyli pobieranymi z sieci. Niektóre bazy danych również komunikują się za pomocą tego formatu, np. MongoDB. Struktura: w pliku JSON obserwacje przechowywane są w słownikach, w których nazwy zmiennych są kluczami, a wartości zmiennych - wartościami. Obserwacje oddzielane są przecinkami, a dodatkowo, wszystkie dane spięte są nawiasami klamrowymi. Przykładowy plik JSON 3.1.4 Excel (XLSX) XLSX to format danych oparty na XML. Pliki tego typu są domyślnymi dokumentami wyjściowymi arkuszy kalkulacyjnych programu Microsoft Excel. Przedstawiają one głównie dane liczbowe i tekstowe w postaci tabel dwuwymiarowych. Przykładowy arkusz kalkulacyjny w Excelu 3.1.5 Otwarte wersje programu Excel Istnieją inne pakiety biurowe, np. LibreOffice, które - w przeciwieństwie do Excela - pozwalają na darmowe korzystanie z arkusza kalkulacyjnego. W przypadku LibreOffice, domyślnym formatem zapisu danych przez Calc (odpowiednik Excela) jest OpenDocument Format (.ods). Przykładowy arkusz kalkulacyjny w LibreOffice 3.1.6 Pliki tekstowe Jednym z najczęściej występujących i najbardziej uniwersalnych formatów przechowujących dane (np. w postaci tabeli) są pliki tekstowe. Mają one najczęściej rozszerzenie txt lub csv (comma separated values). Poniższą charakteryzację różnych metod wczytywania przedstawiamy na podstawie pliku listings.csv 3.1.6.1 Base Podstawową funkcją używaną do wczytywania tego typu plików w postaci tabeli jest funkcja read.table. Ze względu na specyfikację wewnętrzną plików, read.table posiada kilka wariantów, takie jak read.csv(), read.csv2() czy read.delim(). read.csv() używana jest w przypadku, gdy domyślnym separatorem dziesiętnym jest “.”, a wartości w wierszach oddzielone są poprzez “,”; read.csv2() używana jest w przypadku, gdy domyślnym separatorem dziesiętnym jest “,”, a wartości w wierszach oddzielone są poprzez “;”; read.delim() używana jest w przypadku, gdy domyślnym separatorem dziesiętnym jest “.”, a wartości w wierszach oddzielone są poprzez TAB Przykładowy sposób załadowania plików w formacie csv read.csv('./data/csv/listings.csv', header = TRUE, sep = ",") W przypadku read.table() dane zostają zaimportowane jako data.frame. Dla dużych plików wczytwanie za pomocą read.table() bywa jednak czasochłonne. Wówczas możemy użyć funkcji z paczki data.table lub readr. 3.1.6.2 readr readr jest częścią pakietu tidyverse. W tym przypadku import odbywa się za pomocą funkcji o podobnej nazwie, jak w przypadku read.table(), a mianowicie read_csv(). read_csv wczytuje dane oddzielone przecinkami, natomiast read_csv2() - dane oddzielone średnikami. read_csv('./data/csv/listings.csv') W przeciwieństwie do read.csv, funkcja read_csv na wyjściu daje dane w postaci tabeli w bardziej zwartej i przejrzystej formie. Oprócz tego podaje także specyfikację kolumn, tzn. informuje, jaka jest nazwa każdej kolumny oraz jej typ (np. col_double () oznaczają dane liczbowe). Typ danych jaki dostajemy na wyjściu to tbl_df (tzw. tibble), który jest w pewnym sensie zmodyfikowaną wersją tradycyjnej ramki danych data.frame, pozwalającą na łatwiejszą pracę w obrębie tidyverse. 3.1.6.3 data.table Do wczytywania danych z plików csv możemy także użyć funkcji fread z pakietu data.table. fread('./data/csv/listings.csv') Na wyjściu otrzymujemy ramkę danych, jednak wyświetloną w inny sposób niż w przypadku użycia read.csv. Różnica jest widoczna, gdyż po użyciu funkcji class() na fread() jako typ danych otrzymujemy \"data.table\" \"data.frame\". 3.1.6.4 Różnice Najważniejsze różnice pomiędzy wymienionymi sposobami wczytywania plików csv to: Typ danych Base: `data.frame readr: tibble data.table: `data.table data.frame Postać wyświetlania (co jest konsekwencją 1) Base: Wyświetla 62 początkowe wiersze każdej kolumny, wyświetlając informacje o liczbie pozostałych; readr: wyświetla 10 pierwszych wierszy z 10 pierwszych kolumn, z informacją o liczbie pozostałych wierszy i kolumn; automatycznie wyświetlane są też nazwy kolumn oraz skrót informujący o typie zmiennych data.table: wyświetla 5 początkowych i 5 końcowych wartości z każdej kolumny Czas i użycie pamięci przy dużych rozmiarach danych Zarówno czas wczytania danych, jak i wykorzystanie pamięci najkorzystniejsze jest w przypadku funkcji fread. Gdyby przez time oznaczyć czas potrzebny na wczytanie dużych plików, a przez memory zużycie pamięci, to time(fread) < time(read_csv) << time(read.csv) oraz memory(fread) < memory(read.csv) < memory(read_csv). 3.1.7 Arkusze kalkulacyjne i pliki JSON Do wczytywania arkusza kalkulacyjnego (np. pliku excela) używa się funkcji read_excel z pakietu readxl będącego częścią tidyverse. read_excel('./data/excel/listings.xlsx') Oprócz tego, można także użyć pakietu funkcji read.xlsx z pakietu xlsx. Wymaga ona jednak instalacji Javy. Do zaimportowania plików JSON możemy użyć funkcji z pakietu jsonlite listings_js <- jsonlite::fromJSON('./data/json/listings.json') listings_js <- mutate(listings_js, last_review = as_date(last_review)) 3.2 Locale Locale jest to uniksowe narzędzie powłokowe przechowujące ustawienia środowiskowe związane z ustawieniami regionalnymi. Sys.getlocale() ## [1] "LC_CTYPE=pl_PL.UTF-8;LC_NUMERIC=C;LC_TIME=pl_PL.UTF-8;LC_COLLATE=pl_PL.UTF-8;LC_MONETARY=pl_PL.UTF-8;LC_MESSAGES=pl_PL.UTF-8;LC_PAPER=pl_PL.UTF-8;LC_NAME=C;LC_ADDRESS=C;LC_TELEPHONE=C;LC_MEASUREMENT=pl_PL.UTF-8;LC_IDENTIFICATION=C" LC_COLLATE - odpowiada za porządek znaków, ważny przy sortowaniu LC_CTYPE - odpowiada za kodowanie znaków LC_MONETARY - odpowiada za system monetarny: znak waluty, separator tysięcy, liczba cyfr po przecinku itd. LC_NUMERIC - określa separator ułamkowy, separator tysięcy, grupowanie cyfr LC_TIME - odpowiada za system wyświetlania daty Sys.localeconv() ## decimal_point thousands_sep grouping int_curr_symbol currency_symbol ## "." "" "" "PLN " "zł" ## mon_decimal_point mon_thousands_sep mon_grouping positive_sign negative_sign ## "," " " "\\003" "" "-" ## int_frac_digits frac_digits p_cs_precedes p_sep_by_space n_cs_precedes ## "2" "2" "0" "1" "0" ## n_sep_by_space p_sign_posn n_sign_posn ## "1" "1" "1" Powyższa funkcja wyświetla szczegóły dotyczące systemu numerycznego i monetarnego. 3.2.1 Ustawienie locale przez użytkownika Sys.setlocale(category = "LC_ALL", locale = "polish") ## Warning in Sys.setlocale(category = "LC_ALL", locale = "polish"): Żądania raportów OS aby ustawić ## lokalizację na "polish" nie mogą zostać wykonane ## [1] "" W celu ustawienia innego locale niż domyślne systemowe należy użyć powyższej funkcji, przyjmowane przez nią argumenty to category i locale. category - odpowiada za określenie, które zmienne środowiskowe chcemy zmienić, gdzie opcje: “LC_ALL”, “LC_COLLATE”, “LC_CTYPE”, “LC_MONETARY”, “LC_NUMERIC” oraz “LC_TIME” są wspierane na każdym systemie operacyjnym, niektóre systemy wspierają również: “LC_MESSAGES”, “LC_PAPER” i “LC_MEASUREMENT” locale - odpowiada za region, który chcemy ustawić dla systemu windows podajemy angielską nazwę języka (regionu) np.: ‘polish’, w systemach UNIXowych podajemy np.: ‘pl_PL’ lub ‘pl_PL.UTF-8’. 3.2.2 Ustawienie locale poprzez readr Pakiet readr oferuje więcej możliwości dostosowywania locale, więcej informacji na ten temat można znaleźć w tym odnośniku. 3.3 Natywne formaty R R ma dwa natywne sposoby przechowywania danych, RDA(od RData) i RDS. Główną zaletą takiej obsługi danych jest szybkość przetwarzania ich. Zachowuje on także informacje z R o danych(np. typy zmiennych). 3.3.1 RDS W formacie RDS mogą być przechowywane jedynie pojedyncze pliki R. Mogą być one za to przypisywane do dowolnej nazwy. Aby załadować dane korzystamy z: listings_rds <- readRDS("./data/native/listings.rds") Do zapisania danych używamy: saveRDS(object = listings, file = "listings.rds") 3.3.2 RDA W plikach formatu RDA wczytane dane nie są przypisywane do zmiennej, tylko wywołujemy te funkcje i w efekcie plik pojawia się w środowisku. W RDA do załadowania danych służy load("./data/native/listings.rda") Natomiast, aby zapisać dane używamy: save(listings_rr, file = "listings_rr.rda") Korzystając z formatu RDA możemy jednocześnie zapisywać większą ilość plików save(iris, cars, file="data_frame.rda") "],["eksploracyjna-analiza-danych.html", "Chapter 4 Eksploracyjna analiza danych 4.1 Dane tabelaryczne 4.2 Typy zmiennych 4.3 Miary 4.4 R - podsumowanie kolumn", " Chapter 4 Eksploracyjna analiza danych Badanie eksploracyjne danych (ang. exploratory data analysis) dotyczy opisu, wizualizacji i badania zebranych danych bez potrzeby zakładania z góry hipotez badawczych. Badania ekploracyjne obejmują również wstępne sprawdzenie danych w celu skontrolowania założeń modeli statystycznych lub występowania błędów w danych (np. braków odpowiedzi). 4.1 Dane tabelaryczne Dane tabelaryczne to dane, które mają postać tabeli. Tabela to struktura danych, która składa się z wierszy i kolumn. Każdy wiersz odpowiada pewnej obserwacji, której cechy zostały zapisane w kolejnych kolumnach. 4.2 Typy zmiennych Zmienne, które opisują kolejne obserwacje możemy podzielić na: zmienne jakościowe (niemierzalne) porządkowe - np. klasyfikacja wzrostu (niski, średni, wysoki) nominalne - np. kolor oczu, płeć, grupa krwi zmienne ilościowe (mierzalne) dyskretne - np. liczba dzieci, liczba gospodarstw domowych, wiek (w rozumieniu ilości skończonych lat) ciągłe - np. wzrost, masa, wiek (w rozumieniu ilości dni między datą urodzin a datą badania) proporcjonalne - np. masa, długość, temperatura wyrażona w Kelwinach lub stopniach Rankine’a (przyjmujemy istnienie zera i możemy twierdzić, że jedno ciało jest dwukrotnie gorętsze od drugiego) interwałowe - np. temperatura wyrażona w stopniach Celsjusza lub Fahrenheita (możemy twierdzić, że coś jest o 20 °C cieplejsze od czegoś innego, ale nie możemy stwierdzić ilokrotnie cieplejsze jest ciało o temperaturze 40 °C od ciała o temperaturze –10 °C), data kalendarzowa (możemy mówić o stałej różnicy pomiędzy kolejnymi dniami) 4.3 Miary Zapoznając się z danymi chcielibyśmy sprawdzić wokół jakiej wartości są skupione oraz jak bardzo są zmienne wartości danej cechy. Miary lokacji (miary tendencji centralnej) pomagają nam umiejscowić dane na osi. Przykładami takich miar są: średnia - najczęściej arytmetyczna określona jako \\(\\overline{x} = \\frac{1}{n}\\sum\\limits_{i=1}^n x_i\\). dominanta (moda) - ozn. \\(Mo\\) - dla zmiennych o rozkładzie dyskretnym, wartość o największym prawdopodobieństwie wystąpienia lub wartość najczęściej występująca w próbie. Dla zmiennej losowej o rozkładzie ciągłym jest to argument, dla którego funkcja gęstości prawdopodobieństwa ma wartość największą. mediana - ozn. \\(Me\\) - wartość cechy w szeregu uporządkowanym, powyżej i poniżej której znajduje się jednakowa liczba obserwacji. kwantyle rzędu \\(p\\) - wartość cechy w szeregu uporządkowanym, poniżej której znajduje się \\(p \\cdot 100\\%\\) liczby obserwacji, a powyżej której znajduje się \\((1 - p) \\cdot 100\\%\\) liczby obserwacji. Natomiast miary rozrzutu dostarczają informacji jak bardzo zróżnicowane są obserwacje pod względem badanej cechy. Przykładami takich miar są: wariancja - stopień rozrzutu badanej cechy wokół wartości oczekiwanej. Im większa wariancja, tym rozrzut zmiennej jest większy. Nieobciążony estymator wariancji wyraża się wzorem: \\(s^2 = \\frac{1}{n}\\sum\\limits_{i=1}^n\\left(x_i - \\overline{x}\\right)^2\\) odchylenie standardowe - mówi nam o przeciętnym odchyleniu wartości zmiennej losowej od jej wartości oczekiwanej. Im odchylenie standardowe jest większe, tym większe zróżnicowanie wartości badanej cechy. Odchylenie standardowe z próby obliczamy jako pierwiastek z wariancji z próby, tzn. \\(s = \\sqrt{s^2}\\). rozstęp międzykwartylowy - różnica między trzecim a pierwszym kwartylem. Ponieważ pomiędzy tymi kwartylami znajduje się z definicji 50% wszystkich obserwacji (położonych centralnie w rozkładzie), dlatego im większa szerokość tego rozstępu, tym większe zróżnicowanie cechy. Wyróżniamy także miary asymetrii. Miary asymetrii mówią nam, czy większa część populacji klasuje się powyżej, czy poniżej przeciętnego poziomu badanej cechy. Asymetrię rozkładu można zbadać porównując średnią, dominantę i medianę. W przypadku rozkładu symetrycznego wszystkie te parametry są równe. Jeśli zachodzi nierówność \\(Mo < Me < \\mathbb{E} X\\), to mamy do czynienia z prawostronną asymetrycznością rozkładu. Tzn. dużo małych wartości i bardzo mało dużych. Jeśli zachodzi nierówność \\(\\mathbb{E} X < Me < Mo\\), to mamy do czynienia z lewostronną asymetrycznością rozkładu. Tzn. mało małych i bardzo dużo dużych. 4.4 R - podsumowanie kolumn Podstawowymi funkcjami, które pomagają nam zapoznać się z danymi są funkcje: \\(\\texttt{head}\\) - zwraca pierwszą część wektora, macierzy, tabeli lub ramki danych. Domyślnie 6 pierwszych elementów. \\(\\texttt{nrow}\\) - zwraca liczbę wierszy macierzy, tabeli lub ramki danych. \\(\\texttt{ncol}\\) - zwraca liczbę kolumn macierzy, tabeli lub ramki danych. Natomiast podstawowymi funkcjami, które podsumowują kolejne kolumny są funkcje: \\(\\texttt{str}\\) - zwraca strukturę danego obiektu. Wyświetla np. klasę obiektu, liczbę wierszy i kolumn, a także nazwę danej kolumny, typ wartości w niej zawartych, jak i kilka początkowych wartości. \\(\\texttt{summary}\\) - zwraca podsumowanie każdej kolumny. Dla zmiennych ciagłych wyznacza wartości tj.: wartość najmniejsza i największa średnia i mediana 1 (0.25) i 3 (0.75) kwartyl liczba wartości brakujących (NA) Natomiast w przypadku zmiennych dyskretnych wyznacza liczbę obserwacji, które przyjmują daną wartość zmiennej. \\(\\texttt{glimpse}\\) - funkcja z pakietu \\(\\texttt{tidyverse}\\) podobna do \\(\\texttt{str}\\), ale stara się pokazać jak najwięcej danych. Wyświetla np. liczbę wierszy i kolumn, a także nazwę danej kolumny, typ wartości w niej zawartych oraz jak najwięcej wartości z tej kolumny. "],["przetwarzanie-danych-tabelarycznych.html", "Chapter 5 Przetwarzanie danych tabelarycznych 5.1 Wybieranie kolumn 5.2 Zmiana nazw kolumn 5.3 Filtrowanie 5.4 Usuwanie kolumn 5.5 Manipulacje na kolumnach 5.6 Aplikowanie transformacji do każdej kolumny 5.7 Grupowanie i podsumowanie 5.8 Podsumowywanie wszystkich kolumn", " Chapter 5 Przetwarzanie danych tabelarycznych Operacje na danych w R są związane głównie z filtrowaniem, dodawaniem i modyfikowaniem kolumn, grupowaniem oraz podsumowywaniem danych. Można je wykonywać za pomocą funkcji bazowego R lub narzędzi z zaimportowanych pakietów: tidyverse, data.table. Załóżmy, że ramka danych jest przypisana do zmiennej \\(dane\\), a nazwy jej kolumn to: \\(kol.1, kol.2, kol.3,...\\) . 5.1 Wybieranie kolumn Poniżej przedstawione są instrukcje pozwalające na wybieranie konkretnych kolumn z ramki danych w~zależności od metody. Dla uproszczenia przyjmijmy, że wybieramy kolumny: \\(kol.1, kol.2, kol.3\\). base dane = dane[, c(“kol.1”, “kol.2”, “kol.3”)] tidyverse dane = select(dane, kol 1, kol 2, kol 3) dane = dane %>% select(kol 1, kol 2, kol 3) data.table Nazwy kolumn ramki danych zawierą znak “.” . Wprowadźmy zmienną pomocniczą \\(kolumny\\). Będzie ona zawierać nazwy kolumn, ale zastępując znak “.” znakiem ” “. kolumny = c("kol 1", "kol 2", "kol 3") dane = dane[, kolumny] dane = dane[, kolumny, with = FALSE] - dana metoda nie zadziała bez argumentu \\(with~=~FALSE\\), ponieważ szuka w ramce danych kolumn o nazwach zawartych w obiekcie \\(kolumny\\), a nie konkretnie podanych nazw dane = dane[, colnames(dane) %in% kolumny, with = FALSE] dane = dane[, ..kolumny] dane = dane[, list(kol 1, kol 2, kol 3)] dane = dane[, .(kol 1, kol 2, kol 3)] 5.2 Zmiana nazw kolumn Teraz zostaną zaprezentowane sposoby na zmianę nazw kolumn ramki danych. Przyjmijmy, że nowe nazwy kolumn są postaci \\(k1, k2, k3, ...\\) . base colnames(dane) = c(“k1”, “k2”, “k3”) tidyverse dane = dane %>% rename(k1 = kol.1, k2 = kol.2, k3 = kol.3) data.table setnames(dane, c(“kol.1”, “kol.2”, “kol.3”), c(“k1”, “k2”, “k3”)) - zaleta: nie kopiuje ramki danych 5.3 Filtrowanie Dany rozdział skupia się na sposobach filtrowania danych. Przydatne funkcje: unique(dane\\(\\$\\)k1) - zwraca unikalne wartości kolumny \\(k1\\) table(dane\\(\\$\\)k1) - zlicza ilość wystąpienia każdej wartości w kolumnie \\(k1\\) prop.table(table(dane\\(\\$\\)k1)) - pokazuje procentowo ilość wystąpienia każdej wartości w kolumnie \\(k1\\) w\\(~\\)stosunku do wszystkich wartości Przyjmnijmy, że wybieramy z kolumny \\(k1\\) określoną wartość \\(abc\\). base dane[dane\\(\\$\\)k1 == “abc”, ] tidyverse dane %>% filter(k1 == “abc”) można podać kilka warunków (po przecinku), będą one domyślnie rozdzielone spójnikiem \\(i\\) aby połączyć warunki spójnikiem \\(i\\) można również użyć operatora \\(\\&\\) aby połączyć warunki spójnikiem \\(lub\\) należy użyć operatora \\(|\\) data.table dane[k1 == “abc”] 5.4 Usuwanie kolumn Załóżmy, że usuwamy pierwszą kolumnę - \\(k1\\). base dane = dane[, -1] - gdzie \\(1\\) to numer usuwanej kolumny, a “-” oznacza usuwanie tidyverse dane = select(dane, -k1) - jak powyżej, “-” oznacza usuwanie, ale w tym przypadku stosujemy nazwę kolumny a nie jej numer data.table dane[, k1 := NULL] - operator \\(:=\\) (referencja) oznacza, że operacja jest wykonywana bez kopiowania ramki danych dane = dane[, -1, with = FALSE] 5.5 Manipulacje na kolumnach Przyjmijmy, że kolumna \\(k2\\) zawiera tylko liczby. Wartości ujemne zamieniamy na \\(0\\). W tym celu posłużymy się funkcją \\(ifelse\\): \\[ ifelse(warunek \\ logiczny,\\ wartość \\ jeśli \\ spełniony, \\ wartość \\ jeśli\\ niespełniony).\\] 1. base dane[[“k2”]] = ifelse(dane[[“k2”]] < 0, 0, dane[[“k2”]]) tidyverse dane = dane %>% mutate(k2 = ifelse(k2 < 0, 0, k2)) możemy modyfikować kilka kolumn jednocześnie, rozdzielając je przecinkiem data.table dane[, k2 := ifelse(k2 < 0, 0, k2)] - z użyciem referencji dane[[“k2”]] = ifelse(dane[[“k2”]] < 0, 0, dane[[“k2”]]) - bez użycia referencji 5.6 Aplikowanie transformacji do każdej kolumny W tym rozdziale będziemy operować na wszystkich kolumnach ramki danych. Wartości w nich zawarte mogą być typu \\(factor\\), które zamienimy na typ \\(character\\). base poprzez pętlę for (i in 1:ncol(dane)){ if (is.factor(dane[, i])){ dane[, i] = as.character(dane[, i]) } } poprzez funkcję \\(lapply\\) lapply(dane, fun(x){ if(is.factor(x)) x = as.character(x) }) tidyverse przy użyciu funkcji \\(mutate\\_all\\) dane = dane %>% mutate_all(function(x){ if (is.factor(x)){ as.character(x) } else{ x } }) data.table przy użyciu funkcji lapply dane = dane[, lapply(.SD, function(x){ if (is.factor(x)){ as.character(x) } else{ x } })] 5.7 Grupowanie i podsumowanie Załóżmy, że do wyznaczenia wszystkich unkialnych wartości ramki danych potrzebne są kolumny \\(k1\\), \\(k2\\) i \\(k3\\). Natomiast podsumowywana będzie kolumna \\(k4\\) - zostanie wyliczona średnia dla każdej unikalnej wartości. base przy użyciu funkcji \\(aggregate\\) - zastosowana zostanie formuła \\(k4\\) ~ \\(k1 + k2 + k3\\), która oznacza, że będzie podsumowywana zmienna \\(k4\\) w zależności od unikalnych zestawów wartości zmiennych \\(k1\\), \\(k2\\), \\(k3\\) aggregate(k4 ~ k1 + k2 + k3, data = dane, FUN = function(x) mean(x, na.rm = TRUE)) - poprzez zastosowanie własnej funkcji aggregate(k4 ~ k1 + k2 + k3, data = dane, FUN = mean, na.rm = TRUE) - poprzez zastosowanie istniejącej funkcji tidyverse dane %>% group_by(k1, k2, k3) %>% summarize(srednia = mean(k4, na.rm = TRUE), maksimum = max(k4, na.rm = TRUE)) \\(group\\_by\\) - grupuje po kolumnach \\(k1\\), \\(k2\\), \\(k3\\) \\(summarize\\) - podsumowuje według podanych elementów (w tym przypadku wylicza średnią i maksimum z kolumny \\(k4\\)) data.table dane[, list(średnia = mean(k4, na.rm = TRUE), maksimum = max(k4, na.rm = TRUE)), by = c(“k1”, “k2”, “k3”)] 5.8 Podsumowywanie wszystkich kolumn W celu podsumowania kolumn zdefiniujemy poniższą funkcję, która zwróci ilość niepustych wartości. num_unique_noNA = function(input_vector){ sum(!is.na(unique(input_vector))) } base apply(dane, 2, num_unique_noNA) - gdzie \\(2\\) oznacza, że wywołujemy podaną funkcję \\(num\\_unique\\_noNA\\) po kolumnach lapply(dane, num_unique_noNA) sapply(dane, num_unique_noNA) tidyverse summarise_all(dane, num_unique_noNA) data.table dane[, lapply(.SD, num_unique_noNA)] "],["czyste-dane.html", "Chapter 6 Czyste dane 6.1 Dane w formacie wąskim i szerokim 6.2 Rozdzielanie na kolumny (wąska -> szeroka) 6.3 Scalanie kilku kolumn w jedną (szeroka -> wąska) 6.4 Łączenie tabel danych 6.5 Operacje na napisach i datach", " Chapter 6 Czyste dane Transformacja danych jest niezwykle ważnym elementem dobrze zrobionego raportu. Dane te powinny być prezentowane w sposób czytelny i ułatwiający ich porównywanie. To od potrzeby biznesowej zależy w jaki sposób powinniśmy przedstwiać dane. Np. dysponując wynikami finansowymi zbieranymi co miesiąc przez trzy lata bo planowania budżetu na następny rok przyda nam się prezentacja ich w formacie wąskim, czyli skupionym na wydatkach względem każdego roku. Jednakże, jeżeli chcielibyśmy kontrolować wydatki w tym następnym roku prezentacja danych w formacie szerokim będzie bardziej korzystna, gdyż będziemy mieli informację ile średnio wydajemy w danym miesiącu i na bieżąco będziemy mogli podejmować decyzję o inwestowaniu lub zaciskaniu pasa. Niekiedy jednak dane mają bardziej skomplikowaną formę i np. składają się z wielu tabel. Wówczas dla łatwiejszego uzyskania informacji biznesowej będzie połączenie tych tabel. Takie operacje w połączeniu z odpowiednią agregacją i grupowaniem zdecydowanie ułatwia wgląd w aktualną sytuację. Ostatnim tematem, na temat któtego ta notatka traktuje są operacje na napisach i datach. Bardzo łatwo uzmysłowić sobie przydatność w posługiwaniu się takimi operacjami. Ułatwia to konstruowanie prostych funkcji, które są kluczowe w każdym projekcie. Chociażby bazując na imionach i nazwiskach pewnych obywateli Polski łatwo wskazać z dużą pewnością kobiety w tym zbiorze sprawdzając ostatnią literę ich imienia (tj. czy dane imie kończy się na literę “a”). 6.1 Dane w formacie wąskim i szerokim Dane najczęściej są przedstawiane w postaci tabelarycznej. Jednak mogą być w tej tabeli różnie sformatowane. Wyróżnia się między innymi szeroką reprezentacje danych i wąską reprezentacje danych. W zależności od tego, co chcemy z nimi zrobić czasami trzeba przejść z jednej postaci do drugiej. Aby przetransformować dane korzysta się z funkcji z pakietów dplyr i tidyverse. O postaci szerokiej mówimy, gdy pojedyncza zmienna jest rozdzielona pomiędzy kilka kolumn. Różnicę najłatwiej jest pokazać na przykładzie. W tym celu wykorzystamy wbudowany zbiór danych sleep zawierający informacje o wpływie dwóch leków nasennych na ilość przespanych godzin. Kolumna extra zawiera informacje o ilości dodatkowo przespanych godzin. extra group ID 0.7 1 1 -1.6 1 2 -0.2 1 3 -1.2 1 4 -0.1 1 5 3.4 1 6 Dane są przedstawione w postaci wąskiej, każda zmienna jest przedstawiona w oddzielnej kolumnie. Teraz ‘rozbijmy’ kolumnę group na group 1 i group 2. ID group 1 group 2 1 0.7 1.9 2 -1.6 0.8 3 -0.2 1.1 4 -1.2 0.1 5 -0.1 -0.1 6 3.4 4.4 7 3.7 5.5 8 0.8 1.6 9 0.0 4.6 10 2.0 3.4 Można zaobserwować, że wartości z kolumny extra zostały wpisane w poszczególne komórki, a kolumna group została podzielona na dwie oddzielne kolumny group 1 i group 2. Tak sformatowane dane nazywamy szeroką reprezentacją danych. 6.2 Rozdzielanie na kolumny (wąska -> szeroka) Aby przejść z wąskiego formatu przedstawiania danych do szerokiego, można użyć funkcji spread() z pakietu dplyr. Funkcja spread(dataset,key,value) przyjmuje trzy agrumenty: dataset - zbiór danych w formacie wąskim, key - kolumna (klucz) odpowiadająca kolumnie, która ma zostać rozłożona, value - kolumna, w której znajdują się wartości wypełniające nowe kolumny. szeroka <- spread(sleep, group, extra) colnames(szeroka) = c("ID","group 1","group 2") kable_styling(kable(head(szeroka)), position = "center") ID group 1 group 2 1 0.7 1.9 2 -1.6 0.8 3 -0.2 1.1 4 -1.2 0.1 5 -0.1 -0.1 6 3.4 4.4 Drugą opcją na uzyskanie tego samego rezultatu jest użycie funkcji pivot_wider z pakietu tidyverse. Funkcja przyjmuje dwa argumenty pivot_wider(names_from = name, values_from = value): name - nazwa kolumny, która ma zostać rozłożona, value - nazwa kolumny, w której znajdują się wartości. sleep %>% pivot_wider(names_from = group, values_from = extra) 6.3 Scalanie kilku kolumn w jedną (szeroka -> wąska) Można wrócić z postaci szerokiej do wąskiej. W tym celu należy użyć funkcji gather() z pakietu tidyr. Funkcja gather(dataset, key, value, other) przyjmuje również trzy argumenty: dataset - zbiór danych w formacie szerokim, key - nazwy kolumn z kluczami, value - nazwy kolumn z wartościami, other - kolumny dataset, które mają być zawarte w nowej tabeli. Aby wrócić do postaci wąskiej nałóżmy funkcję gather na wygenerowaną wcześniej tabele szeroka. kable_styling(kable(head(szeroka %>% gather(group, extra, -ID))),position = "center") ID group extra 1 1 0.7 2 1 -1.6 3 1 -0.2 4 1 -1.2 5 1 -0.1 6 1 3.4 Drugą funkcją, która umożliwia przejście z szerokiej reprezentacji danych do wąskiej jest funkcja pivot_longer z pakietu tidyverse. Funkcja pivot_longer(col_names, names_to = name, values_to = value) przyjmuje trzy argumenty col_names - ciąg nazw kolumn, które chcemy złączyć, name - nazwa nowo powstałej kolumny, value - nazwa kolumny, w której pojawią się wartości. kable_styling(kable(head(szeroka %>% pivot_longer(c("1", "2"), names_to = "group", values_to = "extra"))), position = "center") 6.4 Łączenie tabel danych Mamy dwie tabele danych tab1 z małymi literami oraz tab2 z wielkimi literami: Table 6.1: tab1 = x indeks litery 1 a 2 b 3 c 4 d 5 e 6 f Table 6.1: tab2 = y indeks LITERY 4 E 5 F 6 G 7 H 8 I 9 J gdzie x = tab1, a y = tab2. Aby połączyć dwie tabele danych na podstawie wskazanych kolumn lub kolumn o wspólnej nazwie można użyć przykładowych funkcji. 6.4.1 merge() Dostępna w bazowym R. Domyślnie funkcja ta łączy tabele względem nazw kolumn, które są wspólne. tabela <- merge(x = tab1, y = tab2) kable(tabela) indeks litery LITERY 4 d E 5 e F 6 f G Jeśli chcemy być pewni, że tabele zostaną połączone po odpowiedniej kolumnie, możemy przekazać nazwę tej kolumny w argumencie. W tym przypadku: merge(tab1, tab2, by = "indeks") # INNER JOIN Jeśli jest więcej kolumn, po których chcemy połączyć tabele, wystarczy przekazać w argumencie by wektor z nazwami tych kolumn. Gdy nazwy kolumn po których chcemy złączyć tabele różnią się, należy wykorzystać argument by.*. Załóżmy, że kolumna tabeli tab1 - indeks zmieniła nazwę na index, zatem: merge(tab1, tab2, by.x = "index", by.y = "indeks") Wartości kolumn indeks w tab1 oraz tab2 różnią się. Dlatego korzystając z funkcji bez dodatkowych argumentów tracimy dane. Aby zapobiec traceniu danych z poszczególnych tabel należy skorzystać z argumentu all, brakujące wartości zostaną uzupełnione NA: merge(tab1, tab2, all.x = TRUE) # LEFT JOIN merge(tab1, tab2, all.y = TRUE) # RIGHT JOIN merge(tab1, tab2, all = TRUE) # OUTER JOIN Dostajemy wtedy kolejno: Table 6.2: all.x = TRUE indeks litery LITERY 1 a NA 2 b NA 3 c NA 4 d E 5 e F 6 f G Table 6.2: all.y = TRUE indeks litery LITERY 4 d E 5 e F 6 f G 7 NA H 8 NA I 9 NA J Table 6.2: all = TRUE indeks litery LITERY 1 a NA 2 b NA 3 c NA 4 d E 5 e F 6 f G 7 NA H 8 NA I 9 NA J Bez sprecyzowania argumentu sort wiersze wyniku merge() zostaną posortowane leksykograficznie po wspólnych kolumnach. Gdy sort = FALSE wiersze będą w nieokreślonej kolejności. Kolumny złączonej tabeli to najpierw kolumny wspólne, następnie pozostałe z x a na końcu pozostałe z y, co widać na przykładach. 6.4.2 join() Funkcja z paczki dplyr. Tabele x i y powinny zwykle pochodzić z tego samego źródła danych, ale jeśli copy = TRUE, y zostanie automatycznie skopiowany do tego samego źródła co x. Są cztery typy join zmieniających: left_join() - zwraca wszystkie wiersze z x i wszystkie kolumny z x i y. Wiersze w x bez dopasowania w y będą miały wartości NA w nowych kolumnach. Jeśli istnieje wiele dopasowań między x a y, zwracane są wszystkie kombinacje dopasowań tabela <- left_join(tab1, tab2) kable(tabela) indeks litery LITERY 1 a NA 2 b NA 3 c NA 4 d E 5 e F 6 f G 6 z G right_join() - analogicznie do left_join(), ale zwraca wszystkie wiersze z y, a wiersze bez dopasowania w x będą miały wartości NA inner_join() - zwraca wszystkie wiersze z x, w których znajdują się pasujące wartości w y, oraz wszystkie kolumny z x i y. Jeśli istnieje wiele dopasowań między x a y, zwracane są wszystkie kombinacje dopasowań. tabela <- inner_join(tab1, tab2) kable(tabela) indeks litery LITERY 4 d E 5 e F 6 f G 6 z G full_join() - zwraca wszystkie wiersze i wszystkie kolumny zarówno z x, jak i y. Jeśli nie ma pasujących wartości, zwraca NA dla brakujących. tabela <- full_join(tab1, tab2) kable(tabela) indeks litery LITERY 1 a NA 2 b NA 3 c NA 4 d E 5 e F 6 f G 6 z G 7 NA H 8 NA I 9 NA J Argument by przyjmuje wektor nazw zmiennych do połączenia. Jeśli by = NULL funkcja *_join() domyślnie połączy tabele dopasowując wartości ze wszystkich kolumn o wspólnych nazwach w obu tabelach. 6.5 Operacje na napisach i datach Większość poniższych funkcji pochodzi z pakietu stringi. 6.5.1 Operacje na napisach Wyznaczanie długości napisów. Funkcja stri_lenght() zwraca długości poszczególnych napisów w danym wektorze, a stri_isempty() sprawdza, które napisy są puste -> ’’. Łączenie i powielanie napisów. Funkcja używana do łączenia kilku wektorów napisów w inny wektor napisów lub nawet w jeden napis, jest stri_paste() i jej warianty. Przykład: x <- LETTERS[1:3] y <- letters[1:3] z <- '!' stri_paste(x, y, z) ## [1] "Aa!" "Bb!" "Cc!" Przycinanie i wypełnianie. Funkcja stri_wrap() wstawia znaki nowego wiersza (n), by napis po wyświetleniu np. przy funkcji cat() miał szerokość nie większą, niż podana, jeżeli to możliwe. W przypadku przetwarzania tekstów pochodzących np. z formularzy na stronach internetowych może zachodzić potrzeba usunięcia tzw. białych znaków, np. spacji z początku lub końca napisu. Możemy to zrobić przy użyciu funkcji stri_trim(). Operacja w pewnym sensie odwrotną do tej można wykonać przy użyciu funkcji stri_pad(). Przykład: stri_trim(' Mama i tata\\n') ## [1] "Mama i tata" Formatowanie napisów na podstawie innych obiektów. Najprostszym sposobem na uzyskanie napisowej reprezentacji danego obiektu jest użycie funkcji as.character(). Przykład: as.character(list(1L, mean, NULL, pi, FALSE)) ## [1] "1" "function (x, ...) \\nUseMethod(\\"mean\\")" ## [3] "NULL" "3.14159265358979" ## [5] "FALSE" x <-data.frame(a=c(TRUE, FALSE, FALSE), b=as.integer(c(1, 2, 3))) as.character(x) ## [1] "c(TRUE, FALSE, FALSE)" "1:3" Zmiana pojedynczych znaków. Zmiana poszczególnych znaków na inne przydaje się między innymi na etapie wstępnego przygotowania danych w celu ujednolicenia tekstowych identyfikatorów obiektów, możemy np. zmieniać wielkości wszystkich liter w napisach. Przykład: stri_trans_toupper('chcemy duże litery') ## [1] "CHCEMY DUŻE LITERY" stri_trans_tolower('ChCemY MałE LiTErY') ## [1] "chcemy małe litery" stri_trans_char('zastępowanie znaków', 'ąćęłńóśżź', 'acelnoszz') ## [1] "zastepowanie znakow" stri_trans_general('żółć', 'Latin-ASCII') ## [1] "zolc" Wyznaczanie podnapisów. Funkcja stri_sub() zwraca podnapis składający się ze znaków leżących na określonych pozycjach danego napisu. Przykład: x <- 'Lasy, pola, pastwiska, koszą traktorem' stri_sub(x, 7) ## [1] "pola, pastwiska, koszą traktorem" 6.5.2 Operacje na datach Funkcją zwracającą aktualną datę systemową jest Sys.Date(), a Sys.time() aktualny czas systemowy wraz z datą. Przykład: (data <- Sys.Date()) ## [1] "2023-10-12" (czas <- Sys.time()) ## [1] "2023-10-12 00:39:40 CEST" Operacje arytmetyczne na datach – dodawanie, odejmowanie i porównywanie. Przykład: data ## [1] "2023-10-12" data-365 ## [1] "2022-10-12" data+365 ## [1] "2024-10-11" (d <- data-as.Date('2021-01-01')) ## Time difference of 1014 days Do konwersji do napisu może służyć przeciążona wersja metody format(), której wywołanie jest tożsame z wywołaniem funkcji strftime() (ang. string-format-time). Przykład: strftime(czas, '%Y-%m-%d %H:%M:%S %Z') ## [1] "2023-10-12 00:39:40 CEST" Do znajdowania “najstarszej” i “najmłodszej” daty używamy funkcji max() oraz min(). Do pracy ze strefami czasowymi możemy używać poniższych funkcji: force_tz() ustawienie strefy czasowej, with_tz() sprawdzenie daty w innej strefie czasowej. "],["katarzyna-frankiewicz-maciej-grabias-jakub-michałowski.html", "Chapter 7 Katarzyna Frankiewicz, Maciej Grabias, Jakub Michałowski 7.1 Wprowadzenie 7.2 Podstawy tworzenia wykresów w ggplot2 7.3 Mapowanie 7.4 Geometria wykresu 7.5 Funkcje pomagające poprawić czytelność wykresu 7.6 Panele", " Chapter 7 Katarzyna Frankiewicz, Maciej Grabias, Jakub Michałowski 7.1 Wprowadzenie Jednym z ważnych elementów przekazywania ciekawych informacji oraz ich analizy jest przedstawienie graficzne interesujących nas danych. W R istnieje kilka sposobów na wizualizację danych. Jednym z nich jest korzytanie z narzędzi oferowanych przez pakiet ggplot2. Bibiloteka ggplot2 oprócz zwykłych funkcji plotowania, implementuje także gramatykę grafiki, co pozwala na wykonanie prawie każdego rodzaju (statystycznej) wizualizacji danych. 7.1.1 Gramatyka grafiki Powyżej wspomnieliśmy o gramatyce grafiki. Dla dokładniejszego uporządkowania wiedzy przypomnijmy, że gramatyka grafiki daje nam możliwość zadawania odpowiednich parametórw dla wszystkich linii, słów, strzałek, itp., które połączone tworzą wykres. Dodatkowo możemy m.in. zmieniać układ współrzędnych, czy korygować położenie każdego obiektu znajdującego się na wykresie. Możliwości jakie oferuje nam gramatyka grafiki będą przedstawione dokładniej w dalszej części notatki. 7.2 Podstawy tworzenia wykresów w ggplot2 Na początku, aby móc tworzyć wizualizacje, musimy załadować pakiet oraz bibilotekę ggplot2. Warto zwrócić uwagę, że ggplot2 posiada również szereg wbudowanych zestawów danych. Aby pokazać możliwości jakie oferuje nam ggplot, przeprowadzimy symulację danych mpg dostępnych w R. library(ggplot2) head(mpg) ## # A tibble: 6 × 11 ## manufacturer model displ year cyl trans drv cty hwy fl class ## <chr> <chr> <dbl> <int> <int> <chr> <chr> <int> <int> <chr> <chr> ## 1 audi a4 1.8 1999 4 auto(l5) f 18 29 p compact ## 2 audi a4 1.8 1999 4 manual(m5) f 21 29 p compact ## 3 audi a4 2 2008 4 manual(m6) f 20 31 p compact ## 4 audi a4 2 2008 4 auto(av) f 21 30 p compact ## 5 audi a4 2.8 1999 6 auto(l5) f 16 26 p compact ## 6 audi a4 2.8 1999 6 manual(m5) f 18 26 p compact Składnia wykresów w ggplot polega na warstwowym budowaniu wykresów. Zaczynamy od doboru danych, jakie chcielibyśmy zwizualizaować. Określamy sposób mapowania zmiennych do aspektów wizualnych. Definiujemy styl wykresu. Dodajemy dodatkowe warstwy personalizujące wykres, tzn. dodajemy tytuł, etykiety, itp. (opcjonalnie) Uwaga! Do dodawania nowych warstw do wykresu używamy symbolu “+” . # Budujemy podstawę wykresu, określając z jakich danych będziemy korzytsać ggplot(mpg) # Mapujemy dane ( na osiach pojawiły się odpowiednie podziałki) ggplot(mpg , aes( x = displ, y = hwy)) # Określamy styl wykresu, dzięki czemu dostemy wykres odpwiednich zależności ggplot(mpg , aes( x = displ, y = hwy))+ geom_point() # Personalizujemy wykres poprzez dodanie tytułu oraz określenie motywu wykresu ggplot(mpg , aes( x = displ, y = hwy))+ geom_point()+ ggtitle("disp vs hwy")+ theme_bw() 7.3 Mapowanie Mapowanie danych jest estetyką, która mówi ggplot’owi, jakie zmienne powinny znajdować się na osi x oraz y. Dodatkowo możemy wpływać na cechy wizualne danych, takie jak kolor ( color = ), kształt ( shape = ), czy przezroczystość ( alpha = ). Wszystkie estetyki dla podziałki wykresu są określone w wywołaniu funkcji aes(). Uwaga! Każda warstwa geom może mieć swoją własną specyfikację aes. Możemy zdecydować, czy na wykresie geom_point punkty mają być zaznaczone jako koła, czy kwadraty. # Punkty na wykresie będą kwadratami ggplot(mpg, aes(x = displ, y = hwy)) + geom_point(shape = 0)+ ggtitle("displ vs hwy")+ theme( axis.title = element_text(size = 1))+ theme_bw() # Punkty na wykresie to czerwone kropki ggplot(mpg, aes(x = displ, y = hwy)) + geom_point(color = "red")+ ggtitle("displ vs hwy")+ theme_bw() 7.4 Geometria wykresu Za pomocą ggplot2 możemy stworzyć prawie każdy rodzaj wykresu. W tym celu musimy zadać typ wykresu jaki nas interesuje. Ggplot2 daje możliwość stworzenia wykresu: punktowego ( geom_point) liniowego ( geom_line) słupkowego ( geom_bar) skrzypcowego ( geom_violin) histogramu ( geom_histogram) boxplota ( geom_boxplot) oraz wielu innych, które powstają m.in. przez zastosowanie funcji: geom_area, geom_density, geom_dotplot, geom_qq, geom_smooth. Uwaga! Wykresy słupkowe i histogramy grupują dane, a następnie prezentują liczbę elementów znajdujących się w poszczególnych grupach Na wykresach liniowych model jest dopasowywany do danych, a nastęonie wykreślane są przewidywania wyznaczone przez model Wykresy pudełkowe obliczają kompleksowe podsumowanie rozkładu wartości Poniżej przedstawione są dwa przykładowe typy wykresów. Pierwszy narysowany przy użyciu funkcji geom_smooth, która służy do rysowania wygładzonych linii, np. dla prostych trendów. Drugi wykres powstał przy zastosowaniu funkcji geom_histogram. W pakiecie ggplot2 bardzo prosto możemy łączyć ze sobą różne geometrie na jednym wykresie. Wystarczy wstawić znak “+” pomiędzy odpowiednimi funkcjami. ggplot(mpg , aes( x = displ, y = hwy))+ geom_point()+ geom_smooth()+ ggtitle("Multiple geomteries")+ theme_bw() 7.5 Funkcje pomagające poprawić czytelność wykresu 7.5.1 Systemy współrzędnych Domyślnym systemem współrzędnych dla ggplot2 jest kartezjański układ współrzędnych. W zależności od danych na jakich działamy, może się okazać, że pokazanie danych w innym układzie współrzędnych, wpłynie na lepszy odbiór informacji z wykresu. Funkcjami, które odpowiadają za przekształcenie układu współrzędnych są m.in. coord_flip która zamienia osie x i y coord_polar wykres jest pokazany we współrzędnych polarnych coord_fixed nadal jesteśmy w kartezjańskim układzie współrzędnych, ale możemy zmienić proporcję między jednostkami na osi x i y 7.5.2 Dopasowanie położenia Każda geometria w ggplot2 ma ustawione domyślne położenie różnych elementów na wykresie względem siebie. Różne opcje ustawienia położenia są dobrze widoczne na wykresach słupkowych. Zacznijmy od stworzenia zwykłego wykresu słupkowego, bez żadnych dodatkowych funkcji. Jeżeli teraz do mapowania dodamy opcję fill = dvr, to każdy prostokąt będzie reprezentował kombinację wartości class oraz dvr. Takie przedstawienie danych nie dla każdego może być czytelne, dlatego możemy skorzystać z opcji position, która przyjmuje m.in. argumenty: “dodge” i “fill”. 7.5.3 Zarządzanie osiami współrzędnych Jedną z możliwości jaką oferuje nam pakiet ggplot2 jest prosta zmiana skali na osiach wykresu. Podstawowymi funkcjami, które to umożliwiają są: scale_x_log10 (zamiast x możemy podać także y) wtedy skala x-ów będzie zlogarytmowana scale_x_reverse powoduje odwrotny kierunek na osi x scale_x_sqrt() skala x-ów będzie spierwiastkowana scale_fill_manual pozwala nam ręcznie wprowadzić oczekiwane przez nas wartości, m.in. możemy zmienić nazwy obiektów na skali, czy podać zakres wartości do uwzględnienia w mapowaniu xlim(5,40) powoduje ograniczenie podziałki na osi x od 5 do 40 (analogicznie z ylim) W ggplot2 z łatwością także dodamy etykiety tekstowe oraz adnotacje. Do wykresu możemy dodać tytuł oraz nazwy osi korzystając m.in. z funkcji labs(). ggplot(mpg, aes(x = displ, y = hwy, color = class)) + geom_point() + labs(title = "Fuel Efficiency by Engine Power", x = "Engine power", y = "Fuel Efficiency", color = "Car Type") 7.5.4 Motywy Theme to dobry sposób na dostosowanie odpowiedniego tytułu, etykiet, czcionek, tła, legendy, czy lini siatki na wykresie. Możemy skorzystać z jednego z dostępnych motywów, takich jak theme_bw(), czy theme_minimal(). Istnieje możliwość zastosowania wielu dostępnych opcji tak, aby odpowiednie elementy wykresu wyglądały tak, jak chcemy. Podstawowymi funkcjami, jakie warto znać są m.in. legend.position, dzięki której możemy ustalić pozycję legendy wykresu, axis.text, która umożliwia nam ustawienie czcionki na wykresie oraz ustalenie jej wielkości czy koloru. Przydatną funkcją pochodzącą z rodziny theme jest ‘theme(axis.text.x = element_text(angle = 90))’, która obraca nazwy znajdujące się na osi x, dzięki, czemu stają się one czytelniejsze. 7.6 Panele Ostatnim z podstawowych funkcji jakie oferuje pakiet ggplot2 jest facets. Panele to sposoby grupowania wykresu danych w wiele różnych części ze względu na zadaną zmienną. Możemy korzystać z funkcji: facet_wrap(), która ustawia panele w prostokątnym układzie facet_grid(), która ustawia panele w kolumny lub w wiersze (zależnie jaką opcję wybierzemy) ggplot(mpg, aes(x = displ, y = hwy)) + geom_point() + facet_grid(~ class) Uwaga! Aby zadać względem, której zmiennej chcemy grupować, w funkcji ‘facet_’ po znaku “~”, podajemy nazwę tej zmiennej. Kiedy korzystamy z funkcji tworzącej panele, automatycznie wszytskie wykresy będą pokazane w układzie współrzędnych dopasowanym do wszytkich okienek. Istnieje jednak możliwość dopasowania układu współrzędnych do każdego panelu osobno. W tym celu możemy wykorzystać funcję ‘scale = “free”’. "],["czysty-i-wydajny-kod-w-r.html", "Chapter 8 Czysty i wydajny kod w R 8.1 Czysty kod 8.2 Styl kodu i narzędzia pomagające w utrzymaniu czystego kodu", " Chapter 8 Czysty i wydajny kod w R 8.1 Czysty kod Na początku zajmiemy się szeroko pojętą czystością kodu. Aby dany kod mógł aspirować do takiego miana, musi przede wszystkim spełniać dwa podstawowe warunki: Być łatwym do zrozumienia Aby kod był łatwy do zrozumienia musi być przede wszystkim czytelny. Niewątpliwie pomoże w\\(~\\)tym odpowiednie nazwanie zmiennych, zadbanie o to, żeby wszystkie użyte funkcje i obiekty miały swoją określoną rolę oraz by relacje między nimi były zrozumiałe. Być łatwym do zmiany Tworząc kod powinniśmy myśleć o tym, że będzie on w przyszłości wykorzystywany. Aby to ułatwić, musi być napisany w taki sposób, żeby można było nanieść drobne poprawki lub zmienić dane bez konieczności zmieniania całego kodu. Jeśli te dwa warunki nie są spełnione, istnieje obawa, że wprowadzenie nawet najmniejszych zmian całkowicie zniszczy kod. 8.1.1 Co jeśli w kodzie jest ,,bałagan’’? Nieuporządkowany i nieklarowny kod może sprawić w przyszłości wiele kłopotów, takich jak na przykład: Zmarnowanie czasu Jeśli my lub ktokolwiek inny będzie chciał w przyszłości wykorzystać taki kod z pewnością straci mnóstwo czasu na próby jego przeczytania i zrozumienia. Gdy już mu się to uda, może napotkać kolejny problem w postaci trudności z wprowadzeniem jakichkolwiek zmian. Ograniczenie lub nawet brak możliwości rozwoju Złe napisanie kodu może spowodować, że po jego jedynym użyciu stanie się bezwartościowy. Nie będzie sensu wprowadzać w nim jakichkolwiek zmian (gdyż będzie to zbyt pracochłonne), ani w żaden sposób rozwinąć by mógł posłużyć do przyszłych projektów (gdyż nawet najmniejsze zmiany mogą ,,zepsuć’’ istniejący kod). Podatność na wystąpienie błędów W nieczytelnym i napisanym w sposób niezrozumiały kodzie, łatwo przemycić błędy, które na pierwszy rzut oka są niewidoczne, ale wychodzą na jaw później. 8.1.2 Opis zmiennych 8.1.3 Opis intencji Aby tworzyć czysty kod musimy pamiętać o kilku zasadach. Jedną z nich jest odpowiednie nazywanie zmiennych. Nie powinniśmy używać do tego skrótów, czy przypadkowych znaków. Idealna nazwa od razu wskazuje na to, czym jest dany obiekt oraz co oznacza. Przedstawia zamiary, jakie mamy do nazywanego obiektu. 8.1.4 Unikanie błędnych informacji Równie ważne jest, aby w nazwach nie znajdywały się błędy lub informacje, które mogą wprowadzić potencjalnego czytelnika w błąd. Mówimy tu np. o: - nazwaniu kilku obiektów zbyt podobnie, - użyciu do nazwania listy (np. osób) słowa \\(\\mathtt{List}\\), choć w rzeczywistości ta ,,lista’’ osób może być wektorem, - użyciu trudno rozróżnialnych znaków (takich jak np. 0 i O), - nazwaniu wszystkich obiektów za pomocą jednej litery i cyfry (np. \\(x_1,x_2,...,x_n\\)). 8.1.4.1 Kilka wskazówek Jakie powinny być idealne nazwy obiektów w R? Oto kilka wskazówek: - zrozumiałe dla osób, dla których jest przeznaczony kod, - utrzymane w jednym stylu, - łatwe do zrozumienia i napisania, - nazwa obiektu powinna być rzeczownikiem, który wskazuje na to, z czym mamy do czynienia, - nazwa funkcji powinna być czasownikiem wskazującym na to, co robi dana funkcja. 8.1.5 Funkcje W tym rozdziale dowiemy się jak pisać ,,dobre’’ funkcje. Tutaj również musimy pamiętać o kilku zasadach. Funkcje powinny: - być możliwie jak najkrótsze, - odpowiadać za jedno pojedyncze zadanie, - być na jednym poziomie abstrakcji, - mieć maksymalnie 3 parametry. To znaczy, że nie jest wskazane, aby tworzyć jedną wielką funkcję, która np. wylicza kilkanaście rzeczy, aby na końcu wygenerować jeden wynik. Zamiast tego lepiej stworzyć kilka mniejszych funkcji, które będą się odwoływały do poprzednich. Dzięki temu nasz kod będzie bardziej przejrzysty oraz w prosty sposób będzie można sprawdzić, czy pojedyncze funkcje działają poprawnie. Co więcej, nie ma sensu tworzyć funkcji, która zwraca nam już oprawioną tabelę z wynikami. Lepiej, gdy zwraca surowe wyniki, a tworzeniem tabeli zajmuje się kolejna funkcja. Przykładowa, poprawnie napisana funkcja: calculate_conf_interval = function(sample, alpha) { len = length(sample) successes = length(sample[sample == 1]) mi = successes / n se = sqrt(mi * (1 - mi) / len) quantile = qt(1 - alpha / 2, len - 1) left = mi - quantile * se right = mi + quantile * se return(c(left, right)) } Przykładowa funkcja, napisana w ,,nieładny’’ sposób: func= function(x,y,temp1,temp2){ n =length(x) s <-length(x[x==1]) m = s/n sgm = sqrt(mi *(1- m)/n) q<-qt(1 - y /2,len-1) tmp = (s + 0.5 * q ^ 2) /(n + q ^ 2) se = sqrt(tmp *(1 - tmp)/ (n+ q^2)) l<- tmp- q* se r = tmp + q*se return(c(l,r))} Główne problemy: - czasem przypisanie jest za pomocą =, czasem <-, - brak spacji po przecinkach, - brak spacji pomiędzy +, -, *, /, itd, - niepoprawnie umiejscowione nawiasy {, }. - nazwa funkcji nie opisuje, co robi ta funkcja, - zmienne mają nic nieznaczące i jednoliterowe nazwy, - nazwa zmiennej tmp także nie mówi, czym ona jest, - dwa nieużywane parametry funkcji. 8.1.6 Komentarze Zazwyczaj komentarze do kodu nie są potrzebne, a wręcz zbędne. Dzieje się tak, ponieważ dobrze napisany kod powinien sam się tłumaczyć, tzn. być na tyle zrozumiałym, żeby dodatkowe komentarze nie były potrzebne. Jeśli jednak w kodzie jest bałagan, dodatkowe komentarze mogą wręcz wprowadzić dodatkowy chaos. Od tej reguły są jednak pewne wyjątki. Jeśli używamy niezbyt oczywistych implementacji lub ,,sztuczek programistycznych’’ warto wspomnieć w komentarzu, co się w danej chwili dzieje. Wyjątkiem są też komentarze informujące o tym, co trzeba jeszcze zrobić lub o potrzebie poprawienia jakiejś części kodu. 8.1.7 Obiekt a struktura danych W kontekście pisania czystego i wydajnego kodu, należy wziąć pod uwagę rozróżnienie pomiędzy klasami a strukturami danych. Te pierwsze zawierają atrybuty i funkcje, a instancje klasy nazywamy obiektem. Zastosowanie klas pozwala na stworzenie interfejsu definującego pewne dane. Struktury danych służą natomiast do reprezentacji danych dowolnego typu a nie ich opisu. 8.2 Styl kodu i narzędzia pomagające w utrzymaniu czystego kodu Dobry styl kodowania jest porównywany do prawidłowego stosowania interpunkcji. Jest możliwe nie stosowanie się do jej zasad, jednak przestrzeganie ich pozwala, aby w zapisie panował ład i porządek. W R dominują dwa style, które pomagają utrzymać dobry układ kodu. Jednym jest tidyverse style, a\\(~\\)drugim, wywodzącym się z poprzedniego, Google style. Istnieją przewodniki, które ułatwiają stosowanie się do zasad panujących w tych stylach. Style ustosunkowują się m.in. do stawiania spacji po przecinkach, przed operatorami matematycznymi oraz po nich, a także podkreślników w nazwach. Dodatkowo można zainstalować pakiety, które będą pomagać w utrzymaniu schludnego kodu: cleanr, stylerr, lintr. "],["interaktywna-wizualizacja-danych-z-pakietem-shiny-interfejs-użytkownika.html", "Chapter 9 Interaktywna wizualizacja danych z pakietem shiny: interfejs użytkownika 9.1 Wstęp 9.2 Tworzenie UI 9.3 Układ strony 9.4 Elementy wejścia i wyjścia 9.5 Przykład użycia 9.6 Wygląd aplikacji 9.7 Wstęp 9.8 Serwer Shiny", " Chapter 9 Interaktywna wizualizacja danych z pakietem shiny: interfejs użytkownika 9.1 Wstęp Shiny jest pakietem R pozwalającym na tworzenie interaktywnych aplikacji webowych w łatwy i przystępny sposób. Aplikacja w shiny zbudowana jest z dwóch następujcych elementów: ui - user interface, czyli obiekt, w którym zawarty jest wygląd aplikacji, server - funkcja organizująca działanie aplikacji. Do uruchomienia aplikacji służy funkcja shinyApp(ui, server). Stworzenie dobrej i czytelnej aplikacji może znacznie ułatwić analizowanie danych. W tej notatce zajmiemy się omówieniem elementów oraz podstawowych schematów budowy UI. library(shiny) library(shinyWidgets) library(shinydashboard) 9.2 Tworzenie UI Do budowania podstawowego interfejsu w shiny będziemy korzystać z funkcji fluidPage, w której tworzymy cały UI. Wszystkie informacje o rodzajach wprowadznych danych, strukturze wyświetlanych danych oraz szeroko rozumianej estetyce aplikacji będą zawarte wewnątrz tej funkcji. ui <- fluidPage( # coś ) 9.3 Układ strony Tym co jest bardzo ważne w UI jest oczywiście wygląd, a dokładniej mówiąc przejrzystość i czytelność, dlatego chcielibyśmy uporządkować wyświetlane elementy tak, aby umożliwić użytkownikowi intuicyjne korzystanie z aplikacji. Pakiet shiny oferuje wiele narzędzi pozwalających na zorganizowanie układu interfejsu zgodnie z naszymi oczekiwaniami. Przydadzą nam się do tego następujące funkcje: titlePanel - funkcja tworząca panel tytułowy, w której podajemy tytuł aplikacji, sidebarLayout - funkcja organizująca wygląd strony jako mniejszy panel boczny po lewej stronie oraz większy panel po prawej stronie, sidebarPanel - funkcja, którą możemy umieścić w poprzedniej funkcji, aby uporządkować panel, w którym będziemy np. wprowadzać dane, mainPanel - funkcja, w której umieszczamy treści, które chcemy, aby znalazły się w panelu głównym, tabsetPanel - funkcja umożliwiająca organizowanie paska zakładek. Aby utworzyć zakładki w jej ciele używamy funkcji tabPanel, w której umieszczamy dowolne treści, np. wykresy lub tabele. Oprócz tego możemy bardziej modyfikować wygląd aplikacji dzięki funkcjom fluidRow i column pozwalającym na uporządkowanie obiektów odpowiednio w wierszach oraz kolumnach. 9.4 Elementy wejścia i wyjścia Układ strony należy oczywiście podporządkować temu jaką funkcję ma pełnić aplikacja, a także temu jaki rodzaj interakcji ma mieć z nią docelowo użytkownik. Interakcje użytkownika z aplikacją można intuicyjnie podzielić na to co zostaje do aplikacji wprowadzone (input) oraz to co ostatecznie w związku z tym aplikacja zwraca (output). Każdy input i output jest w kodzie identyfikowany dzięki nadanej mu przez nas nazwie. Wewnątrz fluidPage możemy zawrzeć różne rodzaje inputów i outputów w zależności od rodzaju wprowadzanych/wyświetlanych danych. 9.4.0.1 Przykładowe elementy wejścia textInput - funkcja tworząca pole, w którym użytkownik może wprowadzić dowolny tekst, ui <- fluidPage( # Okienko do wpisywania tekstu textInput("nazwa_inputu_1", "Tekst wyświetlany w aplikacji") ) numericInput - funkcja tworząca pole, w którym użytkownik może wprowadzić wartość liczbową, ui <- fluidPage( # Okienko do wpisywania liczb numericInput("nazwa_inputu_2", "Tekst wyświetlany w aplikacji", # Wartość domyślna value = 10) ) selectInput - funkcja tworząca listę, z której użytkownik może dokonać wyboru - domyślnie parametr multiple umożliwia wybór jednej pozycji z listy, ui <- fluidPage( # Możliwość wybrania z listy selectInput("nazwa_inputu_3", "Tekst wyświetlany w aplikacji", # Lista możliwości do wyboru choices = c("Wybór_1", "Wybór_2")) ) sliderInput - funkcja tworząca suwak umożliwiający użytkownikowi wybór zakresu interesujących go wartości, ui <- fluidPage( # Suwak do wyboru wartości sliderInput("nazwa_inputu_4", "Tekst wyświetlany w aplikacji", # Wartość domyślna value = 1, # Wartość minimalna min = 0, # Wartość maksymalna max = 10) ) dateRangeInput - funkcja tworząca pole wyboru zakresu interesujących dat. ui <- fluidPage( # Pole wyboru zakresu dat dateRangeInput("nazwa_inputu_5", "Tekst wyświetlany w aplikacji", # Data początkowa start = "2001-01-01", # Data końcowa end = "2010-12-31") ) 9.4.0.2 Przykładowe elementy wyjścia Używanie funkcji wyświetlających outputy jest bardzo proste, ponieważ w UI decydujemy jedynie gdzie i jak wyswietlić output, który jest obiektem utworzonym wewnątrz funkcji server na podstawie wprowadzonego przez użytkownika inputu. textOutput - funkcja wyświetlająca tekst, ui <- fluidPage( # Wyświetla tekst, który stworzyliśmy w serwerze pod daną nazwą textOutput("nazwa_outputu_1") ) tableOutput - podstawowa funkcja wyświetlająca tabelę, ui <- fluidPage( # Wyświetla tabelę stworzoną w serwerze pod daną nazwą tableOutput("nazwa_outputu_2") ) DTOutput - funkcja wyświetlająca interaktywną ramkę danych z użyciem pakietu data.table, ui <- fluidPage( # Interaktywna ramka danych z użyciem data.table DT::DTOutput("nazwa_outputu_3") ) plotOutput - funkcja wyświetlająca wykres. ui <- fluidPage( # Wyświetla wykres stworzony w serwerze plotOutput("nazwa_outputu_4", # Szerokość wykresu width = "100%", # Wysokość wykresu height = "400px") ) 9.5 Przykład użycia Oczywiście powyższe kody były jedynie fragmentami większej całości. Poniżej możemy zobaczyć przykładowy kod obrazujący strukturę budowy interfejsu. Rzeczą, o której należy pamiętać jest oddzielanie funkcji przecinkami. ui <- fluidPage( # Tytuł titlePanel("Tytuł"), # To co będzie wyświetlone z boku interfejsu sidebarLayout( # Panel boczny sidebarPanel( # Pierwszy input - wybór selectInput("nazwa_inputu_1", "Tekst wyświetlany w aplikacji", choices = c("Wybór_1", "Wybór_2")), # Drugi input - suwak sliderInput("nazwa_inputu_2", "Tekst wyświetlany w aplikacji", value = 1, min = 0, max = 10) ), # Główny panel mainPanel( # Tworzymy zakładki tabsetPanel( # Pierwsza zakładka - wykres tabPanel("Tytuł wykresu", plotOutput("nazwa_outputu_1")), # Druga zakładka - ramka danych tabPanel("Tytuł ramki", DT::DTOutput("nazwa_outputu_2")) ) ) ) ) Dodatkowo warto zdawać sobie sprawę, że po wprowadzeniu danych przez użytkownika outputy aktualizują się automatycznie, dlatego często przydatne jest programowanie reaktywne z funkcją observeEvent oraz użycie actionButton, który pozwala na wykonanie danego działania dopiero po kliknięciu odpowiedniego przycisku przez użytkownika. 9.6 Wygląd aplikacji Ostatecznie chcielibyśmy, aby aplikacja wyglądała bardziej estetycznie. Możemy do tego użyć kilku narzędzi. Po pierwsze możemy zmienić motyw naszej aplikacji. Z pomocą przychodzi nam funkcja shinythemes::themeSelector(), którą musimy umieścić w naszym UI. Wtedy w naszej aplikacji pojawia się pole z możliwością wyboru motywu. Gdy już wybierzemy ulubiony motyw zamieniamy poprzednią funkcję w UI na theme = shinythemes::shinytheme('NASZ_MOTYW') i gotowe! Poza tym Shiny umożliwia całkowitą customizację wyglądu aplikacji przy użyciu HTML, CSS oraz JavaScript. Ostatnim narzędziem, o którym warto pamiętać, jest shinyWidgetsGallery(). Jest to bardzo użyteczna aplikacja stworzona w bibliotece shinyWidgets, dzięki której możemy między innymi zobaczyć w praktyce działanie różnego typu inputów oraz kod umożliwiający użycie ich w aplikacji. 9.6.1 Uwaga W tej notatce omówiliśmy podstawowe elementy pozwalające na zbudowanie interfejsu w shiny ale chcielibyśmy też dodać, że w poszukiwaniu bardziej zaawansowanych rozwiązań warto odwiedzić stronę https://shiny.rstudio.com/, gdzie można znaleźć dokumentację pakietu shiny, wiele przykładów oraz nieomówionych tu funkcji. 9.7 Wstęp Shiny to biblioteka w R pozwalająca na budowanie interaktywnych aplikacji w prosty i szybki sposób. Aplikacja Shiny składa się z dwóch części, opisywanych w dwóch osobnych plikach: interfejs użytkownika (UI), czyli jak aplikacja będzie wyglądać u użytkownika oraz sposób przetwarzania danych (serwer). W tej pracy zajmiemy się stroną serwerową Shiny. 9.8 Serwer Shiny Aplikacje Shiny zazwyczaj budujemy w sytuacjach, w których mamy dane, chcemy obliczyć pewne rzeczy i narysować odpowiednie wykresy. Użytkownik widzi efekt końcowy, czyli to jak zaprogramowaliśmy gdzie ma się wyświetlać wynik, natomiast w części serwerowej opisujemy jak ten wynik ma być obliczony. Jest to więc część zależna od pliku UI. Musimy więc w kodzie serwera zamieścić obiekty opisane w UI. Zauważmy, że tworzymy kod serwera jako funkcję od dwóch parametrów: input, output. W środku serwera definiujemy zależności pomiędzy inputami i outputami. Jedną z zalet Shiny jest interaktywność. Dzięki temu użytkownik może na bieżąco zmieniać parametry i generować nowe wykresy. Jednak generowanie kodu na nowo przy każdej zmianie danych nie zawsze jest pożądane. Ważnym pojęciem przy pisaniu strony serwerowej jest reaktywność (żródło infografiki: Shiny Cheat Sheet). reaktywnosc Jeśli zmienna jest reaktywna, to znaczy że jakakolwiek jej zmiana powoduje ponowne uruchomienie funkcji z nią powiązanych. Do budowania reaktywnych wyrażeń używamy funkcji reactive(). Taka zmienna jest liczona tylko raz i wyrażenia z nią związane używają tej wartości aż do momentu aktualizacji wybranego przez użytkownika. Z pojęciem reaktywności wiąże się kilka ważnych funkcji: reactiveValues(...), które tworzy listą reaktywnych zmiennych, isolate(expr) - zapobiega zależności od reaktywnych zmiennych, render*() - funkcje tworzące obiekty do wyświetlenia, które zmieniają się wraz z reaktywnymi zmiennymi, observeEvent(...) - gdy nie chcemy aby model od razu się zaktualizował przy zmianie danych, a przy jakiejś określonej akcji, reactive() - tworzy reaktywne wyrażenia eventReactive - tworzy reaktywne wyrażenia, które nie zależą od wszystkich reaktywnych zmiennych, a zależą jedynie od akcji wymienionych w pierwszym argumencie. "],["podstawy-kontroli-wersji-przy-pomocy-gita.html", "Chapter 10 Podstawy kontroli wersji przy pomocy Gita 10.1 Podstawowe informacje 10.2 Podstawowe komendy 10.3 Repozytoria 10.4 Podstawowe komendy 10.5 Cofanie zmian 10.6 Gałęzie 10.7 Tworzenie własnych repozytoriów", " Chapter 10 Podstawy kontroli wersji przy pomocy Gita 10.1 Podstawowe informacje System kontroli wersji to narzędzie, które zarządza zmianami wprowadzanymi w plikach i katalogach w projekcie. Istnieje wiele systemów kontroli wersji. Przykładem takiego systemu jest Git. Jego mocne strony to: Nic, co jest zapisane w Git, nigdy nie jest tracone, więc zawsze możesz wrócić, aby zobaczyć, które wyniki zostały wygenerowane przez które wersje twoich programów. Git automatycznie powiadamia Cię, gdy Twoja praca koliduje z pracą innej osoby, więc jest trudniej (choć nie jest to niemożliwe) o przypadkowe nadpisanie pracy. Git może synchronizować pracę wykonywaną przez różne osoby na różnych komputerach. Kontrola wersji nie dotyczy tylko oprogramowania: książki, artykuły, zestawy parametrów i wszystko, co zmienia się w czasie lub wymaga udostępnienia, może i powinno być przechowywane i udostępniane za pomocą czegoś takiego jak Git. Każdy z projektów Git składa się z dwóch części: plików i katalogów, które tworzysz i edytujesz bezpośrednio, oraz dodatkowych informacji, które Git rejestruje o historii projektu. Połączenie tych dwóch rzeczy nazywa się repozytorium. Git przechowuje wszystkie dodatkowe informacje w katalogu o nazwie \\(\\texttt{.git}\\) znajdującym się w katalogu głównym repozytorium. 10.2 Podstawowe komendy Używając Gita zapewne często będziemy chcieli sprawdzić stan swojego repozytorium. Aby to zrobić, użyjemy polecenie \\(\\texttt{git status}\\). \\(\\texttt{git status}\\) - wyświetla listę plików, które zostały zmodyfikowane od czasu ostatniego zapisania zmian Git ma obszar przejściowy, w którym przechowuje pliki ze zmianami, które chcemy zapisać, a które nie zostały jeszcze zapisane. \\(\\texttt{git status}\\) - pokazuje, które pliki znajdują się w tym obszarze przejściowy i które mają zmiany, które nie zostały jeszcze zatwierdzone \\(\\texttt{git diff}\\) - pokaże wszystkie zmiany w twoim repozytorium (porównując obecną postać plików z ostatnio zapisaną) \\(\\texttt{git diff directory}\\) - pokaże zmiany w plikach w jakimś katalogu (porównując obecną postać plików z ostatnio zapisaną) \\(\\texttt{git diff filename}\\) - pokaże zmiany w danym pliku (porównując obecną postać z ostatnio zapisaną) Git różnice między dwiema wersjami pliku wyświetla w poniższy sposób: diff --git a/report.txt b/report.txt index e713b17..4c0742a 100644 --- a/report.txt +++ b/report.txt @@ -1,4 +1,5 @@ -# Seasonal Dental Surgeries 2017-18 +# Seasonal Dental Surgeries (2017) 2017-18 +# TODO: write new summary gdzie: \\(\\texttt{a/report.txt, b/report.txt}\\) to pierwsza i druga wersja pliku, linia druga wypisuje klucze do wewnętrznej bazy danych zmian Gita, \\(\\texttt{--- a/report.txt, +++ b/report.txt}\\) oznacza, że usuwane linie oznaczone są przedrostkiem \\(\\texttt{-}\\), dodawane linie oznaczone są przedrostkiem \\(\\texttt{+}\\), linia zaczynająca się od \\(\\texttt{@@}\\) mówi, gdzie wprowadzane są zmiany. Pary liczb to numer lini ,,startowej’’ i liczba linii, kolejne linie są listą zmian, które zostały wprowadzone. \\(\\texttt{git add filename}\\) - dodaje plik do obszaru przejściowego \\(\\texttt{git diff -r HEAD}\\) - porówna pliki z repozytorium z plikami z obszaru przejściowego \\(\\texttt{git diff -r HEAD path/to/file}\\) - porówna konkretny plik z repozytorium z plikiem z obszaru przejściowego \\(\\texttt{nano filename}\\) - otwiera plik w edytorze tekstowym \\(\\texttt{nano}\\) poruszanie się strzałkami \\(\\texttt{Backspace}\\) - usuń znak \\(\\texttt{Ctrl-K}\\): usuń linię \\(\\texttt{Ctrl-U}\\): cofnij usunięcie linii \\(\\texttt{Ctrl-O}\\): zapisz plik \\(\\texttt{Ctrl-X}\\): wyjdź z edytora \\(\\texttt{git commit -m "comment"}\\) - zapisuje zmiany w obszarze przejściowym z jednowierszowym komunikatem o wprowadzonych zmianach \\(\\texttt{git commit --amend - m "new message"}\\) - zmienia ostatni komunikat \\(\\texttt{git log}\\) - wyświetlenie historii projektu (od najnowszych zmian). Wyświetlany zostaje unikatowy identyfikator dla zatwierdzenia oraz informacje na temat tego kto dokonał zmiany, kiedy i jaki komunikat napisał dokonując zmiany. \\(\\texttt{spacja}\\) - przejcie w dół o stronę \\(\\texttt{q}\\) - wyjście \\(\\texttt{git log path}\\) - wyświetlenie historii danego pliku lub katalogu 10.3 Repozytoria    Informacje dotyczące zatwiedzonych zmian przechowywane są poprzez trzypoziomową strukturę. Każde zatwierdzenie (tzw. commit) zwiera komunikat o zatwierdzeniu i informacje o autorze i czasie, w którym zatwierdzenie zmian zostało wykonane. Każdy commit ma również swoje drzewo, które śledzi, gdzie w repozytorium dokonano zmian. Dla każdego pliku w drzewie istnieje tzw. blob (binary large object). Każdy blob zawiera skompresowaną migawkę zawartości pliku, z chwili w której nastąpił commit. Czym jest hash? Każde zatwierdzenie zmian w repozytorium ma unikalny identyfikator zwany hashem. Jest on zapisywany jako 40-znakowy ciąg szesnastkowy. Zazwyczaj jednak wystarczy podać pierwsze 6 lub 8 znaków hasha, by odnaleźć konkretne zatwierdzenie (commit). Identyfikatory jakimi są hashe umożliwiają Gitowi wydajne udostępnianie danych pomiędzy repozytoriami. Jak wyświetlić konkretny commit? By wyświetlić szczegóły dotyczące konkretnego commitu należy użyć komendy git show z pierwszymi 6 znakami hasha danego commmitu np.: git show Oda2f7. Czym jest odpowiednik ścieżki względnej w Git? Innym sposobem identyfikacji zatwierdzenia jest użycie odpowiednika ściezki względnej. By wyświetlić zatem ostatni commit możemy użyć komendy git show z etykietą HEAD. Jeśli natomiast zamiast HEAD wpiszemy HEAD~1 wyświetlony zostanie przedostatni commit, polecenie git show HEAD~2 zwróci nam natomiast jeszcze wcześniejszy commit itp. 10.4 Podstawowe komendy git log - wyświetla całą historię danego pliku lub projektu. W Gicie możemy jednak sprawdzić bardziej szczegółowe informacje. Dzięki poleceniu git annotate file możemy sprawdzić kto i kiedy dokonał ostatniej zmiany w każdej linijce pliku. git diff ID1..ID2 - umożliwia sprawdzenie zmian pomiędzy dwoma commitami, których identyfikatory to odpowiednio ID1 i ID2. git add - polecenie umożliwiające dodanie nowego pliku. Po wykonaniu tego polecenia Git zaczyna śledzić dodany plik. git clean -n - pokazuje listę plików, które są w repozytorium, ale których historia nie jest śledzona przez Gita. git clean -f - usuwa pliki, które są w repozytorium i których historii nie śledzi Git. Z używaniem tego polecenia należy uważać, ponieważ usuwa ono pliki z pamięci na stałe i nie da się ich już odzyskać. git config - -list - wyświetla ustawienia Gita. git config - -system - wyświetla ustawienia każdego użytkownika na danym komputerze. git config - -global - wyświetla ustawienia każdego projektu. git config - -local - wyświetla ustawienia poszczególnego projektu. Każdy poziom zastępuje poziom nad nim, więc ustawienia lokalne (na projekt) mają pierwszeństwo przed ustawieniami globalnymi (na użytkownika), które z kolei mają pierwszeństwo przed ustawieniami systemowymi (dla wszystkich użytkowników na komputerze). git config - -global setting value - zmienia konfigurację odpowiedniej wartości dla wszystkich projektów na danym komputerze. Jako setting należy wpisać to co chcemy zmienić (np. user.name, user.email itp.), a jako value to co chcemy ustawić. 10.5 Cofanie zmian Teraz dowiemy się jak cofnąć wprowadzone zmiany. \\(\\texttt{git reset HEAD}\\)- usuwa ostatnio dodany plik ze śledzenia, \\(\\texttt{git checkout -- filename}\\) - odrzuci zmiany, które nie zostały jeszcze dodane do śledzenia, \\(\\texttt{git reset HEAD path/to/file}\\) - odrzuci ostatnie zmiany w pliku, który został juz dodany do śledzenia, \\(\\texttt{git checkout 2242bd filename}\\)- zamienia aktualna wersje pliku, na tę o hashu ‘2242bd’. Do ostatniej komendy przydatne może być wykonanie poniższzego polecenia, aby sprawdzić hashe plików. \\(\\texttt{git log - 3 filename}\\)- pokaże 3 ostatnie commity dotyczące wskazanego pliku. Poniższe dwie komendy pokazują, jak cofać zmiany na więcej niż jednym pliku. \\(\\texttt{git reset HEAD data}\\)- usuwa ze śledzenia wszystkie pliki z katalogu data. Jeżeli nie podamy nazwy katalogu( wtedy wystarczy samo \\(\\texttt{git reset}\\)) wszystkie pliki zostaną usunięte. \\(\\texttt{git checkout -- data}\\)- wszystkie pliki w katalagu data zostaną cofnięte do poprzednich wersji. 10.6 Gałęzie Jeśli nie używasz kontroli wersji, typowym przepływem pracy jest tworzenie różnych podkatalogów do przechowywania różnych wersji projektu w różnych stanach, na przykład deweloperskich i końcowych. Oczywiście zawsze kończy się to ostateczną aktualizacją i ostateczną aktualizacją-poprawioną. Problem polega na tym, że trudno jest to rozwiązać, jeśli masz odpowiednią wersję każdego pliku w odpowiednim podkatalogu i ryzykujesz utratę pracy.     Jednym z powodów, dla których Git jest popularny, jest jego obsługa tworzenia gałęzi (branchy), co pozwala na posiadanie wielu wersji Twojej pracy i pozwala na systematyczne śledzenie każdej wersji.     Każda gałąź jest jak wszechświat równoległy: zmiany, które wprowadzasz w jednej gałęzi, nie wpływają na inne gałęzie (dopóki nie połączysz ich z powrotem).     Domyślnie kazde repozytorium Gita ma branch zwany master.     Podstawowe komendy związanie z działaniem na branchach (gałęziach):   \\(\\texttt{git branch}\\) - pokazuje wszystkie branche w repozytorium (branch, w którym obecnie się znajdujesz będziesz wylistowany z \\(*\\)).   \\(\\texttt{git diff branch1..branch2}\\) - wyświetla różnice między dwoma branchami   Ciekawostka: \\(\\texttt{git diff branch1..branch2}\\) - -\\(\\texttt{shortstat}\\) - wyświetla konkretną liczbę plików które się różnią między dwoma branchami  \\(\\texttt{git checkout branch1}\\) - pozwala przełączyć się na branch1  \\(\\texttt{git checkout -b branch-name}\\) - pozwala utworzyć nowego brancha o nazwie branch-name    Rozgałęzianie pozwala tworzyć równoległe wszechświaty. Scalanie (merging) to sposób, w jaki łączysz je z powrotem. Kiedy łączysz jedną gałąź (nazwijmy ją źródłową) z inną (nazwijmy ją docelową), Git włącza zmiany wprowadzone w gałęzi źródłowej do gałęzi docelowej. Jeśli te zmiany nie nakładają się, wynikiem jest nowe zatwierdzenie w gałęzi docelowej, które zawiera wszystko z gałęzi źródłowej. Do mergowania dwóch gałęzi używamy polecenia:   \\(\\texttt{git merge source destination}\\) - mergowanie dwóch branchy w jeden  Czasami zmiany w dwóch gałęziach będą ze sobą kolidować: na przykład poprawki błędów mogą dotyczyć tych samych wierszy kodu lub analizy w dwóch różnych gałęziach mogą dołączać nowe (i różne) rekordy do pliku danych podsumowania. W takim przypadku ty decydujesz o sprzeczności zmian.    Jeżeli podczas mergowania występuje konflikt Git informuje Cię, że wystapił problem a \\(\\texttt{git status}\\) poinformuje Cię, które pliki wmagają rozwiązania konfliktów.    Git pozostawia na danym pliku znaczniki, aby poinformować Cię o konkretnym miejscu konfliktu. Znaczniki te wyglądają następująco: <<<<<<< destination-branch-name ...changes from the destination branch... ======= ...changes from the source branch... >>>>>>> source-branch-name   Aby rozwiązać konflikt edytuj plik, usuwając znaczniki i wprowadź wszelkie zmiany potrzbne do rozwiązania kofilktu, a następnie zrób commit tych zmian.    10.7 Tworzenie własnych repozytoriów Przejdźmy do kolejnego zagadnienia związanego z pracą w Gicie.     Do tej pory wszystkie poznane funkcje Gita dotyczyły działań na repozytoriach już istniejących. Aby stworzyć własne repozytorium w bieżącym katalogu roboczym wystarczy komenda:  \\(\\texttt{git init project-name}\\)   Warto wspomnieć, że chociaż Git pozwala tworzyć zagnieżdżone repozytoria nie powinieneś tego robić. Aktualizacja takich repozytoriów bardzo szybko staje się bardzo skomplikowana, ponieważ musisz powiedzieć Gitowi, w którym z dwóch katalogów .git ma być przechowywana aktualizacja.    Nie tworzymy repozytorium w innym już istniejącym!     Poniżej kilka ważnych komend:  \\(\\texttt{git init}\\) - inicjalizacja repozytorium w bieżącym katalogu   \\(\\texttt{git init /path/to/project}\\) - inicjalizacja repozytorium we wskazanym ścieżką katalogu   \\(\\texttt{git clone URL}\\) - tworzenie kopii istniejącego pod wskazanym adresem URL repozytorium \\(\\texttt{git clone /existing/project newprojectname}\\) - tworzenie kopii istniejącego repozytroium o zadanej nazwie - newprojectname   \\(\\texttt{git remote}\\) - wyświetla informację o fizycznej lokalizacji na serwerze Gita, z której zostało sklonowane repo   \\(\\texttt{git remote -v}\\) - wyświetla informację o URL serwerze Gita, z którego zostało sklonowane repo   \\(\\texttt{git remote add remote-name URL}\\) - pozawala na dodanie własnego remota z podanego URL \\(\\texttt{git remote rm remote-name}\\) - usuwanie istniejącego remota   \\(\\texttt{git pull remote branch}\\) - pobieranie zmian w branchu w lokalnym repozytorium i mergowanie ich z bieżacym brnachem w lokalnym repozytorium   Uwaga!   Git powstrzymuje Cię przed pobieraniem ze zdalnego repozytorium zmian, które mogą nadpisać niezapisane lokalnie zmiany. Wystarczy zrobić commit tych zmian lub cofnąć je, a następnie spullować repo ponownie.   \\(\\texttt{git push remote-name branch-name}\\) - pushuje zmiany wprowadzone lokalnie na danym branchu do zdalnego repozytorium   "],["programowanie-obiektowe-w-r-klasy-s3.html", "Chapter 11 Programowanie obiektowe w R: klasy S3 11.1 Systemy programowania obiektowego w R 11.2 S3 11.3 S4", " Chapter 11 Programowanie obiektowe w R: klasy S3 Programując w R jesteśmy oswojeni z myśleniem kategoriami funkcji - przekształceń nakładanych na macierze lub ramki danych. Jest to naturalne ze względu na zastosowanie R głównie w statystyce i pochodnych jej dziedzin. Tymczasem programowanie obiektowe, choć często niepotrzebne do przeprowadzenia analiz lub symulacji, może się okazać użyteczne przy tworzeniu większego projektu, w szczególności projektu współtworzonego przez więcej osób. Zdefiniowanie klas i metod nadaje projektowi strukturę, co sprawia, że jego rozbudowa przebiega mniej chaotycznie. Ponadto, znajomość podstaw systemów programowania obiektowego w R umożliwia nam lepsze zrozumienie działania bazowych funkcji i obiektów R oraz ewentualne ich rozbudowywanie. 11.1 Systemy programowania obiektowego w R W przeciwieństwie do wielu popularnych języków programowania, R nie ma jednego ujednoliconego systemu programowania obiektowego - jest ich wiele, przy czym różnią się nie tylko składnią, ale też funkcjonalnościami. Pierwsze wersje pierwowzoru języka R - języka S nie posiadały żadnego systemu obiektowego. Wraz z trzecią wersją S wprowadzono pierwszy z nich: S3. Następnie, kiedy ten okazał się niewystarczający dla potrzeb użytkowników - S4. Oba systemy finalnie znalazły się w base języka R. Z czasem, w miarę wzrastania potrzeb, powstawały kolejne alternatywne systemy klas, które funkcjonowały równolegle i równoprawnie. Do dzisiaj nie wyróżniamy systemu “oficjalnego” czy preferowanego - każdy z kilku pozostałych w powszechnym użyciu ma swoje zastosowania, w których niekorzystnym lub niewygodnym jest zastąpienie go innym. W tej notatce przyjrzymy się przede wszystkim S3. 11.2 S3 S3 to system, z którym stykamy się najczęśniej. Wszystkie wbudowane klasy obiektów zostały zbudowane właśnie przy pomocy systemu S3. By sprawdzić, do jakiej klasy S3 należy obiekt, używamy funkcji class. W codziennej pracy w R operujemy w wiekszości na obiektach zbudowanych w S3. Klasy S3 to m.in. factor,data.frame,matrix. f <- factor(c("y","n","y","n","n")) class(f) ## [1] "factor" Warto w tym miejscu podkreślić, że klasa zmiennej nie jest równoważna typowi zmiennej, np. macierz liczb jest klasy matrix, ale typu double. m <- matrix(c(1,2,3,4),2,2) class(m) ## [1] "matrix" "array" typeof(m) ## [1] "double" Każdemu obiektowi mogą być (ale nie muszą) przypisane atrybuty. Atrybuty mozna rozumieć jako cechy lub parametry obiektu. W przypadku macierzy są to jej wymiary. attributes(m) ## $dim ## [1] 2 2 11.2.1 Klasy i atrybuty W systemie S3 nie tworzymy definicji klasy, nie określamy również, jakie atrybuty obiekt danej klasy ma. Obiektowi możemy nadać klasę przy jego utworzeniu, z użyciem funkcji structure: kanapka <-structure(c("szynka", "margaryna", "chleb"), class = "jedzenie") class(kanapka) ## [1] "jedzenie" lub w dowolnym momencie po jego utworzeniu z użyciem class: szarlotka <- c("jaja", "mąka", "masło", "cukier") szarlotka <- c(szarlotka, "jabłka") class(szarlotka) <- "jedzenie" class(szarlotka) ## [1] "jedzenie" Każdemu obiektowi możemy również indywidualnie przypisać atrybuty, również na kilka sposobów, przy jego utworzeniu z użyciem structure: hot_dog <- structure(c("parówka", "bułka", "ketchup"), class="jedzenie", kalorie = 300) attributes(hot_dog) ## $class ## [1] "jedzenie" ## ## $kalorie ## [1] 300 class(hot_dog) ## [1] "jedzenie" Lub w dowolnym momencie z użyciem funkcji attr: attr(kanapka, "kalorie")=150 attr(szarlotka, "kalorie")=265 attributes(kanapka) ## $class ## [1] "jedzenie" ## ## $kalorie ## [1] 150 attributes(szarlotka) ## $class ## [1] "jedzenie" ## ## $kalorie ## [1] 265 Z użyciem funkcji attributes i attr można również “dostać się” do wartości atrybutów obiektu: attr(szarlotka, "kalorie") ## [1] 265 attributes(szarlotka)$kalorie ## [1] 265 Tworzenie obiektów różnych klas S3 jest więc bardzo proste i nie wymaga (przynajmniej formalnie) predefiniowania klasy i atrybutów. System jest więc z jednej strony bardzo elastyczny, z drugiej - nieprecyzyjny. Niesie to za sobą pewne konsekwencje, np. formalnie nic nie stoi na przeszkodzie by zrobić coś takiego: droga <- c("asfalt","pobocze", "lewy pas", "prawy pas") class(droga) <- "jedzenie" class(droga) ## [1] "jedzenie" lub takiego… średnia_bez_na <- function(...) mean(na.rm=TRUE,...) class(średnia_bez_na)<-"jedzenie" class(średnia_bez_na) ## [1] "jedzenie" lub takiego: attr(hot_dog, "kalorie") <- "przecież to prawie nie ma kalorii!" bilans_posilkow <- attr(hot_dog,"kalorie")+attr(szarlotka, "kalorie")+attr(kanapka, "kalorie") ## Error in attr(hot_dog, "kalorie") + attr(szarlotka, "kalorie"): argument nieliczbowy przekazany do operatora dwuargumentowego Dlatego należy pamiętać, by klas i atrybutów nie przydzielać chaotycznie, zachować pewne reguły, mimo że formalnie nie są wymagane przy użyciu S3. 11.2.2 Funkcje generyczne i metody Metody to funkcje działające na obiektach danej klasy. Z reguły są predefiniowane przy utworzeniu klasy wraz z polami. Inaczej jednak jest z systemem S3 w R. Nie definiujemy klasy - klasa jest tworzona przy pierwszym przypisaniu jej jakiemuś obiektowi. Metody tworzy się przy pomocy funkcji generycznych (generics). 11.2.2.1 Funkcje generyczne By lepiej zrozumieć logikę stojącą za funkcjami generycznymi, spróbujmy spojrzeć na klasy i ich metody z nieco mniej standardowej perspektywy. Dla różnych klas możemy mieć analogiczne metody, zachowujące się nieco inaczej w zależności od specyfiki klasy, np. inaczej rozumiemy różnicę między dwoma datami a różnicę między dwoma liczbami - liczby odejmujemy od siebie bezpośrednio, podczas gdy w przypadku dat oczekujemy różnicy w dniach pomiędzy nimi - w tym celu nie wystarczy bezpośrednie odjęcie od siebie dwóch dat. W systemie S3 metody nie są przypisane bezpośrednio klasie, są przypisane odpowiedniej funkcji generycznej. Funkcja generyczna określa nazwę metody wspólną dla wszystkich klas i umożliwia tworzenie wariantów metody dla różnych klas pod tą konkretną nazwą. Zanim przejdziemy do tworzenia funkcji generycznych oraz metod dla własnych klas przyjrzyjmy się działaniu już istniejących. Jedną z funkcji generycznych jest funkcja summary - funkcja podsumowująca obiekt (np. summary(lm(X~Y))). summary ## function (object, ...) ## UseMethod("summary") ## <bytecode: 0x55bf1e71aba0> ## <environment: namespace:base> Użyjemy funkcji methods, by wylistować wszystkie dostępne metody dla danej funkcji generycznej. methods(summary) ## [1] summary,ANY-method summary,DBIObject-method ## [3] summary,diagonalMatrix-method summary,marrayInfo-method ## [5] summary,marrayLayout-method summary,marrayNorm-method ## [7] summary,marrayRaw-method summary,mle-method ## [9] summary,sparseMatrix-method summary.aareg* ## [11] summary.allFit* summary.aov ## [13] summary.aovlist* summary.aspell* ## [15] summary.cch* summary.check_packages_in_dir* ## [17] summary.connection summary.corAR1* ## [19] summary.corARMA* summary.corCAR1* ## [21] summary.corCompSymm* summary.corExp* ## [23] summary.corGaus* summary.corIdent* ## [25] summary.corLin* summary.corNatural* ## [27] summary.corRatio* summary.corSpher* ## [29] summary.corStruct* summary.corSymm* ## [31] summary.coxph* summary.coxph.penal* ## [33] summary.data.frame summary.Date ## [35] summary.default summary.Duration* ## [37] summary.ecdf* summary.EList* ## [39] summary.EListRaw* summary.factor ## [41] summary.gam* summary.ggplot* ## [43] summary.glm summary.gls* ## [45] summary.haven_labelled* summary.hcl_palettes* ## [47] summary.infl* summary.Interval* ## [49] summary.lm summary.lme* ## [51] summary.lmList* summary.lmList4* ## [53] summary.loess* summary.loglm* ## [55] summary.MAList* summary.manova ## [57] summary.MArrayLM* summary.matrix ## [59] summary.merMod* summary.mlm* ## [61] summary.modelStruct* summary.negbin* ## [63] summary.nls* summary.nlsList* ## [65] summary.packageStatus* summary.pdBlocked* ## [67] summary.pdCompSymm* summary.pdDiag* ## [69] summary.pdIdent* summary.pdIdnot* ## [71] summary.pdLogChol* summary.pdMat* ## [73] summary.pdNatural* summary.pdSymm* ## [75] summary.pdTens* summary.Period* ## [77] summary.polr* summary.POSIXct ## [79] summary.POSIXlt summary.ppr* ## [81] summary.prcomp* summary.prcomplist* ## [83] summary.princomp* summary.proc_time ## [85] summary.pyears* summary.ratetable* ## [87] summary.reStruct* summary.RGList* ## [89] summary.rlang_error* summary.rlang_message* ## [91] summary.rlang_trace* summary.rlang_warning* ## [93] summary.rlang:::list_of_conditions* summary.rlm* ## [95] summary.RUnitTestData* summary.shingle* ## [97] summary.srcfile summary.srcref ## [99] summary.stepfun summary.stl* ## [101] summary.summary.merMod* summary.survexp* ## [103] summary.survfit* summary.survfitms* ## [105] summary.survreg* summary.table ## [107] summary.TestResults* summary.tmerge* ## [109] summary.trellis* summary.tukeysmooth* ## [111] summary.varComb* summary.varConstPower* ## [113] summary.varConstProp* summary.varExp* ## [115] summary.varFixed* summary.varFunc* ## [117] summary.varIdent* summary.varPower* ## [119] summary.vctrs_sclr* summary.vctrs_vctr* ## [121] summary.warnings summary.XMLInternalDocument* ## see '?methods' for accessing help and source code Każda z wypisanych nazw odpowiada wariantowi metody dla jednej klasy. Zwrócmy uwagę na specyficzną składnię nazw tych funkcji - człon po kropce odpowiada nazwie klasy, jakiej metoda dotyczy. Przyjrzyjmy się wariantom summary dla dwóch różnych klas: lm i matrix. X <- matrix(rep(1,12), 6,2) Y <- c(2,2,3,2,2,2) model <- lm(Y~X) summary.lm(model) ## ## Call: ## lm(formula = Y ~ X) ## ## Residuals: ## 1 2 3 4 5 6 ## -0.1667 -0.1667 0.8333 -0.1667 -0.1667 -0.1667 ## ## Coefficients: (2 not defined because of singularities) ## Estimate Std. Error t value Pr(>|t|) ## (Intercept) 2.1667 0.1667 13 4.8e-05 *** ## X1 NA NA NA NA ## X2 NA NA NA NA ## --- ## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 ## ## Residual standard error: 0.4082 on 5 degrees of freedom summary.matrix(X) ## V1 V2 ## Min. :1 Min. :1 ## 1st Qu.:1 1st Qu.:1 ## Median :1 Median :1 ## Mean :1 Mean :1 ## 3rd Qu.:1 3rd Qu.:1 ## Max. :1 Max. :1 Jednak, by użyć odpowiedniej funkcji dla obiektu, nie musimy specyfikować jego klasy - właśnie dzięki zdefiniowaniu funkcji generycznej. Bez względu na klasę obiektu uzywamy składni funkcja_generyczna(obiekt). Wywoływana jest wówczas funkcja generyczna, która na podstawie klasy lub typu obiektu dopasowuje wariant metody. Spójrzmy jak to wygląda na przykładzie summary: summary(model) ## ## Call: ## lm(formula = Y ~ X) ## ## Residuals: ## 1 2 3 4 5 6 ## -0.1667 -0.1667 0.8333 -0.1667 -0.1667 -0.1667 ## ## Coefficients: (2 not defined because of singularities) ## Estimate Std. Error t value Pr(>|t|) ## (Intercept) 2.1667 0.1667 13 4.8e-05 *** ## X1 NA NA NA NA ## X2 NA NA NA NA ## --- ## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 ## ## Residual standard error: 0.4082 on 5 degrees of freedom summary(X) ## V1 V2 ## Min. :1 Min. :1 ## 1st Qu.:1 1st Qu.:1 ## Median :1 Median :1 ## Mean :1 Mean :1 ## 3rd Qu.:1 3rd Qu.:1 ## Max. :1 Max. :1 Jak widać na przykładzie powyżej wywołanie funkcji generycznej na obiekcie spowodowało dopasowanie odpowiedniej dla klasy obiektu metody i dało identyczny efekt jak wywołanie bezpośrednio dedykowanej funkcji. 11.2.2.2 Tworzenie funkcji generycznych i metod Nowe funkcje generyczne tworzy się według następującego schematu: nazwa_metody <- function (x) { UseMethod("nazwa_metody", x) } Po utworzeniu funkcji generycznej możemy przystąpić do napisania metod dla konkretnych klas. Metody tworzymy jako funkcje nazwane według konwencji nazwa_metody.klasa. Spróbujmy wg powyższego schematu utworzyć metodę dla utworzonej wcześniej klasy jedzenie. Niech nasza metoda nazywa się zjedz: zjedz <- function(x){ UseMethod("zjedz",x) } Mając gotową funkcję generyczną możemy przejść do określenia zachowania metody dla naszej klasy: zjedz.jedzenie <- function(x){ cat("Mniam mniam\\n") } zjedz(szarlotka) ## Mniam mniam zjedz(kanapka) ## Mniam mniam zjedz(hot_dog) ## Mniam mniam Oraz, jeśli potrzebujemy, również dla innych istniejących klas, w tym również wbudowanych, np. matrix. zjedz.matrix <- function(x){ warning("Przeciez to macierz! Tego sie nie je!\\n") } zjedz(matrix(c(1,2,2,3),2,2)) ## Warning in zjedz.matrix(matrix(c(1, 2, 2, 3), 2, 2)): Przeciez to macierz! Tego sie nie je! W szczególności możemy określić zachowanie metody dla pseudoklasy default, czyli określić domyślne zachowanie metody. W kontekście naszego przykładu z klasą jedzenie moglibyśmy oczekiwać, że nasza metoda domyślnie nie będzie “jeść” żadnych obiektów, robiąc wyjątek wyłącznie dla jedzenia. zjedz.default <- function(x,...){ warning("Tego sie nie je!\\n") } zjedz(mean) ## Warning in zjedz.default(mean): Tego sie nie je! zjedz(c(1,2,3,4)) ## Warning in zjedz.default(c(1, 2, 3, 4)): Tego sie nie je! Zauważmy, że istnieje tu pewna hierarchia: zjedz(matrix(c(1,2,3,4,4,5),2,3)) ## Warning in zjedz.matrix(matrix(c(1, 2, 3, 4, 4, 5), 2, 3)): Przeciez to macierz! Tego sie nie je! zjedz(szarlotka) ## Mniam mniam Funkcja generyczna wywołuje metodę dla pseudoklasy default wtedy, gdy dla danej klasy indywidualnie nie ma zdefiniowanej metody. W pierwszej kolejności szuka metody dedykowanej dla danej klasy, dlatego dla obiektów klas jedzenie i matrix zostały wywołane odpowiednio zjedz.jedzenie i zjedz.matrix, a nie zjedz.default. 11.2.3 Dziedziczenie O dziedziczeniu mówimy, gdy jedna z klas przejmuje (dziedziczy) właściwości i cechy innej. W S3 dziedziczenie wprowadza się w zaskakująco oczywisty i prosty sposób. Każdemu obiektowi można przypisać więcej niż jedną klasę, przypisując class(obiekt) nie jedną nazwę klasy, lecz ich wektor, np. dla utworzonego wcześniej obiektu szarlotka: class(szarlotka) <- c("ciasto", "slodkosci", "jedzenie") class(szarlotka) ## [1] "ciasto" "slodkosci" "jedzenie" Kolejność klas w wektorze nie jest przypadkowa: zaczynamy od klasy “najmłodszej” (i najważniejszej, najbardziej specyficznej), a kończymy na “najstarszej” (najogólniejszej). Kolejność ta jest również obowiązująca przy dopasowywaniu metod przez funkcję generyczną: funkcja generyczna najpierw szuka metody dla pierwszej z klas w wektorze, następnie dla drugiej itd. Jeśli nie uda jej się znaleźć metody dla żadnej z klas, wywołuje metodę dla pseudoklasy default. zjedz.ciasto <- function(x){ cat("Mniam mniam, pyszne ciacho!\\n") } zjedz.slodkosci <- function(x){ cat("Słodkie, dobre, mniam.. \\n") } zjedz(szarlotka) ## Mniam mniam, pyszne ciacho! Zgodnie z oczekiwaniami funkcja generyczna wywołała metodę dla pierwszej z klas w wektorze, czyli klasy ciasto, ignorując metody dla klas slodkosci i jedzenie. W większości przypadków jednak wprowadzamy nowe klasy nie po to, by zastępować istniejące metody dla klas ogólniejszych, lecz po to by wprowadzić pewne rozszerzenia, np. metody czy atrybuty, które nie mają sensu dla innych obiektów z klasy - rodzica. Wprowadźmy metodę pokroj. Dla każdego obiektu klasy jedzenie bedzie ona dzialac identycznie - dzielić atrybut kalorie przez wskazane n i zwracać n równych “porcji” : pokroj<-function(x,...){ UseMethod("pokroj",x) } pokroj.jedzenie <- function(x,n){ porcja<-attr(x, "kalorie")/n rep(porcja,n) } pokroj(kanapka,4) ## [1] 37.5 37.5 37.5 37.5 Oprócz tego wprowadźmy metodę sensowną tylko dla klasy ciasto: posyp_cukrem_pudrem. posyp_cukrem_pudrem<-function(x,...){ UseMethod("posyp_cukrem_pudrem",x) } posyp_cukrem_pudrem.ciasto <- function(x){ cat("Syp syp syp\\n") } Zauważmy, że przez wprowadzenie dodatkowych klas obiekt szarlotka nie traci funkcjonalności klasy jedzenie: pokroj(szarlotka,5) ## [1] 53 53 53 53 53 Podczas gdy równocześnie możliwym stało się zdefiniowanie dla niego indywidualnych metod: posyp_cukrem_pudrem(szarlotka) ## Syp syp syp posyp_cukrem_pudrem(hot_dog) ## Error in UseMethod("posyp_cukrem_pudrem", x): niestosowalna metoda dla 'posyp_cukrem_pudrem' zastosowana do obiektu klasy "jedzenie" Dziedziczenie może być bardzo użyteczne, ale należy je stosować z ostrożnością, mając w pamięci elastyczność klas i atrybutów w S3. Bardzo łatwo stracić orientację, w szczególności wprowadzając dziedziczenie po klasach wbudowanych lub po klasach zbudowanych przez kogoś innego - wówczas ciężko nam wziąć pod uwagę wszystkie istniejące dla danych klas metody. Posługując się dziedziczeniem i klasami w sposób rozrzutny i nieprzemyślany łatwo możemy doprowadzić do chaosu. 11.2.3.1 NextMethod NextMethod jest używane w sytuacji, gdy wewnątrz metody klasy chcemy wywołać metodę klasy nadrzędnej (rodzica). zjedz.ciasto<- function(x){ cat("Mniam mniam, pyszne ciacho!\\n") NextMethod() } zjedz(szarlotka) ## Mniam mniam, pyszne ciacho! ## Słodkie, dobre, mniam.. zjedz.slodkosci <- function(x){ cat("Słodkie, dobre, mniam.. \\n") NextMethod() } zjedz(szarlotka) ## Mniam mniam, pyszne ciacho! ## Słodkie, dobre, mniam.. ## Mniam mniam 11.3 S4 S3 jest użyteczny i do niektórych zastosowań wystarczający, ale nie posiada wielu własności znanych z systemów programowania w innych językach. Ponadto, ze względu na dużą swobodę w tworzeniu klas i metod, utrzymanie bardziej złożonych struktur i hierarchii może być uciążliwe i mało przejrzyste z użyciem S3. Pierwszą alternatywą dla S3 był system S4. W S4 metody tworzone z użyciem tej samej logiki - przez funckje generyczne. W przeciwieństie do S3, system S4 wymaga zdefiniowania klasy, w szczególności jej pól (slotów) i dziedziczenia po innych klasach. Klasę definiuje się z użyciem funkcji setClass: # pierwszym argumentem funkcji jest nazwa klasy setClass("nazwa_klasy", slots = c( # tutaj definiowane są sloty i ich typ slot_1 = "data.frame", slot_2 = "list" ), prototype = c( # tutaj definiowane są wartości domyślne (prototyp) dla slotów slot_1 = data.frame(), slot_2 = list() ) ) Funkcja setClass posiada również parametr contains, który odpowiada za dziedziczenie po innych klasach: setClass("klasa_rodzic", slots=c( macierz = "matrix" )) setClass("klasa_dziecko", contains="klasa_rodzic") Funkcje generyczne z użyciem których tworzymy metody S4 są rozróżniane od funkcji generycznych systemu S3. Mechanizm tworzenia metody jest więc bardzo podobny, ale używamy do tego celu dedykowanych dla S4 funkcji. # tworzenie funkcji generycznej S4 setGeneric("nazwa_metody", function(x, ...) standardGeneric("nazwa_metody")) ## [1] "nazwa_metody" # tworzenie metody dla klasy setMethod("nazwa_metody", "nazwa_klasy", function(x,...){ # działanie metody na obiekcie klasy }) 11.3.1 Bonus: przykład wykorzystania systemu S4 S4, choć bardziej restrykcyjny niż S3, nadal daje dużo swobody w przypisywaniu klas, dziedziczeniu, w szczególności w manewrowaniu wbudowanymi klasami R. Wykorzystaliśmy to w naszym raczkującym pakiecie autoeda do ominięcia problemu przypisania różnego zachowania funkcji w zależności od otrzymanego typu danych. Celem było obliczenie tej samej funkcji (np. średniej) dla wszystkich kolumn danych, przy założeniu, że nasz zbiór danych jest średniej wielkości (kilkadziesiąt kolumn - zmiennych). Jeśli niemożliwe jest obliczenie funkcji dla danej kolumny (np. próbujemy obliczyć średnią z kolumny stringów), chcieliśmy uniknąć przerywania pracy funkcji i zwracać NA. By osiągnąć powyższy rezultat zdecydowaliśmy się zdefiniować klasę funkcji - miar obliczanych na kolumnach jako klasę dziedziczącą po… klasie funkcji generycznych: setClass("RankingMeasure", slots = c( name = "character", description = "character" ), prototype = list( name = NA_character_, description = NA_character_ ) ) setClass("BuiltInMeasure", contains = c("standardGeneric", "RankingMeasure") ) Następnie dla każdej potrzebnej nam funkcji utworzyliśmy odpowiadający jej obiekt - funkcję generyczną klasy BuiltInMeasure i zdefiniowaliśmy metody tej funkcji generycznej dla możliwych typów zmiennych, zwracając NA domyślnie i wynik liczbowy, gdzie to możliwe. "],["moduły-w-aplikacjach-shiny.html", "Chapter 12 Moduły w aplikacjach shiny 12.1 Czym jest moduł Shiny 12.2 Budowa modułu Shiny.", " Chapter 12 Moduły w aplikacjach shiny 12.1 Czym jest moduł Shiny Modułem Shiny nazywamy odrębny kawałek aplikacji Shiny. Moduł nie może być wywołany niezależnie od reszty aplikacji. Traktuje się go jako część większej aplikacji lub większego modułu Shiny (moduł może składać się z modułów). 12.1.1 Dlaczego warto używać modułów Shiny? Uproszczenie kodu - moduły pozwalają nam na uporządkowanie złożonego kodu w przypadku dużych i skomplikowanych aplikacji Własna przestrzeń nazw - w aplikacjach shiny ID obiektów z inputów i outputów pochodzą ze wspólnej przestrzeni nazw. To znaczy, że ID każdego z obiektów w całej aplikacji musi być unikalne. Jako że moduł jest osobną funkcją wywołaną w aplikacji, posiada własną przestrzeń nazw. Wystarczy zatem, że ID obiektów są unikalne wewnątrz modułu. Recykling - ponieważ moduł Shiny jest niezależną funkcją, może być użyty zarówno wiele razy w jednej aplikacji, jak i w wielu różnych aplikacjach. Dzięki temu można z łatwością przechowywać gotowe fragmenty aplikacji w eRowych pakietach i wykorzystywać je w razie potrzeby. 12.2 Budowa modułu Shiny. kawałek UI - funkcja odpowiadająca za User Interface w module Shiny kawałek serwera - funkcja zawierająca fragment serwera, który jest wykorzystywany w UI 12.2.1 Jak używać modułów Shiny? Rozważmy aplikację składającą się z dwóch paneli - każdy z wykresem i danymi dla dwóch rozkładów, otrzymaną za pomocą poniższego kodu: library(shiny) library(ggplot2) ui <- fluidPage( tabsetPanel( #generujemy panel dla rozkładu normalnego tabPanel(title = "Rozkład normalny", tabsetPanel( tabPanel( title = "Wykres", numericInput(inputId = "normal_n", label = "Podaj wielkość próby", value = 1000), plotOutput("normal_plot") ), tabPanel( title = "Dane", tableOutput("normal_data") ) ) ), #generujemy panel dla rozkładu wykładniczego tabPanel(title = "Rozkład wykładniczy", tabsetPanel( tabPanel( title = "wykres", numericInput(inputId = "exp_n", label = "Podaj wielkość próby", value = 1000), plotOutput("exp_plot") ), tabPanel( title = "Dane", tableOutput("exp_data") ) ) ) ) ) server <- function(input, output, session) { #generujemy dane normal_data <- reactive({ set.seed(17) data.frame(id = 1:input[["normal_n"]], sample = rnorm(input[["normal_n"]])) }) exp_data <- reactive({ set.seed(17) data.frame(id = 1:input[["exp_n"]], sample = rnorm(input[["exp_n"]])) }) #generujemy tabele output[["normal_data"]] <- renderTable({ normal_data() }) output[["exp_data"]] <- renderTable({ exp_data() }) #generuemy wykresy output[["normal_plot"]] <- renderPlot({ ggplot(normal_data(), aes(x = sample)) + geom_density() }) output[["exp_plot"]] <- renderPlot({ ggplot(exp_data(), aes(x = sample)) + geom_density() + xlim(0, 5) }) } shinyApp(ui, server) Aplikacja wygląda następująco: W naszej przestrzeni wykorzystaliśmy nazwy: inputy - normal_n, exp_n outputy - normal_plot, normal_data, exp_plot, exp_data Co daje razem 6 obiektów. W aplikacji UI zajmuje 36 linijek kodu, a server 29, razem 65 linijek. Zrefaktoryzuemy kod powyższej aplikacji przy użyciu modułów Shiny. Za powtarzające się elementy (tj. panele z wykresem i danymi) będą odpowiedzialne następujące funkcje module_UI oraz module_SERVER (odpowiedniki UI oraz servera dla odrębnego fragmentu aplikacji). module_UI <- function(id) { ns <- NS(id) tagList( tabsetPanel( tabPanel( title = "Wykres", numericInput(inputId = ns("n"), label = "Podaj wielkość próby", value = 1000), plotOutput(ns("plot")) ), tabPanel(title = "Dane", tableOutput(outputId = ns("data")) ) ) ) } Na szczególną uwagę w powyższym kodzie zasługuje linijka ns <- NS(id) Za pomocą funkcji NS() tworzymy osobną przestrzeń nazw ID. module_SERVER <- function(id) { moduleServer(id, function(input, output, session) { #generujemy dane data <- reactive({ set.seed(17) data.frame(id = 1:input[["n"]], sample = rnorm(input[["n"]])) }) #generujemy wykres output[["plot"]] <- renderPlot({ ggplot(data(), aes(x = sample)) + geom_density() }) #generujemy tabelę output[["data"]] <- renderTable({ data() }) }) } Ostatecznie nasza aplikacja używająca pomocniczego modułu wygląda następująco library(shiny) ui <- fluidPage( titlePanel("Przykładowe ciągłe rozkłady prawdopodobieństwa"), tabsetPanel( #generujemy panel dla rozkładu normalnego tabPanel(title = "Rozkład normalny", module_UI("norm") ), #generujemy panel dla rozkładu wykładniczego tabPanel(title = "Rozkład wykładniczy", module_UI("exp") ) ) ) server <- function(input, output, session) { module_SERVER("norm") module_SERVER("exp") } shinyApp(ui, server) Powyższy kod jest czytelniejszy, krótszy, a także rozwiązuje problem wielu zmiennych. "],["404.html", "Page not found", " Page not found The page you requested cannot be found (perhaps it was moved or renamed). You may want to try searching to find the page's new location, or use the table of contents to find the page you are looking for. "]] diff --git a/docs/_book/style.css b/docs/_book/style.css new file mode 100644 index 0000000..f317b43 --- /dev/null +++ b/docs/_book/style.css @@ -0,0 +1,14 @@ +p.caption { + color: #777; + margin-top: 10px; +} +p code { + white-space: inherit; +} +pre { + word-break: normal; + word-wrap: normal; +} +pre code { + white-space: inherit; +} diff --git a/docs/_book/wczytywanie-danych-w-r-1.html b/docs/_book/wczytywanie-danych-w-r-1.html new file mode 100644 index 0000000..03c831f --- /dev/null +++ b/docs/_book/wczytywanie-danych-w-r-1.html @@ -0,0 +1,465 @@ + + + + + + + Chapter 4 Wczytywanie danych w R | Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+ + +
+
+ +
+
+

Chapter 4 Wczytywanie danych w R

+
+

4.1 Formaty danych

+
+

4.1.1 CSV/DSV

+

CSV (Comma Separated Values) to plik tekstowy, w którym wartości rozdzielane są przecinkami, a kolejne wiersze znakiem nowej linii. Plik CSV zazwyczaj przechowuje dane tabelaryczne. Nagłówki kolumn są często dołączane jako pierwszy wiersz (są to nazwy zmiennych), a każdy kolejny wiersz odpowiada jednej obserwacji (jednemu wierszowi w tabeli danych).

+

CSV jest szczególnym przypadkiem formatu danych o nazwie Delimiter Seperated Values (DSV). Jest to plik tekstowy w którym pola w każdym wierszu oddzielone są dowolnym separatorem. Najczęściej spotykane separatory to: przecinek (CSV), tabulator (TSV), średnik.

+

Przykładowy plik CSV

+

+
+
+

4.1.2 XML

+

XML to skrót od nazwy Extensible Markup Language. Dane przechowywane w tym formacie mają zagnieżdżoną strukturę: znaczniki oznaczają nazwy zmiennych, a wewnątrz przechowywane są ich wartości. XML swoją strukturą przypomina plik HTML.

+

Przykładowy plik XML

+

+
+
+

4.1.3 JSON

+

JSON - JavaScript Object Notation - to format przydatny w przypadku pracy z danymi pochodzącymi z REST API, czyli pobieranymi z sieci. Niektóre bazy danych również komunikują się za pomocą tego formatu, np. MongoDB.

+

Struktura: w pliku JSON obserwacje przechowywane są w słownikach, w których nazwy zmiennych są kluczami, a wartości zmiennych - wartościami. Obserwacje oddzielane są przecinkami, a dodatkowo, wszystkie dane spięte są nawiasami klamrowymi.

+

Przykładowy plik JSON

+

+
+
+

4.1.4 Excel (XLSX)

+

XLSX to format danych oparty na XML. Pliki tego typu są domyślnymi dokumentami wyjściowymi arkuszy kalkulacyjnych programu Microsoft Excel. Przedstawiają one głównie dane liczbowe i tekstowe w postaci tabel dwuwymiarowych.

+

Przykładowy arkusz kalkulacyjny w Excelu

+

+
+
+

4.1.5 Otwarte wersje programu Excel

+

Istnieją inne pakiety biurowe, np. LibreOffice, które - w przeciwieństwie do Excela - pozwalają na darmowe korzystanie z arkusza kalkulacyjnego. W przypadku LibreOffice, domyślnym formatem zapisu danych przez Calc (odpowiednik Excela) jest OpenDocument Format (.ods).

+

Przykładowy arkusz kalkulacyjny w LibreOffice

+

+
+
+

4.1.6 Pliki tekstowe

+

Jednym z najczęściej występujących i najbardziej uniwersalnych formatów przechowujących dane (np. w postaci tabeli) są pliki tekstowe. Mają one najczęściej rozszerzenie txt lub csv (comma separated values).
+Poniższą charakteryzację różnych metod wczytywania przedstawiamy na podstawie pliku listings.csv

+
+

4.1.6.1 Base

+

Podstawową funkcją używaną do wczytywania tego typu plików w postaci tabeli jest funkcja read.table. +Ze względu na specyfikację wewnętrzną plików, read.table posiada kilka wariantów, takie jak read.csv(), read.csv2() czy read.delim().

+

read.csv() używana jest w przypadku, gdy domyślnym separatorem dziesiętnym jest “.”, a wartości w wierszach oddzielone są poprzez “,”;
+read.csv2() używana jest w przypadku, gdy domyślnym separatorem dziesiętnym jest “,”, a wartości w wierszach oddzielone są poprzez “;”;
+read.delim() używana jest w przypadku, gdy domyślnym separatorem dziesiętnym jest “.”, a wartości w wierszach oddzielone są poprzez TAB

+

Przykładowy sposób załadowania plików w formacie csv

+
read.csv('./data/csv/listings.csv', header = TRUE, sep = ",")
+

W przypadku read.table() dane zostają zaimportowane jako data.frame.

+

Dla dużych plików wczytwanie za pomocą read.table() bywa jednak czasochłonne. Wówczas możemy użyć funkcji z paczki data.table lub readr.

+
+
+

4.1.6.2 readr

+

readr jest częścią pakietu tidyverse. W tym przypadku import odbywa się za pomocą funkcji o podobnej nazwie, jak w przypadku read.table(), a mianowicie read_csv().
+read_csv wczytuje dane oddzielone przecinkami, natomiast read_csv2() - dane oddzielone średnikami.

+
read_csv('./data/csv/listings.csv')
+

W przeciwieństwie do read.csv, funkcja read_csv na wyjściu daje dane w postaci tabeli w bardziej zwartej i przejrzystej formie. Oprócz tego podaje także specyfikację kolumn, tzn. informuje, jaka jest nazwa każdej kolumny oraz jej typ (np. col_double () oznaczają dane liczbowe).
+Typ danych jaki dostajemy na wyjściu to tbl_df (tzw. tibble), który jest w pewnym sensie zmodyfikowaną wersją tradycyjnej ramki danych data.frame, pozwalającą na łatwiejszą pracę w obrębie tidyverse.

+
+
+

4.1.6.3 data.table

+

Do wczytywania danych z plików csv możemy także użyć funkcji fread z pakietu data.table.

+
fread('./data/csv/listings.csv')
+

Na wyjściu otrzymujemy ramkę danych, jednak wyświetloną w inny sposób niż w przypadku użycia read.csv. Różnica jest widoczna, gdyż po użyciu funkcji class() na fread() jako typ danych otrzymujemy "data.table" "data.frame".

+
+
+

4.1.6.4 Różnice

+

Najważniejsze różnice pomiędzy wymienionymi sposobami wczytywania plików csv to:

+
    +
  1. Typ danych
  2. +
+
    +
  • Base: `data.frame
  • +
  • readr: tibble
  • +
  • data.table: `data.table data.frame
  • +
+
    +
  1. Postać wyświetlania (co jest konsekwencją 1)
  2. +
+
    +
  • Base: Wyświetla 62 początkowe wiersze każdej kolumny, wyświetlając informacje o liczbie pozostałych;
  • +
  • readr: wyświetla 10 pierwszych wierszy z 10 pierwszych kolumn, z informacją o liczbie pozostałych wierszy i kolumn; automatycznie wyświetlane są też nazwy kolumn oraz skrót informujący o typie zmiennych
  • +
  • data.table: wyświetla 5 początkowych i 5 końcowych wartości z każdej kolumny
  • +
+
    +
  1. Czas i użycie pamięci przy dużych rozmiarach danych
    +Zarówno czas wczytania danych, jak i wykorzystanie pamięci najkorzystniejsze jest w przypadku funkcji fread. Gdyby przez time oznaczyć czas potrzebny na wczytanie dużych plików, a przez memory zużycie pamięci, to time(fread) < time(read_csv) << time(read.csv) oraz memory(fread) < memory(read.csv) < memory(read_csv).
  2. +
+
+
+
+

4.1.7 Arkusze kalkulacyjne i pliki JSON

+

Do wczytywania arkusza kalkulacyjnego (np. pliku excela) używa się funkcji read_excel z pakietu readxl będącego częścią tidyverse.

+
read_excel('./data/excel/listings.xlsx')
+

Oprócz tego, można także użyć pakietu funkcji read.xlsx z pakietu xlsx. Wymaga ona jednak instalacji Javy.

+

Do zaimportowania plików JSON możemy użyć funkcji z pakietu jsonlite

+
listings_js <- jsonlite::fromJSON('./data/json/listings.json')
+listings_js <- mutate(listings_js, 
+                      last_review = as_date(last_review))
+
+
+
+

4.2 Locale

+

Locale jest to uniksowe narzędzie powłokowe przechowujące ustawienia środowiskowe związane z ustawieniami regionalnymi.

+
Sys.getlocale()
+
## [1] "LC_CTYPE=pl_PL.UTF-8;LC_NUMERIC=C;LC_TIME=pl_PL.UTF-8;LC_COLLATE=pl_PL.UTF-8;LC_MONETARY=pl_PL.UTF-8;LC_MESSAGES=pl_PL.UTF-8;LC_PAPER=pl_PL.UTF-8;LC_NAME=C;LC_ADDRESS=C;LC_TELEPHONE=C;LC_MEASUREMENT=pl_PL.UTF-8;LC_IDENTIFICATION=C"
+

LC_COLLATE - odpowiada za porządek znaków, ważny przy sortowaniu

+

LC_CTYPE - odpowiada za kodowanie znaków

+

LC_MONETARY - odpowiada za system monetarny: znak waluty, separator tysięcy, liczba cyfr po przecinku itd.

+

LC_NUMERIC - określa separator ułamkowy, separator tysięcy, grupowanie cyfr

+

LC_TIME - odpowiada za system wyświetlania daty

+
Sys.localeconv()
+
##     decimal_point     thousands_sep          grouping   int_curr_symbol   currency_symbol 
+##               "."                ""                ""            "PLN "              "zł" 
+## mon_decimal_point mon_thousands_sep      mon_grouping     positive_sign     negative_sign 
+##               ","               " "            "\003"                ""               "-" 
+##   int_frac_digits       frac_digits     p_cs_precedes    p_sep_by_space     n_cs_precedes 
+##               "2"               "2"               "0"               "1"               "0" 
+##    n_sep_by_space       p_sign_posn       n_sign_posn 
+##               "1"               "1"               "1"
+

Powyższa funkcja wyświetla szczegóły dotyczące systemu numerycznego i monetarnego.

+
+

4.2.1 Ustawienie locale przez użytkownika

+
Sys.setlocale(category = "LC_ALL", locale = "polish")
+
## Warning in Sys.setlocale(category = "LC_ALL", locale = "polish"): Żądania raportów OS aby ustawić
+## lokalizację na "polish" nie mogą zostać wykonane
+
## [1] ""
+

W celu ustawienia innego locale niż domyślne systemowe należy użyć powyższej funkcji, przyjmowane przez nią argumenty to category i locale.

+

category - odpowiada za określenie, które zmienne środowiskowe chcemy zmienić, gdzie opcje: “LC_ALL”, “LC_COLLATE”, “LC_CTYPE”, “LC_MONETARY”, “LC_NUMERIC” oraz “LC_TIME” są wspierane na każdym systemie operacyjnym, niektóre systemy wspierają również: “LC_MESSAGES”, “LC_PAPER” i “LC_MEASUREMENT”

+

locale - odpowiada za region, który chcemy ustawić dla systemu windows podajemy angielską nazwę języka (regionu) np.: ‘polish’, w systemach UNIXowych podajemy np.: ‘pl_PL’ lub ‘pl_PL.UTF-8’.

+
+
+

4.2.2 Ustawienie locale poprzez readr

+

Pakiet readr oferuje więcej możliwości dostosowywania locale, więcej informacji na ten temat można znaleźć w tym odnośniku.

+
+
+
+

4.3 Natywne formaty R

+

R ma dwa natywne sposoby przechowywania danych, RDA(od RData) i RDS. Główną zaletą takiej obsługi danych jest szybkość przetwarzania ich. Zachowuje on także informacje z R o danych(np. typy zmiennych).

+
+

4.3.1 RDS

+

W formacie RDS mogą być przechowywane jedynie pojedyncze pliki R. Mogą być one za to przypisywane do dowolnej nazwy. +Aby załadować dane korzystamy z:

+
listings_rds <- readRDS("./data/native/listings.rds")
+

Do zapisania danych używamy:

+
saveRDS(object = listings, file = "listings.rds")
+
+
+

4.3.2 RDA

+

W plikach formatu RDA wczytane dane nie są przypisywane do zmiennej, tylko wywołujemy te funkcje i w efekcie plik pojawia się w środowisku.

+

W RDA do załadowania danych służy

+
load("./data/native/listings.rda")
+

Natomiast, aby zapisać dane używamy:

+
save(listings_rr, file = "listings_rr.rda")
+

Korzystając z formatu RDA możemy jednocześnie zapisywać większą ilość plików

+
save(iris, cars, file="data_frame.rda")
+ +
+
+
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + diff --git a/docs/_book/wczytywanie-danych-w-r.html b/docs/_book/wczytywanie-danych-w-r.html new file mode 100644 index 0000000..c5441bc --- /dev/null +++ b/docs/_book/wczytywanie-danych-w-r.html @@ -0,0 +1,557 @@ + + + + + + + Chapter 3 Wczytywanie danych w R | Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+
+ + +
+
+ +
+
+

Chapter 3 Wczytywanie danych w R

+
+

3.1 Formaty danych

+
+

3.1.1 CSV/DSV

+

CSV (Comma Separated Values) to plik tekstowy, w którym wartości rozdzielane są przecinkami, a kolejne wiersze znakiem nowej linii. Plik CSV zazwyczaj przechowuje dane tabelaryczne. Nagłówki kolumn są często dołączane jako pierwszy wiersz (są to nazwy zmiennych), a każdy kolejny wiersz odpowiada jednej obserwacji (jednemu wierszowi w tabeli danych).

+

CSV jest szczególnym przypadkiem formatu danych o nazwie Delimiter Seperated Values (DSV). Jest to plik tekstowy w którym pola w każdym wierszu oddzielone są dowolnym separatorem. Najczęściej spotykane separatory to: przecinek (CSV), tabulator (TSV), średnik.

+

Przykładowy plik CSV

+

+
+
+

3.1.2 XML

+

XML to skrót od nazwy Extensible Markup Language. Dane przechowywane w tym formacie mają zagnieżdżoną strukturę: znaczniki oznaczają nazwy zmiennych, a wewnątrz przechowywane są ich wartości. XML swoją strukturą przypomina plik HTML.

+

Przykładowy plik XML

+

+
+
+

3.1.3 JSON

+

JSON - JavaScript Object Notation - to format przydatny w przypadku pracy z danymi pochodzącymi z REST API, czyli pobieranymi z sieci. Niektóre bazy danych również komunikują się za pomocą tego formatu, np. MongoDB.

+

Struktura: w pliku JSON obserwacje przechowywane są w słownikach, w których nazwy zmiennych są kluczami, a wartości zmiennych - wartościami. Obserwacje oddzielane są przecinkami, a dodatkowo, wszystkie dane spięte są nawiasami klamrowymi.

+

Przykładowy plik JSON

+

+
+
+

3.1.4 Excel (XLSX)

+

XLSX to format danych oparty na XML. Pliki tego typu są domyślnymi dokumentami wyjściowymi arkuszy kalkulacyjnych programu Microsoft Excel. Przedstawiają one głównie dane liczbowe i tekstowe w postaci tabel dwuwymiarowych.

+

Przykładowy arkusz kalkulacyjny w Excelu

+

+
+
+

3.1.5 Otwarte wersje programu Excel

+

Istnieją inne pakiety biurowe, np. LibreOffice, które - w przeciwieństwie do Excela - pozwalają na darmowe korzystanie z arkusza kalkulacyjnego. W przypadku LibreOffice, domyślnym formatem zapisu danych przez Calc (odpowiednik Excela) jest OpenDocument Format (.ods).

+

Przykładowy arkusz kalkulacyjny w LibreOffice

+

+
+
+

3.1.6 Pliki tekstowe

+

Jednym z najczęściej występujących i najbardziej uniwersalnych formatów przechowujących dane (np. w postaci tabeli) są pliki tekstowe. Mają one najczęściej rozszerzenie txt lub csv (comma separated values).
+Poniższą charakteryzację różnych metod wczytywania przedstawiamy na podstawie pliku listings.csv

+
+

3.1.6.1 Base

+

Podstawową funkcją używaną do wczytywania tego typu plików w postaci tabeli jest funkcja read.table. +Ze względu na specyfikację wewnętrzną plików, read.table posiada kilka wariantów, takie jak read.csv(), read.csv2() czy read.delim().

+

read.csv() używana jest w przypadku, gdy domyślnym separatorem dziesiętnym jest “.”, a wartości w wierszach oddzielone są poprzez “,”;
+read.csv2() używana jest w przypadku, gdy domyślnym separatorem dziesiętnym jest “,”, a wartości w wierszach oddzielone są poprzez “;”;
+read.delim() używana jest w przypadku, gdy domyślnym separatorem dziesiętnym jest “.”, a wartości w wierszach oddzielone są poprzez TAB

+

Przykładowy sposób załadowania plików w formacie csv

+
read.csv('./data/csv/listings.csv', header = TRUE, sep = ",")
+

W przypadku read.table() dane zostają zaimportowane jako data.frame.

+

Dla dużych plików wczytwanie za pomocą read.table() bywa jednak czasochłonne. Wówczas możemy użyć funkcji z paczki data.table lub readr.

+
+
+

3.1.6.2 readr

+

readr jest częścią pakietu tidyverse. W tym przypadku import odbywa się za pomocą funkcji o podobnej nazwie, jak w przypadku read.table(), a mianowicie read_csv().
+read_csv wczytuje dane oddzielone przecinkami, natomiast read_csv2() - dane oddzielone średnikami.

+
read_csv('./data/csv/listings.csv')
+

W przeciwieństwie do read.csv, funkcja read_csv na wyjściu daje dane w postaci tabeli w bardziej zwartej i przejrzystej formie. Oprócz tego podaje także specyfikację kolumn, tzn. informuje, jaka jest nazwa każdej kolumny oraz jej typ (np. col_double () oznaczają dane liczbowe).
+Typ danych jaki dostajemy na wyjściu to tbl_df (tzw. tibble), który jest w pewnym sensie zmodyfikowaną wersją tradycyjnej ramki danych data.frame, pozwalającą na łatwiejszą pracę w obrębie tidyverse.

+
+
+

3.1.6.3 data.table

+

Do wczytywania danych z plików csv możemy także użyć funkcji fread z pakietu data.table.

+
fread('./data/csv/listings.csv')
+

Na wyjściu otrzymujemy ramkę danych, jednak wyświetloną w inny sposób niż w przypadku użycia read.csv. Różnica jest widoczna, gdyż po użyciu funkcji class() na fread() jako typ danych otrzymujemy "data.table" "data.frame".

+
+
+

3.1.6.4 Różnice

+

Najważniejsze różnice pomiędzy wymienionymi sposobami wczytywania plików csv to:

+
    +
  1. Typ danych
  2. +
+
    +
  • Base: `data.frame
  • +
  • readr: tibble
  • +
  • data.table: `data.table data.frame
  • +
+
    +
  1. Postać wyświetlania (co jest konsekwencją 1)
  2. +
+
    +
  • Base: Wyświetla 62 początkowe wiersze każdej kolumny, wyświetlając informacje o liczbie pozostałych;
  • +
  • readr: wyświetla 10 pierwszych wierszy z 10 pierwszych kolumn, z informacją o liczbie pozostałych wierszy i kolumn; automatycznie wyświetlane są też nazwy kolumn oraz skrót informujący o typie zmiennych
  • +
  • data.table: wyświetla 5 początkowych i 5 końcowych wartości z każdej kolumny
  • +
+
    +
  1. Czas i użycie pamięci przy dużych rozmiarach danych
    +Zarówno czas wczytania danych, jak i wykorzystanie pamięci najkorzystniejsze jest w przypadku funkcji fread. Gdyby przez time oznaczyć czas potrzebny na wczytanie dużych plików, a przez memory zużycie pamięci, to time(fread) < time(read_csv) << time(read.csv) oraz memory(fread) < memory(read.csv) < memory(read_csv).
  2. +
+
+
+
+

3.1.7 Arkusze kalkulacyjne i pliki JSON

+

Do wczytywania arkusza kalkulacyjnego (np. pliku excela) używa się funkcji read_excel z pakietu readxl będącego częścią tidyverse.

+
read_excel('./data/excel/listings.xlsx')
+

Oprócz tego, można także użyć pakietu funkcji read.xlsx z pakietu xlsx. Wymaga ona jednak instalacji Javy.

+

Do zaimportowania plików JSON możemy użyć funkcji z pakietu jsonlite

+
listings_js <- jsonlite::fromJSON('./data/json/listings.json')
+listings_js <- mutate(listings_js, 
+                      last_review = as_date(last_review))
+
+
+
+

3.2 Locale

+

Locale jest to uniksowe narzędzie powłokowe przechowujące ustawienia środowiskowe związane z ustawieniami regionalnymi.

+
Sys.getlocale()
+
## [1] "LC_CTYPE=pl_PL.UTF-8;LC_NUMERIC=C;LC_TIME=pl_PL.UTF-8;LC_COLLATE=pl_PL.UTF-8;LC_MONETARY=pl_PL.UTF-8;LC_MESSAGES=pl_PL.UTF-8;LC_PAPER=pl_PL.UTF-8;LC_NAME=C;LC_ADDRESS=C;LC_TELEPHONE=C;LC_MEASUREMENT=pl_PL.UTF-8;LC_IDENTIFICATION=C"
+

LC_COLLATE - odpowiada za porządek znaków, ważny przy sortowaniu

+

LC_CTYPE - odpowiada za kodowanie znaków

+

LC_MONETARY - odpowiada za system monetarny: znak waluty, separator tysięcy, liczba cyfr po przecinku itd.

+

LC_NUMERIC - określa separator ułamkowy, separator tysięcy, grupowanie cyfr

+

LC_TIME - odpowiada za system wyświetlania daty

+
Sys.localeconv()
+
##     decimal_point     thousands_sep          grouping   int_curr_symbol   currency_symbol 
+##               "."                ""                ""            "PLN "              "zł" 
+## mon_decimal_point mon_thousands_sep      mon_grouping     positive_sign     negative_sign 
+##               ","               " "            "\003"                ""               "-" 
+##   int_frac_digits       frac_digits     p_cs_precedes    p_sep_by_space     n_cs_precedes 
+##               "2"               "2"               "0"               "1"               "0" 
+##    n_sep_by_space       p_sign_posn       n_sign_posn 
+##               "1"               "1"               "1"
+

Powyższa funkcja wyświetla szczegóły dotyczące systemu numerycznego i monetarnego.

+
+

3.2.1 Ustawienie locale przez użytkownika

+
Sys.setlocale(category = "LC_ALL", locale = "polish")
+
## Warning in Sys.setlocale(category = "LC_ALL", locale = "polish"): Żądania raportów OS aby ustawić
+## lokalizację na "polish" nie mogą zostać wykonane
+
## [1] ""
+

W celu ustawienia innego locale niż domyślne systemowe należy użyć powyższej funkcji, przyjmowane przez nią argumenty to category i locale.

+

category - odpowiada za określenie, które zmienne środowiskowe chcemy zmienić, gdzie opcje: “LC_ALL”, “LC_COLLATE”, “LC_CTYPE”, “LC_MONETARY”, “LC_NUMERIC” oraz “LC_TIME” są wspierane na każdym systemie operacyjnym, niektóre systemy wspierają również: “LC_MESSAGES”, “LC_PAPER” i “LC_MEASUREMENT”

+

locale - odpowiada za region, który chcemy ustawić dla systemu windows podajemy angielską nazwę języka (regionu) np.: ‘polish’, w systemach UNIXowych podajemy np.: ‘pl_PL’ lub ‘pl_PL.UTF-8’.

+
+
+

3.2.2 Ustawienie locale poprzez readr

+

Pakiet readr oferuje więcej możliwości dostosowywania locale, więcej informacji na ten temat można znaleźć w tym odnośniku.

+
+
+
+

3.3 Natywne formaty R

+

R ma dwa natywne sposoby przechowywania danych, RDA(od RData) i RDS. Główną zaletą takiej obsługi danych jest szybkość przetwarzania ich. Zachowuje on także informacje z R o danych(np. typy zmiennych).

+
+

3.3.1 RDS

+

W formacie RDS mogą być przechowywane jedynie pojedyncze pliki R. Mogą być one za to przypisywane do dowolnej nazwy. +Aby załadować dane korzystamy z:

+
listings_rds <- readRDS("./data/native/listings.rds")
+

Do zapisania danych używamy:

+
saveRDS(object = listings, file = "listings.rds")
+
+
+

3.3.2 RDA

+

W plikach formatu RDA wczytane dane nie są przypisywane do zmiennej, tylko wywołujemy te funkcje i w efekcie plik pojawia się w środowisku.

+

W RDA do załadowania danych służy

+
load("./data/native/listings.rda")
+

Natomiast, aby zapisać dane używamy:

+
save(listings_rr, file = "listings_rr.rda")
+

Korzystając z formatu RDA możemy jednocześnie zapisywać większą ilość plików

+
save(iris, cars, file="data_frame.rda")
+ +
+
+
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + diff --git "a/docs/_book/wst\304\231p-1.html" "b/docs/_book/wst\304\231p-1.html" new file mode 100644 index 0000000..2a84832 --- /dev/null +++ "b/docs/_book/wst\304\231p-1.html" @@ -0,0 +1,321 @@ + + + + + + + Chapter 6 Wstęp | Dane w formacie wąskim i szerokim. Łączenie tabel danych (join). Operacje na napisach i datach. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+ + +
+
+ +
+
+

Chapter 6 Wstęp

+

Transformacja danych jest niezwykle ważnym elementem dobrze zrobionego raportu. Dane te powinny być prezentowane w sposób czytelny i ułatwiający ich porównywanie. To od potrzeby biznesowej zależy w jaki sposób powinniśmy przedstwiać dane. Np. dysponując wynikami finansowymi zbieranymi co miesiąc przez trzy lata bo planowania budżetu na następny rok przyda nam się prezentacja ich w formacie wąskim, czyli skupionym na wydatkach względem każdego roku. Jednakże, jeżeli chcielibyśmy kontrolować wydatki w tym następnym roku prezentacja danych w formacie szerokim będzie bardziej korzystna, gdyż będziemy mieli informację ile średnio wydajemy w danym miesiącu i na bieżąco będziemy mogli podejmować decyzję o inwestowaniu lub zaciskaniu pasa.

+

Niekiedy jednak dane mają bardziej skomplikowaną formę i np. składają się z wielu tabel. Wówczas dla łatwiejszego uzyskania informacji biznesowej będzie połączenie tych tabel. Takie operacje w połączeniu z odpowiednią agregacją i grupowaniem zdecydowanie ułatwia wgląd w aktualną sytuację.

+

Ostatnim tematem, na temat któtego ta notatka traktuje są operacje na napisach i datach. Bardzo łatwo uzmysłowić sobie przydatność w posługiwaniu się takimi operacjami. Ułatwia to konstruowanie prostych funkcji, które są kluczowe w każdym projekcie. Chociażby bazując na imionach i nazwiskach pewnych obywateli Polski łatwo wskazać z dużą pewnością kobiety w tym zbiorze sprawdzając ostatnią literę ich imienia (tj. czy dane imie kończy się na literę “a”). +

+
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + diff --git "a/docs/_book/\305\202\304\205czenie-tabel-danych.html" "b/docs/_book/\305\202\304\205czenie-tabel-danych.html" new file mode 100644 index 0000000..1059cd9 --- /dev/null +++ "b/docs/_book/\305\202\304\205czenie-tabel-danych.html" @@ -0,0 +1,1114 @@ + + + + + + + Chapter 8 Łączenie tabel danych | Dane w formacie wąskim i szerokim. Łączenie tabel danych (join). Operacje na napisach i datach. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+ + +
+
+ +
+
+

Chapter 8 Łączenie tabel danych

+

Mamy dwie tabele danych tab1 z małymi literami oraz tab2 z wielkimi literami:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Table 8.1: tab1 = x +
+indeks + +litery +
+1 + +a +
+2 + +b +
+3 + +c +
+4 + +d +
+5 + +e +
+6 + +f +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Table 8.1: tab2 = y +
+indeks + +LITERY +
+4 + +E +
+5 + +F +
+6 + +G +
+7 + +H +
+8 + +I +
+9 + +J +
+

gdzie x = tab1, a y = tab2.

+

Aby połączyć dwie tabele danych na podstawie wskazanych kolumn lub kolumn o wspólnej nazwie można użyć przykładowych funkcji.

+
+

8.1 merge()

+

Dostępna w bazowym R. Domyślnie funkcja ta łączy tabele względem nazw kolumn, które są wspólne.

+
tabela <- merge(x = tab1, y = tab2)
+kable(tabela)
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+indeks + +litery + +LITERY +
+4 + +d + +E +
+5 + +e + +F +
+6 + +f + +G +
+

Jeśli chcemy być pewni, że tabele zostaną połączone po odpowiedniej kolumnie, możemy przekazać nazwę tej kolumny w argumencie. W tym przypadku:

+
merge(tab1, tab2, by = "indeks") # INNER JOIN
+

Jeśli jest więcej kolumn, po których chcemy połączyć tabele, wystarczy przekazać w argumencie by wektor z nazwami tych kolumn.

+

Gdy nazwy kolumn po których chcemy złączyć tabele różnią się, należy wykorzystać argument by.*. Załóżmy, że kolumna tabeli tab1 - indeks zmieniła nazwę na index, zatem:

+
merge(tab1, tab2, by.x = "index", by.y = "indeks")
+

Wartości kolumn indeks w tab1 oraz tab2 różnią się. Dlatego korzystając z funkcji bez dodatkowych argumentów tracimy dane.

+

Aby zapobiec traceniu danych z poszczególnych tabel należy skorzystać z argumentu all, brakujące wartości zostaną uzupełnione NA:

+
merge(tab1, tab2, all.x = TRUE) # LEFT JOIN
+merge(tab1, tab2, all.y = TRUE) # RIGHT JOIN
+merge(tab1, tab2, all = TRUE)   # OUTER JOIN
+

Dostajemy wtedy kolejno:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Table 8.2: all.x = TRUE +
+indeks + +litery + +LITERY +
+1 + +a + +NA +
+2 + +b + +NA +
+3 + +c + +NA +
+4 + +d + +E +
+5 + +e + +F +
+6 + +f + +G +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Table 8.2: all.y = TRUE +
+indeks + +litery + +LITERY +
+4 + +d + +E +
+5 + +e + +F +
+6 + +f + +G +
+7 + +NA + +H +
+8 + +NA + +I +
+9 + +NA + +J +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Table 8.2: all = TRUE +
+indeks + +litery + +LITERY +
+1 + +a + +NA +
+2 + +b + +NA +
+3 + +c + +NA +
+4 + +d + +E +
+5 + +e + +F +
+6 + +f + +G +
+7 + +NA + +H +
+8 + +NA + +I +
+9 + +NA + +J +
+

Bez sprecyzowania argumentu sort wiersze wyniku merge() zostaną posortowane leksykograficznie po wspólnych kolumnach. Gdy sort = FALSE wiersze będą w nieokreślonej kolejności.

+

Kolumny złączonej tabeli to najpierw kolumny wspólne, następnie pozostałe z x a na końcu pozostałe z y, co widać na przykładach.

+
+
+

8.2 join()

+

Funkcja z paczki dplyr. Tabele x i y powinny zwykle pochodzić z tego samego źródła danych, ale jeśli copy = TRUE, y zostanie automatycznie skopiowany do tego samego źródła co x.

+

Są cztery typy join zmieniających:

+
    +
  • left_join() - zwraca wszystkie wiersze z x i wszystkie kolumny z x i y. Wiersze w x bez dopasowania w y będą miały wartości NA w nowych kolumnach. Jeśli istnieje wiele dopasowań między x a y, zwracane są wszystkie kombinacje dopasowań
  • +
+
tabela <- left_join(tab1, tab2)
+kable(tabela)
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+indeks + +litery + +LITERY +
+1 + +a + +NA +
+2 + +b + +NA +
+3 + +c + +NA +
+4 + +d + +E +
+5 + +e + +F +
+6 + +f + +G +
+6 + +z + +G +
+
    +
  • right_join() - analogicznie do left_join(), ale zwraca wszystkie wiersze z y, a wiersze bez dopasowania w x będą miały wartości NA

  • +
  • inner_join() - zwraca wszystkie wiersze z x, w których znajdują się pasujące wartości w y, oraz wszystkie kolumny z x i y. Jeśli istnieje wiele dopasowań między x a y, zwracane są wszystkie kombinacje dopasowań.

  • +
+
tabela <- inner_join(tab1, tab2)
+kable(tabela)
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+indeks + +litery + +LITERY +
+4 + +d + +E +
+5 + +e + +F +
+6 + +f + +G +
+6 + +z + +G +
+
    +
  • full_join() - zwraca wszystkie wiersze i wszystkie kolumny zarówno z x, jak i y. Jeśli nie ma pasujących wartości, zwraca NA dla brakujących.
  • +
+
tabela <- full_join(tab1, tab2)
+kable(tabela)
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+indeks + +litery + +LITERY +
+1 + +a + +NA +
+2 + +b + +NA +
+3 + +c + +NA +
+4 + +d + +E +
+5 + +e + +F +
+6 + +f + +G +
+6 + +z + +G +
+7 + +NA + +H +
+8 + +NA + +I +
+9 + +NA + +J +
+

Argument by przyjmuje wektor nazw zmiennych do połączenia. Jeśli by = NULL funkcja *_join() domyślnie połączy tabele dopasowując wartości ze wszystkich kolumn o wspólnych nazwach w obu tabelach.

+
+
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + diff --git a/docs/_bookdown.yml b/docs/_bookdown.yml new file mode 100644 index 0000000..30bf605 --- /dev/null +++ b/docs/_bookdown.yml @@ -0,0 +1,4 @@ +delete_merged_file: true +language: + ui: + chapter_name: "Chapter " diff --git "a/docs/_bookdown_files/_main_files/figure-html/dopasowanie po\305\202o\305\274enia-1.png" "b/docs/_bookdown_files/_main_files/figure-html/dopasowanie po\305\202o\305\274enia-1.png" new file mode 100644 index 0000000..53ae0df Binary files /dev/null and "b/docs/_bookdown_files/_main_files/figure-html/dopasowanie po\305\202o\305\274enia-1.png" differ diff --git "a/docs/_bookdown_files/_main_files/figure-html/dopasowanie po\305\202o\305\274enia2-1.png" "b/docs/_bookdown_files/_main_files/figure-html/dopasowanie po\305\202o\305\274enia2-1.png" new file mode 100644 index 0000000..78d0668 Binary files /dev/null and "b/docs/_bookdown_files/_main_files/figure-html/dopasowanie po\305\202o\305\274enia2-1.png" differ diff --git "a/docs/_bookdown_files/_main_files/figure-html/dopasowanie po\305\202o\305\274enia3-1.png" "b/docs/_bookdown_files/_main_files/figure-html/dopasowanie po\305\202o\305\274enia3-1.png" new file mode 100644 index 0000000..925ed38 Binary files /dev/null and "b/docs/_bookdown_files/_main_files/figure-html/dopasowanie po\305\202o\305\274enia3-1.png" differ diff --git "a/docs/_bookdown_files/_main_files/figure-html/dopasowanie po\305\202o\305\274enia3-2.png" "b/docs/_bookdown_files/_main_files/figure-html/dopasowanie po\305\202o\305\274enia3-2.png" new file mode 100644 index 0000000..db584e1 Binary files /dev/null and "b/docs/_bookdown_files/_main_files/figure-html/dopasowanie po\305\202o\305\274enia3-2.png" differ diff --git a/docs/_bookdown_files/_main_files/figure-html/first plot-1.png b/docs/_bookdown_files/_main_files/figure-html/first plot-1.png new file mode 100644 index 0000000..192e82c Binary files /dev/null and b/docs/_bookdown_files/_main_files/figure-html/first plot-1.png differ diff --git a/docs/_bookdown_files/_main_files/figure-html/first plot-2.png b/docs/_bookdown_files/_main_files/figure-html/first plot-2.png new file mode 100644 index 0000000..7212b79 Binary files /dev/null and b/docs/_bookdown_files/_main_files/figure-html/first plot-2.png differ diff --git a/docs/_bookdown_files/_main_files/figure-html/first plot-3.png b/docs/_bookdown_files/_main_files/figure-html/first plot-3.png new file mode 100644 index 0000000..83cc79c Binary files /dev/null and b/docs/_bookdown_files/_main_files/figure-html/first plot-3.png differ diff --git a/docs/_bookdown_files/_main_files/figure-html/first plot-4.png b/docs/_bookdown_files/_main_files/figure-html/first plot-4.png new file mode 100644 index 0000000..ba37f1e Binary files /dev/null and b/docs/_bookdown_files/_main_files/figure-html/first plot-4.png differ diff --git a/docs/_bookdown_files/_main_files/figure-html/geom plot-1.png b/docs/_bookdown_files/_main_files/figure-html/geom plot-1.png new file mode 100644 index 0000000..f52bb6e Binary files /dev/null and b/docs/_bookdown_files/_main_files/figure-html/geom plot-1.png differ diff --git a/docs/_bookdown_files/_main_files/figure-html/geom plot-2.png b/docs/_bookdown_files/_main_files/figure-html/geom plot-2.png new file mode 100644 index 0000000..59ab9c5 Binary files /dev/null and b/docs/_bookdown_files/_main_files/figure-html/geom plot-2.png differ diff --git a/docs/_bookdown_files/_main_files/figure-html/mapping plot-1.png b/docs/_bookdown_files/_main_files/figure-html/mapping plot-1.png new file mode 100644 index 0000000..e9a9752 Binary files /dev/null and b/docs/_bookdown_files/_main_files/figure-html/mapping plot-1.png differ diff --git a/docs/_bookdown_files/_main_files/figure-html/mapping plot-2.png b/docs/_bookdown_files/_main_files/figure-html/mapping plot-2.png new file mode 100644 index 0000000..1fab50f Binary files /dev/null and b/docs/_bookdown_files/_main_files/figure-html/mapping plot-2.png differ diff --git a/docs/_bookdown_files/_main_files/figure-html/panele-1.png b/docs/_bookdown_files/_main_files/figure-html/panele-1.png new file mode 100644 index 0000000..b9e0176 Binary files /dev/null and b/docs/_bookdown_files/_main_files/figure-html/panele-1.png differ diff --git a/docs/_bookdown_files/_main_files/figure-html/two geoms on plot-1.png b/docs/_bookdown_files/_main_files/figure-html/two geoms on plot-1.png new file mode 100644 index 0000000..36fda8d Binary files /dev/null and b/docs/_bookdown_files/_main_files/figure-html/two geoms on plot-1.png differ diff --git "a/docs/_bookdown_files/_main_files/figure-html/zarz\304\205dzanaie osiami wsp\303\263\305\202rz\304\231dnych-1.png" "b/docs/_bookdown_files/_main_files/figure-html/zarz\304\205dzanaie osiami wsp\303\263\305\202rz\304\231dnych-1.png" new file mode 100644 index 0000000..903f0b8 Binary files /dev/null and "b/docs/_bookdown_files/_main_files/figure-html/zarz\304\205dzanaie osiami wsp\303\263\305\202rz\304\231dnych-1.png" differ diff --git a/docs/_output.yml b/docs/_output.yml new file mode 100644 index 0000000..83f6c10 --- /dev/null +++ b/docs/_output.yml @@ -0,0 +1,17 @@ +bookdown::gitbook: + css: style.css + config: + toc: + before: | +
  • + after: | +
  • Published with bookdown
  • + edit: https://github.com/USERNAME/REPO/edit/BRANCH/%s + download: ["pdf", "epub"] +bookdown::pdf_book: + includes: + in_header: preamble.tex + latex_engine: xelatex + citation_package: natbib + keep_tex: yes +bookdown::epub_book: default diff --git a/docs/book.bib b/docs/book.bib new file mode 100644 index 0000000..f275a09 --- /dev/null +++ b/docs/book.bib @@ -0,0 +1,10 @@ +@Book{xie2015, + title = {Dynamic Documents with {R} and knitr}, + author = {Yihui Xie}, + publisher = {Chapman and Hall/CRC}, + address = {Boca Raton, Florida}, + year = {2015}, + edition = {2nd}, + note = {ISBN 978-1498716963}, + url = {http://yihui.org/knitr/}, +} diff --git a/docs/notatka_lab3/book_render.R b/docs/book_render.R similarity index 100% rename from docs/notatka_lab3/book_render.R rename to docs/book_render.R diff --git a/docs/czyste-dane.html b/docs/czyste-dane.html new file mode 100644 index 0000000..6bd80df --- /dev/null +++ b/docs/czyste-dane.html @@ -0,0 +1,1725 @@ + + + + + + + Chapter 6 Czyste dane | Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + +
    + +
    +
    + + +
    +
    + +
    +
    +

    Chapter 6 Czyste dane

    +

    Transformacja danych jest niezwykle ważnym elementem dobrze zrobionego raportu. Dane te powinny być prezentowane w sposób czytelny i ułatwiający ich porównywanie. To od potrzeby biznesowej zależy w jaki sposób powinniśmy przedstwiać dane. Np. dysponując wynikami finansowymi zbieranymi co miesiąc przez trzy lata bo planowania budżetu na następny rok przyda nam się prezentacja ich w formacie wąskim, czyli skupionym na wydatkach względem każdego roku. Jednakże, jeżeli chcielibyśmy kontrolować wydatki w tym następnym roku prezentacja danych w formacie szerokim będzie bardziej korzystna, gdyż będziemy mieli informację ile średnio wydajemy w danym miesiącu i na bieżąco będziemy mogli podejmować decyzję o inwestowaniu lub zaciskaniu pasa.

    +

    Niekiedy jednak dane mają bardziej skomplikowaną formę i np. składają się z wielu tabel. Wówczas dla łatwiejszego uzyskania informacji biznesowej będzie połączenie tych tabel. Takie operacje w połączeniu z odpowiednią agregacją i grupowaniem zdecydowanie ułatwia wgląd w aktualną sytuację.

    +

    Ostatnim tematem, na temat któtego ta notatka traktuje są operacje na napisach i datach. Bardzo łatwo uzmysłowić sobie przydatność w posługiwaniu się takimi operacjami. Ułatwia to konstruowanie prostych funkcji, które są kluczowe w każdym projekcie. Chociażby bazując na imionach i nazwiskach pewnych obywateli Polski łatwo wskazać z dużą pewnością kobiety w tym zbiorze sprawdzając ostatnią literę ich imienia (tj. czy dane imie kończy się na literę “a”).

    +
    +

    6.1 Dane w formacie wąskim i szerokim

    +

    Dane najczęściej są przedstawiane w postaci tabelarycznej. Jednak mogą być w tej tabeli różnie sformatowane. Wyróżnia się między innymi szeroką reprezentacje danych i wąską reprezentacje danych. W zależności od tego, co chcemy z nimi zrobić czasami trzeba przejść z jednej postaci do drugiej. Aby przetransformować dane korzysta się z funkcji z pakietów dplyr i tidyverse.

    +

    O postaci szerokiej mówimy, gdy pojedyncza zmienna jest rozdzielona pomiędzy kilka kolumn. Różnicę najłatwiej jest pokazać na przykładzie. W tym celu wykorzystamy wbudowany zbiór danych sleep zawierający informacje o wpływie dwóch leków nasennych na ilość przespanych godzin. Kolumna extra zawiera informacje o ilości dodatkowo przespanych godzin.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +extra + +group + +ID +
    +0.7 + +1 + +1 +
    +-1.6 + +1 + +2 +
    +-0.2 + +1 + +3 +
    +-1.2 + +1 + +4 +
    +-0.1 + +1 + +5 +
    +3.4 + +1 + +6 +
    +

    Dane są przedstawione w postaci wąskiej, każda zmienna jest przedstawiona w oddzielnej kolumnie. Teraz ‘rozbijmy’ kolumnę group na group 1 i group 2.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +ID + +group 1 + +group 2 +
    +1 + +0.7 + +1.9 +
    +2 + +-1.6 + +0.8 +
    +3 + +-0.2 + +1.1 +
    +4 + +-1.2 + +0.1 +
    +5 + +-0.1 + +-0.1 +
    +6 + +3.4 + +4.4 +
    +7 + +3.7 + +5.5 +
    +8 + +0.8 + +1.6 +
    +9 + +0.0 + +4.6 +
    +10 + +2.0 + +3.4 +
    +

    Można zaobserwować, że wartości z kolumny extra zostały wpisane w poszczególne komórki, a kolumna group została podzielona na dwie oddzielne kolumny group 1 i group 2. Tak sformatowane dane nazywamy szeroką reprezentacją danych.

    +
    +
    +

    6.2 Rozdzielanie na kolumny (wąska -> szeroka)

    +

    Aby przejść z wąskiego formatu przedstawiania danych do szerokiego, można użyć funkcji spread() z pakietu dplyr.

    +

    Funkcja spread(dataset,key,value) przyjmuje trzy agrumenty:

    +
      +
    • dataset - zbiór danych w formacie wąskim,

    • +
    • key - kolumna (klucz) odpowiadająca kolumnie, która ma zostać rozłożona,

    • +
    • value - kolumna, w której znajdują się wartości wypełniające nowe kolumny.

    • +
    +
    szeroka <- spread(sleep, group, extra)
    +colnames(szeroka) = c("ID","group 1","group 2")
    +kable_styling(kable(head(szeroka)), position = "center")
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +ID + +group 1 + +group 2 +
    +1 + +0.7 + +1.9 +
    +2 + +-1.6 + +0.8 +
    +3 + +-0.2 + +1.1 +
    +4 + +-1.2 + +0.1 +
    +5 + +-0.1 + +-0.1 +
    +6 + +3.4 + +4.4 +
    +

    Drugą opcją na uzyskanie tego samego rezultatu jest użycie funkcji pivot_wider z pakietu tidyverse.

    +

    Funkcja przyjmuje dwa argumenty pivot_wider(names_from = name, values_from = value):

    +
      +
    • name - nazwa kolumny, która ma zostać rozłożona,

    • +
    • value - nazwa kolumny, w której znajdują się wartości.

    • +
    +
    sleep %>%
    +    pivot_wider(names_from = group, values_from = extra)
    +
    +
    +

    6.3 Scalanie kilku kolumn w jedną (szeroka -> wąska)

    +

    Można wrócić z postaci szerokiej do wąskiej. W tym celu należy użyć funkcji gather() z pakietu tidyr.

    +

    Funkcja gather(dataset, key, value, other) przyjmuje również trzy argumenty:

    +
      +
    • dataset - zbiór danych w formacie szerokim,

    • +
    • key - nazwy kolumn z kluczami,

    • +
    • value - nazwy kolumn z wartościami,

    • +
    • other - kolumny dataset, które mają być zawarte w nowej tabeli.

    • +
    +

    Aby wrócić do postaci wąskiej nałóżmy funkcję gather na wygenerowaną wcześniej tabele szeroka.

    +
    kable_styling(kable(head(szeroka %>% 
    +  gather(group, extra, -ID))),position = "center")
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +ID + +group + +extra +
    +1 + +1 + +0.7 +
    +2 + +1 + +-1.6 +
    +3 + +1 + +-0.2 +
    +4 + +1 + +-1.2 +
    +5 + +1 + +-0.1 +
    +6 + +1 + +3.4 +
    +

    Drugą funkcją, która umożliwia przejście z szerokiej reprezentacji danych do wąskiej jest funkcja pivot_longer z pakietu tidyverse.

    +

    Funkcja pivot_longer(col_names, names_to = name, values_to = value) przyjmuje trzy argumenty

    +
      +
    • col_names - ciąg nazw kolumn, które chcemy złączyć,

    • +
    • name - nazwa nowo powstałej kolumny,

    • +
    • value - nazwa kolumny, w której pojawią się wartości.

    • +
    +
    kable_styling(kable(head(szeroka %>% 
    +  pivot_longer(c("1", "2"), names_to = "group", values_to = "extra"))), 
    +  position = "center")
    +
    +
    +

    6.4 Łączenie tabel danych

    +

    Mamy dwie tabele danych tab1 z małymi literami oraz tab2 z wielkimi literami:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Table 6.1: tab1 = x +
    +indeks + +litery +
    +1 + +a +
    +2 + +b +
    +3 + +c +
    +4 + +d +
    +5 + +e +
    +6 + +f +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Table 6.1: tab2 = y +
    +indeks + +LITERY +
    +4 + +E +
    +5 + +F +
    +6 + +G +
    +7 + +H +
    +8 + +I +
    +9 + +J +
    +

    gdzie x = tab1, a y = tab2.

    +

    Aby połączyć dwie tabele danych na podstawie wskazanych kolumn lub kolumn o wspólnej nazwie można użyć przykładowych funkcji.

    +
    +

    6.4.1 merge()

    +

    Dostępna w bazowym R. Domyślnie funkcja ta łączy tabele względem nazw kolumn, które są wspólne.

    +
    tabela <- merge(x = tab1, y = tab2)
    +kable(tabela)
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    +indeks + +litery + +LITERY +
    +4 + +d + +E +
    +5 + +e + +F +
    +6 + +f + +G +
    +

    Jeśli chcemy być pewni, że tabele zostaną połączone po odpowiedniej kolumnie, możemy przekazać nazwę tej kolumny w argumencie. W tym przypadku:

    +
    merge(tab1, tab2, by = "indeks") # INNER JOIN
    +

    Jeśli jest więcej kolumn, po których chcemy połączyć tabele, wystarczy przekazać w argumencie by wektor z nazwami tych kolumn.

    +

    Gdy nazwy kolumn po których chcemy złączyć tabele różnią się, należy wykorzystać argument by.*. Załóżmy, że kolumna tabeli tab1 - indeks zmieniła nazwę na index, zatem:

    +
    merge(tab1, tab2, by.x = "index", by.y = "indeks")
    +

    Wartości kolumn indeks w tab1 oraz tab2 różnią się. Dlatego korzystając z funkcji bez dodatkowych argumentów tracimy dane.

    +

    Aby zapobiec traceniu danych z poszczególnych tabel należy skorzystać z argumentu all, brakujące wartości zostaną uzupełnione NA:

    +
    merge(tab1, tab2, all.x = TRUE) # LEFT JOIN
    +merge(tab1, tab2, all.y = TRUE) # RIGHT JOIN
    +merge(tab1, tab2, all = TRUE)   # OUTER JOIN
    +

    Dostajemy wtedy kolejno:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Table 6.2: all.x = TRUE +
    +indeks + +litery + +LITERY +
    +1 + +a + +NA +
    +2 + +b + +NA +
    +3 + +c + +NA +
    +4 + +d + +E +
    +5 + +e + +F +
    +6 + +f + +G +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Table 6.2: all.y = TRUE +
    +indeks + +litery + +LITERY +
    +4 + +d + +E +
    +5 + +e + +F +
    +6 + +f + +G +
    +7 + +NA + +H +
    +8 + +NA + +I +
    +9 + +NA + +J +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +Table 6.2: all = TRUE +
    +indeks + +litery + +LITERY +
    +1 + +a + +NA +
    +2 + +b + +NA +
    +3 + +c + +NA +
    +4 + +d + +E +
    +5 + +e + +F +
    +6 + +f + +G +
    +7 + +NA + +H +
    +8 + +NA + +I +
    +9 + +NA + +J +
    +

    Bez sprecyzowania argumentu sort wiersze wyniku merge() zostaną posortowane leksykograficznie po wspólnych kolumnach. Gdy sort = FALSE wiersze będą w nieokreślonej kolejności.

    +

    Kolumny złączonej tabeli to najpierw kolumny wspólne, następnie pozostałe z x a na końcu pozostałe z y, co widać na przykładach.

    +
    +
    +

    6.4.2 join()

    +

    Funkcja z paczki dplyr. Tabele x i y powinny zwykle pochodzić z tego samego źródła danych, ale jeśli copy = TRUE, y zostanie automatycznie skopiowany do tego samego źródła co x.

    +

    Są cztery typy join zmieniających:

    +
      +
    • left_join() - zwraca wszystkie wiersze z x i wszystkie kolumny z x i y. Wiersze w x bez dopasowania w y będą miały wartości NA w nowych kolumnach. Jeśli istnieje wiele dopasowań między x a y, zwracane są wszystkie kombinacje dopasowań
    • +
    +
    tabela <- left_join(tab1, tab2)
    +kable(tabela)
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +indeks + +litery + +LITERY +
    +1 + +a + +NA +
    +2 + +b + +NA +
    +3 + +c + +NA +
    +4 + +d + +E +
    +5 + +e + +F +
    +6 + +f + +G +
    +6 + +z + +G +
    +
      +
    • right_join() - analogicznie do left_join(), ale zwraca wszystkie wiersze z y, a wiersze bez dopasowania w x będą miały wartości NA

    • +
    • inner_join() - zwraca wszystkie wiersze z x, w których znajdują się pasujące wartości w y, oraz wszystkie kolumny z x i y. Jeśli istnieje wiele dopasowań między x a y, zwracane są wszystkie kombinacje dopasowań.

    • +
    +
    tabela <- inner_join(tab1, tab2)
    +kable(tabela)
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +indeks + +litery + +LITERY +
    +4 + +d + +E +
    +5 + +e + +F +
    +6 + +f + +G +
    +6 + +z + +G +
    +
      +
    • full_join() - zwraca wszystkie wiersze i wszystkie kolumny zarówno z x, jak i y. Jeśli nie ma pasujących wartości, zwraca NA dla brakujących.
    • +
    +
    tabela <- full_join(tab1, tab2)
    +kable(tabela)
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +indeks + +litery + +LITERY +
    +1 + +a + +NA +
    +2 + +b + +NA +
    +3 + +c + +NA +
    +4 + +d + +E +
    +5 + +e + +F +
    +6 + +f + +G +
    +6 + +z + +G +
    +7 + +NA + +H +
    +8 + +NA + +I +
    +9 + +NA + +J +
    +

    Argument by przyjmuje wektor nazw zmiennych do połączenia. Jeśli by = NULL funkcja *_join() domyślnie połączy tabele dopasowując wartości ze wszystkich kolumn o wspólnych nazwach w obu tabelach.

    +
    +
    +
    +

    6.5 Operacje na napisach i datach

    +

    Większość poniższych funkcji pochodzi z pakietu stringi.

    +
    +

    6.5.1 Operacje na napisach

    +
      +
    1. Wyznaczanie długości napisów. +Funkcja stri_lenght() zwraca długości poszczególnych napisów w danym wektorze, a stri_isempty() sprawdza, które napisy są puste -> ’’.

    2. +
    3. Łączenie i powielanie napisów. +Funkcja używana do łączenia kilku wektorów napisów w inny wektor napisów lub nawet w jeden napis, jest stri_paste() i jej warianty.

    4. +
    +

    Przykład:

    +
      x <- LETTERS[1:3]
    +    y <- letters[1:3] 
    +    z <- '!'
    +    stri_paste(x, y, z)
    +
    ## [1] "Aa!" "Bb!" "Cc!"
    +
      +
    1. Przycinanie i wypełnianie.
    2. +
    +

    Funkcja stri_wrap() wstawia znaki nowego wiersza (n), by napis po wyświetleniu np. przy funkcji cat() miał szerokość nie większą, niż podana, jeżeli to możliwe.

    +

    W przypadku przetwarzania tekstów pochodzących np. z formularzy na stronach internetowych może zachodzić potrzeba usunięcia tzw. białych znaków, np. spacji z początku lub końca napisu. Możemy to zrobić przy użyciu funkcji stri_trim(). Operacja w pewnym sensie odwrotną do tej można wykonać przy użyciu funkcji stri_pad().

    +

    Przykład:

    +
    stri_trim('            Mama i tata\n')
    +
    ## [1] "Mama i tata"
    +
      +
    1. Formatowanie napisów na podstawie innych obiektów. +Najprostszym sposobem na uzyskanie napisowej reprezentacji danego obiektu jest użycie funkcji as.character().
    2. +
    +

    Przykład:

    +
    as.character(list(1L, mean, NULL, pi, FALSE))
    +
    ## [1] "1"                                       "function (x, ...) \nUseMethod(\"mean\")"
    +## [3] "NULL"                                    "3.14159265358979"                       
    +## [5] "FALSE"
    +
    x <-data.frame(a=c(TRUE, FALSE, FALSE), b=as.integer(c(1, 2, 3)))
    +as.character(x)
    +
    ## [1] "c(TRUE, FALSE, FALSE)" "1:3"
    +
      +
    1. Zmiana pojedynczych znaków. +Zmiana poszczególnych znaków na inne przydaje się między innymi na etapie wstępnego przygotowania danych w celu ujednolicenia tekstowych identyfikatorów obiektów, możemy np. zmieniać wielkości wszystkich liter w napisach.
    2. +
    +

    Przykład:

    +
    stri_trans_toupper('chcemy duże litery')
    +
    ## [1] "CHCEMY DUŻE LITERY"
    +
    stri_trans_tolower('ChCemY MałE LiTErY')
    +
    ## [1] "chcemy małe litery"
    +
    stri_trans_char('zastępowanie znaków', 'ąćęłńóśżź', 'acelnoszz')
    +
    ## [1] "zastepowanie znakow"
    +
    stri_trans_general('żółć', 'Latin-ASCII')
    +
    ## [1] "zolc"
    +
      +
    1. Wyznaczanie podnapisów. +Funkcja stri_sub() zwraca podnapis składający się ze znaków leżących na określonych pozycjach danego napisu.
    2. +
    +

    Przykład:

    +
    x <- 'Lasy, pola, pastwiska, koszą traktorem'
    +        stri_sub(x, 7)  
    +
    ## [1] "pola, pastwiska, koszą traktorem"
    +
    +
    +

    6.5.2 Operacje na datach

    +
      +
    1. Funkcją zwracającą aktualną datę systemową jest Sys.Date(), a Sys.time() aktualny czas systemowy wraz z datą.
    2. +
    +

    Przykład:

    +
    (data <- Sys.Date())
    +
    ## [1] "2023-10-12"
    +
    (czas <- Sys.time())
    +
    ## [1] "2023-10-12 00:42:21 CEST"
    +
      +
    1. Operacje arytmetyczne na datach – dodawanie, odejmowanie i porównywanie.
    2. +
    +

    Przykład:

    +
    data
    +
    ## [1] "2023-10-12"
    +
    data-365
    +
    ## [1] "2022-10-12"
    +
    data+365
    +
    ## [1] "2024-10-11"
    +
    (d <- data-as.Date('2021-01-01'))
    +
    ## Time difference of 1014 days
    +
      +
    1. Do konwersji do napisu może służyć przeciążona wersja metody format(), której wywołanie jest tożsame z wywołaniem funkcji strftime() (ang. string-format-time).
    2. +
    +

    Przykład:

    +
    strftime(czas, '%Y-%m-%d %H:%M:%S %Z')  
    +
    ## [1] "2023-10-12 00:42:21 CEST"
    +
      +
    1. Do znajdowania “najstarszej” i “najmłodszej” daty używamy funkcji max() oraz min().

    2. +
    3. Do pracy ze strefami czasowymi możemy używać poniższych funkcji:

    4. +
    +
      +
    1. force_tz() ustawienie strefy czasowej,
    2. +
    3. with_tz() sprawdzenie daty w innej strefie czasowej.
    4. +
    + +
    +
    +
    +
    + +
    +
    +
    + + +
    +
    + + + + + + + + + + + + + + + diff --git a/docs/czysty-i-wydajny-kod-w-r.html b/docs/czysty-i-wydajny-kod-w-r.html new file mode 100644 index 0000000..773943b --- /dev/null +++ b/docs/czysty-i-wydajny-kod-w-r.html @@ -0,0 +1,509 @@ + + + + + + + Chapter 8 Czysty i wydajny kod w R | Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + +
    + +
    +
    + + +
    +
    + +
    +
    +

    Chapter 8 Czysty i wydajny kod w R

    +
    +

    8.1 Czysty kod

    +

    Na początku zajmiemy się szeroko pojętą czystością kodu. Aby dany kod mógł aspirować do takiego miana, musi przede wszystkim spełniać dwa podstawowe warunki:

    +
      +
    1. Być łatwym do zrozumienia
    2. +
    +

    Aby kod był łatwy do zrozumienia musi być przede wszystkim czytelny. Niewątpliwie pomoże w\(~\)tym odpowiednie nazwanie zmiennych, zadbanie o to, żeby wszystkie użyte funkcje i obiekty miały swoją określoną rolę oraz by relacje między nimi były zrozumiałe.

    +
      +
    1. Być łatwym do zmiany
    2. +
    +

    Tworząc kod powinniśmy myśleć o tym, że będzie on w przyszłości wykorzystywany. Aby to ułatwić, musi być napisany w taki sposób, żeby można było nanieść drobne poprawki lub zmienić dane bez konieczności zmieniania całego kodu.

    +

    Jeśli te dwa warunki nie są spełnione, istnieje obawa, że wprowadzenie nawet najmniejszych zmian całkowicie zniszczy kod.

    +
    +

    8.1.1 Co jeśli w kodzie jest ,,bałagan’’?

    +

    Nieuporządkowany i nieklarowny kod może sprawić w przyszłości wiele kłopotów, takich jak na przykład:

    +
      +
    1. Zmarnowanie czasu
    2. +
    +

    Jeśli my lub ktokolwiek inny będzie chciał w przyszłości wykorzystać taki kod z pewnością straci mnóstwo czasu na próby jego przeczytania i zrozumienia. Gdy już mu się to uda, może napotkać kolejny problem w postaci trudności z wprowadzeniem jakichkolwiek zmian.

    +
      +
    1. Ograniczenie lub nawet brak możliwości rozwoju
    2. +
    +

    Złe napisanie kodu może spowodować, że po jego jedynym użyciu stanie się bezwartościowy. Nie będzie sensu wprowadzać w nim jakichkolwiek zmian (gdyż będzie to zbyt pracochłonne), ani w żaden sposób rozwinąć by mógł posłużyć do przyszłych projektów (gdyż nawet najmniejsze zmiany mogą ,,zepsuć’’ istniejący kod).

    +
      +
    1. Podatność na wystąpienie błędów
    2. +
    +

    W nieczytelnym i napisanym w sposób niezrozumiały kodzie, łatwo przemycić błędy, które na pierwszy rzut oka są niewidoczne, ale wychodzą na jaw później.

    +
    +
    +

    8.1.2 Opis zmiennych

    +
    +
    +

    8.1.3 Opis intencji

    +

    Aby tworzyć czysty kod musimy pamiętać o kilku zasadach. Jedną z nich jest odpowiednie nazywanie zmiennych. Nie powinniśmy używać do tego skrótów, czy przypadkowych znaków. Idealna nazwa od razu wskazuje na to, czym jest dany obiekt oraz co oznacza. Przedstawia zamiary, jakie mamy do nazywanego obiektu.

    +
    +
    +

    8.1.4 Unikanie błędnych informacji

    +

    Równie ważne jest, aby w nazwach nie znajdywały się błędy lub informacje, które mogą wprowadzić potencjalnego czytelnika w błąd. Mówimy tu np. o: +- nazwaniu kilku obiektów zbyt podobnie, +- użyciu do nazwania listy (np. osób) słowa \(\mathtt{List}\), choć w rzeczywistości ta ,,lista’’ osób może być wektorem, +- użyciu trudno rozróżnialnych znaków (takich jak np. 0 i O), +- nazwaniu wszystkich obiektów za pomocą jednej litery i cyfry (np. \(x_1,x_2,...,x_n\)).

    +
    +

    8.1.4.1 Kilka wskazówek

    +

    Jakie powinny być idealne nazwy obiektów w R? Oto kilka wskazówek: +- zrozumiałe dla osób, dla których jest przeznaczony kod, +- utrzymane w jednym stylu,
    +- łatwe do zrozumienia i napisania,
    +- nazwa obiektu powinna być rzeczownikiem, który wskazuje na to, z czym mamy do czynienia,
    +- nazwa funkcji powinna być czasownikiem wskazującym na to, co robi dana funkcja.

    +
    +
    +
    +

    8.1.5 Funkcje

    +

    W tym rozdziale dowiemy się jak pisać ,,dobre’’ funkcje. Tutaj również musimy pamiętać o kilku zasadach.
    +Funkcje powinny: +- być możliwie jak najkrótsze,
    +- odpowiadać za jedno pojedyncze zadanie, +- być na jednym poziomie abstrakcji, +- mieć maksymalnie 3 parametry.

    +

    To znaczy, że nie jest wskazane, aby tworzyć jedną wielką funkcję, która np. wylicza kilkanaście rzeczy, aby na końcu wygenerować jeden wynik. Zamiast tego lepiej stworzyć kilka mniejszych funkcji, które będą się odwoływały do poprzednich. Dzięki temu nasz kod będzie bardziej przejrzysty oraz w prosty sposób będzie można sprawdzić, czy pojedyncze funkcje działają poprawnie.

    +

    Co więcej, nie ma sensu tworzyć funkcji, która zwraca nam już oprawioną tabelę z wynikami. Lepiej, gdy zwraca surowe wyniki, a tworzeniem tabeli zajmuje się kolejna funkcja.

    +

    Przykładowa, poprawnie napisana funkcja:

    +
    calculate_conf_interval = function(sample, alpha) {
    +  len = length(sample)
    +  successes = length(sample[sample == 1])
    +  mi = successes / n
    +  se = sqrt(mi * (1 - mi) / len)
    +  quantile = qt(1 - alpha / 2, len - 1)
    +  left = mi - quantile * se
    +  right = mi + quantile * se
    +  return(c(left, right))
    +}
    +

    Przykładowa funkcja, napisana w ,,nieładny’’ sposób:

    +
    func= function(x,y,temp1,temp2){
    +  n =length(x)
    +  s <-length(x[x==1])
    +  m = s/n
    +  sgm = sqrt(mi *(1- m)/n)
    +  q<-qt(1 - y /2,len-1)
    +  tmp = (s + 0.5 * q ^ 2) /(n + q ^ 2)
    +  se = sqrt(tmp *(1 - tmp)/ (n+ q^2))
    +  l<- tmp- q* se
    +  r = tmp + q*se
    +return(c(l,r))}
    +

    Główne problemy: +- czasem przypisanie jest za pomocą =, czasem <-,
    +- brak spacji po przecinkach,
    +- brak spacji pomiędzy +, -, *, /, itd,
    +- niepoprawnie umiejscowione nawiasy {, }.
    +- nazwa funkcji nie opisuje, co robi ta funkcja,
    +- zmienne mają nic nieznaczące i jednoliterowe nazwy,
    +- nazwa zmiennej tmp także nie mówi, czym ona jest,
    +- dwa nieużywane parametry funkcji.

    +
    +
    +

    8.1.6 Komentarze

    +

    Zazwyczaj komentarze do kodu nie są potrzebne, a wręcz zbędne. Dzieje się tak, ponieważ dobrze napisany kod powinien sam się tłumaczyć, tzn. być na tyle zrozumiałym, żeby dodatkowe komentarze nie były potrzebne.

    +

    Jeśli jednak w kodzie jest bałagan, dodatkowe komentarze mogą wręcz wprowadzić dodatkowy chaos.

    +

    Od tej reguły są jednak pewne wyjątki. Jeśli używamy niezbyt oczywistych implementacji lub ,,sztuczek programistycznych’’ warto wspomnieć w komentarzu, co się w danej chwili dzieje. Wyjątkiem są też komentarze informujące o tym, co trzeba jeszcze zrobić lub o potrzebie poprawienia jakiejś części kodu.

    +
    +
    +

    8.1.7 Obiekt a struktura danych

    +

    W kontekście pisania czystego i wydajnego kodu, należy wziąć pod uwagę rozróżnienie pomiędzy klasami a strukturami danych. Te pierwsze zawierają atrybuty i funkcje, a instancje klasy nazywamy obiektem. Zastosowanie klas pozwala na stworzenie interfejsu definującego pewne dane. Struktury danych służą natomiast do reprezentacji danych dowolnego typu a nie ich opisu.

    +
    +
    +
    +

    8.2 Styl kodu i narzędzia pomagające w utrzymaniu czystego kodu

    +

    Dobry styl kodowania jest porównywany do prawidłowego stosowania interpunkcji. Jest możliwe nie stosowanie się do jej zasad, jednak przestrzeganie ich pozwala, aby w zapisie panował ład i porządek. +W R dominują dwa style, które pomagają utrzymać dobry układ kodu. Jednym jest tidyverse style, a\(~\)drugim, wywodzącym się z poprzedniego, Google style. Istnieją przewodniki, które ułatwiają stosowanie się do zasad panujących w tych stylach. Style ustosunkowują się m.in. do stawiania spacji po przecinkach, przed operatorami matematycznymi oraz po nich, a także podkreślników w nazwach.
    +Dodatkowo można zainstalować pakiety, które będą pomagać w utrzymaniu schludnego kodu: cleanr, stylerr, lintr.

    + +
    +
    +
    + +
    +
    +
    + + +
    +
    + + + + + + + + + + + + + + + diff --git a/docs/eksploracyjna-analiza-danych.html b/docs/eksploracyjna-analiza-danych.html new file mode 100644 index 0000000..a4a159e --- /dev/null +++ b/docs/eksploracyjna-analiza-danych.html @@ -0,0 +1,465 @@ + + + + + + + Chapter 4 Eksploracyjna analiza danych | Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + +
    + +
    +
    + + +
    +
    + +
    +
    +

    Chapter 4 Eksploracyjna analiza danych

    +

    Badanie eksploracyjne danych (ang. exploratory data analysis) dotyczy opisu, wizualizacji i badania zebranych danych bez potrzeby zakładania z góry hipotez badawczych. Badania ekploracyjne obejmują również wstępne sprawdzenie danych w celu skontrolowania założeń modeli statystycznych lub występowania błędów w danych (np. braków odpowiedzi).

    +
    +

    4.1 Dane tabelaryczne

    +

    Dane tabelaryczne to dane, które mają postać tabeli. Tabela to struktura danych, która składa się z wierszy i kolumn. Każdy wiersz odpowiada pewnej obserwacji, której cechy zostały zapisane w kolejnych kolumnach.

    +
    +
    +

    4.2 Typy zmiennych

    +

    Zmienne, które opisują kolejne obserwacje możemy podzielić na:

    +
      +
    • zmienne jakościowe (niemierzalne) +
        +
      • porządkowe - np. klasyfikacja wzrostu (niski, średni, wysoki)
      • +
      • nominalne - np. kolor oczu, płeć, grupa krwi
      • +
    • +
    • zmienne ilościowe (mierzalne) +
        +
      • dyskretne - np. liczba dzieci, liczba gospodarstw domowych, wiek (w rozumieniu ilości skończonych lat)
      • +
      • ciągłe - np. wzrost, masa, wiek (w rozumieniu ilości dni między datą urodzin a datą badania)
      • +
      • proporcjonalne - np. masa, długość, temperatura wyrażona w Kelwinach lub stopniach Rankine’a (przyjmujemy istnienie zera i możemy twierdzić, że jedno ciało jest dwukrotnie gorętsze od drugiego)
      • +
      • interwałowe - np. temperatura wyrażona w stopniach Celsjusza lub Fahrenheita (możemy twierdzić, że coś jest o 20 °C cieplejsze od czegoś innego, ale nie możemy stwierdzić ilokrotnie cieplejsze jest ciało o temperaturze 40 °C od ciała o temperaturze –10 °C), data kalendarzowa (możemy mówić o stałej różnicy pomiędzy kolejnymi dniami)
      • +
    • +
    +
    +
    +

    4.3 Miary

    +

    Zapoznając się z danymi chcielibyśmy sprawdzić wokół jakiej wartości są skupione oraz jak bardzo są zmienne wartości danej cechy.

    +

    Miary lokacji (miary tendencji centralnej) pomagają nam umiejscowić dane na osi. Przykładami takich miar są:

    +
      +
    • średnia - najczęściej arytmetyczna określona jako \(\overline{x} = \frac{1}{n}\sum\limits_{i=1}^n x_i\).
    • +
    • dominanta (moda) - ozn. \(Mo\) - dla zmiennych o rozkładzie dyskretnym, wartość o największym prawdopodobieństwie wystąpienia lub wartość najczęściej występująca w próbie. Dla zmiennej losowej o rozkładzie ciągłym jest to argument, dla którego funkcja gęstości prawdopodobieństwa ma wartość największą.
    • +
    • mediana - ozn. \(Me\) - wartość cechy w szeregu uporządkowanym, powyżej i poniżej której znajduje się jednakowa liczba obserwacji.
    • +
    • kwantyle rzędu \(p\) - wartość cechy w szeregu uporządkowanym, poniżej której znajduje się \(p \cdot 100\%\) liczby obserwacji, a powyżej której znajduje się \((1 - p) \cdot 100\%\) liczby obserwacji.
    • +
    +

    Natomiast miary rozrzutu dostarczają informacji jak bardzo zróżnicowane są obserwacje pod względem badanej cechy. Przykładami takich miar są:

    +
      +
    • wariancja - stopień rozrzutu badanej cechy wokół wartości oczekiwanej. Im większa wariancja, tym rozrzut zmiennej jest większy. Nieobciążony estymator wariancji wyraża się wzorem: \(s^2 = \frac{1}{n}\sum\limits_{i=1}^n\left(x_i - \overline{x}\right)^2\)
    • +
    • odchylenie standardowe - mówi nam o przeciętnym odchyleniu wartości zmiennej losowej od jej wartości oczekiwanej. Im odchylenie standardowe jest większe, tym większe zróżnicowanie wartości badanej cechy. Odchylenie standardowe z próby obliczamy jako pierwiastek z wariancji z próby, tzn. \(s = \sqrt{s^2}\).
    • +
    • rozstęp międzykwartylowy - różnica między trzecim a pierwszym kwartylem. Ponieważ pomiędzy tymi kwartylami znajduje się z definicji 50% wszystkich obserwacji (położonych centralnie w rozkładzie), dlatego im większa szerokość tego rozstępu, tym większe zróżnicowanie cechy.
    • +
    +

    Wyróżniamy także miary asymetrii. Miary asymetrii mówią nam, czy większa część populacji klasuje się powyżej, czy poniżej przeciętnego poziomu badanej cechy. Asymetrię rozkładu można zbadać porównując średnią, dominantę i medianę.

    +
      +
    • W przypadku rozkładu symetrycznego wszystkie te parametry są równe.
    • +
    • Jeśli zachodzi nierówność \(Mo < Me < \mathbb{E} X\), to mamy do czynienia z prawostronną asymetrycznością rozkładu. Tzn. dużo małych wartości i bardzo mało dużych.
    • +
    • Jeśli zachodzi nierówność \(\mathbb{E} X < Me < Mo\), to mamy do czynienia z lewostronną asymetrycznością rozkładu. Tzn. mało małych i bardzo dużo dużych.
    • +
    +
    +
    +

    4.4 R - podsumowanie kolumn

    +

    Podstawowymi funkcjami, które pomagają nam zapoznać się z danymi są funkcje:

    +
      +
    • \(\texttt{head}\) - zwraca pierwszą część wektora, macierzy, tabeli lub ramki danych. Domyślnie 6 pierwszych elementów.
    • +
    • \(\texttt{nrow}\) - zwraca liczbę wierszy macierzy, tabeli lub ramki danych.
    • +
    • \(\texttt{ncol}\) - zwraca liczbę kolumn macierzy, tabeli lub ramki danych.
    • +
    +

    Natomiast podstawowymi funkcjami, które podsumowują kolejne kolumny są funkcje:

    +
      +
    • \(\texttt{str}\) - zwraca strukturę danego obiektu. Wyświetla np. klasę obiektu, liczbę wierszy i kolumn, a także nazwę danej kolumny, typ wartości w niej zawartych, jak i kilka początkowych wartości.

    • +
    • \(\texttt{summary}\) - zwraca podsumowanie każdej kolumny. Dla zmiennych ciagłych wyznacza wartości tj.:

      +
        +
      • wartość najmniejsza i największa
      • +
      • średnia i mediana
      • +
      • 1 (0.25) i 3 (0.75) kwartyl
      • +
      • liczba wartości brakujących (NA)
      • +
      +

      Natomiast w przypadku zmiennych dyskretnych wyznacza liczbę obserwacji, które przyjmują daną wartość zmiennej.

    • +
    • \(\texttt{glimpse}\) - funkcja z pakietu \(\texttt{tidyverse}\) podobna do \(\texttt{str}\), ale stara się pokazać jak najwięcej danych. Wyświetla np. liczbę wierszy i kolumn, a także nazwę danej kolumny, typ wartości w niej zawartych oraz jak najwięcej wartości z tej kolumny.

    • +
    + +
    +
    +
    + +
    +
    +
    + + +
    +
    + + + + + + + + + + + + + + + diff --git a/docs/img/app.R b/docs/img/app.R new file mode 100644 index 0000000..0db40a0 --- /dev/null +++ b/docs/img/app.R @@ -0,0 +1,72 @@ +library(shiny) +library(ggplot2) + +ui <- fluidPage( + titlePanel("Przykładowe ciągłe rozkłady prawdopodobieństwa"), + tabsetPanel( + #generujemy panel dla rozkładu normalnego + tabPanel(title = "Rozkład normalny", + tabsetPanel( + tabPanel( + title = "Wykres", + numericInput(inputId = "normal_n", + label = "Podaj wielkość próby", + value = 1000), + plotOutput("normal_plot") + ), + tabPanel( + title = "Dane", + tableOutput("normal_data") + ) + ) + ), + #generujemy panel dla rozkładu wykładniczego + tabPanel(title = "Rozkład wykładniczy", + tabsetPanel( + tabPanel( + title = "wykres", + numericInput(inputId = "exp_n", + label = "Podaj wielkość próby", + value = 1000), + plotOutput("exp_plot") + ), + tabPanel( + title = "Dane", + tableOutput("exp_data") + ) + ) + ) + ) +) + +server <- function(input, output, session) { + #generujemy dane + normal_data <- reactive({ + set.seed(17) + data.frame(id = 1:input[["normal_n"]], + sample = rnorm(input[["normal_n"]])) + }) + exp_data <- reactive({ + set.seed(17) + data.frame(id = 1:input[["exp_n"]], + sample = rnorm(input[["exp_n"]])) + }) + #generujemy tabele + output[["normal_data"]] <- renderTable({ + normal_data() + }) + output[["exp_data"]] <- renderTable({ + exp_data() + }) + #generuemy wykresy + output[["normal_plot"]] <- renderPlot({ + ggplot(normal_data(), aes(x = sample)) + + geom_density() + }) + output[["exp_plot"]] <- renderPlot({ + ggplot(exp_data(), aes(x = sample)) + + geom_density() + + xlim(0, 5) + }) +} +shinyApp(ui, server) diff --git a/docs/img/app_screen1.PNG b/docs/img/app_screen1.PNG new file mode 100644 index 0000000..acc50a3 Binary files /dev/null and b/docs/img/app_screen1.PNG differ diff --git a/docs/img/csv_example.gif b/docs/img/csv_example.gif new file mode 100644 index 0000000..f53273e Binary files /dev/null and b/docs/img/csv_example.gif differ diff --git a/docs/img/excel_example.png b/docs/img/excel_example.png new file mode 100644 index 0000000..a889552 Binary files /dev/null and b/docs/img/excel_example.png differ diff --git a/docs/img/json_example.jpeg b/docs/img/json_example.jpeg new file mode 100644 index 0000000..5bcb72e Binary files /dev/null and b/docs/img/json_example.jpeg differ diff --git a/docs/img/libreoffice_example.png b/docs/img/libreoffice_example.png new file mode 100644 index 0000000..0fef933 Binary files /dev/null and b/docs/img/libreoffice_example.png differ diff --git a/docs/img/module_app.R b/docs/img/module_app.R new file mode 100644 index 0000000..b67f5d8 --- /dev/null +++ b/docs/img/module_app.R @@ -0,0 +1,64 @@ +library(shiny) +library(ggplot2) + +module_UI <- function(id) { + ns <- NS(id) + tagList( + tabsetPanel( + tabPanel( + title = "Wykres", + numericInput(inputId = ns("n"), + label = "Podaj wielkość próby", + value = 1000), + plotOutput(ns("plot")) + ), + tabPanel(title = "Dane", + tableOutput(outputId = ns("data")) + ) + ) + ) +} + +module_SERVER <- function(id) { + moduleServer(id, function(input, output, session) { + #generujemy dane + data <- reactive({ + set.seed(17) + data.frame(id = 1:input[["n"]], + sample = rnorm(input[["n"]])) + }) + #generujemy wykres + output[["plot"]] <- renderPlot({ + ggplot(data(), aes(x = sample)) + + geom_density() + }) + #generujemy tabelę + output[["data"]] <- renderTable({ + data() + }) + }) +} + + +ui <- fluidPage( + titlePanel("Przykładowe ciągłe rozkłady prawdopodobieństwa"), + tabsetPanel( + #generujemy panel dla rozkładu normalnego + tabPanel(title = "Rozkład normalny", + module_UI("norm") + ), + #generujemy panel dla rozkładu wykładniczego + tabPanel(title = "Rozkład wykładniczy", + module_UI("exp") + ) + ) +) + +server <- function(input, output, session) { + module_SERVER("norm") + module_SERVER("exp") +} + + +shinyApp(ui, server) + diff --git a/docs/img/przyklad3.png b/docs/img/przyklad3.png new file mode 100644 index 0000000..923e45b Binary files /dev/null and b/docs/img/przyklad3.png differ diff --git a/docs/img/xml_example.gif b/docs/img/xml_example.gif new file mode 100644 index 0000000..405cf63 Binary files /dev/null and b/docs/img/xml_example.gif differ diff --git a/docs/index.Rmd b/docs/index.Rmd new file mode 100644 index 0000000..1677ef3 --- /dev/null +++ b/docs/index.Rmd @@ -0,0 +1,36 @@ +--- +title: "Notatki z laboratoriów ,,Programowanie i analiza danych w R''" +subtitle: "Instytut Matematyczny, Uniwersytet Wrocławski" +author: "Mateusz Staniak" +date: "`r Sys.Date()`" +site: bookdown::bookdown_site +documentclass: book +bibliography: [book.bib, packages.bib] +# url: your book url like https://bookdown.org/yihui/bookdown +# cover-image: path to the social sharing image like images/cover.jpg +description: | + Notatki z laboratoriów Programowanie i analiza danych w R. +link-citations: yes +github-repo: rstudio/bookdown-demo +--- + +# Wstęp + + +Autorzy poszczególnych rozdziałów: + + - _Podstawy języka R_: Michał Dylewicz, Marcela Kamchen, Anna Krasoń, Katarzyna Kulon, Arkadiusz Soból (z wyjątkiem podrozdziału _Funkcje_). + - _Wczytywanie danych_: Marta Kałużna, Sebastian Jachimek, Joanna Grunwald, Wojciech Wojnar. + - _Eksploracyjna analiza danych_: Magdalena Mazur, Agata Rogowska, Zuzanna Różak, Aleksandra Siepiela. [Także podrozdział _Funkcje_ pierwszego rozdziału.] + - _Podstawy kontroli wersji z Gitem_: Magdalena Mazur, Agata Rogowska, Zuzanna Różak, Aleksandra Siepiela. + - _Przetwarzanie danych tabelarycznych_: Weronika Domaszewska, Ewelina Grzmocińska, Gracjan Hrynczyszyn, Dominik Jaźwiecki, Michał Ociepa. + - _Czyste dane_: Kacper Ambroży, Dominika Szewc, Radosław Szudra, Helena Wołoch. + - _Wizualizacja danych z pakietem ggplot2_: Katarzyna Frankiewicz, Maciej Grabias, Jakub Michałowski + - _Czysty i wydajny kod w R_: Paulina Bannert, Natalia Bercz, Piotr Mrozik, Dariusz Sudół, Monika Wyźnikiewicz + - _Interaktywna wizualizacja danych z pakietem shiny: interfejs użytkownika_: Stanisław Banaszek, Mateusz Drobina, Dominik Mika, Adrian Płoszczyca, Jakub Sobkowiak + - _Interaktywna wizualizacja danych z pakietem shiny: strona serwerowa_: Wojciech Leszkowicz, Małgorzata Stawińska, Tomasz Szmyd, Maciej Tadej. + +Dodatkowe rozdziały: + - _Podstawy kontroli wersji przy pomocy Gita_: Magdalena Mazur, Agata Rogowska, Zuzanna Różak, Aleksandra Siepiela. + - _Programowanie obiektowe w R: klasy S3_: Agata Cieślik. + - _Moduły w aplikacjach shiny_: Krystyna Grzesiak. diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..63fadfb --- /dev/null +++ b/docs/index.html @@ -0,0 +1,420 @@ + + + + + + + Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + +
    + +
    +
    + + +
    +
    + +
    + +
    +

    Chapter 1 Wstęp

    +

    Autorzy poszczególnych rozdziałów:

    +
      +
    • Podstawy języka R: Michał Dylewicz, Marcela Kamchen, Anna Krasoń, Katarzyna Kulon, Arkadiusz Soból (z wyjątkiem podrozdziału Funkcje).
    • +
    • Wczytywanie danych: Marta Kałużna, Sebastian Jachimek, Joanna Grunwald, Wojciech Wojnar.
    • +
    • Eksploracyjna analiza danych: Magdalena Mazur, Agata Rogowska, Zuzanna Różak, Aleksandra Siepiela. [Także podrozdział Funkcje pierwszego rozdziału.]
    • +
    • Podstawy kontroli wersji z Gitem: Magdalena Mazur, Agata Rogowska, Zuzanna Różak, Aleksandra Siepiela.
    • +
    • Przetwarzanie danych tabelarycznych: Weronika Domaszewska, Ewelina Grzmocińska, Gracjan Hrynczyszyn, Dominik Jaźwiecki, Michał Ociepa.
    • +
    • Czyste dane: Kacper Ambroży, Dominika Szewc, Radosław Szudra, Helena Wołoch.
    • +
    • Wizualizacja danych z pakietem ggplot2: Katarzyna Frankiewicz, Maciej Grabias, Jakub Michałowski
    • +
    • Czysty i wydajny kod w R: Paulina Bannert, Natalia Bercz, Piotr Mrozik, Dariusz Sudół, Monika Wyźnikiewicz
    • +
    • Interaktywna wizualizacja danych z pakietem shiny: interfejs użytkownika: Stanisław Banaszek, Mateusz Drobina, Dominik Mika, Adrian Płoszczyca, Jakub Sobkowiak
    • +
    • Interaktywna wizualizacja danych z pakietem shiny: strona serwerowa: Wojciech Leszkowicz, Małgorzata Stawińska, Tomasz Szmyd, Maciej Tadej.
    • +
    +

    Dodatkowe rozdziały: +- Podstawy kontroli wersji przy pomocy Gita: Magdalena Mazur, Agata Rogowska, Zuzanna Różak, Aleksandra Siepiela. +- Programowanie obiektowe w R: klasy S3: Agata Cieślik. +- Moduły w aplikacjach shiny: Krystyna Grzesiak.

    + +
    +
    + +
    +
    +
    + + +
    +
    + + + + + + + + + + + + + + + diff --git "a/docs/interaktywna-wizualizacja-danych-z-pakietem-shiny-interfejs-u\305\274ytkownika.html" "b/docs/interaktywna-wizualizacja-danych-z-pakietem-shiny-interfejs-u\305\274ytkownika.html" new file mode 100644 index 0000000..8e55869 --- /dev/null +++ "b/docs/interaktywna-wizualizacja-danych-z-pakietem-shiny-interfejs-u\305\274ytkownika.html" @@ -0,0 +1,571 @@ + + + + + + + Chapter 9 Interaktywna wizualizacja danych z pakietem shiny: interfejs użytkownika | Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + +
    + +
    +
    + + +
    +
    + +
    +
    +

    Chapter 9 Interaktywna wizualizacja danych z pakietem shiny: interfejs użytkownika

    +
    +

    9.1 Wstęp

    +

    Shiny jest pakietem R pozwalającym na tworzenie interaktywnych aplikacji webowych w łatwy i przystępny sposób. Aplikacja w shiny zbudowana jest z dwóch następujcych elementów:

    +
      +
    • ui - user interface, czyli obiekt, w którym zawarty jest wygląd aplikacji,

    • +
    • server - funkcja organizująca działanie aplikacji.

    • +
    +

    Do uruchomienia aplikacji służy funkcja shinyApp(ui, server). Stworzenie dobrej i czytelnej aplikacji może znacznie ułatwić analizowanie danych.

    +

    W tej notatce zajmiemy się omówieniem elementów oraz podstawowych schematów budowy UI.

    +
    library(shiny)
    +library(shinyWidgets)
    +library(shinydashboard)
    +
    +
    +

    9.2 Tworzenie UI

    +

    Do budowania podstawowego interfejsu w shiny będziemy korzystać z funkcji fluidPage, w której tworzymy cały UI. Wszystkie informacje o rodzajach wprowadznych danych, strukturze wyświetlanych danych oraz szeroko rozumianej estetyce aplikacji będą zawarte wewnątrz tej funkcji.

    +
    ui <- fluidPage(
    + # coś
    +)
    +
    +
    +

    9.3 Układ strony

    +

    Tym co jest bardzo ważne w UI jest oczywiście wygląd, a dokładniej mówiąc przejrzystość i czytelność, dlatego chcielibyśmy uporządkować wyświetlane elementy tak, aby umożliwić użytkownikowi intuicyjne korzystanie z aplikacji. Pakiet shiny oferuje wiele narzędzi pozwalających na zorganizowanie układu interfejsu zgodnie z naszymi oczekiwaniami.

    +

    Przydadzą nam się do tego następujące funkcje:

    +
      +
    • titlePanel - funkcja tworząca panel tytułowy, w której podajemy tytuł aplikacji,

    • +
    • sidebarLayout - funkcja organizująca wygląd strony jako mniejszy panel boczny po lewej stronie oraz większy panel po prawej stronie,

    • +
    • sidebarPanel - funkcja, którą możemy umieścić w poprzedniej funkcji, aby uporządkować panel, w którym będziemy np. wprowadzać dane,

    • +
    • mainPanel - funkcja, w której umieszczamy treści, które chcemy, aby znalazły się w panelu głównym,

    • +
    • tabsetPanel - funkcja umożliwiająca organizowanie paska zakładek. Aby utworzyć zakładki w jej ciele używamy funkcji tabPanel, w której umieszczamy dowolne treści, np. wykresy lub tabele.

    • +
    +

    Oprócz tego możemy bardziej modyfikować wygląd aplikacji dzięki funkcjom fluidRow i column pozwalającym na uporządkowanie obiektów odpowiednio w wierszach oraz kolumnach.

    +
    +
    +

    9.4 Elementy wejścia i wyjścia

    +

    Układ strony należy oczywiście podporządkować temu jaką funkcję ma pełnić aplikacja, a także temu jaki rodzaj interakcji ma mieć z nią docelowo użytkownik. Interakcje użytkownika z aplikacją można intuicyjnie podzielić na to co zostaje do aplikacji wprowadzone (input) oraz to co ostatecznie w związku z tym aplikacja zwraca (output). Każdy input i output jest w kodzie identyfikowany dzięki nadanej mu przez nas nazwie. Wewnątrz fluidPage możemy zawrzeć różne rodzaje inputów i outputów w zależności od rodzaju wprowadzanych/wyświetlanych danych.

    +
    +

    9.4.0.1 Przykładowe elementy wejścia

    +
      +
    • textInput - funkcja tworząca pole, w którym użytkownik może wprowadzić dowolny tekst,
    • +
    +
    ui <- fluidPage(
    +  # Okienko do wpisywania tekstu
    +  textInput("nazwa_inputu_1", "Tekst wyświetlany w aplikacji")
    +)
    +
      +
    • numericInput - funkcja tworząca pole, w którym użytkownik może wprowadzić wartość liczbową,
    • +
    +
    ui <- fluidPage(
    +  # Okienko do wpisywania liczb
    +  numericInput("nazwa_inputu_2", "Tekst wyświetlany w aplikacji",
    +              # Wartość domyślna
    +              value = 10)
    +)
    +
      +
    • selectInput - funkcja tworząca listę, z której użytkownik może dokonać wyboru - domyślnie parametr multiple umożliwia wybór jednej pozycji z listy,
    • +
    +
    ui <- fluidPage(
    +  # Możliwość wybrania z listy
    +   selectInput("nazwa_inputu_3", "Tekst wyświetlany w aplikacji", 
    +               # Lista możliwości do wyboru
    +               choices = c("Wybór_1", "Wybór_2"))
    +)
    +
      +
    • sliderInput - funkcja tworząca suwak umożliwiający użytkownikowi wybór zakresu interesujących go wartości,
    • +
    +
    ui <- fluidPage(
    +  # Suwak do wyboru wartości
    +  sliderInput("nazwa_inputu_4", "Tekst wyświetlany w aplikacji",
    +              # Wartość domyślna
    +              value = 1,
    +              # Wartość minimalna
    +              min = 0,
    +              # Wartość maksymalna
    +              max = 10)
    +)
    +
      +
    • dateRangeInput - funkcja tworząca pole wyboru zakresu interesujących dat.
    • +
    +
    ui <- fluidPage(
    +  # Pole wyboru zakresu dat
    +  dateRangeInput("nazwa_inputu_5", "Tekst wyświetlany w aplikacji",
    +                 # Data początkowa
    +                 start = "2001-01-01",
    +                 # Data końcowa
    +                 end   = "2010-12-31")
    +)
    +
    +
    +

    9.4.0.2 Przykładowe elementy wyjścia

    +

    Używanie funkcji wyświetlających outputy jest bardzo proste, ponieważ w UI decydujemy jedynie gdzie i jak wyswietlić output, który jest obiektem utworzonym wewnątrz funkcji server na podstawie wprowadzonego przez użytkownika inputu.

    +
      +
    • textOutput - funkcja wyświetlająca tekst,
    • +
    +
    ui <- fluidPage(
    +  # Wyświetla tekst, który stworzyliśmy w serwerze pod daną nazwą
    +  textOutput("nazwa_outputu_1")
    +)
    +
      +
    • tableOutput - podstawowa funkcja wyświetlająca tabelę,
    • +
    +
    ui <- fluidPage(
    +  # Wyświetla tabelę stworzoną w serwerze pod daną nazwą
    +  tableOutput("nazwa_outputu_2")
    +)
    +
      +
    • DTOutput - funkcja wyświetlająca interaktywną ramkę danych z użyciem pakietu data.table,
    • +
    +
    ui <- fluidPage(
    +  # Interaktywna ramka danych z użyciem data.table
    +  DT::DTOutput("nazwa_outputu_3")
    +)
    +
      +
    • plotOutput - funkcja wyświetlająca wykres.
    • +
    +
    ui <- fluidPage(
    +  # Wyświetla wykres stworzony w serwerze
    +  plotOutput("nazwa_outputu_4",
    +             # Szerokość wykresu
    +             width = "100%", 
    +             # Wysokość wykresu
    +             height = "400px")
    +)
    +
    +
    +
    +

    9.5 Przykład użycia

    +

    Oczywiście powyższe kody były jedynie fragmentami większej całości. Poniżej możemy zobaczyć przykładowy kod obrazujący strukturę budowy interfejsu. Rzeczą, o której należy pamiętać jest oddzielanie funkcji przecinkami.

    +
    ui <- fluidPage( 
    +  # Tytuł
    +  titlePanel("Tytuł"),
    +  
    +  # To co będzie wyświetlone z boku interfejsu
    +  sidebarLayout(
    +    
    +    # Panel boczny
    +    sidebarPanel(
    +      
    +      # Pierwszy input - wybór
    +      selectInput("nazwa_inputu_1", "Tekst wyświetlany w aplikacji", 
    +                  choices = c("Wybór_1", "Wybór_2")),
    +      # Drugi input - suwak
    +      sliderInput("nazwa_inputu_2", "Tekst wyświetlany w aplikacji",
    +                  value = 1, min = 0, max = 10) 
    +    ),
    +  
    +  # Główny panel
    +  mainPanel( 
    +    
    +    # Tworzymy zakładki
    +    tabsetPanel( 
    +      
    +      # Pierwsza zakładka - wykres
    +      tabPanel("Tytuł wykresu", plotOutput("nazwa_outputu_1")),
    +      # Druga zakładka - ramka danych
    +      tabPanel("Tytuł ramki", DT::DTOutput("nazwa_outputu_2"))
    +      )
    +    )
    +  )
    +)
    +

    Dodatkowo warto zdawać sobie sprawę, że po wprowadzeniu danych przez użytkownika outputy aktualizują się automatycznie, dlatego często przydatne jest programowanie reaktywne z funkcją observeEvent oraz użycie actionButton, który pozwala na wykonanie danego działania dopiero po kliknięciu odpowiedniego przycisku przez użytkownika.

    +
    +
    +

    9.6 Wygląd aplikacji

    +

    Ostatecznie chcielibyśmy, aby aplikacja wyglądała bardziej estetycznie. Możemy do tego użyć kilku narzędzi. Po pierwsze możemy zmienić motyw naszej aplikacji.

    +

    Z pomocą przychodzi nam funkcja shinythemes::themeSelector(), którą musimy umieścić w naszym UI. Wtedy w naszej aplikacji pojawia się pole z możliwością wyboru motywu.

    +

    Gdy już wybierzemy ulubiony motyw zamieniamy poprzednią funkcję w UI na theme = shinythemes::shinytheme('NASZ_MOTYW') i gotowe!

    +

    Poza tym Shiny umożliwia całkowitą customizację wyglądu aplikacji przy użyciu HTML, CSS oraz JavaScript.

    +

    Ostatnim narzędziem, o którym warto pamiętać, jest shinyWidgetsGallery(). Jest to bardzo użyteczna aplikacja stworzona w bibliotece shinyWidgets, dzięki której możemy między innymi zobaczyć w praktyce działanie różnego typu inputów oraz kod umożliwiający użycie ich w aplikacji.

    +
    +

    9.6.1 Uwaga

    +

    W tej notatce omówiliśmy podstawowe elementy pozwalające na zbudowanie interfejsu w shiny ale chcielibyśmy też dodać, że w poszukiwaniu bardziej zaawansowanych rozwiązań warto odwiedzić stronę https://shiny.rstudio.com/, gdzie można znaleźć dokumentację pakietu shiny, wiele przykładów oraz nieomówionych tu funkcji.

    + +
    +
    +
    +
    + +
    +
    +
    + + +
    +
    + + + + + + + + + + + + + + + diff --git a/docs/interaktywna-wizualizacja-danych-z-pakietem-shiny-strona-serwerowa.html b/docs/interaktywna-wizualizacja-danych-z-pakietem-shiny-strona-serwerowa.html new file mode 100644 index 0000000..8e5947c --- /dev/null +++ b/docs/interaktywna-wizualizacja-danych-z-pakietem-shiny-strona-serwerowa.html @@ -0,0 +1,420 @@ + + + + + + + Chapter 10 Interaktywna wizualizacja danych z pakietem shiny: strona serwerowa | Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + +
    + +
    +
    + + +
    +
    + +
    +
    +

    Chapter 10 Interaktywna wizualizacja danych z pakietem shiny: strona serwerowa

    +
    +

    10.1 Wstęp

    +

    Shiny to biblioteka w R pozwalająca na budowanie interaktywnych aplikacji w prosty i szybki sposób. Aplikacja Shiny składa się z dwóch części, opisywanych w dwóch osobnych plikach: interfejs użytkownika (UI), czyli jak aplikacja będzie wyglądać u użytkownika oraz sposób przetwarzania danych (serwer). W tej pracy zajmiemy się stroną serwerową Shiny.

    +
    +
    +

    10.2 Serwer Shiny

    +

    Aplikacje Shiny zazwyczaj budujemy w sytuacjach, w których mamy dane, chcemy obliczyć pewne rzeczy i narysować odpowiednie wykresy. Użytkownik widzi efekt końcowy, czyli to jak zaprogramowaliśmy gdzie ma się wyświetlać wynik, natomiast w części serwerowej opisujemy jak ten wynik ma być obliczony. Jest to więc część zależna od pliku UI.

    +

    Musimy więc w kodzie serwera zamieścić obiekty opisane w UI. Zauważmy, że tworzymy kod serwera jako funkcję od dwóch parametrów: input, output. W środku serwera definiujemy zależności pomiędzy inputami i outputami.

    +

    Jedną z zalet Shiny jest interaktywność. Dzięki temu użytkownik może na bieżąco zmieniać parametry i generować nowe wykresy. Jednak generowanie kodu na nowo przy każdej zmianie danych nie zawsze jest pożądane. Ważnym pojęciem przy pisaniu strony serwerowej jest reaktywność (żródło infografiki: Shiny Cheat Sheet).

    +
    + +

    reaktywnosc

    +
    +

    Jeśli zmienna jest reaktywna, to znaczy że jakakolwiek jej zmiana powoduje ponowne uruchomienie funkcji z nią powiązanych. Do budowania reaktywnych wyrażeń używamy funkcji reactive(). Taka zmienna jest liczona tylko raz i wyrażenia z nią związane używają tej wartości aż do momentu aktualizacji wybranego przez użytkownika. Z pojęciem reaktywności wiąże się kilka ważnych funkcji:

    +
      +
    • reactiveValues(...), które tworzy listą reaktywnych zmiennych,
    • +
    • isolate(expr) - zapobiega zależności od reaktywnych zmiennych,
    • +
    • render*() - funkcje tworzące obiekty do wyświetlenia, które zmieniają się wraz z reaktywnymi zmiennymi,
    • +
    • observeEvent(...) - gdy nie chcemy aby model od razu się zaktualizował przy zmianie danych, a przy jakiejś określonej akcji,
    • +
    • reactive() - tworzy reaktywne wyrażenia
    • +
    • eventReactive - tworzy reaktywne wyrażenia, które nie zależą od wszystkich reaktywnych zmiennych, a zależą jedynie od akcji wymienionych w pierwszym argumencie.
    • +
    + +
    +
    +
    + +
    +
    +
    + + +
    +
    + + + + + + + + + + + + + + + diff --git "a/docs/katarzyna-frankiewicz-maciej-grabias-jakub-micha\305\202owski.html" "b/docs/katarzyna-frankiewicz-maciej-grabias-jakub-micha\305\202owski.html" new file mode 100644 index 0000000..032dfa0 --- /dev/null +++ "b/docs/katarzyna-frankiewicz-maciej-grabias-jakub-micha\305\202owski.html" @@ -0,0 +1,553 @@ + + + + + + + Chapter 7 Katarzyna Frankiewicz, Maciej Grabias, Jakub Michałowski | Notatki z laboratoriów ,,Programowanie i analiza danych w R’’ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + +
    + +
    +
    + + +
    +
    + +
    +
    +

    Chapter 7 Katarzyna Frankiewicz, Maciej Grabias, Jakub Michałowski

    +
    +

    7.1 Wprowadzenie

    +

    Jednym z ważnych elementów przekazywania ciekawych informacji oraz ich analizy jest przedstawienie graficzne interesujących nas danych. W R istnieje kilka sposobów na wizualizację danych. Jednym z nich jest korzytanie z narzędzi oferowanych przez pakiet ggplot2. Bibiloteka ggplot2 oprócz zwykłych funkcji plotowania, implementuje także gramatykę grafiki, co pozwala na wykonanie prawie każdego rodzaju (statystycznej) wizualizacji danych.

    +
    +

    7.1.1 Gramatyka grafiki

    +

    Powyżej wspomnieliśmy o gramatyce grafiki. Dla dokładniejszego uporządkowania wiedzy przypomnijmy, że gramatyka grafiki daje nam możliwość zadawania odpowiednich parametórw dla wszystkich linii, słów, strzałek, itp., które połączone tworzą wykres. Dodatkowo możemy m.in. zmieniać układ współrzędnych, czy korygować położenie każdego obiektu znajdującego się na wykresie. Możliwości jakie oferuje nam gramatyka grafiki będą przedstawione dokładniej w dalszej części notatki.

    +
    +
    +
    +

    7.2 Podstawy tworzenia wykresów w ggplot2

    +

    Na początku, aby móc tworzyć wizualizacje, musimy załadować pakiet oraz bibilotekę ggplot2. Warto zwrócić uwagę, że ggplot2 posiada również szereg wbudowanych zestawów danych. Aby pokazać możliwości jakie oferuje nam ggplot, przeprowadzimy symulację danych mpg dostępnych w R.

    +
    library(ggplot2)
    +head(mpg)
    +
    ## # A tibble: 6 × 11
    +##   manufacturer model displ  year   cyl trans      drv     cty   hwy fl    class  
    +##   <chr>        <chr> <dbl> <int> <int> <chr>      <chr> <int> <int> <chr> <chr>  
    +## 1 audi         a4      1.8  1999     4 auto(l5)   f        18    29 p     compact
    +## 2 audi         a4      1.8  1999     4 manual(m5) f        21    29 p     compact
    +## 3 audi         a4      2    2008     4 manual(m6) f        20    31 p     compact
    +## 4 audi         a4      2    2008     4 auto(av)   f        21    30 p     compact
    +## 5 audi         a4      2.8  1999     6 auto(l5)   f        16    26 p     compact
    +## 6 audi         a4      2.8  1999     6 manual(m5) f        18    26 p     compact
    +

    Składnia wykresów w ggplot polega na warstwowym budowaniu wykresów.

    +
      +
    1. Zaczynamy od doboru danych, jakie chcielibyśmy zwizualizaować.
    2. +
    3. Określamy sposób mapowania zmiennych do aspektów wizualnych.
    4. +
    5. Definiujemy styl wykresu.
    6. +
    7. Dodajemy dodatkowe warstwy personalizujące wykres, tzn. dodajemy tytuł, etykiety, itp. (opcjonalnie)
    8. +
    +

    Uwaga!

    +

    Do dodawania nowych warstw do wykresu używamy symbolu “+” .

    +
    # Budujemy podstawę wykresu, określając z jakich danych będziemy korzytsać
    +ggplot(mpg)
    +

    +
    # Mapujemy dane ( na osiach pojawiły się odpowiednie podziałki)
    +ggplot(mpg , aes( x = displ, y = hwy))
    +

    +
    # Określamy styl wykresu, dzięki czemu dostemy wykres odpwiednich zależności
    +ggplot(mpg , aes( x = displ, y = hwy))+
    +  geom_point()
    +

    +
    # Personalizujemy wykres poprzez dodanie tytułu oraz określenie motywu wykresu
    +ggplot(mpg , aes( x = displ, y = hwy))+
    +  geom_point()+
    +  ggtitle("disp vs hwy")+
    +  theme_bw()
    +

    +
    +
    +

    7.3 Mapowanie

    +

    Mapowanie danych jest estetyką, która mówi ggplot’owi, jakie zmienne powinny znajdować się na osi x oraz y. Dodatkowo możemy wpływać na cechy wizualne danych, takie jak kolor ( color = ), kształt ( shape = ), czy przezroczystość ( alpha = ). Wszystkie estetyki dla podziałki wykresu są określone w wywołaniu funkcji aes().

    +

    Uwaga!

    +

    Każda warstwa geom może mieć swoją własną specyfikację aes. Możemy zdecydować, czy na wykresie geom_point punkty mają być zaznaczone jako koła, czy kwadraty.

    +
    # Punkty na wykresie będą kwadratami
    +ggplot(mpg, aes(x = displ, y = hwy)) +
    +  geom_point(shape = 0)+
    +  ggtitle("displ vs hwy")+
    +  theme( axis.title = element_text(size = 1))+
    +  theme_bw()
    +

    +
    # Punkty na wykresie to czerwone kropki
    +ggplot(mpg, aes(x = displ, y = hwy)) +
    +  geom_point(color = "red")+
    +  ggtitle("displ vs hwy")+
    +  theme_bw()
    +

    +
    +
    +

    7.4 Geometria wykresu

    +

    Za pomocą ggplot2 możemy stworzyć prawie każdy rodzaj wykresu. W tym celu musimy zadać typ wykresu jaki nas interesuje. Ggplot2 daje możliwość stworzenia wykresu:

    +
      +
    • punktowego ( geom_point)
    • +
    • liniowego ( geom_line)
    • +
    • słupkowego ( geom_bar)
    • +
    • skrzypcowego ( geom_violin)
    • +
    • histogramu ( geom_histogram)
    • +
    • boxplota ( geom_boxplot)
    • +
    +

    oraz wielu innych, które powstają m.in. przez zastosowanie funcji: geom_area, geom_density, geom_dotplot, geom_qq, geom_smooth.

    +

    Uwaga!

    +
      +
    • Wykresy słupkowe i histogramy grupują dane, a następnie prezentują liczbę elementów znajdujących się w poszczególnych grupach
    • +
    • Na wykresach liniowych model jest dopasowywany do danych, a nastęonie wykreślane są przewidywania wyznaczone przez model
    • +
    • Wykresy pudełkowe obliczają kompleksowe podsumowanie rozkładu wartości
    • +
    +

    Poniżej przedstawione są dwa przykładowe typy wykresów. Pierwszy narysowany przy użyciu funkcji geom_smooth, która służy do rysowania wygładzonych linii, np. dla prostych trendów. Drugi wykres powstał przy zastosowaniu funkcji geom_histogram. +

    +

    W pakiecie ggplot2 bardzo prosto możemy łączyć ze sobą różne geometrie na jednym wykresie. Wystarczy wstawić znak “+” pomiędzy odpowiednimi funkcjami.

    +
    ggplot(mpg , aes( x = displ, y = hwy))+
    +  geom_point()+
    +  geom_smooth()+
    +  ggtitle("Multiple geomteries")+
    +  theme_bw()
    +

    +
    +
    +

    7.5 Funkcje pomagające poprawić czytelność wykresu

    +
    +

    7.5.1 Systemy współrzędnych

    +

    Domyślnym systemem współrzędnych dla ggplot2 jest kartezjański układ współrzędnych. W zależności od danych na jakich działamy, może się okazać, że pokazanie danych w innym układzie współrzędnych, wpłynie na lepszy odbiór informacji z wykresu.

    +

    Funkcjami, które odpowiadają za przekształcenie układu współrzędnych są m.in.

    +
      +
    • coord_flip która zamienia osie x i y
    • +
    • coord_polar wykres jest pokazany we współrzędnych polarnych
    • +
    • coord_fixed nadal jesteśmy w kartezjańskim układzie współrzędnych, ale możemy zmienić proporcję między jednostkami na osi x i y
    • +
    +
    +
    +

    7.5.2 Dopasowanie położenia

    +

    Każda geometria w ggplot2 ma ustawione domyślne położenie różnych elementów na wykresie względem siebie. Różne opcje ustawienia położenia są dobrze widoczne na wykresach słupkowych. Zacznijmy od stworzenia zwykłego wykresu słupkowego, bez żadnych dodatkowych funkcji.

    +

    +

    Jeżeli teraz do mapowania dodamy opcję fill = dvr, to każdy prostokąt będzie reprezentował kombinację wartości class oraz dvr.

    +

    +

    Takie przedstawienie danych nie dla każdego może być czytelne, dlatego możemy skorzystać z opcji position, która przyjmuje m.in. argumenty: “dodge” i “fill”.

    +

    +
    +
    +

    7.5.3 Zarządzanie osiami współrzędnych

    +

    Jedną z możliwości jaką oferuje nam pakiet ggplot2 jest prosta zmiana skali na osiach wykresu. Podstawowymi funkcjami, które to umożliwiają są:

    +
      +
    • scale_x_log10 (zamiast x możemy podać także y) wtedy skala x-ów będzie zlogarytmowana
    • +
    • scale_x_reverse powoduje odwrotny kierunek na osi x
    • +
    • scale_x_sqrt() skala x-ów będzie spierwiastkowana
    • +
    • scale_fill_manual pozwala nam ręcznie wprowadzić oczekiwane przez nas wartości, m.in. możemy zmienić nazwy obiektów na skali, czy podać zakres wartości do uwzględnienia w mapowaniu
    • +
    • xlim(5,40) powoduje ograniczenie podziałki na osi x od 5 do 40 (analogicznie z ylim)
    • +
    +

    W ggplot2 z łatwością także dodamy etykiety tekstowe oraz adnotacje. Do wykresu możemy dodać tytuł oraz nazwy osi korzystając m.in. z funkcji labs().

    +
    ggplot(mpg, aes(x = displ, y = hwy, color = class)) +
    +  geom_point() +
    +  labs(title = "Fuel Efficiency by Engine Power",
    +       x = "Engine power",
    +       y = "Fuel Efficiency",
    +       color = "Car Type")
    +

    +
    +
    +

    7.5.4 Motywy

    +

    Theme to dobry sposób na dostosowanie odpowiedniego tytułu, etykiet, czcionek, tła, legendy, czy lini siatki na wykresie. Możemy skorzystać z jednego z dostępnych motywów, takich jak theme_bw(), czy theme_minimal(). Istnieje możliwość zastosowania wielu dostępnych opcji tak, aby odpowiednie elementy wykresu wyglądały tak, jak chcemy. Podstawowymi funkcjami, jakie warto znać są m.in. legend.position, dzięki której możemy ustalić pozycję legendy wykresu, axis.text, która umożliwia nam ustawienie czcionki na wykresie oraz ustalenie jej wielkości czy koloru. Przydatną funkcją pochodzącą z rodziny theme jest ‘theme(axis.text.x = element_text(angle = 90))’, która obraca nazwy znajdujące się na osi x, dzięki, czemu stają się one czytelniejsze.

    +
    +
    +
    +

    7.6 Panele

    +

    Ostatnim z podstawowych funkcji jakie oferuje pakiet ggplot2 jest facets.

    +

    Panele to sposoby grupowania wykresu danych w wiele różnych części ze względu na zadaną zmienną. Możemy korzystać z funkcji:

    +
      +
    • facet_wrap(), która ustawia panele w prostokątnym układzie
    • +
    • facet_grid(), która ustawia panele w kolumny lub w wiersze (zależnie jaką opcję wybierzemy)
    • +
    +
    ggplot(mpg, aes(x = displ, y = hwy)) +
    +  geom_point() +
    +  facet_grid(~ class)
    +

    +

    Uwaga!

    +

    Aby zadać względem, której zmiennej chcemy grupować, w funkcji ‘facet_’ po znaku “~”, podajemy nazwę tej zmiennej.

    +

    Kiedy korzystamy z funkcji tworzącej panele, automatycznie wszytskie wykresy będą pokazane w układzie współrzędnych dopasowanym do wszytkich okienek. Istnieje jednak możliwość dopasowania układu współrzędnych do każdego panelu osobno. W tym celu możemy wykorzystać funcję ‘scale = “free”’.

    + +
    +
    +
    + +
    +
    +
    + + +
    +
    + + + + + + + + + + + + + + + diff --git a/docs/libs/anchor-sections-1.1.0/anchor-sections-hash.css b/docs/libs/anchor-sections-1.1.0/anchor-sections-hash.css new file mode 100644 index 0000000..b563ec9 --- /dev/null +++ b/docs/libs/anchor-sections-1.1.0/anchor-sections-hash.css @@ -0,0 +1,2 @@ +/* Styles for section anchors */ +a.anchor-section::before {content: '#';font-size: 80%;} diff --git a/docs/libs/anchor-sections-1.1.0/anchor-sections-icon.css b/docs/libs/anchor-sections-1.1.0/anchor-sections-icon.css new file mode 100644 index 0000000..a24ab75 --- /dev/null +++ b/docs/libs/anchor-sections-1.1.0/anchor-sections-icon.css @@ -0,0 +1,8 @@ +/* Styles for section anchors content */ +/* From https://icons.getbootstrap.com/icons/link-45deg/ + Licence: https://github.com/twbs/icons/blob/main/LICENSE.md */ + +a.anchor-section:before { + display: inline-block; + content: url("data:image/svg+xml,"); +} diff --git a/docs/libs/anchor-sections-1.1.0/anchor-sections-symbol.css b/docs/libs/anchor-sections-1.1.0/anchor-sections-symbol.css new file mode 100644 index 0000000..952f956 --- /dev/null +++ b/docs/libs/anchor-sections-1.1.0/anchor-sections-symbol.css @@ -0,0 +1,6 @@ +/* Styles for section anchors content */ +/* Link symbol : https://codepoints.net/U+1F517 + with variation selector https://graphemica.com/FE0E */ +a.anchor-section::before{content: '\01F517\00FE0E';font-size: 20px;} +/* deactivate underline for link symbol */ +a.anchor-section:hover {text-decoration: none;} diff --git a/docs/libs/anchor-sections-1.1.0/anchor-sections.css b/docs/libs/anchor-sections-1.1.0/anchor-sections.css new file mode 100644 index 0000000..041905f --- /dev/null +++ b/docs/libs/anchor-sections-1.1.0/anchor-sections.css @@ -0,0 +1,4 @@ +/* Styles for section anchors */ +a.anchor-section {margin-left: 10px; visibility: hidden; color: inherit;} +.hasAnchor:hover a.anchor-section {visibility: visible;} +ul > li > .anchor-section {display: none;} diff --git a/docs/libs/anchor-sections-1.1.0/anchor-sections.js b/docs/libs/anchor-sections-1.1.0/anchor-sections.js new file mode 100644 index 0000000..fee005d --- /dev/null +++ b/docs/libs/anchor-sections-1.1.0/anchor-sections.js @@ -0,0 +1,11 @@ +document.addEventListener('DOMContentLoaded', function () { + // If section divs is used, we need to put the anchor in the child header + const headers = document.querySelectorAll("div.hasAnchor.section[class*='level'] > :first-child") + + headers.forEach(function (x) { + // Add to the header node + if (!x.classList.contains('hasAnchor')) x.classList.add('hasAnchor') + // Remove from the section or div created by Pandoc + x.parentElement.classList.remove('hasAnchor') + }) +}) diff --git a/docs/libs/gitbook-2.6.7/css/fontawesome/fontawesome-webfont.ttf b/docs/libs/gitbook-2.6.7/css/fontawesome/fontawesome-webfont.ttf new file mode 100644 index 0000000..35acda2 Binary files /dev/null and b/docs/libs/gitbook-2.6.7/css/fontawesome/fontawesome-webfont.ttf differ diff --git a/docs/libs/gitbook-2.6.7/css/plugin-bookdown.css b/docs/libs/gitbook-2.6.7/css/plugin-bookdown.css new file mode 100644 index 0000000..ab7c20e --- /dev/null +++ b/docs/libs/gitbook-2.6.7/css/plugin-bookdown.css @@ -0,0 +1,105 @@ +.book .book-header h1 { + padding-left: 20px; + padding-right: 20px; +} +.book .book-header.fixed { + position: fixed; + right: 0; + top: 0; + left: 0; + border-bottom: 1px solid rgba(0,0,0,.07); +} +span.search-highlight { + background-color: #ffff88; +} +@media (min-width: 600px) { + .book.with-summary .book-header.fixed { + left: 300px; + } +} +@media (max-width: 1240px) { + .book .book-body.fixed { + top: 50px; + } + .book .book-body.fixed .body-inner { + top: auto; + } +} +@media (max-width: 600px) { + .book.with-summary .book-header.fixed { + left: calc(100% - 60px); + min-width: 300px; + } + .book.with-summary .book-body { + transform: none; + left: calc(100% - 60px); + min-width: 300px; + } + .book .book-body.fixed { + top: 0; + } +} + +.book .book-body.fixed .body-inner { + top: 50px; +} +.book .book-body .page-wrapper .page-inner section.normal sub, .book .book-body .page-wrapper .page-inner section.normal sup { + font-size: 85%; +} + +@media print { + .book .book-summary, .book .book-body .book-header, .fa { + display: none !important; + } + .book .book-body.fixed { + left: 0px; + } + .book .book-body,.book .book-body .body-inner, .book.with-summary { + overflow: visible !important; + } +} +.kable_wrapper { + border-spacing: 20px 0; + border-collapse: separate; + border: none; + margin: auto; +} +.kable_wrapper > tbody > tr > td { + vertical-align: top; +} +.book .book-body .page-wrapper .page-inner section.normal table tr.header { + border-top-width: 2px; +} +.book .book-body .page-wrapper .page-inner section.normal table tr:last-child td { + border-bottom-width: 2px; +} +.book .book-body .page-wrapper .page-inner section.normal table td, .book .book-body .page-wrapper .page-inner section.normal table th { + border-left: none; + border-right: none; +} +.book .book-body .page-wrapper .page-inner section.normal table.kable_wrapper > tbody > tr, .book .book-body .page-wrapper .page-inner section.normal table.kable_wrapper > tbody > tr > td { + border-top: none; +} +.book .book-body .page-wrapper .page-inner section.normal table.kable_wrapper > tbody > tr:last-child > td { + border-bottom: none; +} + +div.theorem, div.lemma, div.corollary, div.proposition, div.conjecture { + font-style: italic; +} +span.theorem, span.lemma, span.corollary, span.proposition, span.conjecture { + font-style: normal; +} +div.proof>*:last-child:after { + content: "\25a2"; + float: right; +} +.header-section-number { + padding-right: .5em; +} +#header .multi-author { + margin: 0.5em 0 -0.5em 0; +} +#header .date { + margin-top: 1.5em; +} diff --git a/docs/libs/gitbook-2.6.7/css/plugin-clipboard.css b/docs/libs/gitbook-2.6.7/css/plugin-clipboard.css new file mode 100644 index 0000000..6844a70 --- /dev/null +++ b/docs/libs/gitbook-2.6.7/css/plugin-clipboard.css @@ -0,0 +1,18 @@ +div.sourceCode { + position: relative; +} + +.copy-to-clipboard-button { + position: absolute; + right: 0; + top: 0; + visibility: hidden; +} + +.copy-to-clipboard-button:focus { + outline: 0; +} + +div.sourceCode:hover > .copy-to-clipboard-button { + visibility: visible; +} diff --git a/docs/libs/gitbook-2.6.7/css/plugin-fontsettings.css b/docs/libs/gitbook-2.6.7/css/plugin-fontsettings.css new file mode 100644 index 0000000..3fa6f35 --- /dev/null +++ b/docs/libs/gitbook-2.6.7/css/plugin-fontsettings.css @@ -0,0 +1,303 @@ +/* + * Theme 1 + */ +.color-theme-1 .dropdown-menu { + background-color: #111111; + border-color: #7e888b; +} +.color-theme-1 .dropdown-menu .dropdown-caret .caret-inner { + border-bottom: 9px solid #111111; +} +.color-theme-1 .dropdown-menu .buttons { + border-color: #7e888b; +} +.color-theme-1 .dropdown-menu .button { + color: #afa790; +} +.color-theme-1 .dropdown-menu .button:hover { + color: #73553c; +} +/* + * Theme 2 + */ +.color-theme-2 .dropdown-menu { + background-color: #2d3143; + border-color: #272a3a; +} +.color-theme-2 .dropdown-menu .dropdown-caret .caret-inner { + border-bottom: 9px solid #2d3143; +} +.color-theme-2 .dropdown-menu .buttons { + border-color: #272a3a; +} +.color-theme-2 .dropdown-menu .button { + color: #62677f; +} +.color-theme-2 .dropdown-menu .button:hover { + color: #f4f4f5; +} +.book .book-header .font-settings .font-enlarge { + line-height: 30px; + font-size: 1.4em; +} +.book .book-header .font-settings .font-reduce { + line-height: 30px; + font-size: 1em; +} + +/* sidebar transition background */ +div.book.color-theme-1 { + background: #f3eacb; +} +.book.color-theme-1 .book-body { + color: #704214; + background: #f3eacb; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section { + background: #f3eacb; +} + +/* sidebar transition background */ +div.book.color-theme-2 { + background: #1c1f2b; +} + +.book.color-theme-2 .book-body { + color: #bdcadb; + background: #1c1f2b; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section { + background: #1c1f2b; +} +.book.font-size-0 .book-body .page-inner section { + font-size: 1.2rem; +} +.book.font-size-1 .book-body .page-inner section { + font-size: 1.4rem; +} +.book.font-size-2 .book-body .page-inner section { + font-size: 1.6rem; +} +.book.font-size-3 .book-body .page-inner section { + font-size: 2.2rem; +} +.book.font-size-4 .book-body .page-inner section { + font-size: 4rem; +} +.book.font-family-0 { + font-family: Georgia, serif; +} +.book.font-family-1 { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal { + color: #704214; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal a { + color: inherit; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h1, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h2, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h3, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h4, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h5, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h6 { + color: inherit; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h1, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h2 { + border-color: inherit; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h6 { + color: inherit; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal hr { + background-color: inherit; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal blockquote { + border-color: #c4b29f; + opacity: 0.9; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code { + background: #fdf6e3; + color: #657b83; + border-color: #f8df9c; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal .highlight { + background-color: inherit; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table th, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table td { + border-color: #f5d06c; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table tr { + color: inherit; + background-color: #fdf6e3; + border-color: #444444; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n) { + background-color: #fbeecb; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal { + color: #bdcadb; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal a { + color: #3eb1d0; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h1, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h2, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h3, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h4, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h5, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h6 { + color: #fffffa; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h1, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h2 { + border-color: #373b4e; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h6 { + color: #373b4e; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal hr { + background-color: #373b4e; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal blockquote { + border-color: #373b4e; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code { + color: #9dbed8; + background: #2d3143; + border-color: #2d3143; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal .highlight { + background-color: #282a39; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table th, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table td { + border-color: #3b3f54; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table tr { + color: #b6c2d2; + background-color: #2d3143; + border-color: #3b3f54; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n) { + background-color: #35394b; +} +.book.color-theme-1 .book-header { + color: #afa790; + background: transparent; +} +.book.color-theme-1 .book-header .btn { + color: #afa790; +} +.book.color-theme-1 .book-header .btn:hover { + color: #73553c; + background: none; +} +.book.color-theme-1 .book-header h1 { + color: #704214; +} +.book.color-theme-2 .book-header { + color: #7e888b; + background: transparent; +} +.book.color-theme-2 .book-header .btn { + color: #3b3f54; +} +.book.color-theme-2 .book-header .btn:hover { + color: #fffff5; + background: none; +} +.book.color-theme-2 .book-header h1 { + color: #bdcadb; +} +.book.color-theme-1 .book-body .navigation { + color: #afa790; +} +.book.color-theme-1 .book-body .navigation:hover { + color: #73553c; +} +.book.color-theme-2 .book-body .navigation { + color: #383f52; +} +.book.color-theme-2 .book-body .navigation:hover { + color: #fffff5; +} +/* + * Theme 1 + */ +.book.color-theme-1 .book-summary { + color: #afa790; + background: #111111; + border-right: 1px solid rgba(0, 0, 0, 0.07); +} +.book.color-theme-1 .book-summary .book-search { + background: transparent; +} +.book.color-theme-1 .book-summary .book-search input, +.book.color-theme-1 .book-summary .book-search input:focus { + border: 1px solid transparent; +} +.book.color-theme-1 .book-summary ul.summary li.divider { + background: #7e888b; + box-shadow: none; +} +.book.color-theme-1 .book-summary ul.summary li i.fa-check { + color: #33cc33; +} +.book.color-theme-1 .book-summary ul.summary li.done > a { + color: #877f6a; +} +.book.color-theme-1 .book-summary ul.summary li a, +.book.color-theme-1 .book-summary ul.summary li span { + color: #877f6a; + background: transparent; + font-weight: normal; +} +.book.color-theme-1 .book-summary ul.summary li.active > a, +.book.color-theme-1 .book-summary ul.summary li a:hover { + color: #704214; + background: transparent; + font-weight: normal; +} +/* + * Theme 2 + */ +.book.color-theme-2 .book-summary { + color: #bcc1d2; + background: #2d3143; + border-right: none; +} +.book.color-theme-2 .book-summary .book-search { + background: transparent; +} +.book.color-theme-2 .book-summary .book-search input, +.book.color-theme-2 .book-summary .book-search input:focus { + border: 1px solid transparent; +} +.book.color-theme-2 .book-summary ul.summary li.divider { + background: #272a3a; + box-shadow: none; +} +.book.color-theme-2 .book-summary ul.summary li i.fa-check { + color: #33cc33; +} +.book.color-theme-2 .book-summary ul.summary li.done > a { + color: #62687f; +} +.book.color-theme-2 .book-summary ul.summary li a, +.book.color-theme-2 .book-summary ul.summary li span { + color: #c1c6d7; + background: transparent; + font-weight: 600; +} +.book.color-theme-2 .book-summary ul.summary li.active > a, +.book.color-theme-2 .book-summary ul.summary li a:hover { + color: #f4f4f5; + background: #252737; + font-weight: 600; +} diff --git a/docs/libs/gitbook-2.6.7/css/plugin-highlight.css b/docs/libs/gitbook-2.6.7/css/plugin-highlight.css new file mode 100644 index 0000000..2aabd3d --- /dev/null +++ b/docs/libs/gitbook-2.6.7/css/plugin-highlight.css @@ -0,0 +1,426 @@ +.book .book-body .page-wrapper .page-inner section.normal pre, +.book .book-body .page-wrapper .page-inner section.normal code { + /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ + /* Tomorrow Comment */ + /* Tomorrow Red */ + /* Tomorrow Orange */ + /* Tomorrow Yellow */ + /* Tomorrow Green */ + /* Tomorrow Aqua */ + /* Tomorrow Blue */ + /* Tomorrow Purple */ +} +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-comment, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-comment, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-title { + color: #8e908c; +} +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-variable, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-variable, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-attribute, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-attribute, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-tag, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-tag, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-regexp, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-regexp, +.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-constant, +.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-constant, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-tag .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-tag .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-pi, +.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-pi, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-doctype, +.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-doctype, +.book .book-body .page-wrapper .page-inner section.normal pre .html .hljs-doctype, +.book .book-body .page-wrapper .page-inner section.normal code .html .hljs-doctype, +.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-id, +.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-id, +.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-class, +.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-class, +.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-pseudo, +.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-pseudo { + color: #c82829; +} +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-number, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-number, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-preprocessor, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-preprocessor, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-pragma, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-pragma, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-built_in, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-built_in, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-literal, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-literal, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-params, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-params, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-constant, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-constant { + color: #f5871f; +} +.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-class .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-class .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-rules .hljs-attribute, +.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-rules .hljs-attribute { + color: #eab700; +} +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-string, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-string, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-value, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-value, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-inheritance, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-inheritance, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-header, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-header, +.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-symbol, +.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-symbol, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-cdata, +.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-cdata { + color: #718c00; +} +.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-hexcolor, +.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-hexcolor { + color: #3e999f; +} +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-function, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-function, +.book .book-body .page-wrapper .page-inner section.normal pre .python .hljs-decorator, +.book .book-body .page-wrapper .page-inner section.normal code .python .hljs-decorator, +.book .book-body .page-wrapper .page-inner section.normal pre .python .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal code .python .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-function .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-function .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-title .hljs-keyword, +.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-title .hljs-keyword, +.book .book-body .page-wrapper .page-inner section.normal pre .perl .hljs-sub, +.book .book-body .page-wrapper .page-inner section.normal code .perl .hljs-sub, +.book .book-body .page-wrapper .page-inner section.normal pre .javascript .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal code .javascript .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal pre .coffeescript .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal code .coffeescript .hljs-title { + color: #4271ae; +} +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-keyword, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-keyword, +.book .book-body .page-wrapper .page-inner section.normal pre .javascript .hljs-function, +.book .book-body .page-wrapper .page-inner section.normal code .javascript .hljs-function { + color: #8959a8; +} +.book .book-body .page-wrapper .page-inner section.normal pre .hljs, +.book .book-body .page-wrapper .page-inner section.normal code .hljs { + display: block; + background: white; + color: #4d4d4c; + padding: 0.5em; +} +.book .book-body .page-wrapper .page-inner section.normal pre .coffeescript .javascript, +.book .book-body .page-wrapper .page-inner section.normal code .coffeescript .javascript, +.book .book-body .page-wrapper .page-inner section.normal pre .javascript .xml, +.book .book-body .page-wrapper .page-inner section.normal code .javascript .xml, +.book .book-body .page-wrapper .page-inner section.normal pre .tex .hljs-formula, +.book .book-body .page-wrapper .page-inner section.normal code .tex .hljs-formula, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .javascript, +.book .book-body .page-wrapper .page-inner section.normal code .xml .javascript, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .vbscript, +.book .book-body .page-wrapper .page-inner section.normal code .xml .vbscript, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .css, +.book .book-body .page-wrapper .page-inner section.normal code .xml .css, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-cdata, +.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-cdata { + opacity: 0.5; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code { + /* + +Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull + +*/ + /* Solarized Green */ + /* Solarized Cyan */ + /* Solarized Blue */ + /* Solarized Yellow */ + /* Solarized Orange */ + /* Solarized Red */ + /* Solarized Violet */ +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs { + display: block; + padding: 0.5em; + background: #fdf6e3; + color: #657b83; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-comment, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-comment, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-template_comment, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-template_comment, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .diff .hljs-header, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .diff .hljs-header, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-doctype, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-doctype, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-pi, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-pi, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .lisp .hljs-string, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .lisp .hljs-string, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-javadoc, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-javadoc { + color: #93a1a1; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-keyword, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-keyword, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-winutils, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-winutils, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .method, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .method, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-addition, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-addition, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-tag, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .css .hljs-tag, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-request, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-request, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-status, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-status, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .nginx .hljs-title, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .nginx .hljs-title { + color: #859900; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-number, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-number, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-command, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-command, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-string, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-string, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-tag .hljs-value, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-tag .hljs-value, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-rules .hljs-value, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-rules .hljs-value, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-phpdoc, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-phpdoc, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .tex .hljs-formula, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .tex .hljs-formula, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-regexp, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-regexp, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-hexcolor, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-hexcolor, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-link_url, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-link_url { + color: #2aa198; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-title, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-title, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-localvars, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-localvars, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-chunk, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-chunk, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-decorator, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-decorator, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-built_in, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-built_in, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-identifier, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-identifier, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .vhdl .hljs-literal, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .vhdl .hljs-literal, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-id, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-id, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-function, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .css .hljs-function { + color: #268bd2; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-attribute, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-attribute, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-variable, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-variable, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .lisp .hljs-body, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .lisp .hljs-body, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .smalltalk .hljs-number, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .smalltalk .hljs-number, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-constant, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-constant, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-class .hljs-title, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-class .hljs-title, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-parent, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-parent, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .haskell .hljs-type, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .haskell .hljs-type, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-link_reference, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-link_reference { + color: #b58900; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-preprocessor, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-preprocessor, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-preprocessor .hljs-keyword, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-preprocessor .hljs-keyword, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-pragma, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-pragma, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-shebang, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-shebang, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-symbol, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-symbol, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-symbol .hljs-string, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-symbol .hljs-string, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .diff .hljs-change, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .diff .hljs-change, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-special, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-special, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-attr_selector, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-attr_selector, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-subst, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-subst, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-cdata, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-cdata, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .clojure .hljs-title, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .clojure .hljs-title, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-pseudo, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .css .hljs-pseudo, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-header, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-header { + color: #cb4b16; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-deletion, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-deletion, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-important, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-important { + color: #dc322f; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-link_label, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-link_label { + color: #6c71c4; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .tex .hljs-formula, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .tex .hljs-formula { + background: #eee8d5; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code { + /* Tomorrow Night Bright Theme */ + /* Original theme - https://github.com/chriskempson/tomorrow-theme */ + /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ + /* Tomorrow Comment */ + /* Tomorrow Red */ + /* Tomorrow Orange */ + /* Tomorrow Yellow */ + /* Tomorrow Green */ + /* Tomorrow Aqua */ + /* Tomorrow Blue */ + /* Tomorrow Purple */ +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-comment, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-comment, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-title { + color: #969896; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-variable, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-variable, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-attribute, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-attribute, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-tag, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-tag, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-regexp, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-regexp, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-constant, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-constant, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-tag .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-tag .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-pi, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-pi, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-doctype, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-doctype, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .html .hljs-doctype, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .html .hljs-doctype, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-id, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-id, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-class, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-class, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-pseudo, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-pseudo { + color: #d54e53; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-number, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-number, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-preprocessor, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-preprocessor, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-pragma, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-pragma, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-built_in, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-built_in, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-literal, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-literal, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-params, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-params, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-constant, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-constant { + color: #e78c45; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-class .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-class .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-rules .hljs-attribute, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-rules .hljs-attribute { + color: #e7c547; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-string, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-string, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-value, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-value, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-inheritance, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-inheritance, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-header, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-header, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-symbol, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-symbol, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-cdata, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-cdata { + color: #b9ca4a; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-hexcolor, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-hexcolor { + color: #70c0b1; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-function, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-function, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .python .hljs-decorator, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .python .hljs-decorator, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .python .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .python .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-function .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-function .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-title .hljs-keyword, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-title .hljs-keyword, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .perl .hljs-sub, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .perl .hljs-sub, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .javascript .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .javascript .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .coffeescript .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .coffeescript .hljs-title { + color: #7aa6da; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-keyword, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-keyword, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .javascript .hljs-function, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .javascript .hljs-function { + color: #c397d8; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs { + display: block; + background: black; + color: #eaeaea; + padding: 0.5em; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .coffeescript .javascript, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .coffeescript .javascript, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .javascript .xml, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .javascript .xml, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .tex .hljs-formula, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .tex .hljs-formula, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .javascript, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .javascript, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .vbscript, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .vbscript, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .css, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .css, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-cdata, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-cdata { + opacity: 0.5; +} diff --git a/docs/libs/gitbook-2.6.7/css/plugin-search.css b/docs/libs/gitbook-2.6.7/css/plugin-search.css new file mode 100644 index 0000000..c85e557 --- /dev/null +++ b/docs/libs/gitbook-2.6.7/css/plugin-search.css @@ -0,0 +1,31 @@ +.book .book-summary .book-search { + padding: 6px; + background: transparent; + position: absolute; + top: -50px; + left: 0px; + right: 0px; + transition: top 0.5s ease; +} +.book .book-summary .book-search input, +.book .book-summary .book-search input:focus, +.book .book-summary .book-search input:hover { + width: 100%; + background: transparent; + border: 1px solid #ccc; + box-shadow: none; + outline: none; + line-height: 22px; + padding: 7px 4px; + color: inherit; + box-sizing: border-box; +} +.book.with-search .book-summary .book-search { + top: 0px; +} +.book.with-search .book-summary ul.summary { + top: 50px; +} +.with-search .summary li[data-level] a[href*=".html#"] { + display: none; +} diff --git a/docs/libs/gitbook-2.6.7/css/plugin-table.css b/docs/libs/gitbook-2.6.7/css/plugin-table.css new file mode 100644 index 0000000..7fba1b9 --- /dev/null +++ b/docs/libs/gitbook-2.6.7/css/plugin-table.css @@ -0,0 +1 @@ +.book .book-body .page-wrapper .page-inner section.normal table{display:table;width:100%;border-collapse:collapse;border-spacing:0;overflow:auto}.book .book-body .page-wrapper .page-inner section.normal table td,.book .book-body .page-wrapper .page-inner section.normal table th{padding:6px 13px;border:1px solid #ddd}.book .book-body .page-wrapper .page-inner section.normal table tr{background-color:#fff;border-top:1px solid #ccc}.book .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n){background-color:#f8f8f8}.book .book-body .page-wrapper .page-inner section.normal table th{font-weight:700} diff --git a/docs/libs/gitbook-2.6.7/css/style.css b/docs/libs/gitbook-2.6.7/css/style.css new file mode 100644 index 0000000..cba69b2 --- /dev/null +++ b/docs/libs/gitbook-2.6.7/css/style.css @@ -0,0 +1,13 @@ +/*! normalize.css v2.1.0 | MIT License | git.io/normalize */img,legend{border:0}*{-webkit-font-smoothing:antialiased}sub,sup{position:relative}.book .book-body .page-wrapper .page-inner section.normal hr:after,.book-langs-index .inner .languages:after,.buttons:after,.dropdown-menu .buttons:after{clear:both}body,html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}.hidden,[hidden]{display:none}audio:not([controls]){display:none;height:0}html{font-family:sans-serif}body,figure{margin:0}a:focus{outline:dotted thin}a:active,a:hover{outline:0}h1{font-size:2em;margin:.67em 0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:"\201C" "\201D" "\2018" "\2019"}small{font-size:80%}sub,sup{font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}svg:not(:root){overflow:hidden}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{padding:0}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}button,input{line-height:normal}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button{margin-right:10px;}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}/*! + * Preboot v2 + * + * Open sourced under MIT license by @mdo. + * Some variables and mixins from Bootstrap (Apache 2 license). + */.link-inherit,.link-inherit:focus,.link-inherit:hover{color:inherit}/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('./fontawesome/fontawesome-webfont.ttf?v=4.7.0') format('truetype');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} +.book .book-header,.book .book-summary{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}.book-langs-index{width:100%;height:100%;padding:40px 0;margin:0;overflow:auto}@media (max-width:600px){.book-langs-index{padding:0}}.book-langs-index .inner{max-width:600px;width:100%;margin:0 auto;padding:30px;background:#fff;border-radius:3px}.book-langs-index .inner h3{margin:0}.book-langs-index .inner .languages{list-style:none;padding:20px 30px;margin-top:20px;border-top:1px solid #eee}.book-langs-index .inner .languages:after,.book-langs-index .inner .languages:before{content:" ";display:table;line-height:0}.book-langs-index .inner .languages li{width:50%;float:left;padding:10px 5px;font-size:16px}@media (max-width:600px){.book-langs-index .inner .languages li{width:100%;max-width:100%}}.book .book-header{overflow:visible;height:50px;padding:0 8px;z-index:2;font-size:.85em;color:#7e888b;background:0 0}.book .book-header .btn{display:block;height:50px;padding:0 15px;border-bottom:none;color:#ccc;text-transform:uppercase;line-height:50px;-webkit-box-shadow:none!important;box-shadow:none!important;position:relative;font-size:14px}.book .book-header .btn:hover{position:relative;text-decoration:none;color:#444;background:0 0}.book .book-header h1{margin:0;font-size:20px;font-weight:200;text-align:center;line-height:50px;opacity:0;padding-left:200px;padding-right:200px;-webkit-transition:opacity .2s ease;-moz-transition:opacity .2s ease;-o-transition:opacity .2s ease;transition:opacity .2s ease;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.book .book-header h1 a,.book .book-header h1 a:hover{color:inherit;text-decoration:none}@media (max-width:1000px){.book .book-header h1{display:none}}.book .book-header h1 i{display:none}.book .book-header:hover h1{opacity:1}.book.is-loading .book-header h1 i{display:inline-block}.book.is-loading .book-header h1 a{display:none}.dropdown{position:relative}.dropdown-menu{position:absolute;top:100%;left:0;z-index:100;display:none;float:left;min-width:160px;padding:0;margin:2px 0 0;list-style:none;font-size:14px;background-color:#fafafa;border:1px solid rgba(0,0,0,.07);border-radius:1px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175);background-clip:padding-box}.dropdown-menu.open{display:block}.dropdown-menu.dropdown-left{left:auto;right:4%}.dropdown-menu.dropdown-left .dropdown-caret{right:14px;left:auto}.dropdown-menu .dropdown-caret{position:absolute;top:-8px;left:14px;width:18px;height:10px;float:left;overflow:hidden}.dropdown-menu .dropdown-caret .caret-inner,.dropdown-menu .dropdown-caret .caret-outer{display:inline-block;top:0;border-left:9px solid transparent;border-right:9px solid transparent;position:absolute}.dropdown-menu .dropdown-caret .caret-outer{border-bottom:9px solid rgba(0,0,0,.1);height:auto;left:0;width:auto;margin-left:-1px}.dropdown-menu .dropdown-caret .caret-inner{margin-top:-1px;top:1px;border-bottom:9px solid #fafafa}.dropdown-menu .buttons{border-bottom:1px solid rgba(0,0,0,.07)}.dropdown-menu .buttons:after,.dropdown-menu .buttons:before{content:" ";display:table;line-height:0}.dropdown-menu .buttons:last-child{border-bottom:none}.dropdown-menu .buttons .button{border:0;background-color:transparent;color:#a6a6a6;width:100%;text-align:center;float:left;line-height:1.42857143;padding:8px 4px}.alert,.dropdown-menu .buttons .button:hover{color:#444}.dropdown-menu .buttons .button:focus,.dropdown-menu .buttons .button:hover{outline:0}.dropdown-menu .buttons .button.size-2{width:50%}.dropdown-menu .buttons .button.size-3{width:33%}.alert{padding:15px;margin-bottom:20px;background:#eee;border-bottom:5px solid #ddd}.alert-success{background:#dff0d8;border-color:#d6e9c6;color:#3c763d}.alert-info{background:#d9edf7;border-color:#bce8f1;color:#31708f}.alert-danger{background:#f2dede;border-color:#ebccd1;color:#a94442}.alert-warning{background:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.book .book-summary{position:absolute;top:0;left:-300px;bottom:0;z-index:1;width:300px;color:#364149;background:#fafafa;border-right:1px solid rgba(0,0,0,.07);-webkit-transition:left 250ms ease;-moz-transition:left 250ms ease;-o-transition:left 250ms ease;transition:left 250ms ease}.book .book-summary ul.summary{position:absolute;top:0;left:0;right:0;bottom:0;overflow-y:auto;list-style:none;margin:0;padding:0;-webkit-transition:top .5s ease;-moz-transition:top .5s ease;-o-transition:top .5s ease;transition:top .5s ease}.book .book-summary ul.summary li{list-style:none}.book .book-summary ul.summary li.divider{height:1px;margin:7px 0;overflow:hidden;background:rgba(0,0,0,.07)}.book .book-summary ul.summary li i.fa-check{display:none;position:absolute;right:9px;top:16px;font-size:9px;color:#3c3}.book .book-summary ul.summary li.done>a{color:#364149;font-weight:400}.book .book-summary ul.summary li.done>a i{display:inline}.book .book-summary ul.summary li a,.book .book-summary ul.summary li span{display:block;padding:10px 15px;border-bottom:none;color:#364149;background:0 0;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;position:relative}.book .book-summary ul.summary li span{cursor:not-allowed;opacity:.3;filter:alpha(opacity=30)}.book .book-summary ul.summary li a:hover,.book .book-summary ul.summary li.active>a{color:#008cff;background:0 0;text-decoration:none}.book .book-summary ul.summary li ul{padding-left:20px}@media (max-width:600px){.book .book-summary{width:calc(100% - 60px);bottom:0;left:-100%}}.book.with-summary .book-summary{left:0}.book.without-animation .book-summary{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;transition:none!important}.book{position:relative;width:100%;height:100%}.book .book-body,.book .book-body .body-inner{position:absolute;top:0;left:0;overflow-y:auto;bottom:0;right:0}.book .book-body{color:#000;background:#fff;-webkit-transition:left 250ms ease;-moz-transition:left 250ms ease;-o-transition:left 250ms ease;transition:left 250ms ease}.book .book-body .page-wrapper{position:relative;outline:0}.book .book-body .page-wrapper .page-inner{max-width:800px;margin:0 auto;padding:20px 0 40px}.book .book-body .page-wrapper .page-inner section{margin:0;padding:5px 15px;background:#fff;border-radius:2px;line-height:1.7;font-size:1.6rem}.book .book-body .page-wrapper .page-inner .btn-group .btn{border-radius:0;background:#eee;border:0}@media (max-width:1240px){.book .book-body{-webkit-transition:-webkit-transform 250ms ease;-moz-transition:-moz-transform 250ms ease;-o-transition:-o-transform 250ms ease;transition:transform 250ms ease;padding-bottom:20px}.book .book-body .body-inner{position:static;min-height:calc(100% - 50px)}}@media (min-width:600px){.book.with-summary .book-body{left:300px}}@media (max-width:600px){.book.with-summary{overflow:hidden}.book.with-summary .book-body{-webkit-transform:translate(calc(100% - 60px),0);-moz-transform:translate(calc(100% - 60px),0);-ms-transform:translate(calc(100% - 60px),0);-o-transform:translate(calc(100% - 60px),0);transform:translate(calc(100% - 60px),0)}}.book.without-animation .book-body{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;transition:none!important}.buttons:after,.buttons:before{content:" ";display:table;line-height:0}.button{border:0;background:#eee;color:#666;width:100%;text-align:center;float:left;line-height:1.42857143;padding:8px 4px}.button:hover{color:#444}.button:focus,.button:hover{outline:0}.button.size-2{width:50%}.button.size-3{width:33%}.book .book-body .page-wrapper .page-inner section{display:none}.book .book-body .page-wrapper .page-inner section.normal{display:block;word-wrap:break-word;overflow:hidden;color:#333;line-height:1.7;text-size-adjust:100%;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%}.book .book-body .page-wrapper .page-inner section.normal *{box-sizing:border-box;-webkit-box-sizing:border-box;}.book .book-body .page-wrapper .page-inner section.normal>:first-child{margin-top:0!important}.book .book-body .page-wrapper .page-inner section.normal>:last-child{margin-bottom:0!important}.book .book-body .page-wrapper .page-inner section.normal blockquote,.book .book-body .page-wrapper .page-inner section.normal code,.book .book-body .page-wrapper .page-inner section.normal figure,.book .book-body .page-wrapper .page-inner section.normal img,.book .book-body .page-wrapper .page-inner section.normal pre,.book .book-body .page-wrapper .page-inner section.normal table,.book .book-body .page-wrapper .page-inner section.normal tr{page-break-inside:avoid}.book .book-body .page-wrapper .page-inner section.normal h2,.book .book-body .page-wrapper .page-inner section.normal h3,.book .book-body .page-wrapper .page-inner section.normal h4,.book .book-body .page-wrapper .page-inner section.normal h5,.book .book-body .page-wrapper .page-inner section.normal p{orphans:3;widows:3}.book .book-body .page-wrapper .page-inner section.normal h1,.book .book-body .page-wrapper .page-inner section.normal h2,.book .book-body .page-wrapper .page-inner section.normal h3,.book .book-body .page-wrapper .page-inner section.normal h4,.book .book-body .page-wrapper .page-inner section.normal h5{page-break-after:avoid}.book .book-body .page-wrapper .page-inner section.normal b,.book .book-body .page-wrapper .page-inner section.normal strong{font-weight:700}.book .book-body .page-wrapper .page-inner section.normal em{font-style:italic}.book .book-body .page-wrapper .page-inner section.normal blockquote,.book .book-body .page-wrapper .page-inner section.normal dl,.book .book-body .page-wrapper .page-inner section.normal ol,.book .book-body .page-wrapper .page-inner section.normal p,.book .book-body .page-wrapper .page-inner section.normal table,.book .book-body .page-wrapper .page-inner section.normal ul{margin-top:0;margin-bottom:.85em}.book .book-body .page-wrapper .page-inner section.normal a{color:#4183c4;text-decoration:none;background:0 0}.book .book-body .page-wrapper .page-inner section.normal a:active,.book .book-body .page-wrapper .page-inner section.normal a:focus,.book .book-body .page-wrapper .page-inner section.normal a:hover{outline:0;text-decoration:underline}.book .book-body .page-wrapper .page-inner section.normal img{border:0;max-width:100%}.book .book-body .page-wrapper .page-inner section.normal hr{height:4px;padding:0;margin:1.7em 0;overflow:hidden;background-color:#e7e7e7;border:none}.book .book-body .page-wrapper .page-inner section.normal hr:after,.book .book-body .page-wrapper .page-inner section.normal hr:before{display:table;content:" "}.book .book-body .page-wrapper .page-inner section.normal h1,.book .book-body .page-wrapper .page-inner section.normal h2,.book .book-body .page-wrapper .page-inner section.normal h3,.book .book-body .page-wrapper .page-inner section.normal h4,.book .book-body .page-wrapper .page-inner section.normal h5,.book .book-body .page-wrapper .page-inner section.normal h6{margin-top:1.275em;margin-bottom:.85em;}.book .book-body .page-wrapper .page-inner section.normal h1{font-size:2em}.book .book-body .page-wrapper .page-inner section.normal h2{font-size:1.75em}.book .book-body .page-wrapper .page-inner section.normal h3{font-size:1.5em}.book .book-body .page-wrapper .page-inner section.normal h4{font-size:1.25em}.book .book-body .page-wrapper .page-inner section.normal h5{font-size:1em}.book .book-body .page-wrapper .page-inner section.normal h6{font-size:1em;color:#777}.book .book-body .page-wrapper .page-inner section.normal code,.book .book-body .page-wrapper .page-inner section.normal pre{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;direction:ltr;border:none;color:inherit}.book .book-body .page-wrapper .page-inner section.normal pre{overflow:auto;word-wrap:normal;margin:0 0 1.275em;padding:.85em 1em;background:#f7f7f7}.book .book-body .page-wrapper .page-inner section.normal pre>code{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;font-size:.85em;white-space:pre;background:0 0}.book .book-body .page-wrapper .page-inner section.normal pre>code:after,.book .book-body .page-wrapper .page-inner section.normal pre>code:before{content:normal}.book .book-body .page-wrapper .page-inner section.normal code{padding:.2em;margin:0;font-size:.85em;background-color:#f7f7f7}.book .book-body .page-wrapper .page-inner section.normal code:after,.book .book-body .page-wrapper .page-inner section.normal code:before{letter-spacing:-.2em;content:"\00a0"}.book .book-body .page-wrapper .page-inner section.normal ol,.book .book-body .page-wrapper .page-inner section.normal ul{padding:0 0 0 2em;margin:0 0 .85em}.book .book-body .page-wrapper .page-inner section.normal ol ol,.book .book-body .page-wrapper .page-inner section.normal ol ul,.book .book-body .page-wrapper .page-inner section.normal ul ol,.book .book-body .page-wrapper .page-inner section.normal ul ul{margin-top:0;margin-bottom:0}.book .book-body .page-wrapper .page-inner section.normal ol ol{list-style-type:lower-roman}.book .book-body .page-wrapper .page-inner section.normal blockquote{margin:0 0 .85em;padding:0 15px;opacity:0.75;border-left:4px solid #dcdcdc}.book .book-body .page-wrapper .page-inner section.normal blockquote:first-child{margin-top:0}.book .book-body .page-wrapper .page-inner section.normal blockquote:last-child{margin-bottom:0}.book .book-body .page-wrapper .page-inner section.normal dl{padding:0}.book .book-body .page-wrapper .page-inner section.normal dl dt{padding:0;margin-top:.85em;font-style:italic;font-weight:700}.book .book-body .page-wrapper .page-inner section.normal dl dd{padding:0 .85em;margin-bottom:.85em}.book .book-body .page-wrapper .page-inner section.normal dd{margin-left:0}.book .book-body .page-wrapper .page-inner section.normal .glossary-term{cursor:help;text-decoration:underline}.book .book-body .navigation{position:absolute;top:50px;bottom:0;margin:0;max-width:150px;min-width:90px;display:flex;justify-content:center;align-content:center;flex-direction:column;font-size:40px;color:#ccc;text-align:center;-webkit-transition:all 350ms ease;-moz-transition:all 350ms ease;-o-transition:all 350ms ease;transition:all 350ms ease}.book .book-body .navigation:hover{text-decoration:none;color:#444}.book .book-body .navigation.navigation-next{right:0}.book .book-body .navigation.navigation-prev{left:0}@media (max-width:1240px){.book .book-body .navigation{position:static;top:auto;max-width:50%;width:50%;display:inline-block;float:left}.book .book-body .navigation.navigation-unique{max-width:100%;width:100%}}.book .book-body .page-wrapper .page-inner section.glossary{margin-bottom:40px}.book .book-body .page-wrapper .page-inner section.glossary h2 a,.book .book-body .page-wrapper .page-inner section.glossary h2 a:hover{color:inherit;text-decoration:none}.book .book-body .page-wrapper .page-inner section.glossary .glossary-index{list-style:none;margin:0;padding:0}.book .book-body .page-wrapper .page-inner section.glossary .glossary-index li{display:inline;margin:0 8px;white-space:nowrap}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-overflow-scrolling:auto;-webkit-tap-highlight-color:transparent;-webkit-text-size-adjust:none;-webkit-touch-callout:none}a{text-decoration:none}body,html{height:100%}html{font-size:62.5%}body{text-rendering:optimizeLegibility;font-smoothing:antialiased;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;letter-spacing:.2px;text-size-adjust:100%} +.book .book-summary ul.summary li a span {display:inline;padding:initial;overflow:visible;cursor:auto;opacity:1;} +/* show arrow before summary tag as in bootstrap */ +details > summary {display:list-item;cursor:pointer;} diff --git a/docs/libs/gitbook-2.6.7/js/app.min.js b/docs/libs/gitbook-2.6.7/js/app.min.js new file mode 100644 index 0000000..643f1f9 --- /dev/null +++ b/docs/libs/gitbook-2.6.7/js/app.min.js @@ -0,0 +1 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o"'`]/g,reHasEscapedHtml=RegExp(reEscapedHtml.source),reHasUnescapedHtml=RegExp(reUnescapedHtml.source);var reEscape=/<%-([\s\S]+?)%>/g,reEvaluate=/<%([\s\S]+?)%>/g,reInterpolate=/<%=([\s\S]+?)%>/g;var reIsDeepProp=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/,reIsPlainProp=/^\w*$/,rePropName=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g;var reRegExpChars=/^[:!,]|[\\^$.*+?()[\]{}|\/]|(^[0-9a-fA-Fnrtuvx])|([\n\r\u2028\u2029])/g,reHasRegExpChars=RegExp(reRegExpChars.source);var reComboMark=/[\u0300-\u036f\ufe20-\ufe23]/g;var reEscapeChar=/\\(\\)?/g;var reEsTemplate=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;var reFlags=/\w*$/;var reHasHexPrefix=/^0[xX]/;var reIsHostCtor=/^\[object .+?Constructor\]$/;var reIsUint=/^\d+$/;var reLatin1=/[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g;var reNoMatch=/($^)/;var reUnescapedString=/['\n\r\u2028\u2029\\]/g;var reWords=function(){var upper="[A-Z\\xc0-\\xd6\\xd8-\\xde]",lower="[a-z\\xdf-\\xf6\\xf8-\\xff]+";return RegExp(upper+"+(?="+upper+lower+")|"+upper+"?"+lower+"|"+upper+"+|[0-9]+","g")}();var contextProps=["Array","ArrayBuffer","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Math","Number","Object","RegExp","Set","String","_","clearTimeout","isFinite","parseFloat","parseInt","setTimeout","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap"];var templateCounter=-1;var typedArrayTags={};typedArrayTags[float32Tag]=typedArrayTags[float64Tag]=typedArrayTags[int8Tag]=typedArrayTags[int16Tag]=typedArrayTags[int32Tag]=typedArrayTags[uint8Tag]=typedArrayTags[uint8ClampedTag]=typedArrayTags[uint16Tag]=typedArrayTags[uint32Tag]=true;typedArrayTags[argsTag]=typedArrayTags[arrayTag]=typedArrayTags[arrayBufferTag]=typedArrayTags[boolTag]=typedArrayTags[dateTag]=typedArrayTags[errorTag]=typedArrayTags[funcTag]=typedArrayTags[mapTag]=typedArrayTags[numberTag]=typedArrayTags[objectTag]=typedArrayTags[regexpTag]=typedArrayTags[setTag]=typedArrayTags[stringTag]=typedArrayTags[weakMapTag]=false;var cloneableTags={};cloneableTags[argsTag]=cloneableTags[arrayTag]=cloneableTags[arrayBufferTag]=cloneableTags[boolTag]=cloneableTags[dateTag]=cloneableTags[float32Tag]=cloneableTags[float64Tag]=cloneableTags[int8Tag]=cloneableTags[int16Tag]=cloneableTags[int32Tag]=cloneableTags[numberTag]=cloneableTags[objectTag]=cloneableTags[regexpTag]=cloneableTags[stringTag]=cloneableTags[uint8Tag]=cloneableTags[uint8ClampedTag]=cloneableTags[uint16Tag]=cloneableTags[uint32Tag]=true;cloneableTags[errorTag]=cloneableTags[funcTag]=cloneableTags[mapTag]=cloneableTags[setTag]=cloneableTags[weakMapTag]=false;var deburredLetters={"À":"A","Á":"A","Â":"A","Ã":"A","Ä":"A","Å":"A","à":"a","á":"a","â":"a","ã":"a","ä":"a","å":"a","Ç":"C","ç":"c","Ð":"D","ð":"d","È":"E","É":"E","Ê":"E","Ë":"E","è":"e","é":"e","ê":"e","ë":"e","Ì":"I","Í":"I","Î":"I","Ï":"I","ì":"i","í":"i","î":"i","ï":"i","Ñ":"N","ñ":"n","Ò":"O","Ó":"O","Ô":"O","Õ":"O","Ö":"O","Ø":"O","ò":"o","ó":"o","ô":"o","õ":"o","ö":"o","ø":"o","Ù":"U","Ú":"U","Û":"U","Ü":"U","ù":"u","ú":"u","û":"u","ü":"u","Ý":"Y","ý":"y","ÿ":"y","Æ":"Ae","æ":"ae","Þ":"Th","þ":"th","ß":"ss"};var htmlEscapes={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"};var htmlUnescapes={"&":"&","<":"<",">":">",""":'"',"'":"'","`":"`"};var objectTypes={function:true,object:true};var regexpEscapes={0:"x30",1:"x31",2:"x32",3:"x33",4:"x34",5:"x35",6:"x36",7:"x37",8:"x38",9:"x39",A:"x41",B:"x42",C:"x43",D:"x44",E:"x45",F:"x46",a:"x61",b:"x62",c:"x63",d:"x64",e:"x65",f:"x66",n:"x6e",r:"x72",t:"x74",u:"x75",v:"x76",x:"x78"};var stringEscapes={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"};var freeExports=objectTypes[typeof exports]&&exports&&!exports.nodeType&&exports;var freeModule=objectTypes[typeof module]&&module&&!module.nodeType&&module;var freeGlobal=freeExports&&freeModule&&typeof global=="object"&&global&&global.Object&&global;var freeSelf=objectTypes[typeof self]&&self&&self.Object&&self;var freeWindow=objectTypes[typeof window]&&window&&window.Object&&window;var moduleExports=freeModule&&freeModule.exports===freeExports&&freeExports;var root=freeGlobal||freeWindow!==(this&&this.window)&&freeWindow||freeSelf||this;function baseCompareAscending(value,other){if(value!==other){var valIsNull=value===null,valIsUndef=value===undefined,valIsReflexive=value===value;var othIsNull=other===null,othIsUndef=other===undefined,othIsReflexive=other===other;if(value>other&&!othIsNull||!valIsReflexive||valIsNull&&!othIsUndef&&othIsReflexive||valIsUndef&&othIsReflexive){return 1}if(value-1){}return index}function charsRightIndex(string,chars){var index=string.length;while(index--&&chars.indexOf(string.charAt(index))>-1){}return index}function compareAscending(object,other){return baseCompareAscending(object.criteria,other.criteria)||object.index-other.index}function compareMultiple(object,other,orders){var index=-1,objCriteria=object.criteria,othCriteria=other.criteria,length=objCriteria.length,ordersLength=orders.length;while(++index=ordersLength){return result}var order=orders[index];return result*(order==="asc"||order===true?1:-1)}}return object.index-other.index}function deburrLetter(letter){return deburredLetters[letter]}function escapeHtmlChar(chr){return htmlEscapes[chr]}function escapeRegExpChar(chr,leadingChar,whitespaceChar){if(leadingChar){chr=regexpEscapes[chr]}else if(whitespaceChar){chr=stringEscapes[chr]}return"\\"+chr}function escapeStringChar(chr){return"\\"+stringEscapes[chr]}function indexOfNaN(array,fromIndex,fromRight){var length=array.length,index=fromIndex+(fromRight?0:-1);while(fromRight?index--:++index=9&&charCode<=13)||charCode==32||charCode==160||charCode==5760||charCode==6158||charCode>=8192&&(charCode<=8202||charCode==8232||charCode==8233||charCode==8239||charCode==8287||charCode==12288||charCode==65279)}function replaceHolders(array,placeholder){var index=-1,length=array.length,resIndex=-1,result=[];while(++index>>1;var MAX_SAFE_INTEGER=9007199254740991;var metaMap=WeakMap&&new WeakMap;var realNames={};function lodash(value){if(isObjectLike(value)&&!isArray(value)&&!(value instanceof LazyWrapper)){if(value instanceof LodashWrapper){return value}if(hasOwnProperty.call(value,"__chain__")&&hasOwnProperty.call(value,"__wrapped__")){return wrapperClone(value)}}return new LodashWrapper(value)}function baseLodash(){}function LodashWrapper(value,chainAll,actions){this.__wrapped__=value;this.__actions__=actions||[];this.__chain__=!!chainAll}var support=lodash.support={};lodash.templateSettings={escape:reEscape,evaluate:reEvaluate,interpolate:reInterpolate,variable:"",imports:{_:lodash}};function LazyWrapper(value){this.__wrapped__=value;this.__actions__=[];this.__dir__=1;this.__filtered__=false;this.__iteratees__=[];this.__takeCount__=POSITIVE_INFINITY;this.__views__=[]}function lazyClone(){var result=new LazyWrapper(this.__wrapped__);result.__actions__=arrayCopy(this.__actions__);result.__dir__=this.__dir__;result.__filtered__=this.__filtered__;result.__iteratees__=arrayCopy(this.__iteratees__);result.__takeCount__=this.__takeCount__;result.__views__=arrayCopy(this.__views__);return result}function lazyReverse(){if(this.__filtered__){var result=new LazyWrapper(this);result.__dir__=-1;result.__filtered__=true}else{result=this.clone();result.__dir__*=-1}return result}function lazyValue(){var array=this.__wrapped__.value(),dir=this.__dir__,isArr=isArray(array),isRight=dir<0,arrLength=isArr?array.length:0,view=getView(0,arrLength,this.__views__),start=view.start,end=view.end,length=end-start,index=isRight?end:start-1,iteratees=this.__iteratees__,iterLength=iteratees.length,resIndex=0,takeCount=nativeMin(length,this.__takeCount__);if(!isArr||arrLength=LARGE_ARRAY_SIZE?createCache(values):null,valuesLength=values.length;if(cache){indexOf=cacheIndexOf;isCommon=false;values=cache}outer:while(++indexlength?0:length+start}end=end===undefined||end>length?length:+end||0;if(end<0){end+=length}length=start>end?0:end>>>0;start>>>=0;while(startlength?0:length+start}end=end===undefined||end>length?length:+end||0;if(end<0){end+=length}length=start>end?0:end-start>>>0;start>>>=0;var result=Array(length);while(++index=LARGE_ARRAY_SIZE,seen=isLarge?createCache():null,result=[];if(seen){indexOf=cacheIndexOf;isCommon=false}else{isLarge=false;seen=iteratee?[]:result}outer:while(++index>>1,computed=array[mid];if((retHighest?computed<=value:computed2?sources[length-2]:undefined,guard=length>2?sources[2]:undefined,thisArg=length>1?sources[length-1]:undefined;if(typeof customizer=="function"){customizer=bindCallback(customizer,thisArg,5);length-=2}else{customizer=typeof thisArg=="function"?thisArg:undefined;length-=customizer?1:0}if(guard&&isIterateeCall(sources[0],sources[1],guard)){customizer=length<3?undefined:customizer;length=1}while(++index-1?collection[index]:undefined}return baseFind(collection,predicate,eachFunc)}}function createFindIndex(fromRight){return function(array,predicate,thisArg){if(!(array&&array.length)){return-1}predicate=getCallback(predicate,thisArg,3);return baseFindIndex(array,predicate,fromRight)}}function createFindKey(objectFunc){return function(object,predicate,thisArg){predicate=getCallback(predicate,thisArg,3);return baseFind(object,predicate,objectFunc,true)}}function createFlow(fromRight){return function(){var wrapper,length=arguments.length,index=fromRight?length:-1,leftIndex=0,funcs=Array(length);while(fromRight?index--:++index=LARGE_ARRAY_SIZE){return wrapper.plant(value).value()}var index=0,result=length?funcs[index].apply(this,args):value;while(++index=length||!nativeIsFinite(length)){return""}var padLength=length-strLength;chars=chars==null?" ":chars+"";return repeat(chars,nativeCeil(padLength/chars.length)).slice(0,padLength)}function createPartialWrapper(func,bitmask,thisArg,partials){var isBind=bitmask&BIND_FLAG,Ctor=createCtorWrapper(func);function wrapper(){var argsIndex=-1,argsLength=arguments.length,leftIndex=-1,leftLength=partials.length,args=Array(leftLength+argsLength);while(++leftIndexarrLength)){return false}while(++index-1&&value%1==0&&value-1&&value%1==0&&value<=MAX_SAFE_INTEGER}function isStrictComparable(value){return value===value&&!isObject(value)}function mergeData(data,source){var bitmask=data[1],srcBitmask=source[1],newBitmask=bitmask|srcBitmask,isCommon=newBitmask0){if(++count>=HOT_COUNT){return key}}else{count=0}return baseSetData(key,value)}}();function shimKeys(object){var props=keysIn(object),propsLength=props.length,length=propsLength&&object.length;var allowIndexes=!!length&&isLength(length)&&(isArray(object)||isArguments(object));var index=-1,result=[];while(++index=120?createCache(othIndex&&value):null}var array=arrays[0],index=-1,length=array?array.length:0,seen=caches[0];outer:while(++index-1){splice.call(array,fromIndex,1)}}return array}var pullAt=restParam(function(array,indexes){indexes=baseFlatten(indexes);var result=baseAt(array,indexes);basePullAt(array,indexes.sort(baseCompareAscending));return result});function remove(array,predicate,thisArg){var result=[];if(!(array&&array.length)){return result}var index=-1,indexes=[],length=array.length;predicate=getCallback(predicate,thisArg,3);while(++index2?arrays[length-2]:undefined,thisArg=length>1?arrays[length-1]:undefined;if(length>2&&typeof iteratee=="function"){length-=2}else{iteratee=length>1&&typeof thisArg=="function"?(--length,thisArg):undefined;thisArg=undefined}arrays.length=length;return unzipWith(arrays,iteratee,thisArg)});function chain(value){var result=lodash(value);result.__chain__=true;return result}function tap(value,interceptor,thisArg){interceptor.call(thisArg,value);return value}function thru(value,interceptor,thisArg){return interceptor.call(thisArg,value)}function wrapperChain(){return chain(this)}function wrapperCommit(){return new LodashWrapper(this.value(),this.__chain__)}var wrapperConcat=restParam(function(values){values=baseFlatten(values);return this.thru(function(array){return arrayConcat(isArray(array)?array:[toObject(array)],values)})});function wrapperPlant(value){var result,parent=this;while(parent instanceof baseLodash){var clone=wrapperClone(parent);if(result){previous.__wrapped__=clone}else{result=clone}var previous=clone;parent=parent.__wrapped__}previous.__wrapped__=value;return result}function wrapperReverse(){var value=this.__wrapped__;var interceptor=function(value){return wrapped&&wrapped.__dir__<0?value:value.reverse()};if(value instanceof LazyWrapper){var wrapped=value;if(this.__actions__.length){wrapped=new LazyWrapper(this)}wrapped=wrapped.reverse();wrapped.__actions__.push({func:thru,args:[interceptor],thisArg:undefined});return new LodashWrapper(wrapped,this.__chain__)}return this.thru(interceptor)}function wrapperToString(){return this.value()+""}function wrapperValue(){return baseWrapperValue(this.__wrapped__,this.__actions__)}var at=restParam(function(collection,props){return baseAt(collection,baseFlatten(props))});var countBy=createAggregator(function(result,value,key){hasOwnProperty.call(result,key)?++result[key]:result[key]=1});function every(collection,predicate,thisArg){var func=isArray(collection)?arrayEvery:baseEvery;if(thisArg&&isIterateeCall(collection,predicate,thisArg)){predicate=undefined}if(typeof predicate!="function"||thisArg!==undefined){predicate=getCallback(predicate,thisArg,3)}return func(collection,predicate)}function filter(collection,predicate,thisArg){var func=isArray(collection)?arrayFilter:baseFilter;predicate=getCallback(predicate,thisArg,3);return func(collection,predicate)}var find=createFind(baseEach);var findLast=createFind(baseEachRight,true);function findWhere(collection,source){return find(collection,baseMatches(source))}var forEach=createForEach(arrayEach,baseEach);var forEachRight=createForEach(arrayEachRight,baseEachRight);var groupBy=createAggregator(function(result,value,key){if(hasOwnProperty.call(result,key)){result[key].push(value)}else{result[key]=[value]}});function includes(collection,target,fromIndex,guard){var length=collection?getLength(collection):0;if(!isLength(length)){collection=values(collection);length=collection.length}if(typeof fromIndex!="number"||guard&&isIterateeCall(target,fromIndex,guard)){fromIndex=0}else{fromIndex=fromIndex<0?nativeMax(length+fromIndex,0):fromIndex||0}return typeof collection=="string"||!isArray(collection)&&isString(collection)?fromIndex<=length&&collection.indexOf(target,fromIndex)>-1:!!length&&getIndexOf(collection,target,fromIndex)>-1}var indexBy=createAggregator(function(result,value,key){result[key]=value});var invoke=restParam(function(collection,path,args){var index=-1,isFunc=typeof path=="function",isProp=isKey(path),result=isArrayLike(collection)?Array(collection.length):[];baseEach(collection,function(value){var func=isFunc?path:isProp&&value!=null?value[path]:undefined;result[++index]=func?func.apply(value,args):invokePath(value,path,args)});return result});function map(collection,iteratee,thisArg){var func=isArray(collection)?arrayMap:baseMap;iteratee=getCallback(iteratee,thisArg,3);return func(collection,iteratee)}var partition=createAggregator(function(result,value,key){result[key?0:1].push(value)},function(){return[[],[]]});function pluck(collection,path){return map(collection,property(path))}var reduce=createReduce(arrayReduce,baseEach);var reduceRight=createReduce(arrayReduceRight,baseEachRight);function reject(collection,predicate,thisArg){var func=isArray(collection)?arrayFilter:baseFilter;predicate=getCallback(predicate,thisArg,3);return func(collection,function(value,index,collection){return!predicate(value,index,collection)})}function sample(collection,n,guard){if(guard?isIterateeCall(collection,n,guard):n==null){collection=toIterable(collection);var length=collection.length;return length>0?collection[baseRandom(0,length-1)]:undefined}var index=-1,result=toArray(collection),length=result.length,lastIndex=length-1;n=nativeMin(n<0?0:+n||0,length);while(++index0){result=func.apply(this,arguments)}if(n<=1){func=undefined}return result}}var bind=restParam(function(func,thisArg,partials){var bitmask=BIND_FLAG;if(partials.length){var holders=replaceHolders(partials,bind.placeholder);bitmask|=PARTIAL_FLAG}return createWrapper(func,bitmask,thisArg,partials,holders)});var bindAll=restParam(function(object,methodNames){methodNames=methodNames.length?baseFlatten(methodNames):functions(object);var index=-1,length=methodNames.length;while(++indexwait){complete(trailingCall,maxTimeoutId)}else{timeoutId=setTimeout(delayed,remaining)}}function maxDelayed(){complete(trailing,timeoutId)}function debounced(){args=arguments;stamp=now();thisArg=this;trailingCall=trailing&&(timeoutId||!leading);if(maxWait===false){var leadingCall=leading&&!timeoutId}else{if(!maxTimeoutId&&!leading){lastCalled=stamp}var remaining=maxWait-(stamp-lastCalled),isCalled=remaining<=0||remaining>maxWait;if(isCalled){if(maxTimeoutId){maxTimeoutId=clearTimeout(maxTimeoutId)}lastCalled=stamp;result=func.apply(thisArg,args)}else if(!maxTimeoutId){maxTimeoutId=setTimeout(maxDelayed,remaining)}}if(isCalled&&timeoutId){timeoutId=clearTimeout(timeoutId)}else if(!timeoutId&&wait!==maxWait){timeoutId=setTimeout(delayed,wait)}if(leadingCall){isCalled=true;result=func.apply(thisArg,args)}if(isCalled&&!timeoutId&&!maxTimeoutId){args=thisArg=undefined}return result}debounced.cancel=cancel;return debounced}var defer=restParam(function(func,args){return baseDelay(func,1,args)});var delay=restParam(function(func,wait,args){return baseDelay(func,wait,args)});var flow=createFlow();var flowRight=createFlow(true);function memoize(func,resolver){if(typeof func!="function"||resolver&&typeof resolver!="function"){throw new TypeError(FUNC_ERROR_TEXT)}var memoized=function(){var args=arguments,key=resolver?resolver.apply(this,args):args[0],cache=memoized.cache;if(cache.has(key)){return cache.get(key)}var result=func.apply(this,args);memoized.cache=cache.set(key,result);return result};memoized.cache=new memoize.Cache;return memoized}var modArgs=restParam(function(func,transforms){transforms=baseFlatten(transforms);if(typeof func!="function"||!arrayEvery(transforms,baseIsFunction)){throw new TypeError(FUNC_ERROR_TEXT)}var length=transforms.length;return restParam(function(args){var index=nativeMin(args.length,length);while(index--){args[index]=transforms[index](args[index])}return func.apply(this,args)})});function negate(predicate){if(typeof predicate!="function"){throw new TypeError(FUNC_ERROR_TEXT)}return function(){return!predicate.apply(this,arguments)}}function once(func){return before(2,func)}var partial=createPartial(PARTIAL_FLAG);var partialRight=createPartial(PARTIAL_RIGHT_FLAG);var rearg=restParam(function(func,indexes){return createWrapper(func,REARG_FLAG,undefined,undefined,undefined,baseFlatten(indexes))});function restParam(func,start){if(typeof func!="function"){throw new TypeError(FUNC_ERROR_TEXT)}start=nativeMax(start===undefined?func.length-1:+start||0,0);return function(){var args=arguments,index=-1,length=nativeMax(args.length-start,0),rest=Array(length);while(++indexother}function gte(value,other){return value>=other}function isArguments(value){return isObjectLike(value)&&isArrayLike(value)&&hasOwnProperty.call(value,"callee")&&!propertyIsEnumerable.call(value,"callee")}var isArray=nativeIsArray||function(value){return isObjectLike(value)&&isLength(value.length)&&objToString.call(value)==arrayTag};function isBoolean(value){return value===true||value===false||isObjectLike(value)&&objToString.call(value)==boolTag}function isDate(value){return isObjectLike(value)&&objToString.call(value)==dateTag}function isElement(value){return!!value&&value.nodeType===1&&isObjectLike(value)&&!isPlainObject(value)}function isEmpty(value){if(value==null){return true}if(isArrayLike(value)&&(isArray(value)||isString(value)||isArguments(value)||isObjectLike(value)&&isFunction(value.splice))){return!value.length}return!keys(value).length}function isEqual(value,other,customizer,thisArg){customizer=typeof customizer=="function"?bindCallback(customizer,thisArg,3):undefined;var result=customizer?customizer(value,other):undefined;return result===undefined?baseIsEqual(value,other,customizer):!!result}function isError(value){return isObjectLike(value)&&typeof value.message=="string"&&objToString.call(value)==errorTag}function isFinite(value){return typeof value=="number"&&nativeIsFinite(value)}function isFunction(value){return isObject(value)&&objToString.call(value)==funcTag}function isObject(value){var type=typeof value;return!!value&&(type=="object"||type=="function")}function isMatch(object,source,customizer,thisArg){customizer=typeof customizer=="function"?bindCallback(customizer,thisArg,3):undefined;return baseIsMatch(object,getMatchData(source),customizer)}function isNaN(value){return isNumber(value)&&value!=+value}function isNative(value){if(value==null){return false}if(isFunction(value)){return reIsNative.test(fnToString.call(value))}return isObjectLike(value)&&reIsHostCtor.test(value)}function isNull(value){return value===null}function isNumber(value){return typeof value=="number"||isObjectLike(value)&&objToString.call(value)==numberTag}function isPlainObject(value){var Ctor;if(!(isObjectLike(value)&&objToString.call(value)==objectTag&&!isArguments(value))||!hasOwnProperty.call(value,"constructor")&&(Ctor=value.constructor,typeof Ctor=="function"&&!(Ctor instanceof Ctor))){return false}var result;baseForIn(value,function(subValue,key){result=key});return result===undefined||hasOwnProperty.call(value,result)}function isRegExp(value){return isObject(value)&&objToString.call(value)==regexpTag}function isString(value){return typeof value=="string"||isObjectLike(value)&&objToString.call(value)==stringTag}function isTypedArray(value){return isObjectLike(value)&&isLength(value.length)&&!!typedArrayTags[objToString.call(value)]}function isUndefined(value){return value===undefined}function lt(value,other){return value0;while(++index=nativeMin(start,end)&&value=0&&string.indexOf(target,position)==position}function escape(string){string=baseToString(string);return string&&reHasUnescapedHtml.test(string)?string.replace(reUnescapedHtml,escapeHtmlChar):string}function escapeRegExp(string){string=baseToString(string);return string&&reHasRegExpChars.test(string)?string.replace(reRegExpChars,escapeRegExpChar):string||"(?:)"}var kebabCase=createCompounder(function(result,word,index){return result+(index?"-":"")+word.toLowerCase()});function pad(string,length,chars){string=baseToString(string);length=+length;var strLength=string.length;if(strLength>=length||!nativeIsFinite(length)){return string}var mid=(length-strLength)/2,leftLength=nativeFloor(mid),rightLength=nativeCeil(mid);chars=createPadding("",rightLength,chars);return chars.slice(0,leftLength)+string+chars}var padLeft=createPadDir();var padRight=createPadDir(true);function parseInt(string,radix,guard){if(guard?isIterateeCall(string,radix,guard):radix==null){radix=0}else if(radix){radix=+radix}string=trim(string);return nativeParseInt(string,radix||(reHasHexPrefix.test(string)?16:10))}function repeat(string,n){var result="";string=baseToString(string);n=+n;if(n<1||!string||!nativeIsFinite(n)){return result}do{if(n%2){result+=string}n=nativeFloor(n/2);string+=string}while(n);return result}var snakeCase=createCompounder(function(result,word,index){return result+(index?"_":"")+word.toLowerCase()});var startCase=createCompounder(function(result,word,index){return result+(index?" ":"")+(word.charAt(0).toUpperCase()+word.slice(1))});function startsWith(string,target,position){string=baseToString(string);position=position==null?0:nativeMin(position<0?0:+position||0,string.length);return string.lastIndexOf(target,position)==position}function template(string,options,otherOptions){var settings=lodash.templateSettings;if(otherOptions&&isIterateeCall(string,options,otherOptions)){options=otherOptions=undefined}string=baseToString(string);options=assignWith(baseAssign({},otherOptions||options),settings,assignOwnDefaults);var imports=assignWith(baseAssign({},options.imports),settings.imports,assignOwnDefaults),importsKeys=keys(imports),importsValues=baseValues(imports,importsKeys);var isEscaping,isEvaluating,index=0,interpolate=options.interpolate||reNoMatch,source="__p += '";var reDelimiters=RegExp((options.escape||reNoMatch).source+"|"+interpolate.source+"|"+(interpolate===reInterpolate?reEsTemplate:reNoMatch).source+"|"+(options.evaluate||reNoMatch).source+"|$","g");var sourceURL="//# sourceURL="+("sourceURL"in options?options.sourceURL:"lodash.templateSources["+ ++templateCounter+"]")+"\n";string.replace(reDelimiters,function(match,escapeValue,interpolateValue,esTemplateValue,evaluateValue,offset){interpolateValue||(interpolateValue=esTemplateValue);source+=string.slice(index,offset).replace(reUnescapedString,escapeStringChar);if(escapeValue){isEscaping=true;source+="' +\n__e("+escapeValue+") +\n'"}if(evaluateValue){isEvaluating=true;source+="';\n"+evaluateValue+";\n__p += '"}if(interpolateValue){source+="' +\n((__t = ("+interpolateValue+")) == null ? '' : __t) +\n'"}index=offset+match.length;return match});source+="';\n";var variable=options.variable;if(!variable){source="with (obj) {\n"+source+"\n}\n"}source=(isEvaluating?source.replace(reEmptyStringLeading,""):source).replace(reEmptyStringMiddle,"$1").replace(reEmptyStringTrailing,"$1;");source="function("+(variable||"obj")+") {\n"+(variable?"":"obj || (obj = {});\n")+"var __t, __p = ''"+(isEscaping?", __e = _.escape":"")+(isEvaluating?", __j = Array.prototype.join;\n"+"function print() { __p += __j.call(arguments, '') }\n":";\n")+source+"return __p\n}";var result=attempt(function(){return Function(importsKeys,sourceURL+"return "+source).apply(undefined,importsValues)});result.source=source;if(isError(result)){throw result}return result}function trim(string,chars,guard){var value=string;string=baseToString(string);if(!string){return string}if(guard?isIterateeCall(value,chars,guard):chars==null){return string.slice(trimmedLeftIndex(string),trimmedRightIndex(string)+1)}chars=chars+"";return string.slice(charsLeftIndex(string,chars),charsRightIndex(string,chars)+1)}function trimLeft(string,chars,guard){var value=string;string=baseToString(string);if(!string){return string}if(guard?isIterateeCall(value,chars,guard):chars==null){return string.slice(trimmedLeftIndex(string))}return string.slice(charsLeftIndex(string,chars+""))}function trimRight(string,chars,guard){var value=string;string=baseToString(string);if(!string){return string}if(guard?isIterateeCall(value,chars,guard):chars==null){return string.slice(0,trimmedRightIndex(string)+1)}return string.slice(0,charsRightIndex(string,chars+"")+1)}function trunc(string,options,guard){if(guard&&isIterateeCall(string,options,guard)){options=undefined}var length=DEFAULT_TRUNC_LENGTH,omission=DEFAULT_TRUNC_OMISSION;if(options!=null){if(isObject(options)){var separator="separator"in options?options.separator:separator;length="length"in options?+options.length||0:length;omission="omission"in options?baseToString(options.omission):omission}else{length=+options||0}}string=baseToString(string);if(length>=string.length){return string}var end=length-omission.length;if(end<1){return omission}var result=string.slice(0,end);if(separator==null){return result+omission}if(isRegExp(separator)){if(string.slice(end).search(separator)){var match,newEnd,substring=string.slice(0,end);if(!separator.global){separator=RegExp(separator.source,(reFlags.exec(separator)||"")+"g")}separator.lastIndex=0;while(match=separator.exec(substring)){newEnd=match.index}result=result.slice(0,newEnd==null?end:newEnd)}}else if(string.indexOf(separator,end)!=end){var index=result.lastIndexOf(separator);if(index>-1){result=result.slice(0,index)}}return result+omission}function unescape(string){string=baseToString(string);return string&&reHasEscapedHtml.test(string)?string.replace(reEscapedHtml,unescapeHtmlChar):string}function words(string,pattern,guard){if(guard&&isIterateeCall(string,pattern,guard)){pattern=undefined}string=baseToString(string);return string.match(pattern||reWords)||[]}var attempt=restParam(function(func,args){try{return func.apply(undefined,args)}catch(e){return isError(e)?e:new Error(e)}});function callback(func,thisArg,guard){if(guard&&isIterateeCall(func,thisArg,guard)){thisArg=undefined}return isObjectLike(func)?matches(func):baseCallback(func,thisArg)}function constant(value){return function(){return value}}function identity(value){return value}function matches(source){return baseMatches(baseClone(source,true))}function matchesProperty(path,srcValue){return baseMatchesProperty(path,baseClone(srcValue,true))}var method=restParam(function(path,args){return function(object){return invokePath(object,path,args)}});var methodOf=restParam(function(object,args){return function(path){return invokePath(object,path,args)}});function mixin(object,source,options){if(options==null){var isObj=isObject(source),props=isObj?keys(source):undefined,methodNames=props&&props.length?baseFunctions(source,props):undefined;if(!(methodNames?methodNames.length:isObj)){methodNames=false;options=source;source=object;object=this}}if(!methodNames){methodNames=baseFunctions(source,keys(source))}var chain=true,index=-1,isFunc=isFunction(object),length=methodNames.length;if(options===false){chain=false}else if(isObject(options)&&"chain"in options){chain=options.chain}while(++index0||end<0)){return new LazyWrapper(result)}if(start<0){result=result.takeRight(-start)}else if(start){result=result.drop(start)}if(end!==undefined){end=+end||0;result=end<0?result.dropRight(-end):result.take(end-start)}return result};LazyWrapper.prototype.takeRightWhile=function(predicate,thisArg){return this.reverse().takeWhile(predicate,thisArg).reverse()};LazyWrapper.prototype.toArray=function(){return this.take(POSITIVE_INFINITY)};baseForOwn(LazyWrapper.prototype,function(func,methodName){var checkIteratee=/^(?:filter|map|reject)|While$/.test(methodName),retUnwrapped=/^(?:first|last)$/.test(methodName),lodashFunc=lodash[retUnwrapped?"take"+(methodName=="last"?"Right":""):methodName];if(!lodashFunc){return}lodash.prototype[methodName]=function(){var args=retUnwrapped?[1]:arguments,chainAll=this.__chain__,value=this.__wrapped__,isHybrid=!!this.__actions__.length,isLazy=value instanceof LazyWrapper,iteratee=args[0],useLazy=isLazy||isArray(value);if(useLazy&&checkIteratee&&typeof iteratee=="function"&&iteratee.length!=1){isLazy=useLazy=false}var interceptor=function(value){return retUnwrapped&&chainAll?lodashFunc(value,1)[0]:lodashFunc.apply(undefined,arrayPush([value],args))};var action={func:thru,args:[interceptor],thisArg:undefined},onlyLazy=isLazy&&!isHybrid;if(retUnwrapped&&!chainAll){if(onlyLazy){value=value.clone();value.__actions__.push(action);return func.call(value)}return lodashFunc.call(undefined,this.value())[0]}if(!retUnwrapped&&useLazy){value=onlyLazy?value:new LazyWrapper(this);var result=func.apply(value,args);result.__actions__.push(action);return new LodashWrapper(result,chainAll)}return this.thru(interceptor)}});arrayEach(["join","pop","push","replace","shift","sort","splice","split","unshift"],function(methodName){var func=(/^(?:replace|split)$/.test(methodName)?stringProto:arrayProto)[methodName],chainName=/^(?:push|sort|unshift)$/.test(methodName)?"tap":"thru",retUnwrapped=/^(?:join|pop|replace|shift)$/.test(methodName);lodash.prototype[methodName]=function(){var args=arguments;if(retUnwrapped&&!this.__chain__){return func.apply(this.value(),args)}return this[chainName](function(value){return func.apply(value,args)})}});baseForOwn(LazyWrapper.prototype,function(func,methodName){var lodashFunc=lodash[methodName];if(lodashFunc){var key=lodashFunc.name,names=realNames[key]||(realNames[key]=[]);names.push({name:methodName,func:lodashFunc})}});realNames[createHybridWrapper(undefined,BIND_KEY_FLAG).name]=[{name:"wrapper",func:undefined}];LazyWrapper.prototype.clone=lazyClone;LazyWrapper.prototype.reverse=lazyReverse;LazyWrapper.prototype.value=lazyValue;lodash.prototype.chain=wrapperChain;lodash.prototype.commit=wrapperCommit;lodash.prototype.concat=wrapperConcat;lodash.prototype.plant=wrapperPlant;lodash.prototype.reverse=wrapperReverse;lodash.prototype.toString=wrapperToString;lodash.prototype.run=lodash.prototype.toJSON=lodash.prototype.valueOf=lodash.prototype.value=wrapperValue;lodash.prototype.collect=lodash.prototype.map;lodash.prototype.head=lodash.prototype.first;lodash.prototype.select=lodash.prototype.filter;lodash.prototype.tail=lodash.prototype.rest;return lodash}var _=runInContext();if(typeof define=="function"&&typeof define.amd=="object"&&define.amd){root._=_;define(function(){return _})}else if(freeExports&&freeModule){if(moduleExports){(freeModule.exports=_)._=_}else{freeExports._=_}}else{root._=_}}).call(this)}).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{}],3:[function(require,module,exports){(function(window,document,undefined){var _MAP={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",18:"alt",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"ins",46:"del",91:"meta",93:"meta",224:"meta"};var _KEYCODE_MAP={106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"};var _SHIFT_MAP={"~":"`","!":"1","@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\"};var _SPECIAL_ALIASES={option:"alt",command:"meta",return:"enter",escape:"esc",plus:"+",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"};var _REVERSE_MAP;for(var i=1;i<20;++i){_MAP[111+i]="f"+i}for(i=0;i<=9;++i){_MAP[i+96]=i}function _addEvent(object,type,callback){if(object.addEventListener){object.addEventListener(type,callback,false);return}object.attachEvent("on"+type,callback)}function _characterFromEvent(e){if(e.type=="keypress"){var character=String.fromCharCode(e.which);if(!e.shiftKey){character=character.toLowerCase()}return character}if(_MAP[e.which]){return _MAP[e.which]}if(_KEYCODE_MAP[e.which]){return _KEYCODE_MAP[e.which]}return String.fromCharCode(e.which).toLowerCase()}function _modifiersMatch(modifiers1,modifiers2){return modifiers1.sort().join(",")===modifiers2.sort().join(",")}function _eventModifiers(e){var modifiers=[];if(e.shiftKey){modifiers.push("shift")}if(e.altKey){modifiers.push("alt")}if(e.ctrlKey){modifiers.push("ctrl")}if(e.metaKey){modifiers.push("meta")}return modifiers}function _preventDefault(e){if(e.preventDefault){e.preventDefault();return}e.returnValue=false}function _stopPropagation(e){if(e.stopPropagation){e.stopPropagation();return}e.cancelBubble=true}function _isModifier(key){return key=="shift"||key=="ctrl"||key=="alt"||key=="meta"}function _getReverseMap(){if(!_REVERSE_MAP){_REVERSE_MAP={};for(var key in _MAP){if(key>95&&key<112){continue}if(_MAP.hasOwnProperty(key)){_REVERSE_MAP[_MAP[key]]=key}}}return _REVERSE_MAP}function _pickBestAction(key,modifiers,action){if(!action){action=_getReverseMap()[key]?"keydown":"keypress"}if(action=="keypress"&&modifiers.length){action="keydown"}return action}function _keysFromString(combination){if(combination==="+"){return["+"]}combination=combination.replace(/\+{2}/g,"+plus");return combination.split("+")}function _getKeyInfo(combination,action){var keys;var key;var i;var modifiers=[];keys=_keysFromString(combination);for(i=0;i1){_bindSequence(combination,sequence,callback,action);return}info=_getKeyInfo(combination,action);self._callbacks[info.key]=self._callbacks[info.key]||[];_getMatches(info.key,info.modifiers,{type:info.action},sequenceName,combination,level);self._callbacks[info.key][sequenceName?"unshift":"push"]({callback:callback,modifiers:info.modifiers,action:info.action,seq:sequenceName,level:level,combo:combination})}self._bindMultiple=function(combinations,callback,action){for(var i=0;i-1){return false}if(_belongsTo(element,self.target)){return false}return element.tagName=="INPUT"||element.tagName=="SELECT"||element.tagName=="TEXTAREA"||element.isContentEditable};Mousetrap.prototype.handleKey=function(){var self=this;return self._handleKey.apply(self,arguments)};Mousetrap.init=function(){var documentMousetrap=Mousetrap(document);for(var method in documentMousetrap){if(method.charAt(0)!=="_"){Mousetrap[method]=function(method){return function(){return documentMousetrap[method].apply(documentMousetrap,arguments)}}(method)}}};Mousetrap.init();window.Mousetrap=Mousetrap;if(typeof module!=="undefined"&&module.exports){module.exports=Mousetrap}if(typeof define==="function"&&define.amd){define(function(){return Mousetrap})}})(window,document)},{}],4:[function(require,module,exports){(function(process){function normalizeArray(parts,allowAboveRoot){var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up--;up){parts.unshift("..")}}return parts}var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;var splitPath=function(filename){return splitPathRe.exec(filename).slice(1)};exports.resolve=function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:process.cwd();if(typeof path!=="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){continue}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=path.charAt(0)==="/"}resolvedPath=normalizeArray(filter(resolvedPath.split("/"),function(p){return!!p}),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."};exports.normalize=function(path){var isAbsolute=exports.isAbsolute(path),trailingSlash=substr(path,-1)==="/";path=normalizeArray(filter(path.split("/"),function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path};exports.isAbsolute=function(path){return path.charAt(0)==="/"};exports.join=function(){var paths=Array.prototype.slice.call(arguments,0);return exports.normalize(filter(paths,function(p,index){if(typeof p!=="string"){throw new TypeError("Arguments to path.join must be strings")}return p}).join("/"))};exports.relative=function(from,to){from=exports.resolve(from).substr(1);to=exports.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i1){for(var i=1;i= 0x80 (not a basic code point)","invalid-input":"Invalid input"},baseMinusTMin=base-tMin,floor=Math.floor,stringFromCharCode=String.fromCharCode,key;function error(type){throw RangeError(errors[type])}function map(array,fn){var length=array.length;var result=[];while(length--){result[length]=fn(array[length])}return result}function mapDomain(string,fn){var parts=string.split("@");var result="";if(parts.length>1){result=parts[0]+"@";string=parts[1]}string=string.replace(regexSeparators,".");var labels=string.split(".");var encoded=map(labels,fn).join(".");return result+encoded}function ucs2decode(string){var output=[],counter=0,length=string.length,value,extra;while(counter=55296&&value<=56319&&counter65535){value-=65536;output+=stringFromCharCode(value>>>10&1023|55296);value=56320|value&1023}output+=stringFromCharCode(value);return output}).join("")}function basicToDigit(codePoint){if(codePoint-48<10){return codePoint-22}if(codePoint-65<26){return codePoint-65}if(codePoint-97<26){return codePoint-97}return base}function digitToBasic(digit,flag){return digit+22+75*(digit<26)-((flag!=0)<<5)}function adapt(delta,numPoints,firstTime){var k=0;delta=firstTime?floor(delta/damp):delta>>1;delta+=floor(delta/numPoints);for(;delta>baseMinusTMin*tMax>>1;k+=base){delta=floor(delta/baseMinusTMin)}return floor(k+(baseMinusTMin+1)*delta/(delta+skew))}function decode(input){var output=[],inputLength=input.length,out,i=0,n=initialN,bias=initialBias,basic,j,index,oldi,w,k,digit,t,baseMinusT;basic=input.lastIndexOf(delimiter);if(basic<0){basic=0}for(j=0;j=128){error("not-basic")}output.push(input.charCodeAt(j))}for(index=basic>0?basic+1:0;index=inputLength){error("invalid-input")}digit=basicToDigit(input.charCodeAt(index++));if(digit>=base||digit>floor((maxInt-i)/w)){error("overflow")}i+=digit*w;t=k<=bias?tMin:k>=bias+tMax?tMax:k-bias;if(digitfloor(maxInt/baseMinusT)){error("overflow")}w*=baseMinusT}out=output.length+1;bias=adapt(i-oldi,out,oldi==0);if(floor(i/out)>maxInt-n){error("overflow")}n+=floor(i/out);i%=out;output.splice(i++,0,n)}return ucs2encode(output)}function encode(input){var n,delta,handledCPCount,basicLength,bias,j,m,q,k,t,currentValue,output=[],inputLength,handledCPCountPlusOne,baseMinusT,qMinusT;input=ucs2decode(input);inputLength=input.length;n=initialN;delta=0;bias=initialBias;for(j=0;j=n&¤tValuefloor((maxInt-delta)/handledCPCountPlusOne)){error("overflow")}delta+=(m-n)*handledCPCountPlusOne;n=m;for(j=0;jmaxInt){error("overflow")}if(currentValue==n){for(q=delta,k=base;;k+=base){t=k<=bias?tMin:k>=bias+tMax?tMax:k-bias;if(q0&&len>maxKeys){len=maxKeys}for(var i=0;i=0){kstr=x.substr(0,idx);vstr=x.substr(idx+1)}else{kstr=x;vstr=""}k=decodeURIComponent(kstr);v=decodeURIComponent(vstr);if(!hasOwnProperty(obj,k)){obj[k]=v}else if(isArray(obj[k])){obj[k].push(v)}else{obj[k]=[obj[k],v]}}return obj};var isArray=Array.isArray||function(xs){return Object.prototype.toString.call(xs)==="[object Array]"}},{}],8:[function(require,module,exports){"use strict";var stringifyPrimitive=function(v){switch(typeof v){case"string":return v;case"boolean":return v?"true":"false";case"number":return isFinite(v)?v:"";default:return""}};module.exports=function(obj,sep,eq,name){sep=sep||"&";eq=eq||"=";if(obj===null){obj=undefined}if(typeof obj==="object"){return map(objectKeys(obj),function(k){var ks=encodeURIComponent(stringifyPrimitive(k))+eq;if(isArray(obj[k])){return map(obj[k],function(v){return ks+encodeURIComponent(stringifyPrimitive(v))}).join(sep)}else{return ks+encodeURIComponent(stringifyPrimitive(obj[k]))}}).join(sep)}if(!name)return"";return encodeURIComponent(stringifyPrimitive(name))+eq+encodeURIComponent(stringifyPrimitive(obj))};var isArray=Array.isArray||function(xs){return Object.prototype.toString.call(xs)==="[object Array]"};function map(xs,f){if(xs.map)return xs.map(f);var res=[];for(var i=0;i",'"',"`"," ","\r","\n","\t"],unwise=["{","}","|","\\","^","`"].concat(delims),autoEscape=["'"].concat(unwise),nonHostChars=["%","/","?",";","#"].concat(autoEscape),hostEndingChars=["/","?","#"],hostnameMaxLen=255,hostnamePartPattern=/^[a-z0-9A-Z_-]{0,63}$/,hostnamePartStart=/^([a-z0-9A-Z_-]{0,63})(.*)$/,unsafeProtocol={javascript:true,"javascript:":true},hostlessProtocol={javascript:true,"javascript:":true},slashedProtocol={http:true,https:true,ftp:true,gopher:true,file:true,"http:":true,"https:":true,"ftp:":true,"gopher:":true,"file:":true},querystring=require("querystring");function urlParse(url,parseQueryString,slashesDenoteHost){if(url&&isObject(url)&&url instanceof Url)return url;var u=new Url;u.parse(url,parseQueryString,slashesDenoteHost);return u}Url.prototype.parse=function(url,parseQueryString,slashesDenoteHost){if(!isString(url)){throw new TypeError("Parameter 'url' must be a string, not "+typeof url)}var rest=url;rest=rest.trim();var proto=protocolPattern.exec(rest);if(proto){proto=proto[0];var lowerProto=proto.toLowerCase();this.protocol=lowerProto;rest=rest.substr(proto.length)}if(slashesDenoteHost||proto||rest.match(/^\/\/[^@\/]+@[^@\/]+/)){var slashes=rest.substr(0,2)==="//";if(slashes&&!(proto&&hostlessProtocol[proto])){rest=rest.substr(2);this.slashes=true}}if(!hostlessProtocol[proto]&&(slashes||proto&&!slashedProtocol[proto])){var hostEnd=-1;for(var i=0;i127){newpart+="x"}else{newpart+=part[j]}}if(!newpart.match(hostnamePartPattern)){var validParts=hostparts.slice(0,i);var notHost=hostparts.slice(i+1);var bit=part.match(hostnamePartStart);if(bit){validParts.push(bit[1]);notHost.unshift(bit[2])}if(notHost.length){rest="/"+notHost.join(".")+rest}this.hostname=validParts.join(".");break}}}}if(this.hostname.length>hostnameMaxLen){this.hostname=""}else{this.hostname=this.hostname.toLowerCase()}if(!ipv6Hostname){var domainArray=this.hostname.split(".");var newOut=[];for(var i=0;i0?result.host.split("@"):false;if(authInHost){result.auth=authInHost.shift();result.host=result.hostname=authInHost.shift()}}result.search=relative.search;result.query=relative.query;if(!isNull(result.pathname)||!isNull(result.search)){result.path=(result.pathname?result.pathname:"")+(result.search?result.search:"")}result.href=result.format();return result}if(!srcPath.length){result.pathname=null;if(result.search){result.path="/"+result.search}else{result.path=null}result.href=result.format();return result}var last=srcPath.slice(-1)[0];var hasTrailingSlash=(result.host||relative.host)&&(last==="."||last==="..")||last==="";var up=0;for(var i=srcPath.length;i>=0;i--){last=srcPath[i];if(last=="."){srcPath.splice(i,1)}else if(last===".."){srcPath.splice(i,1);up++}else if(up){srcPath.splice(i,1);up--}}if(!mustEndAbs&&!removeAllDots){for(;up--;up){srcPath.unshift("..")}}if(mustEndAbs&&srcPath[0]!==""&&(!srcPath[0]||srcPath[0].charAt(0)!=="/")){srcPath.unshift("")}if(hasTrailingSlash&&srcPath.join("/").substr(-1)!=="/"){srcPath.push("")}var isAbsolute=srcPath[0]===""||srcPath[0]&&srcPath[0].charAt(0)==="/";if(psychotic){result.hostname=result.host=isAbsolute?"":srcPath.length?srcPath.shift():"";var authInHost=result.host&&result.host.indexOf("@")>0?result.host.split("@"):false;if(authInHost){result.auth=authInHost.shift();result.host=result.hostname=authInHost.shift()}}mustEndAbs=mustEndAbs||result.host&&srcPath.length;if(mustEndAbs&&!isAbsolute){srcPath.unshift("")}if(!srcPath.length){result.pathname=null;result.path=null}else{result.pathname=srcPath.join("/")}if(!isNull(result.pathname)||!isNull(result.search)){result.path=(result.pathname?result.pathname:"")+(result.search?result.search:"")}result.auth=relative.auth||result.auth;result.slashes=result.slashes||relative.slashes;result.href=result.format();return result};Url.prototype.parseHost=function(){var host=this.host;var port=portPattern.exec(host);if(port){port=port[0];if(port!==":"){this.port=port.substr(1)}host=host.substr(0,host.length-port.length)}if(host)this.hostname=host};function isString(arg){return typeof arg==="string"}function isObject(arg){return typeof arg==="object"&&arg!==null}function isNull(arg){return arg===null}function isNullOrUndefined(arg){return arg==null}},{punycode:6,querystring:9}],11:[function(require,module,exports){var $=require("jquery");function toggleDropdown(e){var $dropdown=$(e.currentTarget).parent().find(".dropdown-menu");$dropdown.toggleClass("open");e.stopPropagation();e.preventDefault()}function closeDropdown(e){$(".dropdown-menu").removeClass("open")}function init(){$(document).on("click",".toggle-dropdown",toggleDropdown);$(document).on("click",".dropdown-menu",function(e){e.stopPropagation()});$(document).on("click",closeDropdown)}module.exports={init:init}},{jquery:1}],12:[function(require,module,exports){var $=require("jquery");module.exports=$({})},{jquery:1}],13:[function(require,module,exports){var $=require("jquery");var _=require("lodash");var storage=require("./storage");var dropdown=require("./dropdown");var events=require("./events");var state=require("./state");var keyboard=require("./keyboard");var navigation=require("./navigation");var sidebar=require("./sidebar");var toolbar=require("./toolbar");function start(config){sidebar.init();keyboard.init();dropdown.init();navigation.init();toolbar.createButton({index:0,icon:"fa fa-align-justify",label:"Toggle Sidebar",onClick:function(e){e.preventDefault();sidebar.toggle()}});events.trigger("start",config);navigation.notify()}var gitbook={start:start,events:events,state:state,toolbar:toolbar,sidebar:sidebar,storage:storage,keyboard:keyboard};var MODULES={gitbook:gitbook,jquery:$,lodash:_};window.gitbook=gitbook;window.$=$;window.jQuery=$;gitbook.require=function(mods,fn){mods=_.map(mods,function(mod){mod=mod.toLowerCase();if(!MODULES[mod]){throw new Error("GitBook module "+mod+" doesn't exist")}return MODULES[mod]});fn.apply(null,mods)};module.exports={}},{"./dropdown":11,"./events":12,"./keyboard":14,"./navigation":16,"./sidebar":18,"./state":19,"./storage":20,"./toolbar":21,jquery:1,lodash:2}],14:[function(require,module,exports){var Mousetrap=require("mousetrap");var navigation=require("./navigation");var sidebar=require("./sidebar");function bindShortcut(keys,fn){Mousetrap.bind(keys,function(e){fn();return false})}function init(){bindShortcut(["right"],function(e){navigation.goNext()});bindShortcut(["left"],function(e){navigation.goPrev()});bindShortcut(["s"],function(e){sidebar.toggle()})}module.exports={init:init,bind:bindShortcut}},{"./navigation":16,"./sidebar":18,mousetrap:3}],15:[function(require,module,exports){var state=require("./state");function showLoading(p){state.$book.addClass("is-loading");p.always(function(){state.$book.removeClass("is-loading")});return p}module.exports={show:showLoading}},{"./state":19}],16:[function(require,module,exports){var $=require("jquery");var url=require("url");var events=require("./events");var state=require("./state");var loading=require("./loading");var usePushState=typeof history.pushState!=="undefined";function handleNavigation(relativeUrl,push){var uri=url.resolve(window.location.pathname,relativeUrl);notifyPageChange();location.href=relativeUrl;return}function updateNavigationPosition(){var bodyInnerWidth,pageWrapperWidth;bodyInnerWidth=parseInt($(".body-inner").css("width"),10);pageWrapperWidth=parseInt($(".page-wrapper").css("width"),10);$(".navigation-next").css("margin-right",bodyInnerWidth-pageWrapperWidth+"px")}function notifyPageChange(){events.trigger("page.change")}function preparePage(notify){var $bookBody=$(".book-body");var $bookInner=$bookBody.find(".body-inner");var $pageWrapper=$bookInner.find(".page-wrapper");updateNavigationPosition();$bookInner.scrollTop(0);$bookBody.scrollTop(0);if(notify!==false)notifyPageChange()}function isLeftClickEvent(e){return e.button===0}function isModifiedEvent(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function handlePagination(e){if(isModifiedEvent(e)||!isLeftClickEvent(e)){return}e.stopPropagation();e.preventDefault();var url=$(this).attr("href");if(url)handleNavigation(url,true)}function goNext(){var url=$(".navigation-next").attr("href");if(url)handleNavigation(url,true)}function goPrev(){var url=$(".navigation-prev").attr("href");if(url)handleNavigation(url,true)}function init(){$.ajaxSetup({});if(location.protocol!=="file:"){history.replaceState({path:window.location.href},"")}window.onpopstate=function(event){if(event.state===null){return}return handleNavigation(event.state.path,false)};$(document).on("click",".navigation-prev",handlePagination);$(document).on("click",".navigation-next",handlePagination);$(document).on("click",".summary [data-path] a",handlePagination);$(window).resize(updateNavigationPosition);preparePage(false)}module.exports={init:init,goNext:goNext,goPrev:goPrev,notify:notifyPageChange}},{"./events":12,"./loading":15,"./state":19,jquery:1,url:10}],17:[function(require,module,exports){module.exports={isMobile:function(){return document.body.clientWidth<=600}}},{}],18:[function(require,module,exports){var $=require("jquery");var _=require("lodash");var storage=require("./storage");var platform=require("./platform");var state=require("./state");function toggleSidebar(_state,animation){if(state!=null&&isOpen()==_state)return;if(animation==null)animation=true;state.$book.toggleClass("without-animation",!animation);state.$book.toggleClass("with-summary",_state);storage.set("sidebar",isOpen())}function isOpen(){return state.$book.hasClass("with-summary")}function init(){if(platform.isMobile()){toggleSidebar(false,false)}else{toggleSidebar(storage.get("sidebar",true),false)}$(document).on("click",".book-summary li.chapter a",function(e){if(platform.isMobile())toggleSidebar(false,false)})}function filterSummary(paths){var $summary=$(".book-summary");$summary.find("li").each(function(){var path=$(this).data("path");var st=paths==null||_.contains(paths,path);$(this).toggle(st);if(st)$(this).parents("li").show()})}module.exports={init:init,isOpen:isOpen,toggle:toggleSidebar,filter:filterSummary}},{"./platform":17,"./state":19,"./storage":20,jquery:1,lodash:2}],19:[function(require,module,exports){var $=require("jquery");var url=require("url");var path=require("path");var state={};state.update=function(dom){var $book=$(dom.find(".book"));state.$book=$book;state.level=$book.data("level");state.basePath=$book.data("basepath");state.innerLanguage=$book.data("innerlanguage");state.revision=$book.data("revision");state.filepath=$book.data("filepath");state.chapterTitle=$book.data("chapter-title");state.root=url.resolve(location.protocol+"//"+location.host,path.dirname(path.resolve(location.pathname.replace(/\/$/,"/index.html"),state.basePath))).replace(/\/?$/,"/");state.bookRoot=state.innerLanguage?url.resolve(state.root,".."):state.root};state.update($);module.exports=state},{jquery:1,path:4,url:10}],20:[function(require,module,exports){var baseKey="";module.exports={setBaseKey:function(key){baseKey=key},set:function(key,value){key=baseKey+":"+key;try{sessionStorage[key]=JSON.stringify(value)}catch(e){}},get:function(key,def){key=baseKey+":"+key;if(sessionStorage[key]===undefined)return def;try{var v=JSON.parse(sessionStorage[key]);return v==null?def:v}catch(err){return sessionStorage[key]||def}},remove:function(key){key=baseKey+":"+key;sessionStorage.removeItem(key)}}},{}],21:[function(require,module,exports){var $=require("jquery");var _=require("lodash");var events=require("./events");var buttons=[];function insertAt(parent,selector,index,element){var lastIndex=parent.children(selector).length;if(index<0){index=Math.max(0,lastIndex+1+index)}parent.append(element);if(index",{class:"dropdown-menu",html:''});if(_.isString(dropdown)){$menu.append(dropdown)}else{var groups=_.map(dropdown,function(group){if(_.isArray(group))return group;else return[group]});_.each(groups,function(group){var $group=$("
    ",{class:"buttons"});var sizeClass="size-"+group.length;_.each(group,function(btn){btn=_.defaults(btn||{},{text:"",className:"",onClick:defaultOnClick});var $btn=$("'; + var clipboard; + + gitbook.events.bind("page.change", function() { + + if (!ClipboardJS.isSupported()) return; + + // the page.change event is thrown twice: before and after the page changes + if (clipboard) { + // clipboard is already defined but we are on the same page + if (clipboard._prevPage === window.location.pathname) return; + // clipboard is already defined and url path change + // we can deduct that we are before page changes + clipboard.destroy(); // destroy the previous events listeners + clipboard = undefined; // reset the clipboard object + return; + } + + $(copyButton).prependTo("div.sourceCode"); + + clipboard = new ClipboardJS(".copy-to-clipboard-button", { + text: function(trigger) { + return trigger.parentNode.textContent; + } + }); + + clipboard._prevPage = window.location.pathname + + }); + +}); diff --git a/docs/libs/gitbook-2.6.7/js/plugin-fontsettings.js b/docs/libs/gitbook-2.6.7/js/plugin-fontsettings.js new file mode 100644 index 0000000..a70f0fb --- /dev/null +++ b/docs/libs/gitbook-2.6.7/js/plugin-fontsettings.js @@ -0,0 +1,152 @@ +gitbook.require(["gitbook", "lodash", "jQuery"], function(gitbook, _, $) { + var fontState; + + var THEMES = { + "white": 0, + "sepia": 1, + "night": 2 + }; + + var FAMILY = { + "serif": 0, + "sans": 1 + }; + + // Save current font settings + function saveFontSettings() { + gitbook.storage.set("fontState", fontState); + update(); + } + + // Increase font size + function enlargeFontSize(e) { + e.preventDefault(); + if (fontState.size >= 4) return; + + fontState.size++; + saveFontSettings(); + }; + + // Decrease font size + function reduceFontSize(e) { + e.preventDefault(); + if (fontState.size <= 0) return; + + fontState.size--; + saveFontSettings(); + }; + + // Change font family + function changeFontFamily(index, e) { + e.preventDefault(); + + fontState.family = index; + saveFontSettings(); + }; + + // Change type of color + function changeColorTheme(index, e) { + e.preventDefault(); + + var $book = $(".book"); + + if (fontState.theme !== 0) + $book.removeClass("color-theme-"+fontState.theme); + + fontState.theme = index; + if (fontState.theme !== 0) + $book.addClass("color-theme-"+fontState.theme); + + saveFontSettings(); + }; + + function update() { + var $book = gitbook.state.$book; + + $(".font-settings .font-family-list li").removeClass("active"); + $(".font-settings .font-family-list li:nth-child("+(fontState.family+1)+")").addClass("active"); + + $book[0].className = $book[0].className.replace(/\bfont-\S+/g, ''); + $book.addClass("font-size-"+fontState.size); + $book.addClass("font-family-"+fontState.family); + + if(fontState.theme !== 0) { + $book[0].className = $book[0].className.replace(/\bcolor-theme-\S+/g, ''); + $book.addClass("color-theme-"+fontState.theme); + } + }; + + function init(config) { + var $bookBody, $book; + + //Find DOM elements. + $book = gitbook.state.$book; + $bookBody = $book.find(".book-body"); + + // Instantiate font state object + fontState = gitbook.storage.get("fontState", { + size: config.size || 2, + family: FAMILY[config.family || "sans"], + theme: THEMES[config.theme || "white"] + }); + + update(); + }; + + + gitbook.events.bind("start", function(e, config) { + var opts = config.fontsettings; + if (!opts) return; + + // Create buttons in toolbar + gitbook.toolbar.createButton({ + icon: 'fa fa-font', + label: 'Font Settings', + className: 'font-settings', + dropdown: [ + [ + { + text: 'A', + className: 'font-reduce', + onClick: reduceFontSize + }, + { + text: 'A', + className: 'font-enlarge', + onClick: enlargeFontSize + } + ], + [ + { + text: 'Serif', + onClick: _.partial(changeFontFamily, 0) + }, + { + text: 'Sans', + onClick: _.partial(changeFontFamily, 1) + } + ], + [ + { + text: 'White', + onClick: _.partial(changeColorTheme, 0) + }, + { + text: 'Sepia', + onClick: _.partial(changeColorTheme, 1) + }, + { + text: 'Night', + onClick: _.partial(changeColorTheme, 2) + } + ] + ] + }); + + + // Init current settings + init(opts); + }); +}); + + diff --git a/docs/libs/gitbook-2.6.7/js/plugin-search.js b/docs/libs/gitbook-2.6.7/js/plugin-search.js new file mode 100644 index 0000000..747fcce --- /dev/null +++ b/docs/libs/gitbook-2.6.7/js/plugin-search.js @@ -0,0 +1,270 @@ +gitbook.require(["gitbook", "lodash", "jQuery"], function(gitbook, _, $) { + var index = null; + var fuse = null; + var _search = {engine: 'lunr', opts: {}}; + var $searchInput, $searchLabel, $searchForm; + var $highlighted = [], hi, hiOpts = { className: 'search-highlight' }; + var collapse = false, toc_visible = []; + + function init(config) { + // Instantiate search settings + _search = gitbook.storage.get("search", { + engine: config.search.engine || 'lunr', + opts: config.search.options || {}, + }); + }; + + // Save current search settings + function saveSearchSettings() { + gitbook.storage.set("search", _search); + } + + // Use a specific index + function loadIndex(data) { + // [Yihui] In bookdown, I use a character matrix to store the chapter + // content, and the index is dynamically built on the client side. + // Gitbook prebuilds the index data instead: https://github.com/GitbookIO/plugin-search + // We can certainly do that via R packages V8 and jsonlite, but let's + // see how slow it really is before improving it. On the other hand, + // lunr cannot handle non-English text very well, e.g. the default + // tokenizer cannot deal with Chinese text, so we may want to replace + // lunr with a dumb simple text matching approach. + if (_search.engine === 'lunr') { + index = lunr(function () { + this.ref('url'); + this.field('title', { boost: 10 }); + this.field('body'); + }); + data.map(function(item) { + index.add({ + url: item[0], + title: item[1], + body: item[2] + }); + }); + return; + } + fuse = new Fuse(data.map((_data => { + return { + url: _data[0], + title: _data[1], + body: _data[2] + }; + })), Object.assign( + { + includeScore: true, + threshold: 0.1, + ignoreLocation: true, + keys: ["title", "body"] + }, + _search.opts + )); + } + + // Fetch the search index + function fetchIndex() { + return $.getJSON(gitbook.state.basePath+"/search_index.json") + .then(loadIndex); // [Yihui] we need to use this object later + } + + // Search for a term and return results + function search(q) { + let results = []; + switch (_search.engine) { + case 'fuse': + if (!fuse) return; + results = fuse.search(q).map(function(result) { + var parts = result.item.url.split('#'); + return { + path: parts[0], + hash: parts[1] + }; + }); + break; + case 'lunr': + default: + if (!index) return; + results = _.chain(index.search(q)).map(function(result) { + var parts = result.ref.split("#"); + return { + path: parts[0], + hash: parts[1] + }; + }) + .value(); + } + + // [Yihui] Highlight the search keyword on current page + $highlighted = $('.page-inner') + .unhighlight(hiOpts).highlight(q, hiOpts).find('span.search-highlight'); + scrollToHighlighted(0); + + return results; + } + + // [Yihui] Scroll the chapter body to the i-th highlighted string + function scrollToHighlighted(d) { + var n = $highlighted.length; + hi = hi === undefined ? 0 : hi + d; + // navignate to the previous/next page in the search results if reached the top/bottom + var b = hi < 0; + if (d !== 0 && (b || hi >= n)) { + var path = currentPath(), n2 = toc_visible.length; + if (n2 === 0) return; + for (var i = b ? 0 : n2; (b && i < n2) || (!b && i >= 0); i += b ? 1 : -1) { + if (toc_visible.eq(i).data('path') === path) break; + } + i += b ? -1 : 1; + if (i < 0) i = n2 - 1; + if (i >= n2) i = 0; + var lnk = toc_visible.eq(i).find('a[href$=".html"]'); + if (lnk.length) lnk[0].click(); + return; + } + if (n === 0) return; + var $p = $highlighted.eq(hi); + $p[0].scrollIntoView(); + $highlighted.css('background-color', ''); + // an orange background color on the current item and removed later + $p.css('background-color', 'orange'); + setTimeout(function() { + $p.css('background-color', ''); + }, 2000); + } + + function currentPath() { + var href = window.location.pathname; + href = href.substr(href.lastIndexOf('/') + 1); + return href === '' ? 'index.html' : href; + } + + // Create search form + function createForm(value) { + if ($searchForm) $searchForm.remove(); + if ($searchLabel) $searchLabel.remove(); + if ($searchInput) $searchInput.remove(); + + $searchForm = $('
    ', { + 'class': 'book-search', + 'role': 'search' + }); + + $searchLabel = $('