---
title: "Konstrukcje warunkowe"
author: "Bartosz Maćkiewicz"
subtitle: Statystyka I z R
output:
  html_document: default
  pdf_document: default
---

# Konstrukcje warunkowe

W tym notatniku zajmiemy się różnymi rodzajami konstrukcji warunkowych w R. 

Podstawowe konstrukcje to: konstrukcjami:

- `if / else`
- `ifelse`
- `for`
- `while`

Jako materiał dydaktyczny posłuży nam tekst piosenki Ice Cube "It Was A Good Day". W piosence tej Cube opowiada o swoim dobrym dniu. Zacznijmy więc od początku.

*Just wakin up in the mornin gotta thank God*

*I don't know but today seems kinda odd*

*No barkin from the dog, no smog*

*[...] I can't believe, today was a good day*

Spróbujmy się dowiedzieć, czy dzisiaj był dla Ice Cube'a dobry dzień. Zacznijmy od szczekania psa.

## `if`

Najprostszą konstrukcją warunkową jest pętla `if`. Wygląda dokładnie tak samo jak w każdym innym języku programowania - jeżeli warunek jest prawdziwy, to wykonuje okreslone polecenia.

```{r}
barkinFromTheDog <- FALSE # wiemy, że pies dzisiaj nie szczekał

if (barkinFromTheDog == FALSE) print('It was a good day')
```

Możemy łatwo wprowadzić do naszego wyrażenia negację za pomocą operatora `!`.

```{r}
!barkinFromTheDog
if (!barkinFromTheDog) print('It was a good day')
```

Oraz konstruować z operatorów funktorów logicznych koniunkcji (`&`) i alternatywy (`|`) bardziej skomplikowane warunki. Nasze instrukcje moga być wielolinikowe, musimy je jednak otoczyć nawiasami klamrowymi (`{}`).

```{r}
smog <- FALSE

if (barkinFromTheDog == FALSE & smog == FALSE) {
    print("It was")
    print('a good day')
    }
```

Oczywiście jeżeli warunek jest fałszywy, to pętla nie zrobi nic.

```{r}
smog = TRUE
if (barkinFromTheDog == FALSE & smog == FALSE) {
    print('It was a good day')
    }
```

Możemy naturalnie, jak w każdym języku programowania, określić co ma się dziać, jeżeli warunek nie jest spełniony. Służy nam do tego słowo kluczowe `else`.

```{r}
if (barkinFromTheDog == FALSE | smog == FALSE) {
    print('It was a mediocre day')
} else {
    print('It was not a good day')
     }

# To zadziała
```

**Uwaga** - `else` musi być w tej samej linijce, co nawiasy klamrowe okalające blok `if`

```{r eval = F}
if (barkinFromTheDog == FALSE | smog == FALSE) {
    print('It was a mediocre day')
} 
else {
    print('It was not a good day')
}
# A to nie zadziała
```

Przy łączeniu kilku warunków w jeden - tak samo jak na logice - należy pamiętać o nawiasach. Te dwie konstrukcje nie są równoważne!

```{r}
smog = FALSE

if (!(barkinFromTheDog & smog)) {
    print('It was a good day')
} else {
    print('It was not a good day')
}

if (!barkinFromTheDog & smog) {
    print('It was a good day')
} else {
    print('It was not a good day')
     }
```

Cube rapuje, że w zeszłym tygodniu grając w koszykówkę zdobył *triple-double* czyli dwucyfrową zdobycz w trzech kategoriach statystycznych (punkty, asysty, zbiórki, przechwyty lub bloki)

*"Last week [...] around and got a triple double"*

Spróbujmy skonstruować odpowiedni warunek. Wykorzystamy do tego interesującą własność R.

Stwórzmy najpierw pięć zmiennych, w których będziemy przechowywać liczbę punktów, asyst, zbiórek, bloków i przechwytów.

```{r}
points <- 12
assists <- 14
rebounds <- 7
blocks <- 10
steals <- 3
```

Następnie stwórzmy wektor, w którym znajdą się nasze statystyki

```{r}
stats <- c(points, assists, rebounds, blocks, steals)
stats
```

Sprawdźmy, ile ze statystyk nich było większe lub równe 10. 

```{r}
stats >= 10
greather_than_10 <- stats[stats >= 10]
greather_than_10
```

Stwórzmy następnie nową zmienną, która przyjmie wartość `TRUE` tylko wtedy, gdy Ice Cube zdobył *triple-double*.

```{r}
triple_double <- length(greather_than_10) >= 3
triple_double
```

Następnie możemy włączyć naszą nową zmienną do warunku w konstrukcji warunkowej `if`.

```{r}
if (!(barkinFromTheDog & smog) & triple_double) {
    print('It was a good day')
} else {
    print('It was not a good day')
     }
```

## `ifelse`

