1 Macierze

Macierze, podobnie jak wektory, mogą zawierać elementy dowolnego, ale tego samego typu (nie można utworzyć macierzy zawierającej jednocześnie np. wartości liczbowych i tekstowych).

Podstawowa polecenie służące tworzeniu macierzy to
matrix(vec, nrow=n, ncol=p, byrow=T),
gdzie vec jest wektorem zawierającym kolejne elementy macierzy, które standardowo będą rozmieszczone kolumnami. W celu rozmieszczenia elementów wierszami należy wybrać opcję byrow=T.

1.1 Tworzenie macierzy

a=1:20
x1=matrix(a,nrow=5) #Tworzy macierz 5x4 z wektora `a` (wartości uporządkowane są kolumnami)
x1
x2=matrix(a,nrow=5,byrow=T) #Tworzy macierz 5x4 
#z wektora `a` (wartości uporządkowane są wierszami) 

1.2 Wybrane operacje na macierzach

Przetestować działanie poniższych poleceń

x3=t(x2) #Transpozycja macierzy x2
b=x1*x2 #Iloczyn macierzy element po elemencie
b=x3%*%x2 #Standardowy iloczyn macierzy
dim(b) #Podaje wymiar macierzy
b[3,2] #Wybranie elementu b32 macierzy b
b[3,2]=5 #Przypisanie wartości do elementu macierzy
b[,2] #Wybranie drugiej kolumny macierzy b
b[c(3,4),] #Wybranie wiersza 3 i 4 macierzy b
b[-2,] #Wybranie wszystkich wierszy macierzy b poza drugim wierszem
b[,-c(2,4)] #Wybranie wszystkich kolumn macierzy b poza drugą i czwartą
b[1,]>600 # Wykonanie testu na pierwszym wierszu macierzy
ind=which(b[1,]>600)
b[,ind] #Wybranie tych kolumn macierzy b , których pierwszy element
jest większy niż 600 (jako wynik wcześniejszego polecenia)
rbind(x1,x2) #Połączenie wierszowe macierzy, czyli utworzenie macierzy,
której wierszami są kolejne wiersze macierzy x1,
a następnie macierzy x2. Polecenie to pozwala na
dołączanie kolejnych wierszy do macierzy, efektem jest macierz postaci blokowej:\[ \left[ \begin{array}{c} x_1\\x_2 \end{array} \right] \]
cbind(x1,x2)

#Poziome, “kolumnowe” połączenie dwóch macierzy, czyli utworzenie macierzy, w której (licząc od lewej) najpierw są kolumny macierzy x1,
a następnie kolumny macierzy x2. W postaci blokowej:

\[\left[ \begin{array}{c c} x_1 & x_2 \end{array} \right] \]

apply(x1,2,sum) #Funkcja apply pozwala na zastosowanie funkcji działającej
na wektorach do macierzy x1 (w tym przypadku sumy)
do kolumn (drugi argument jest tutaj równy 2) lub wierszy (drugi argument byłby równy 1). W wyniku dostajemy wektor zawierający sumy elementów
poszczególnych kolumn macierzy x1
apply(x1,1,sum) #Jak wyżej, tylko tym razem otrzymujemy sumy elementów
wierszami macierzy x1
apply(x1,1,max) #Otrzymujmy wektor zawierający element maksymalny
każdego z wierszy macierzy x1

1.3 Zadania - macierze

1.3.1 Zadanie

  1. Utworzyć wektor x=(2;2;2;2;2) za pomocą funkcji rep oraz wektor y=(-1;0;1;2;3) za pomocą funkcji seq.

  2. Utworzyć macierz A o 5 wierszach i 2 kolumnach, w której pierwsza kolumna składa się z elementów wektora x a druga z elementów wektora y .

  3. Zmienić drugi wiersz macierzy A na wiersz postaci (2;4)

