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
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
elementjest 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 nadołą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
\[\left[ \begin{array}{c c} x_1 & x_2 \end{array} \right] \] |
apply(x1,2,sum) |
#Funkcja apply pozwala na zastosowanie funkcji
działającejna 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
Zadanie 1
Utworzyć wektor
x=(2;2;2;2;2)
za pomocą funkcjirep
oraz wektory=(-1;0;1;2;3)
za pomocą funkcjiseq
.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 wektoray
.Zmienić drugi wiersz macierzy A na wiersz postaci (2;4)
Zadanie 2
- 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/lubmatrix
. Czy da się utworzyć tą macierz za pomocą poleceniarbind
? - Obliczyć średnią oraz iloczyn elementów w wierszach i kolumnach
macierzy A za pomocą funkcji
apply
,sum
orazprod
. - Obliczyć sumę wszystkich liczb z dwóch pierwszych wierszy macierzy A, a następnie sumę wszystkich liczb z pierwszej i trzeciej kolumny
- Co zwracają polecenia
t(A)
,det(A)
idiag(A)
? Wyznaczyć ślad macierzy A. - Co zwracają polecenia
A^2
,A*A
,A%*%A
? - Co zwracają polecenia
1/A
,A^(-1)
,solve(A)
?
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] |
v4=rnorm(30) |
#30 niezależnych realizacji rozkładu normalnego o średniej 0 i wariancji 1 |
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 |
head(xx) |
# Wyświetla 6 pierwszy wierszów ramki danych xx |
tail(xx) |
# Wyświetla 6 ostatnich wierszów ramki danych xx |
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) |
2.2 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.3 Zadania - ramki danych
Zadanie 1 W analizie danych bardzo często
wykorzystuje się ramki danych. Dlatego też przetestujemy poznane
operacje na zbiorze danych mieszkania znajdującym się w bibliotece
“Przewodnik”. Ponieważ te dane nie są dostępne w podstawowej bibliotece,
wymagana jest na początku jednorazowa instalacja komendą
install.packages("Przewodnik")
. Następnie, wystarczy
załadować bibliotekę library("Przewodnik")
oraz komendą
head(mieszkania)
możemy wyświetlić 6 pierwszych rekordów,
aby zobaczyć co składa się na rozważany zbiór danych.
Wykonaj poniższe operacje na zbiorze danych “mieszkania”:
- Wypisz pierwszy, trzeci i piąty wiersz ramki danych.
- Wypisz pierwszy, trzeci i piąty wiersz oraz od 2 do 5 kolumny ramki danych.
- Wpisz polecenie
summary(mieszkania)
. Jaka jest średnia cena mieszkań? Jaka jest maksymalna powierzchnia mieszkania z naszym zbiorze danych? Ile jest mieszkań znajdujących się w wieżowcach? - Aby wypisać pierwszą kolumnę, jaką jest cena mieszkań możemy użyć:
mieszkania[,1]
lubmieszkania$cena
lubmieszkania[, "cena"]
. Znajdź jaka jest największa cena za mieszkanie w rozważanym zbiorze danych, a następnie wyświetl wiersz zawierający maksymalną cenę. - Używając funkcji
order
posortuj dane ze względu na cenę. - Stwórz nową ramkę danych która zawiera tylko cenę, powierzchnię i dzielnicę mieszkań, które mają powierzchnię większą niż 20.
Zadanie 2
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 |
- 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
irow.names
). Dla ułatwienia, macierz danych liczbowych można przekopiować z przykładu
macierz_miast=matrix(c(12220,15310,112958,8960,12125,6596,6999,40244,6170,8233,7223,10921,46146,4661,6653,357,395,1247,279,553,2239,3111,7629,4013,3178),ncol=5)
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
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
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.4 Zadanie - metoda D’Hondta podziału mandatów w systemach wyborczych
2.4.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
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.4.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
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.4.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.4.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żesort()
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.4.5 Zadanie właściwe - część trzecia
Powtórzyć podział mandatów dla innego, wybranego okręgu i porównać z oficjalnymi wynikami.
2.4.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.4.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.4.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.
2.5 Działania na zmiennych jakościowych (i ilościowych)
Przypomnijmy, że do ramek danych możemy umieścić zmienne jakościowe oraz zmienne ilościowe opisywane za pomocą liczb. Przyjrzymy się teraz dokładniej tym pierwszym. Zmienne jakościowe reprezentują kategorie lub grupy, które nie mają naturalnego porządku liczbowego. Przykładami mogą być płeć (mężczyzna, kobieta), kolor (czerwony, zielony, niebieski) czy odpowiedzi typu tak/nie.
sex=sample(c('M','F'),100,rep=T) |
Wygenerowanie 100 realizacji zmiennej “sex” o wartościach “M” i “F” (mężczyzna, kobieta) |
table(sex) |
Wyświetla tablice liczebności dla zmiennej “sex” |
barplot(sex) |
Graficzne przedstawienie liczebności dla zmiennej jakościowej |
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” |
Oczywiście, warto podkreślić, że stosowanie metod statystycznych odpowiednich dla zmiennych ilościowych do analizy zmiennych jakościowych może prowadzić do błędów lub bezsensownych wyników. Przykładem może być próba obliczenia średniej dla zmiennej jakościowej.
## Warning in mean.default(kolory): argument nie jest wartością liczbową ani
## logiczną: zwracanie wartości NA
## [1] NA
2.6 Zadania - zmienne jakościowe
Zadanie 3 Kontynuując zadanie 1 i wykorzystując zbioru danych mieszkania:
Oblicz średnią cenę osobno dla każdej dzielnicy, używając funkcji
tapply
orazmean
.Wypisz wszystkie mieszkania dla których typ budynku to wieżowiec, a następnie posortuj je ze względu na powierzchnię.
ROZWIĄZANIA 3
tapply(mieszkania$cena, mieszkania$dzielnica,mean)
x=mieszkania[which(mieszkania$typ.budynku=='wiezowiec'),]
x[order(x$powierzchnia),]
3 Listy
Lista jest uporządkowanym zbiorem obiektów, nie koniecznie tego
samego typu. Elementami list mogą być dowolne obiekty zdefiniowane w R,
którym można nadać nazwy. Na przykład, można utworzyć 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 z nazwami elementów listy odpowiednio
num , y , a . |
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 wartości własnych i wektorów 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 |
lapply(x,mean) |
Polecenie lapply stosuje zadaną funkcję (tutaj
mean ) do każdego elementu listy |
3.1 Zadania - Listy
Zadanie 1
Utworzyć 5-elementową listę zawierającą dowolne dwa miasta, jedno państwo, imię damskie i imię męskie, trzy zwierzęta oraz cyfrę, takie że miasto, państwo, imię, zwierze zaczynają się na tą samą literę co twoje imię a cyfra to suma cyfr twojego numeru indeksu. Nazwy elementów listy to “miasto”, “państwo”, “imię”, “zwierze” oraz “cyfra”.
Zadanie 2
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/lubmatrix
.Obliczyć wartości własne i wektory własne macierzy. Czy macierz jest diagonalizowalna?
Zadanie 3 Załaduj listę, która zawiera różne typy
danych.
data_list <- list( numeryczny1 = c(10, 20, 30, 40, 50), numeryczny2 = c(10, 200, 34, 4, -50), kategoryczny = c("tak", "nie", "tak", "nie", "tak"), macierz = matrix(1:9, nrow=3))
Następnie, wykonaj następujące zadania:
Dla wektorów numerycznych oblicz medianę.
Dla zmiennych jakościowych oblicz liczbę wystąpień każdej kategorii.
Dla macierzy oblicz sumę każdego wiersza i każdej kolumny.
Zadanie 4 Dla poniższej listy
datalist <- list( zestaw1 = c(10, 20, 30, 40, 50), zestaw2 = c(5, 15, 25, 35, 45), zestaw3 = c(10, 10, 20, 20, 30))
używając funkcji lapply
policz odchylenie standardowe oraz
średnią dla każdego elementu z listy.