Konstrukcja `ifelse` nie jest prawdziwą pętlą, ale jest do pętli podobna i można jej użyć do bardzo wielu zadań. Funkcja ta przyjmuje trzy argumenty i zwraca wektor. Ogólna konstrukcja wygląda tak:

```{r eval = F}
ifelse(test,yes,no)
```

lub bardziej opisowo

```{r eval = F}
ifelse(warunek, wartosc_kiedy_spelniony, wartosc_kiedy_niespelniony)
```

- `warunek` - wektor typu `logical`
- `wartosc_kiedy_spelniony` - wartość w wynikowym wektorze dla pozycji, dla których w `warunek` znajduje się `TRUE`
- `wartosc_kiedy_niespelniony` - wartość w wynikowym wektorze dla pozycji, dla których w `warunek` znajduje się `FALSE`

Jest ona o tyle przydatna, że jeżeli przekażemy jej na wejściu wektor, to warunek będzie sprawdzany dla każdego elementu i funkcja również zwróci wektor. Przećwiczmy wykorzystanie tej funkcji na przykładzie znanego nam zbioru danych `mtcars`.

```{r}
data(mtcars) # załadujmy zbiór mtcars
head(mtcars) # pierwsze 6 wierszy
```

Dzięki funkcji `ifelse` łatwo możemy pogrupować samochody na szybkie i wolne. Przyjmijmy arbitralną granicę, że 150 koni mechanicznych oznacza, że samochód jest szybki.

```{r}
head(mtcars$hp)
ifelse(mtcars$hp > 150, 'szybki', 'wolny')
```

Możemy teraz do naszej ramki danych dołączyć dodatkową kolumnę z informacją, czy dany samochóð jest szybki.

```{r}
mtcars['predkosc'] = ifelse(mtcars$hp > 150, 'szybki', 'wolny')
```

```{r}
head(mtcars, 7)
```

A co jeżeli mamy więcej niż dwie kategorie? Załóżmy, że chcemy podzielić samochody pod względem ekonomiczności na 3 kategorie - tanie (do 15 w kolumnie `mpg`), średnie (między 16 a 25 w kolumnie `mprg`) i drogie (więcej niż 25 w kolumnie `mpg`) ze względu na to ile palą benzyny. Do takich zadań są specjalnie pakiety, ale mozemy zrobić to również za pomocą kilku `ifelse`.

Dzielimy najpierw samochody na średnie i tanie.

```{r}
mtcars['ekonomicznosc'] <- ifelse(mtcars$mpg > 15, 'średni', 'tani')
mtcars$ekonomicznosc
```

Następnie tym, które przekraczają próg "drogości" przypisujemy 'drogi', a resztę wartości zostawiamy.

```{r}
mtcars['ekonomicznosc'] = ifelse(mtcars$mpg > 25, 'drogi', mtcars$ekonomicznosc)
mtcars$ekonomicznosc
tail(mtcars)
```

Inny sposób to użycie funkcji `cut`. Za za jej pomocą możemy stworzyć `factor` dzieląć wektor liczbowy na równe części.

```{r}
cut(mtcars$mpg, 3)
cut(mtcars$mpg, 3, labels = c('tani', 'sredni', 'drogi'))
```

## `for`

Pętla `for` wygląda dość standardowo. Jej konstrukcja wygląda następująco:

```{r eval = F}
for (wartosc in wektor) polecenie`
```

Wewnątrz pętli `for` możemy odwoływać się do aktualnej wartości za pomocą jej nazwy (`wartosc`).

Wyświetlenie wszystkich liczb od 1 do 10 i od 20 do 30 możemy więc wykonać w ten sposób:

```{r}
for (i in 1:10) print(i)
```

```{r}
for (i in 20:30) print(i)
```

Staramy się nie robić tak, jak w przykładzie niżej. Nie dlatego, że to nie działa, ale dlatego, że uznawane jest to za kiepski styl w R. Jeżeli mamy wektor, po którym możemy iterować, to nie powinniśmy wprowadzać dodatkowych zmiennych w naszym kodzie. W wielu językach programowania konstrukcje, takie jak ta poniżej, są w porzadku, w R jednak nie powinniśmy ich używać.

```{r}
n <- 20
for (i in 1:11){
    print(n)
    n <- n + 1
}
```

Wektory po których iteruje pętla `for` nie muszą być liczbami. w R możliwe jest iterowanie po dowolnym wektorze.

```{r}
rymowanka <- c('Entliczek', 'Pętliczek', 'Czerwony', 'Stoliczek')
for (slowo in rymowanka){ # tak robimy w R
    print(slowo) # możemy odwołać się do wartości za pomocą `slowo`
}
```

Dlatego też staramy się nie robić tak jak tutaj:

```{r}
for(i in 1:length(rymowanka)) print(rymowanka[i]) # tak nie robimy w R PROSZĘ!
```

Czasami będziemy jednak do tego zmuszeni. Wyobraźmy sobie, że chcemy za pomocą R stworzyć krótkie podsumowanie naszej dotychczasowej pracy nad samochodami. Nauczymy się później jak zrobić to bardziej idiomatycznie dla R, teraz zróbmy to jednak za pomocą pętli `for`. Idea jest taka, żeby dla każdego samochodu wydrukować na ekranie zdanie dotyczace jego ekonomiczności. Skorzystamy w tym celu z funkcji `paste`.

```{r}
for (wiersz in 1:nrow(mtcars)){
    zdanie <- paste( # funkcja umożliwiająca konkatenację zmiennych typu `character`
      row.names(mtcars)[wiersz], # row.names służy do wydobywania z ramki danych nazw wierszy
      'to samochód',
      mtcars[wiersz, 'ekonomicznosc'], # wybieramy dla każdego samochodu wartość z kolumny `ekonomicznosc`
      'w eksploatacji.'
      )
    print(zdanie)
}
```

Jeżeli już *koniecznie* musimy z jakiegoś powodu iterować po każdej wartości z macierzy, możemy to zrobić za pomocą zagnieżdżonych dwóch pętli `for`. Spróbujmy stworzyć macierz i dodać 5 do każdej niepodzielnej przez trzy liczby oraz odjąć 5 od każdej podzielnej przez 3.

```{r}
macierz = matrix(1:25, 5,5)
macierz
```

```{r}
# Podzielność przez 3 sprawdzamy operatorem modulo czyli %
for (kolumna in 1:ncol(macierz)){ # pierwsza pętla for iteruje po kolumnach
    for (wiersz in 1:nrow(macierz)){ # druga pętla for po wierszach
        if (macierz[wiersz,kolumna] %% 3 == 0){ 
            macierz[wiersz,kolumna] <- macierz[wiersz,kolumna] - 5} else {
            macierz[wiersz,kolumna] <- macierz[wiersz,kolumna] + 5}
}}
macierz
```

Bardziej obeznany z R zrobiłby to oczywiście inaczej. Niezłym pomysłem jest przekonwertowanie macierzy do postaci wektora, zaaplikowanie funkcji `ifelse` i stworzenie z wektora nowej macierzy.

```{r}
macierz <- matrix(1:25, 5,5)
m <- as.numeric(macierz)
matrix(ifelse(m %% 3 == 0,  # warunek
              m - 5,  # jeśli warunek spełniony
              m + 5) # jeśli warunek niespełniony
       ,5,5)
```

# `while`

Istotą pętli `while` jest powtarzanie pewnego bloku kodu, dopóki `warunek` jest prawdziwy.

```{r eval = F}
while (warunek) polecenie
```

Spróbujmy za pomocą pętli `while` stworzyć kod do sprawdzania, czy dana liczba jest liczbą pierwszą implementując kiepski algorytm, który próbuje podzielić ją przez kolejne liczby naturalne większe od 2.

```{r}
n <- 421 # liczba do sprawdzenia
pierwsza <- FALSE # czy jest to liczba pierwsza?
d <- 2 # pierwszy potencjalny dzielnik jaki sprawdzamy

while(!pierwsza){    
    if (n %% d == 0){
      print(d)
      pierwsza = TRUE
      }
    d <- d+1
}
pierwsza
```

Zamiast while możemy użyć pętli `for` ze kluczowym słowem `break`. `break` użyte w kodzie sprawia, że R "ucieka" z nadrzędnej pętli. W naszym wypadku po znalezieniu dzielnika R opuszcza nadrzędną pętle `for`.

```{r}
n <- 35
for(i in 2:n){
    if(n %% i == 0){
        print(paste('Dzielnik to', as.character(i)))
        break # jeżeli warunek się spełni, to nie wykona się już żadna iteracja `for`
    } else{
        print(paste(as.character(i), 'nie jest dzielnikiem.'))
    }
}
```

Tutaj mamy trochę lepszy algorytm, który wykorzystuje fakt że:

- jeżeli liczba $n$ nie jest liczbą pierwszą, to pierwiastek każdego jej dzielnika jest od niej mniejszy
- po sprawdzeniu podzielności przez $2$ nie musimy sprawdziać, czy dana liczb dzieli się przez liczby parzyste
- po sprawdzeniu podzielności przez $3$ nie musimy sprawdzać, czy dzieli się przez wielokrotności $3$

```{r}
n <- 421
pierwsza <- FALSE

while(!pierwsza){    
    if (n <= 3){
      print('To jest liczba pierwsza')
      pierwsza = TRUE
      }
    if (n %% 2 == 0 | n %% 3 == 0){
      print('Dzielnik to 2 lub 3')
      pierwsza = FALSE
      }
    i = 5
    while (sqrt(i) < n){
        if((n %% i) == 0 | (n %% (i+2)) == 0){
            p <- paste('Dzielnik to', as.character(i), 'lub', as.character(i+2))
            print(p)
            pierwsza = TRUE}
        i <- i + 6
    }
}
```