1.3.2 Zadanie

  1. Utworzyć macierz
    \(\mathbf{A}=\left( \begin{array}{ccc} 2&23&8\\10&6&90\\4&7&12\end{array}\right)\)
    za pomocą poleceń cbind i/lub matrix. Czy da się utworzyć tą macierz za pomocą polecenia rbind?
  2. Obliczyć średnią oraz iloczyn elementów w wierszach i kolumnach macierzy A za pomocą funkcji apply, sum oraz prod.
  3. Obliczyć sumę wszystkich liczb z dwóch pierwszych wierszy macierzy A, a następnie sumę wszystkich liczb z pierwszej i trzeciej kolumny
  4. Co zwracają polecenia t(A), det(A) i diag(A)? Wyznaczyć ślad macierzy A.
  5. Co zwracają polecenia A^2, A*A, A%*%A?
  6. Co zwracają polecenia 1/A, A^(-1), solve(A)?
  7. Rozwiązać układ równań

    \(\left\{ \begin{array}{ccc} 2x+23y+8z&=&5\\ 10x+6y+90z&=&6\\ 4x+7y+12z&=&7 \end{array} \right.\)
  8. Wybrać te wiersze macierzy A, w których suma elementów jest większa niż 30.

2 Ramki danych

Ramki danych (data.frame) są tablicami, których kolumny mogą być niejednorodne. Przykładowo, ramka może zawierać zarówno kolumny znakowe jak i numeryczne, ale każda kolumna zawiera elementy tego samego rodzaju. Jest to najważniejsza klasa obiektów dedykowana specjalnie do przechowywania danych.

Przykładowo, w ramce danych możemy umieścić zmienne jakościowe opisywane za pomocą znaków (np. kolumnę opisującą płeć M/K osoby lub jej status społeczno-zawodowy) oraz zmienne ilościowe opisywane za pomocą liczb (np. wzrost lub wiek). Wiele narzędzi statystycznych dostępnych w R jest dedykowanych tej klasie obiektów. Ramki danych tworzone są też zazwyczaj przez R podczas importu danych.

Ramki danych mogą być utworzone poprzez zgrupowanie wektorów var1, var2,… o tej samej długości poleceniem
data.frame(nazwa1=var1, nazwa2=var2,…)

Możliwe jest też przekształcenie macierzy w ramkę danych za pomocą polecenia as.data.frame.

2.1 Tworzenie ramek danych i podstawowe operacje na ramkach

v1=sample(1:12,30,rep=T) #Losuje ze zbioru {1…12} ciąg 30 liczb z powtórzeniami
v2=sample(LETTERS[1:10],30,rep=T) #Losuje ze zbioru liter {A,B,…J} ciąg 30 liter z powtórzeniami
v3=runif(30) #30 niezależnych realizacji rozkładu jednostajnego na przedziale [0,1]
hist(v3) #Rysuje histogram zmiennej v3
v4=rnorm(30) #30 niezależnych realizacji rozkładu normalnego o średniej 0 i wariancji 1
hist(v4) #Rysuje histogram zmiennej v4
xx=data.frame(Age=v1,Firstname=v2,Height=v3,Weight=v4) #Tworzy ramkę danych z 4 zmiennymi i 30 osobnikami/obserwacjami
str(xx) #Wyświetla strukturę obiektu
xx$Firstname #Do zmiennych możemy się odnosić poprzez $ i nazwę
xx[,1] #Do zmiennych możemy się odnieść przez [ ], tak jak w przypadku macierzy
xx$Firstname[3] #Dostęp do trzeciego elementu wektora xx$Firstname
xx[3,2] #To samo co wyżej
summary(xx) #Podstawowe statystyki dla zmiennych
str(xx) #Struktura obiektu może się różnić w zależności od występujących zmiennych ilościowych i jakościowych
ma=matrix(1:15,nrow=3); ma #Tworzy nową macierz
ma=as.data.frame(ma) #Zmienia macierz na ramkę danych
ma #Wyświetlenie ramki
str(ma) #Struktura ramki
names(ma)=c('VA','VB','VC','VD','VE') #Przypisanie nazw zmiennym (kolumny ramki)
row.names(ma)=c('l1','l2','l3') #Przypisanie nazw obserwacjom (wiersze ramki)
ma #Wyświetlenie ramki

2.2 Działania na zmiennych jakościowych (i ilościowych)

sex=sample(c('M','F'),100,rep=T) Wygenerowanie 100 realizacji zmiennej “sex” o wartościach “M” i “F” (mężczyzna, kobieta)
height=rnorm(100,mean=170,sd=10) Wygenerowanie 100 realizacji zmiennej “height” z rozkładu normalnego o średniej “mean=170” i odchyleniu standardowym “sd=10”
height[which(sex=='M')]=height[which(sex=='M')]+10 Dodanie do zmiennej “height” wartości 10, o ile zmiennej “sex” odpowiada wartość “M” - inaczej, mężczyźni statystycznie są wyżsi
tapply(height,sex,mean) Obliczenie średniej zmiennej “height” osobno dla “M” i “F”
tapply(height,sex,sd) Obliczenie odchylenia standardowego zmiennej “height” osobno dla “M” i “F”

2.3 Gotowe zestawy danych

W R istnieją pewne gotowe zestawy danych, które można wykorzystać do ćwiczeń

data() Otwiera okno tekstowe z listą wszystkich tabel danych dostępnych w R
women Wyświetla tabelę z danymi “women”
? women Wyświetla opis ramki danych “women”. Dowiemy się, że wzrost jest w calach, a waga w funtach.
names(women) Nazwy zmiennych w “women”
attributes(women) Pewna charakterystyka “women”, w tym nazwy zmiennych, typ danych i nazwy obserwacji
women$height Wyświetla wartości zmiennej “height” dla ramki women
apply(women,1,sum) Funkcję “apply” można stosować do ramek danych
apply(women,2,max) Funkcję “apply” można stosować do ramek danych

2.4 Zadania - Ramki danych

2.4.1 Zadanie

W ćwiczeniu wykorzystamy zbiór danych “iris” dostępny w R

  1. Sprawdzić, czy jest to obiekt typu data.frame (ramka danych)

  2. Co znajduje się w zbiorze danych?

  3. Wpisz polecenie summary(iris). Ile osobników gatunku versicolor znajduje się w tej bazie danych? Jaka jest średnia zmiennej Sepal.Width? Jaka jest najmniejsza wartość?

  4. Utwórz ramkę danych o nazwie “iris2”, który zawiera tylko osobniki gatunku “setosa”.

  5. Posortuj osobniki gatunku “setosa” według długości działki kielicha (eng. sepal)

  6. Znajdź średnie zmiennych Sepal.Length, Sepal.Width, Petal.Lehgth oraz Petal.Width przy użyciu funkcji apply oraz mean.

  7. Znajdź średnie zmiennej Sepal.Length dla każdego z trzech gatunków używając funkcji tapply oraz mean.

2.4.2 Zadanie

Poniższa tabela przedstawia liczbę studentów przyjętych w pięciu miastach z podziałem na 5 typów kierunków: nauki humanistyczne, ścisłe, medyczne, sportowe i techniczne w 2006 roku:

n. human. n. ścisłe medyczne sportowe techniczne
Bordeaux 12220 6596 7223 357 2239
Lyon 15310 6999 10921 395 3111
Paryż 112958 40244 46146 1247 7629
Rennes 8960 6170 4661 279 4013
Tuluza 12125 8233 6653 553 3178
  1. Stworzyć ramkę danych zawierających dane z powyższej tabeli. W szczególności należy uzupełnić nazwy kolumn i wierszy jak wyżej (polecenia names i row.names)

  2. Obliczyć całkowita liczbę studentów w danym mieście, a następnie uporządkować tabelę w porządku rosnącym względem tej liczby

  3. Obliczyć całkowita liczbę studentów danego typu kierunków, a następnie uporządkować tabelę w porządku rosnącym względem tej liczby

  4. Utworzyć nowa ramkę danych zawierającą tylko te miasta, gdzie liczba przyjętych na kierunki ścisłe jest wyższa niż liczba przyjęć na kierunki medyczne (jedno polecenie/jedna linijka)

2.5 Zadanie - metoda D’Hondta podziału mandatów w systemach wyborczych

2.5.1 Wstęp

Weźmiemy pod uwage wyniki wyborów do Sejmu RP z 15 października 2023, dane dla okręgu nr 13 (Kraków i okolice) dostępne na stronie

PKW wybory 2023

W tym okręgu do obsadzenia jest 14 mandatów, próg wyborczy 5% przekroczyło 5 komitetów wyborczych i tylko te komitety bierzemy pod uwagę:

partie=c("KO","PiS", "3Droga","Lewica", "Konfederacja")
glosy =c(232799,232430,127693,83633,58435)
l_mandatow=14

Gdyby zastosować wprost proporcjonalność zdobytych mandatów do liczby otrzymanych głosów dostalibyśmy wyniki ułamkowe:

#proporcjonalny podział głosów prowadzi do ułamkowych mandatów
razemglosy=sum(glosy)
udzial=glosy/razemglosy
ulamkowe_mandaty=l_mandatow*udzial
ulamkowe_mandaty
## [1] 4.434327 4.427298 2.432281 1.593031 1.113063

i taki wynik byłby trudny do zinterpretowania.

2.5.2 Metoda D’Hondta

Metoda ta jest algorytmem umożliwiającym niejako rozparcelowanie ułamkowych mandatów między komitety wyborcze. Opis algorytmu można znaleźć na

Wikipedia Metoda d’Hondta

W skrócie algorytm polega na zbudowaniu tabelki jak niżej, gdzie w pierwszym wierszu umieszczamy liczby głosów uzyskane przez poszczególne komitety, a następne wiersze powstają przez podzielenie (zawsze) pierwszego wiersza przez kolejne liczby naturalne. Ilość wierszy takiej tabelki odpowiada liczbie mandatów do rozdzielenia (istnieje możliwość, że ktoś zgarnie wszystkie mandaty).

Następnie z tabelki wybieramy najwyższe wyniki aż do wyczerpania liczby mandatów do obsadzenia w okręgu.

2.5.3 Zadanie właściwe - część pierwsza

Zbudować ramkę danych - tabelkę wyników z ilorazami wynikającymi z metody d’Hondta. Efekt powinien przypominać

##           KO       PiS     3Droga    Lewica Konfederacja
## 1  232799.00 232430.00 127693.000 83633.000    58435.000
## 2  116399.50 116215.00  63846.500 41816.500    29217.500
## 3   77599.67  77476.67  42564.333 27877.667    19478.333
## 4   58199.75  58107.50  31923.250 20908.250    14608.750
## 5   46559.80  46486.00  25538.600 16726.600    11687.000
## 6   38799.83  38738.33  21282.167 13938.833     9739.167
## 7   33257.00  33204.29  18241.857 11947.571     8347.857
## 8   29099.88  29053.75  15961.625 10454.125     7304.375
## 9   25866.56  25825.56  14188.111  9292.556     6492.778
## 10  23279.90  23243.00  12769.300  8363.300     5843.500
## 11  21163.55  21130.00  11608.455  7603.000     5312.273
## 12  19399.92  19369.17  10641.083  6969.417     4869.583
## 13  17907.62  17879.23   9822.538  6433.308     4495.000
## 14  16628.50  16602.14   9120.929  5973.786     4173.929

W powyższej ramce nazwy kolumn to komitety wyborcze, a nazwy wierszy to kolejne dzielniki.

Wskazówki:

  • liczba mandatów/wektor dzielników daje wektor ilorazów

  • wektory można połączyć w macierz za pomocą cbind

  • przydatne: as.data.frame, colnames, rownames

Z powyższej ramki danych należałoby wybrać 14 największych liczb (14, bo taka jest liczba mandatów w okręgu 13). Niestety, sortowanie macierzy jest nieco mało wygodne, w celu zautomatyzowania obliczeń zbudujemy inną, “lepszą” ramkę danych:

2.5.4 Zadanie właściwe - część druga

Ponieważ łatwiej posortować wektor, zbudujemy ramkę danych postaci:

##   partia   ilorazy
## 1     KO 232799.00
## 2     KO 116399.50
## 3     KO  77599.67
## 4     KO  58199.75
## 5     KO  46559.80
## 6     KO  38799.83
##          partia  ilorazy
## 65 Konfederacja 6492.778
## 66 Konfederacja 5843.500
## 67 Konfederacja 5312.273
## 68 Konfederacja 4869.583
## 69 Konfederacja 4495.000
## 70 Konfederacja 4173.929

gdzie pierwsza kolumna to kolejno sklonowane nazwy komitetów wyborczych (w liczbie równej liczbie mandatów), a druga kolumna to kolejne zestawy ilorazów.

Wskazówka: Ramkę tego typu możemy zbudować od podstaw albo korzystając z ramki z pierwszej części i funkcji stack().

Mając ramkę jak powyżej należy:

  • posortować malejąco kolumnę ilorazów ( order(), czy może sort() będzie lepszy?)

  • wybrać pierwsze 14 wyników (odpowiadających liczbie mandatów)

  • wybrać nazwy komitetów odpowiadających 14 najlepszym wynikom/ilorazom

  • zliczyć liczbę mandatów za pomocą table()

Powinniśmy dostać coś podobnego jak niżej, gdzie wyniki table() zostały jeszcze raz posortowane

## 
##           KO          PiS       3Droga Konfederacja       Lewica 
##            5            5            2            1            1
## [1] 4.434327 4.427298 2.432281 1.593031 1.113063

Mamy też porównanie z ułamkowymi mandatami, widzimy gdzie te ułamkowe części się poprzesuwały.

Można też sprawdzić, czy wyliczony przez nas podział zgadza się z oficjalnymi wynikami

2.5.5 Zadanie właściwe - część trzecia

Powtórzyć podział mandatów dla innego, wybranego okręgu i porównać z oficjalnymi wynikami.

2.5.6 Zadanie dodatkowe 1

Sprawdzić, jak wyglądałby podział mandatów, gdyby komitety zawarły koalicje, np. jakbyśmy zsumowali głosy KO+Lewica+3droga i PiS+Konfederacja.

2.5.7 Zadanie dodatkowe 2 - metoda Sainte-Laguë

Innym sposobem podziału mandatów jest wykorzystanie metody Sainte-Laguë. Różnica w stosunku do metody D’Hondta polega na tym, że dzielniki to kolejne liczby nieparzyste.

Więcej informacji na Wikipedia metoda Sainte-Laguë

Zadanie:

  • zmodyfikować kod tak, aby obliczał liczbę mandatów metodą Sainte-Laguë

  • porównać otrzymane wyniki z metody Sainte-Laguë oraz metody D’Hondta

Możliwe jest, że w niektórych okręgach liczby mandatów otrzymanych za pomocą obu metod będą różne (metoda D’Hondta bardziej sprzyja większym partiom)

2.5.8 Ciekawostka

W polskim systemie wyborczym, gdy znamy już liczbę mandatów przypadającą danemu komitetowi, bierzemy pod uwagę indywidualne wyniki kandydatów z danej listy.

Stąd też “układanki wyborcze”, tak aby znane nazwisko zdobyło nie tylko mandat dla siebie, ale znacznie poprawiło ogólny wynik listy, co daje większą liczbę mandatów.

3 Listy

Lista jest uporządkowanym zbiorem obiektów, nie koniecznie tego samego typu. Elementami list mogą być dowolne obiekty zdefiniowane w R. Można utworzyć na przykład listę, która zawiera wektor liczbowy i macierz znaków. Własność ta jest wykorzystywana w szczególności przez niektóre funkcje do zwracania złożonych wyników w formie pojedynczego obiektu. Listę tworzy się za pomocą funkcji list(nazwa1=el1, nazwa2=el2,…). Do każdego elementu listy można przejść używając jego indeksu w nawiasach podwójnych [[…]] lub jego nazwy poprzedzonej znakiem $.

li=list(num=1:5,y="color",a=TRUE) utworzenie listy z 3 obiektami
li wyświetlenie listy
li$num dostęp do elementu listy przez $
li$a dostęp do elementu listy przez $
li[[1]] dostęp do elementu listy przez [[…]]
li[[3]] dostęp do elementu listy przez [[…]]
a=matrix(c(6,2,0,2,6,0,0,0,36),nrow=3) utworzenie macierzy
eigen(a) obliczenie rozkładu Jordana macierzy
res=eigen(a) Wynik podawany jest w postaci listy
attributes(res) Pozwala na wyświetlenie nazw elementów listy
str(res) Wyświetla strukturę listy
res$values Wyświetlenie wartości własnych
res$vectors Wyświetlenie wektorów własnych
res$vectors[,1] Wyświetlenie pierwszego wektora własnego
diag(res$values) Macierz Jordana (dokładniej macierz diagonalna, której elementy ma przekątnej pochodzą z wektora zawierającego wartości własne)
res$vectors%*%diag(res$values)%*%t(res$vectors) Rozkład Jordana, macierz odwrotna = transponowana
x<-list(a=1:10,beta=exp(-3:3),logic=c(TRUE,FALSE,FALSE,TRUE)) utworzenie nowej listy
lapply(x,mean) Polecenie lapply stosuje zadaną funkcję (tutaj mean) do każdego elementu listy

3.1 Zadania - Listy

3.1.1 Zadanie

Utworzyć 3-elementową listę zawierającą Twoje nazwisko, imiona rodziców oraz miejsce urodzenia (fikcyjne, RODO itd). Nazwy elementów listy to “nazwisko”, “imiona_rodzicow” i “miejsce_urodzenia”.

3.1.2 Zadanie

  1. Utworzyć (ponownie) macierz
    \(\mathbf{A}=\left( \begin{array}{ccc} 2&23&8\\10&6&90\\4&7&12\end{array}\right)\)
    za pomocą poleceń cbind i/lub matrix.

  2. Obliczyć wartości własne i wektory własne macierzy. Czy macierz jest diagonalizowalna?

  3. Na podstawie poprzedniego pytania obliczyć \(\mathbf{A}^n\) dla \(n=10\) oraz \(n=50\) nie używając pętli (algebra liniowa…). Może się przydać polecenie inv z pakietu matlib . Ładowanie za pomocą library(matlib)