W tej części kursu będziemy zajmowac się indeksowaniem. Bardzo często chcemy z naszych danych “wybrać” tylko niektóre z nich - te obserwacje, które nas interesują. R jest językiem oferującym ogromne możliwości manipulacji danymi. Można “filtrować” dane na wiele sposobów, my jednak zajmiemy się zupełnie podstawowymi sposobami. Wykorzystamy podstawowe konstrukcje i nauczymy się tworzyć z nich bardziej skomplikowane.
Zaprezentuje tutaj pewne podejście do kwesti indeksowania/filtrowania danych, dzięki któremu żadne tego typu zadanie nie będzie straszne. Wymagać to będzie jednak pewnej koncentracji.
Zacznijmy od prostego przypadku - stwórzmy pięcioelementowy wektor z liczbami.
x <- c(1001,1002,1003,1004,1005)
x
## [1] 1001 1002 1003 1004 1005
W jaki sposób wydobyć z tego wektora tylko jego trzeci element? Bardzo prosto! Możemy w tym celu użyć nawiasów kwadratowych.
x[3]
## [1] 1003
W przeciwieństwie do niektórych nie mamy eleganckiej możliwości wybierania elementów “od końca”. Możemy jednak posłużyć się pewnym trikiem i wykorzystać funkcję length
, która zwraca długość wektora.
# length(x) zwraca długość x, odejmujemy jeden, aby wybrać przedostatni
x[length(x)-1]
## [1] 1004
Świetnie nam poszło! Co jednak, gdybyśmy chcieli wybrać trzecią, czwartą i piątą liczbę? Spróbujmy w nawiasy kwadratowe wstawić wektor z więcej niż jednym elementem, który utworzymy za pomocą funkcji c()
.
x[c(3,4,5)]
## [1] 1003 1004 1005
Bingo! W poprzednim notatniku poznaliśmy sposób generowania wektorów kolejnych liczb. Jednym z nich była konstrukcja dolna_granica:gorna_granica
. Sprawdźmy w takim razie, czy zadziała ona w naszym przypadku.
x[3:5]
## [1] 1003 1004 1005
Doskonale! Dla wielu osób obeznanych z innymi językami programowania taka konstrukcja wygląda bardzo znajomo. Musimy jednak mieć świadomość, że w przypadku R podawanie dwukropkowych zakresów w nawiasach kwadratowych nie jest specjalną składnią do wybierania wycinków wektorów. Składnia z dwukropkiem tworzy po prostu wektor i ten właśnie wektor przekazujemy w nawiasach kwadratowych, aby wybrać wycinek naszego oryginalnego wektora.
Omówione do tej pory przykłady mogą wydawać się dość trywialne. W praktyce, analizując dane, rzadko wiemy, który element wektora chcemy wybrać. Zwykle chcemy kierować się jakimś kryterium. Rozważmy prosty przypadek. Załóżmy, że mamy wektor, w którym znajduje się wiek (w latach) uczestników badania. Moglibyśmy chcieć wybrać tylko te wartości, które są większe lub równe niż 18 po to, aby potem sprawdzić jak długi jest nasz wektor i dzięki temu dowiedzieć się, ilu mieliśmy uczestników badania.
age <- c(14, 18, 22, 18, 19, 33, 45, 14, 17, 16, 23, 26, 42)
Moglibyśmy oczywiście wyświetlić naszą zmienną i wybrać z niej tylko interesujące nas obserwacje, korzystając z poznanego przed chwilą sposobu. To jednak bardzo niepraktyczne - musielibyśmy przecież ręcznie liczyć, na których pozycjach w naszym wektorze znajdują się liczby większe niż 18! Czy da się to zrobić bardziej przebiegle? Okazuje się, że tak.
Przyjrzyjmy się temu, jak działają w R operatory arytmetyczne takie jak \(>\), \(<\), \(>=\), itp.
age >= 18
## [1] FALSE TRUE TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE TRUE TRUE
## [13] TRUE
Otrzymaliśmy wektor wartości logicznych (logical
). Wartość logiczna TRUE
znajduje się na tych pozycjach, dla których w oryginalnym wektorze age
nasz warunek (\(>=\)) był spełniony. Z kolei FALSE
znajduje się na tych pozycjach, gdzie warunek spelniony nie był.
Okazuje się, że do indeksowania możemy użyć nie tylko wektorów z pozycjami, które nas interesują, ale również wektorów z wartościami logicznymi.
age
## [1] 14 18 22 18 19 33 45 14 17 16 23 26 42
age >= 18
## [1] FALSE TRUE TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE TRUE TRUE
## [13] TRUE
age[age >= 18]
## [1] 18 22 18 19 33 45 23 26 42
W tym wypadku wyrażenie to zwraca te elementy z wektora age
, które znajdują się na pozycjach, na których w wektorze age >= 18
występuje wartość TRUE
. Na początku może wydawać się to dość skomplikowane, ale podejście to otwiera niesamowicie dużo możliwości.
Wiemy już że w nawiasach kwadratowych mogą znajdować się dwa rodzaje wartości - albo wektor typu integer
albo wektor typu logical
.
Najtrudniejsze za nami.
Czy możemy wykorzystać nasze umiejętności w sytuacji, gdy chcemy wybrać wartości z dwuwymiarowej macierzy? Zasada jest bardzo podobna. Musimy w nawiasach kwadratowych umieścić dwie wartości - numer(y) wiersza i numer(y) kolumny. Zilustrujmy to przykładem:
macierz <- matrix(1:20, 5,4)
macierz
## [,1] [,2] [,3] [,4]
## [1,] 1 6 11 16
## [2,] 2 7 12 17
## [3,] 3 8 13 18
## [4,] 4 9 14 19
## [5,] 5 10 15 20
# Pamiętamy o zasadzie: *wiersze pierwsze*!
macierz[3,4] # trzeci wiersz i czwarta kolumna
## [1] 18
Co jeśli chcemy wybrać wiersze lub kolumny w całości? R oferuje tutaj dość przyjazną składnie. Jeżeli zostawimy po którejś stronie przecinka w nawiasie kwadratowym puste miejsce, R potraktuje to jako wszystkie kolumny lub wiersze.
Przykładowo, jeśli chcemy wybrać drugą, trzecią oraz czwartą kolumnę, to możemy to musimy powiedzieć R, że chcemy: - wybrać wszystkie wiersze, - wybrać kolumny 2, 3 oraz 4.
Wynikiem takiej operacji będzie nowa macierz składająca się z 3 kolumn oraz takiej liczby wierszy, jaką miala oryginalna macierz.
# wszystkie wiersze, ostatnie trzy kolumny (używamy pustego miejsca)
macierz[,2:4]
## [,1] [,2] [,3]
## [1,] 6 11 16
## [2,] 7 12 17
## [3,] 8 13 18
## [4,] 9 14 19
## [5,] 10 15 20
Dokładnie w ten sam sposób możemy zastosować do wierszy:
macierz[1:3,] # analogicznie pierwsze trzy wiersze
## [,1] [,2] [,3] [,4]
## [1,] 1 6 11 16
## [2,] 2 7 12 17
## [3,] 3 8 13 18
Sprobujmy zrobić coś nieco trudniejszego. Załóżmy, że chcemy stworzyć nową macierz, w której znajdą się wszystkie kolumny z oryginalnej macierzy, ale wyłącznie dla tych wierszy, dla których w drugiej kolumnie występowała wartość większa niż 6.
Początkowo może brzmieć to skomplikowanie, ale wystarczy, że wykorzystamy nasze dodatkowe umiejętności. Odpowiednie wyrażenie wygląda tak:
macierz[macierz[,2] >6,]
## [,1] [,2] [,3] [,4]
## [1,] 2 7 12 17
## [2,] 3 8 13 18
## [3,] 4 9 14 19
## [4,] 5 10 15 20
Jak widzimy wyrażenie to prawidłowo wybrało trzeci oraz czwarty wiesz.
Spróbujmy prześledzić powyższy przykład krok po kroku.
Oto nasza macierz w całej okazałości:
macierz
## [,1] [,2] [,3] [,4]
## [1,] 1 6 11 16
## [2,] 2 7 12 17
## [3,] 3 8 13 18
## [4,] 4 9 14 19
## [5,] 5 10 15 20
Jej drugą kolumnę możemy wybrać, korzystając ze znanego nam sposobu.
macierz[ ,2]
## [1] 6 7 8 9 10
Następnie musimy sprawdzić, które wartości w tej kolumnie są większe niż 6. Wykorzystamy do tego standardowy operator arytmetyczny \(>\).
macierz[ ,2] > 6
## [1] FALSE TRUE TRUE TRUE TRUE
Wiemy teraz, że w drugiej kolumnie wartości większe niż 6 znajdują się w trzecim i czwartym wierszu. Zauważmy, że wyrażenie to zwróciło wektor typu logical
. Wobec tego możemy go użyć do wybrania z oryginalnej macierzy odpowiednich wierszy, w pewnym sensie składając wszystkie te wyrażenia w jedną całość.
macierz[macierz[,2] > 7, ]
## [,1] [,2] [,3] [,4]
## [1,] 3 8 13 18
## [2,] 4 9 14 19
## [3,] 5 10 15 20
Nie było to takie straszne!
Listy w przeciwieństwie do wektorów przechowują sekwencje wartości różnych typów. Możemy w niej przechowywać wektory różnych typów o różnej długości a nawet inne listy. W tym sensie są podobne do list z niektórych innych języków programowania. Listy tworzymy za pomocą funkcji list
.
lista <- list(1:10, # pierwszy element: wektor typu `integer`
# drugi element: wektor typu `character`
c('Entliczek', "Pętliczek", 'Czerwony', 'Stoliczek'),
TRUE, # trzeci element: wektor typu `logical`
TRUE, # czwarty element: wektor typu `logical`
list( # piąty element: lista
# pierwszy element: wektor typu `character`
c('Lista', "zagnieżdżona", "w", "drugiej", "liście")
))
lista
## [[1]]
## [1] 1 2 3 4 5 6 7 8 9 10
##
## [[2]]
## [1] "Entliczek" "Pętliczek" "Czerwony" "Stoliczek"
##
## [[3]]
## [1] TRUE
##
## [[4]]
## [1] TRUE
##
## [[5]]
## [[5]][[1]]
## [1] "Lista" "zagnieżdżona" "w" "drugiej" "liście"
Elementy listy możemy wybrać za pomocą podwójnego nawiasu kwadratowego [[]]
. Przykładowo:
lista[[3]] # trzeci element listy
## [1] TRUE
W przypadku zagnieżdżonych list bardzo często chcemy wybrać pewien element pewnego elementu listy. Wówczas musimy użyć znanej już nam techniki. Spróbujmy wybrać słowo “Czerwony”.
lista[[2]][3] # trzeci element drugiego elementu listy
## [1] "Czerwony"
Podobnie, jeżeli mamy listy zagnieżdżone, możemy użyć kilku indeksów jednocześnie. Wybierzmy więc słowo “drugiej”:
lista[[5]][[1]][4] # czwarty element pierwszego elementu listy piątego elementu listy
## [1] "drugiej"
Elementom listy możemy nadawać nazwy. Dlatego w niektórych sytuacjach możemy traktować je jak słowniki z innych języków programowania, przechowujące pary klucz-wartość.
Nazwane listy również tworzymy przy pomocy funkcji list
.
lista_nazwana <- list(imiona = c('Marek', 'Kasia', 'Sławek'),
czesci_ciala = c('Noga', 'Ręka', 'Palec', 'Głowa')
)
lista_nazwana
## $imiona
## [1] "Marek" "Kasia" "Sławek"
##
## $czesci_ciala
## [1] "Noga" "Ręka" "Palec" "Głowa"
W przypadku list z nazwanymi elementami, możemy wybierać je za pomocą liczb albo nazw.
lista_nazwana[[1]] # możemy wybierać cały czas za pomocą liczb
## [1] "Marek" "Kasia" "Sławek"
lista_nazwana[['imiona']] # ale często wygodniej będzien nam użyć nazw
## [1] "Marek" "Kasia" "Sławek"
lista_nazwana$imiona # krótszy, często spotykany zapis
## [1] "Marek" "Kasia" "Sławek"
W przypadku ramek danych, to wybieranie z nich elementów działa bardzo podobnie, co wybieranie elementow z macierzy oraz z list.
Muszę uprzedzić, że pod koniec lekcji kod stanie się niestety strasznie “makaroniasty”. Istnieje wiele bibliotek do R, które ułatwiają tego rodzaju zadania. Zależy mi jednak, aby państwo uważnie prześledzili poniższe przykłady i spróbowali zrozumieć każdy krok oraz nauczyli się przeprowadzać “dekonstrukcje” poszczególnych elementów długiego i zagmatwanego wyrażenia.
W tym przykładzie będziemy korzystać z ramki danych mtcars
dostępnej w standardowej instalacji R. Ten zbiór danych zawiera informacje o kilkunastu modelach samochodów. Możemy podejrzeć pierwsze 6 wierszy za pomocą funkcji head
.
data(mtcars)
head(mtcars)
## mpg cyl disp hp drat wt qsec vs am gear carb
## Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
## Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
## Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
## Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
## Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
## Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
Rozpoczniemy od przyjrzenia się naszemu zbiorowi danych. Zobaczmy, ile ma kolumn, ile wierszy i jak nazywają się poszczególne kolumny.
dim(mtcars)
## [1] 32 11
Mamy 32 wiersze oraz 11 kolumn. Pierwsza kolumna (z nazwami samochodów) to nazwa wiersza (rownames
).
colnames(mtcars)
## [1] "mpg" "cyl" "disp" "hp" "drat" "wt" "qsec" "vs" "am" "gear"
## [11] "carb"
Za pomocą pojedynczych nawiasów kwadratowych możemy wybrać z danej ramki danych pewien jej podzbiór. Wybierzmy pierwszą kolumnę.
head(mtcars[1]) # domyślnie head zwraca pierwsze 6 wierszy
## mpg
## Mazda RX4 21.0
## Mazda RX4 Wag 21.0
## Datsun 710 22.8
## Hornet 4 Drive 21.4
## Hornet Sportabout 18.7
## Valiant 18.1
Spróbujmy wybrać drugą, czwartą i szóstą kolumnę umieszczając w nawiasach kwadratowych trzyelementowy wektor. Funkcja tail
jest bardzo podobna do funkcji head
i umożliwia wybranie ostatnich 6 wierszy.
tail(mtcars[c(2,4,6)])
## cyl hp wt
## Porsche 914-2 4 91 2.140
## Lotus Europa 4 113 1.513
## Ford Pantera L 8 264 3.170
## Ferrari Dino 6 175 2.770
## Maserati Bora 8 335 3.570
## Volvo 142E 4 109 2.780
Jeżeli chcemy, to ramki danych indeksować możemy dokładnie tak jak macierze. Wybierzmy wartość z drugiego wiersza (Mazda RX4 WAG) czwartej kolumny (hp
czyli liczba koni mechanicznych).
mtcars[2,4]
## [1] 110
Za pomocą podwójnych nawiasów kwadratowych możemy wybrać całą kolumnę jako wektor.
mtcars[[3]]
## [1] 160.0 160.0 108.0 258.0 360.0 225.0 360.0 146.7 140.8 167.6 167.6 275.8
## [13] 275.8 275.8 472.0 460.0 440.0 78.7 75.7 71.1 120.1 318.0 304.0 350.0
## [25] 400.0 79.0 120.3 95.1 351.0 145.0 301.0 121.0
Do indeksowania możemy używać oczywiście również nazw. Wybierzmy dwie kolumny - opisujące liczbę biegów i liczbę koni mechanicznych.
head(mtcars[c('hp', 'gear')])
## hp gear
## Mazda RX4 110 4
## Mazda RX4 Wag 110 4
## Datsun 710 93 4
## Hornet 4 Drive 110 3
## Hornet Sportabout 175 3
## Valiant 105 3
Przekazując w nawiasach kwadratowych nazwę kolumny otrzymujemy ją jako wektor.
mtcars[['hp']]
## [1] 110 110 93 110 175 105 245 62 95 123 123 180 180 180 205 215 230 66 52
## [20] 65 97 150 150 245 175 66 91 113 264 175 335 109
Nic nie stoi na przeszkodzie, żebyśmy wybrali zarówno kolumny, jak i wiersze. Robimy to tak samo jak w przypadku macierzy, oddzielając wartości przecinkiem.
mtcars[2:5, c(3,5)]
## disp drat
## Mazda RX4 Wag 160 3.90
## Datsun 710 108 3.85
## Hornet 4 Drive 258 3.08
## Hornet Sportabout 360 3.15
Ale uwaga! Jeśli wybierzemy tylko jedną konkretną wartość jako kolumnę (a nie zakres) to nie otrzymamy ramki danych z jednym elementem, ale konkretną wartość odpowiedniego typu. Bywa to czasami uciążliwe.
Zilustrujmy to na przykładzie. Jeżeli wybierzemy drugi wiersz kolumny 3, 4 i 5, to R zwróci nam ramkę danych.
mtcars[2,3:5]
## disp hp drat
## Mazda RX4 Wag 160 110 3.9
Z kolei jeśli wybierzemy wiersze 3, 4 i 5 oraz drugą kolumnę, to R zwróci nam wektor.
mtcars[3:5,2]
## [1] 4 6 8
Za pomocą poznanych narzędzi możemy łatwo filtrować dane. Wyobraźmy sobie, że interesuje nas ramka danych z tylko dwoma wartosciami - liczbą koni mechanicznych oraz mil na galon benzyny. Chcemy jednak wybrać wyłącznie te samochody, które mają więcej niż 150 koni mechanicznych. Operację tę możemy wykonać w dwóch krokach:
Zastanawiamy się jak wydobyć z naszej ramki danych tylko te wiersze, w których wartość hp jest większa niż 150.
Zastanawiamy się, jak stworzyć ramkę w której będą tylko te dwie kolumny.
Do pierwszego zadania wykorzystamy sposób analogiczny do tego poznanego przy macierzach, drugie zrobimy sposobem poznanym przed chwilą.
Stwórzmy nową ramkę danych, w której będą tylko samochody mające więcej niż 150 koni mechanicznych.
mt_cars_150hp = mtcars[mtcars[['hp']] > 150,] # Krok 1 - filtrujemy
Następnie z tej nowoutworzonej ramki danych wybierzmy dwie kolumny - mpg
(miles per galon czyli liczba mil, które można przejechać na jednym galonie benzyny) oraz hp
.
mt_cars_150hp[c('mpg','hp')] # Krok 2 - wybieramy
## mpg hp
## Hornet Sportabout 18.7 175
## Duster 360 14.3 245
## Merc 450SE 16.4 180
## Merc 450SL 17.3 180
## Merc 450SLC 15.2 180
## Cadillac Fleetwood 10.4 205
## Lincoln Continental 10.4 215
## Chrysler Imperial 14.7 230
## Camaro Z28 13.3 245
## Pontiac Firebird 19.2 175
## Ford Pantera L 15.8 264
## Ferrari Dino 19.7 175
## Maserati Bora 15.0 335
Obie operacje możemy również wykonać w jednym kroku.
mtcars[mtcars[['hp']] > 150, c('mpg','hp')]
## mpg hp
## Hornet Sportabout 18.7 175
## Duster 360 14.3 245
## Merc 450SE 16.4 180
## Merc 450SL 17.3 180
## Merc 450SLC 15.2 180
## Cadillac Fleetwood 10.4 205
## Lincoln Continental 10.4 215
## Chrysler Imperial 14.7 230
## Camaro Z28 13.3 245
## Pontiac Firebird 19.2 175
## Ford Pantera L 15.8 264
## Ferrari Dino 19.7 175
## Maserati Bora 15.0 335
Do zawartości kolumn możemy dostać się także za pomocą znaku dolara ($
) oraz ich nazw. Sposób ten jest równoważny z wybieraniem wartości za pomocą podwójnego nawiasu kwadratowego (mtcars[["mpg"]]
). Rezultatem tej operacji jest nie ramka danych (jak w przypadku mtcars["mpg"]
), lecz wektor.
mtcars$mpg
## [1] 21.0 21.0 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 17.8 16.4 17.3 15.2 10.4
## [16] 10.4 14.7 32.4 30.4 33.9 21.5 15.5 15.2 13.3 19.2 27.3 26.0 30.4 15.8 19.7
## [31] 15.0 21.4
Zobaczmy, jakie wartości w kolumnie ‘mpg’ występują dla samochodów, które mają powyżej 150 koni mechanicznych. Użyjemy do tego dokładnie takiej samej techniki jak w przypadku macierzy, zastępujemy jednak liczbę określającą kolumnę jej nazwą.
mtcars[mtcars[['hp']] > 150,'mpg']
## [1] 18.7 14.3 16.4 17.3 15.2 10.4 10.4 14.7 13.3 19.2 15.8 19.7 15.0
Załóżmy, że że chcemy się dowiedzieć, jaka jest najwyższa wartość w tej kolumnie. Użyjemy więc funkcji max
, która zwraca największą wartość w danym wektorze.
max(mtcars[mtcars[['hp']] > 150,'mpg']) # analogicznie moglibyśmy użyć funkcji min
## [1] 19.7
Nie wiemy jednak co to za samochód! Oczywiście, moglibyśmy po prostu zajrzeć do naszej oryginalnej ramki danych, ale chcielibyśmy, żeby R zrobił to za nas. Wiemy, jaka jest największa wartość w kolumnie mpg
dla samochodów, które mają więcej niż 150 koni mechanicznych - jest to \(19.7\). W takim razie możemy wybrać z naszej ramki danych tylko ten wiesz, w którym wartość w kolumnie mpg
jest równa właśnie \(19.7\).
mtcars[mtcars$mpg == max(mtcars[which(mtcars[['hp']] > 150),'mpg']), ]
## mpg cyl disp hp drat wt qsec vs am gear carb
## Ferrari Dino 19.7 6 145 175 3.62 2.77 15.5 0 1 5 6
Spróbujmy rozbić to na kilka mniejszych kroków.
Krok pierwszy: Wybranie wartości z kolumny mpg
dla samochodów, które mają więcej niż 150 koni mechanicznych:
mpg_for_over_150hp <- mtcars[which(mtcars[['hp']] > 150),'mpg']
mpg_for_over_150hp
## [1] 18.7 14.3 16.4 17.3 15.2 10.4 10.4 14.7 13.3 19.2 15.8 19.7 15.0
Krok drugi: ustalenie, jaka jest najwyższa wartość w tym wektorze:
max_mpg_for_over_150hp <- max(mpg_for_over_150hp)
max_mpg_for_over_150hp
## [1] 19.7
Krok trzeci: sprawdzenie, dla których wierszy w ramce danych mtcars
wartość w kolumnie mpg
wynosi max_mpg_for_over_150hp
:
which_max_mpg_for_over_150hp <- mtcars$mpg == max_mpg_for_over_150hp
which_max_mpg_for_over_150hp
## [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [13] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [25] FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE
Krok czwarty: użycie wynikowego wektora typu logical
do wybrania interesującego nas wiersza:
mtcars[which_max_mpg_for_over_150hp, ]
## mpg cyl disp hp drat wt qsec vs am gear carb
## Ferrari Dino 19.7 6 145 175 3.62 2.77 15.5 0 1 5 6
Wszystko to możemy (jeżeli chcemy) zrobić w jednym kroku, ale kod może być nieczytelny - szczególnie dla początkujących programistów w R!
mtcars[mtcars$mpg == max(mtcars[which(mtcars[['hp']] > 150),'mpg']), ]
## mpg cyl disp hp drat wt qsec vs am gear carb
## Ferrari Dino 19.7 6 145 175 3.62 2.77 15.5 0 1 5 6
Zauważmy, że w ten sposób możemy konstruować bardzo skomplikowane reguły wybierania wartości z ramek danych. Możemy używać dowolnych operatorów arytmetycznych oraz logicznych, dzięki czemu możemy tworzyć złożone warunki. Czasami jednak (jak w tym przykładzie) warto rozbić kod na kilka kroków.