Redukcja liczności zbiorów z wykorzystaniem systemu R

Transkrypt

Redukcja liczności zbiorów z wykorzystaniem systemu R
XVI Konferencja PLOUG
Kościelisko
Październik 2010
Redukcja liczności zbiorów
z wykorzystaniem systemu R
Artur Gramacki, Jarosław Gramacki
Uniwersytet Zielonogórski
[email protected], [email protected]
Abstrakt. W praktyce często okazuje się, że duża ilość posiadanych danych wcale nie musi iść w parze z łatwością ich analizowania i,
co ważniejsze, wyciągania na ich podstawie wartościowych i użytecznych wniosków. Mówiąc o dużych ilościach danych mamy na
myśli, mówiąc w języku bazodanowym, zarówno dużą liczbę rekordów, jak i dużą liczbę atrybutów. Jeżeli uda nam się sensownie
zredukować którąś z tych wielkości (lub obie naraz) to może okazać się, że tak zredukowany zbiór danych zacznie przedstawiać większą wartość, niż niezredukowany oryginał. W artykule omawiamy różne techniki redukcji liczności zbiorów. Wykorzystujemy do tego
bardzo popularny w środowiskach naukowych, oraz coraz bardziej doceniany również w środowiskach inżynierskich, pakiet do obliczeń statystycznych o nazwie „R”. Zagadnienie redukcji liczby atrybutów (zwane również redukcją wymiarowości) było tematem
wykładu prezentowanego podczas ubiegłorocznej konferencji PLOUG.
Informacja o autorach. dr inż. Artur Gramacki jest adiunktem na Wydziale Elektrotechniki, Informatyki i Telekomunikacji w Instytucie Informatyki i Elektroniki Uniwersytetu Zielonogórskiego. Interesuje się zagadnieniami związanymi z bazami danych oraz eksploracją danych. W swej pracy wykorzystuje głównie rozwiązania firmy Oracle, choć nieobce są mu również rozwiązania bazodanowe
innych producentów, w tym również te spod znaku OpenSource. Od wielu lat prowadzi zajęcia dydaktyczne dotyczące baz danych,
systemów zarządzania bazami danych oraz budowania bazodanowych aplikacji użytkowych. Brał czynny udział w różnych projektach
informatycznych z tego zakresu.
Dr inż. Jarosław Gramacki jest adiunktem na Wydziale Elektrotechniki, Informatyki i Telekomunikacji w Instytucie Informatyki
i Elektroniki Uniwersytetu Zielonogórskiego. W pracy naukowej zajmuje się różnymi technikami przetwarzania danych, ich analizą,
zastosowaniami ukierunkowanymi na przetwarzanie danych. Zajmuje się ponadto projektowaniem, wykonywaniem oraz wdrażaniem
aplikacji usprawniających szeroko rozumiane zarządzanie Uczelnią. Od wielu lat prowadzi zajęcia dydaktyczne dotyczące baz danych,
systemów zarządzania bazami danych oraz wykorzystania technologii bazodanowych (głównie firmy Oracle) do budowy aplikacji
użytkowych.
Redukcja liczności zbiorów z wykorzystaniem systemu R
175
1. Wprowadzenie
Ważnym etapem poprzedzającym właściwą analizę danych jest (powinna być) ich wstępna obróbka (ang. data preprocessing). Typowe czynności, które składają się na tą obróbkę to (HaKo00,
HMS05):
• czyszczenie danych,
• integracja danych,
• transformacja danych,
• redukcja danych.
Celem tej obróbki jest usunięcie lub ograniczenie różnego rodzaju niedoskonałości i pewnych
niekorzystnych właściwości, które występująz w danych i które mogą bardzo negatywnie wpłynąć
na otrzymywane wyniki, w skrajnych przypadkach całkowicie je fałszując. Ponadto, nie zawsze
prawdą jest, że posiadanie dużej ilości danych umożliwia nam wyciąganie lepszych wniosków,
uzyskiwanie dokładniejszych, czy też bardziej wiarygodnych, wyników prowadzonych analiz. To
ostatnie stwierdzenie jest o tyle istotne z praktycznego punktu widzenia, że obecnie nie jest wielkim problemem technicznym i finansowym gromadzenie praktycznie dowolnych ilości danych.
Problemem jest natomiast efektywne ich analizowanie, zwłaszcza, gdy wykonujemy tzw. analizy
eksploracyjne, gdzie niejako z definicji nie jesteśmy w stanie a priori sformułować dokładnej hipotezy badawczej i na posiadanych danych lub ich fragmentach musimy wykonać wiele różnych
analiz, zanim odkryjemy jakieś interesujące zależności. Analizy te często są czasochłonne,
zwłaszcza, gdy danych do przeanalizowania jest dużo. Jest to sytuacja odmienna od tej, z którą
mamy do czynienia w przypadku wykonywania typowych analiz statystycznych. Tam zwykle nie
analizuje się jakiś specjalnie wielkich zbiorów danych a ponadto zwykle wiemy, jakich zależności
w danych szukamy i zwykle chodzi nam o potwierdzenie, bądź też zaprzeczenie pewnej z góry
stawianej hipotezie. Stawiając jakąś hipotezę opieramy ją na pewnej „wiedzy eksperckiej”, np.
twierdzimy, że ludzie młodzi i lepiej wykształceni rzadziej głosują na partie konserwatywne niż
ludzie starsi i gorzej wykształceni. W przypadku zadania eksploracyjnego raczej bardziej naturalne
będzie bardziej ogólne pytanie (trudno je nazwać hipotezą), np. „jak wiek wyborców wpływa na
preferencje wyborcze”.
Z wymienionych na początku rozdziału czterech podstawowych czynności składających się na
wstępną obróbkę danych, trzy pierwsze mają charakter porządkujący dane. Zwykle bowiem jest
tak, że posiadane dane zawierają różnego rodzaju niedoskonałości i braki. Często też ich źródło
nie jest jednolite, co powoduje, że trudno jest z nich skorzystać bez wcześniejszego ich uporządkowania. Czwarta czynność (redukcja danych) jest głównym tematem niniejszego artykułu. Pod
pojęciem redukcji danych kryją się dwie podstawowe czynności wykonywane na danych:
• redukcja wymiarowości (ang. Attribute selection, feature selection, subset selection),
• redukcja liczności (ang. Instancje selection, example selection, numerosity reduction).
Pod pojęciem redukcji wymiarowości zwykle rozumiemy proces transformacji danych wielowymiarowych (w sensie dużej liczby atrybutów) do przestrzeni o sensownie mniejszym wymiarze.
Z powodów czysto praktycznych (możliwość łatwej wizualizacji) najczęściej dane redukowane są
do dwóch lub trzech wymiarów. W praktyce często okazuje się, że wiele z atrybutów jest ze sobą
dosyć mocno powiązanych (skorelowanych). Wówczas do otrzymania pełnego obrazu opisywanego zjawiska, czy zauważenia pewnych prawidłowości w danych, wystarczy uwzględnić jedynie
niewielki ich podzbiór lub też na bazie oryginalnych atrybutów wygenerować zmniejszony zestaw
nowych „atrybutów ukrytych”. Redukcja wymiarowości zwykle wiąże się z utratą pewnej ilości
informacji, jednak często jest tak, że to, co tracimy na redukcji wymiarowości odzyskujemy z na-
176
Artur Gramacki, Jarosław Gramacki
wiązką w postaci danych, które łatwiej jest po prostu analizować. Temat redukcji wymiarowości
omawiany był w artykule autorów zaprezentowanym podczas ubiegłorocznej konferencji PLOUG
[Gra09]. Literatura przedmiotu jest bardzo obszerna. Osoby zainteresowane tą tematyką zachęcamy do zapoznania się materiałami przeglądowymi [HMS05, Sch99], gdzie wskazane są teksty
źródłowe prezentowanych tam wielu różnych metod redukcji wymiarowości.
Pod pojęciem redukcji liczności zwykle rozumiemy proces wyboru z posiadanego dużego zbioru danych (używając terminologii bazodanowej będzie to relacja, używając terminologii statystycznej będzie to populacja) pewnego podzbioru, zwanego próbą reprezentatywną. Warunek
wyboru próby reprezentatywnej dla rozpatrywanej populacji jest warunkiem koniecznym dla poprawnego wyciągania wniosków o tej populacji. Zwięzłe omówienie tej tematyki w ujęciu statystycznym można znaleźć w podręczniku [KoMi04].
Zwróćmy również w tym miejscu uwagę na pewne istotne rozróżnienie: w naukach społecznych i ekonomicznych mówiąc o populacji (populacji generalnej, zbiorowości generalnej) mamy
na myśli pewien zbiór elementów, podlegających badaniu statystycznemu. W praktyce prawie
zawsze jest tak, że przeprowadzenie badania na całej populacji jest albo nieuzasadnione ekonomicznie, lub wręcz niemożliwe do wykonania (przykładowo trudno jest sobie wyobrazić zadanie
jakiegoś pytania wszystkim bez wyjątku Polakom w wieku od 18 do 25 lat). W takich sytuacjach
redukcja liczności (zwana wtedy doborem próby losowej) jest niejako koniecznością. W przypadku natomiast analizowania danych zgromadzonych w bazach danych „populacja” jest dokładnie
znana, gdyż można uznać, że jest nią cały zgromadzony zbiór danych. Niemniej jednak, w przypadku, gdy zbiór ten jest bardzo duży, wygodniej jest pracować na jakimś jego reprezentatywnym
fragmencie. Niezależnie jednak od tego, z jakim charakterem danych mamy do czynienia, metody
wybierania próby reprezentatywnej pozostają te same.
W artykule skupiamy się na przedstawieniu wybranych metod redukcji liczności danych zaimplementowanych w systemie R [Rpro]. Nie omawiamy systemu R jako takiego, gdyż zostało to już
uczynione w innym artykule autorów [Gra09]. Poza tym literatura na ten temat jest bardzo bogata
i ogólnie dostępna. Na stronie głównej systemu [Rpro] można znaleźć odnośniki do przystępnie
opracowanych materiałów omawiających zarówno podstawy R, jak i różne bardziej szczegółowe
zagadnienia techniczne (np. zasady tworzenia pakietów). Są tam również pozycje w języku polskim. Warto również polecić niedawno wydane książki [Bie08] oraz [WaGa09].
2. Redukcja liczności
Istnieją dwie podstawowe techniki redukcji liczności zbiorów:
• metody parametryczne,
• metody nieparametryczne.
Z metodami parametrycznymi mamy do czynienia wtedy, gdy jesteśmy w stanie dopasować
posiadane dane do modelu dostatecznie dobrze opisanego jakąś formułą matematyczną. Wówczas
wystarczy przechowywać parametry tego modelu, zamiast wszystkich danych, na podstawie których on powstał. Typowym przykładem jest tutaj zadanie regresji liniowej, która podaje w postaci
prostej formuły matematycznej zależności, jakie istnieją pomiędzy analizowanymi danymi
(zmiennymi). Innym równie typowym przykładem jest zadanie konstruowania funkcji gęstości
prawdopodobieństwa z posiadanych danych. Gdy np. posiadane dane dobrze wpisują się w typowy
rozkład normalny, wówczas do zapamiętania kształtu krzywej gęstości wymagane jest zapamiętanie zaledwie dwóch liczb (średnia oraz wariancja) będących parametrami gaussowskiej funkcji
prawdopodobieństwa. Widać więc, że metody parametryczne nie powodują, że zmniejsza się
w jakikolwiek sposób analizowany zbiór, natomiast ich istota polega na jednokrotnym przejrzeniu
wszystkich danych, zbudowaniu na ich podstawie pożądanego modelu, a następnie na używaniu
tego modelu do przyszłych analiz. Tym zagadnieniem nie zajmujemy się w niniejszym artykule.
Redukcja liczności zbiorów z wykorzystaniem systemu R
177
Spośród metod nieparametrycznych podstawowe znaczenie mają różne schematy losowego doboru próby (ang. sampling design). Literatura przedmiotu jest bardzo bogata, dwie często cytowane pozycje przeglądowe to [Kis99, Loh99]. Krótkie omówienie najważniejszych schematów losowania wraz z ich analizą statystyczną można też znaleźć w podręczniku [KoMi04]. Podano tam
między innymi informacje o sposobach oceny i porównywania „jakości” różnych schematów losowania. Dla pozycji [Loh99] opracowano w R pakiet o nazwie SDaA, w którym zaimplementowano wszystkie zamieszczone tam przykłady obliczeniowe wraz z danymi, na których one operują.
Często też do metod redukcji liczności zbiorów zalicza się techniki związane z tworzeniem histogramów oraz grupowaniem danych1. Za pewną formę redukcji danych można też uznać dyskretyzację danych o charakterze ciągłym.
Poniżej prezentujemy wybrane techniki redukcji liczności zbiorów zaimplementowane w systemie R. Podając nazwy używanych funkcji będziemy również w nawiasie podawać nazwę pakietu, w którym znajduje się dana funkcja. Warto tak robić, gdyż unikamy przez to niejednoznaczności (funkcje o tych samych nazwach mogą być w różnych pakietach). Ponadto na stronie internetowej projektu R nie można wyszukiwać funkcji po ich nazwach, trzeba widzieć w jakim znajdują
się pakiecie.
Rozważmy na początek funkcję sample(base), gdzie zaimplementowano dwa najbardziej
typowe i popularne chyba schematy losowania: proste losowanie bez zwracania (ang. simple random sampling without replacement) oraz proste losowane ze zwracaniem (ang. simple random
sampling with replacement). Poniżej zamieszczono kody w języku R ilustrujące działanie omawianej funkcji2.
# Biblioteki base w zasadzie nie trzeba jawnie ładować, bo ładuje się
# automatycznie po uruchomieniu środowiska R
> library(base)
> x <- 1:10
# Parametry opcjonalne przyjmą wartości domyślne
> sample(x)
[1] 3 1 4 8 9 10 7 2 5 6
# Losowanie ze zwracaniem
> sample(x, size=5, replace=T)
[1] 8 4 7 10 6
# Losowanie bez zwracania
> sample(x, size=5, replace=F)
[1] 1 7 10 3 9
# Określamy prawdopodobieństwo wylosowania każdej liczby
# Prawdopodobieństwa nie muszą sumować się do jedności, ale nie mogą być
# to liczby ujemne ani też wszystkie nie mogą być zerami
> x <- 1:5
> sample(x, size=20, replace=T, prob=c(0.1, 0.1, 0.1, 0.1, 0.6))
[1] 5 1 2 5 2 1 5 5 5 5 5 5 5 5 5 5 5 5 1 5
> sample(x, size=20, replace=T, prob=c(0.0, 0.0, 0.1, 0.1, 0.1))
[1] 4 5 3 3 4 5 4 4 4 4 3 4 4 5 5 4 5 3 5 5
1
2
W języku polskim przyjęło się również używać zwrotu klastrowanie. Nie brzmi on jednak zbyt dobrze
i w artykule konsekwentnie posługujemy się zwrotem grupowanie.
Czcionką pogrubioną podajemy polecenia wpisywane przez użytkownika w konsoli R. Czcionką pochyłą
wpisujemy komentarze a czcionką zwykłą wyniki otrzymywane na konsoli. Po znaku zachęty > znajdują
się wprowadzane komendy (znaku zachęty nie przepisujemy, jedynie to, co jest za nim). Znak + oznacza
kontynuację wpisywania polecenia w kolejnej linii.
178
Artur Gramacki, Jarosław Gramacki
Podobnie działające funkcje odnajdziemy w pakiecie sampling. W pakiecie tym nieco inaczej
rozwiązano sposób prezentowania wyników losowania. Zwracany jest wektor o rozmiarze N
(wielkość populacji). Każdy element k tego wektora pokazuje ilość jego powtórzeń w wylosowanej próbce.
# Ta biblioteka nie jest ładowana automatycznie, więc trzeba to zrobić ręcznie
> library(sampling)
> n <- 5
> N <- 10
# Wybieramy 'n' elementów z hipotetycznej próbki 'N' elementów
# Losowanie ze zwracaniem
> (s <- srswr(n, N))3
[1] 2 0 0 2 0 0 1 0 0 0
# Patrzymy, które elementy zostały wybrane
> (1:N)[s != 0]
[1] 1 4 7
# Ile jest replikacji
> s[s != 0]
[1] 2 2 1
# Tym razem losowanie bez zwracania
> (s <- srswor(n, N))
[1] 1 1 0 1 0 1 0 0 1 0
# Które elementy zostały wybrane
> (1:N)[s == 1]
[1] 1 2 4 6 9
# Tym razem oczywiście nie może być replikacji (bo losowanie bez zwracania)
> s[s != 0]
[1] 1 1 1 1 1
# Wykorzystujemy predefiniowany wektor. Są też inne, np. 'letters'
> miesiace <- month.name
> (s <- srswor(n, N))
[1] 1 1 0 1 0 0 0 1 1 0
> as.vector(miesiace[s == 1])
[1] "January" "February" "April" "August" "September" "November" "December"
Kolejnym często używanym w praktyce schematem losowania jest losowanie warstwowe (ang.
stratified sampling). Jeżeli w rozpatrywanej populacji można wyróżnić pewne naturalne warstwy
(np. kobiety, mężczyźni, studenci, uczniowie, obszary wiejskie, obszary miejskie itd.), to wyboru
reprezentatywnej próby z takiej populacji można dokonać, losując w każdej warstwie pewną liczbę
elementów (używając na przykład któregoś ze schematów opisanych wyżej, dla każdej warstwy
może być inny) i następnie łącząc tak wybrane próby w jedną większą próbę. Można wykazać
[KoMi04], że taki sposób losowania zapewnia mniejszą zmienność, niż próby wybierane według
schematów opisanych wyżej.
Ponieważ będziemy używali kilku specyficznych funkcji do manipulowania danymi
(rbind(base), cbind(base), data.frame(base)), najpierw na prostym przykładzie zademonstrujemy ich działanie.
# Budujemy macierz poprzez połączenie 2. wektorów
# Wersja kolumnowa
> (mojaMacierzKol <- cbind(2, 1:5))
[,1] [,2]
[1,]
2
1
3
Jeżeli przypisanie otoczymy nawiasami, to zmuszamy tym R do wypisania wyniku przypisania (tu: zawartości zmiennej s).
Redukcja liczności zbiorów z wykorzystaniem systemu R
179
[2,]
2
2
[3,]
2
3
[4,]
2
4
[5,]
2
5
# Wersja wierszowa
> (mojaMacierzWier <- rbind(2, 1:5))
[,1] [,2] [,3] [,4] [,5]
[1,]
2
2
2
2
2
[2,]
1
2
3
4
5
# Tworzymy ramkę danych4
> (mojaRamka <- data.frame(kol1=c(1:5), kol22=c("a","b","c","d","e"),
+
kol3=c(T,T,F,F,T)))
kol1 kol22 kol3
1
1
a TRUE
2
2
b TRUE
3
3
c FALSE
4
4
d FALSE
5
5
e TRUE
# Łączymy dane w wynikową ramkę
> (wynikowaRamka <- cbind.data.frame(mojaMacierzKol, mojaRamka))
1 2 kol1 kol22 kol3
1 2 1
1
a TRUE
2 2 2
2
b TRUE
3 2 3
3
c FALSE
4 2 4
4
d FALSE
5 2 5
5
e TRUE
Po tym przykładzie wyjaśniającym możemy już przejść do właściwego przykładu prezentującego losowanie warstwowe. Będziemy używali funkcji strata(sampling).
#
#
#
#
#
>
+
+
>
+
+
#
>
#
>
1
2
3
4
5
6
4
Przygotowujemy dane o kilku 'warstwach' (wojwództwa a w nich powiaty):
- zmienna 'wojewodztwo' ma 2 kategorie ('A' oraz 'B')
- zmienna 'powiat' ma 3 kategorie (1,2 oraz 3)
- zmienna dochód jest losowo wygenerowaną liczbą dodatnią
Tworzymy ramkę z danymi
dane <- rbind(
matrix(rep("A",165), 165, 1, byrow=TRUE),
matrix(rep("B",70), 70, 1, byrow=TRUE))
dane <- cbind.data.frame(
dane, c(rep(1,100), rep(2,50), rep(3,15),
rep(1,30), rep(2,40)), round(1000*runif(235), digits=2))
Kolumnom nadajemy nazwy
names(dane) <- c("wojewodztwo", "powiat", "dochod")
Wyświetlamy kilka pierwszych wierszy
head(dane)
wojewodztwo powiat
dochod
A
1 293.47
A
1 691.67
A
1 239.18
A
1 292.22
A
1 997.46
A
1 633.54
Ramka danych w R przechowuje dane tabelaryczne. W przeciwieństwie jednak do macierzy, każda kolumna może przechowywać dane innego typu. Chcąc odwoływać się do poszczególnych kolumn, należy
używać operatora $ (np. mojaRamka$kol1).
180
Artur Gramacki, Jarosław Gramacki
# Wyświetlamy kilka ostatnich wierszy
> tail(dane)
wojewodztwo powiat
dochod
230
B
2 497.95
231
B
2 257.21
232
B
2 202.96
233
B
2 349.03
234
B
2 746.79
235
B
2 325.28
# Wyznaczamy 'rozmiary' warstw
# Dane przygotowano tak, że w wojewodztwie B nie ma powiatu numer 3
> table(dane$powiat, dane$wojewodztwo)
A
B
1 100 30
2 50 40
3 15
0
# Dokonujemy próbkowania według metodologii losowania warstwowego
# Ustalamy wielkości warstw po losowaniu na 2,1,2,2,3
# Metoda losowania dla każdej warstwy to 'srswor'
> s <- strata(dane, c("powiat", "wojewodztwo"), size=c(2,1,2,2,3),
+
method="srswor")
# Wyciągamy dane z wylosowanej próbki
> getdata(dane, s)
dochod powiat wojewodztwo ID_unit
Prob Stratum
1 633.54
1
A
6 0.02000000
1
2 434.47
1
A
67 0.02000000
1
3 735.47
2
A
116 0.02000000
2
4 705.69
3
A
157 0.13333333
3
5 839.68
3
A
162 0.13333333
3
6 193.95
1
B
169 0.06666667
4
7 398.18
1
B
184 0.06666667
4
8 101.93
2
B
201 0.07500000
5
9 919.23
2
B
205 0.07500000
5
10 279.49
2
B
215 0.07500000
5
# Podobnie jak wyżej ale metoda losowania 'systematyczna'
> s <- strata(dane, c("powiat","wojewodztwo"), size=c(2,1,2,2,3),
+
method="systematic", pik=dane$dochod)
> getdata(dane,s)
dochod powiat wojewodztwo ID_unit
Prob Stratum
1 649.32
1
A
12 0.02686249
1
2 501.96
1
A
63 0.02076647
1
3 735.47
2
A
116 0.02545322
2
4 731.01
3
A
151 0.15852116
3
5 996.86
3
A
160 0.21617129
3
6 320.27
1
B
179 0.04548740
4
7 901.43
1
B
194 0.12802506
4
8 736.56
2
B
206 0.12206621
5
9 917.04
2
B
219 0.15197609
5
10 349.03
2
B
233 0.05784414
5
Kolejnym zaprezentowanym schematem losowania będzie losowanie grupowe (ang. cluster
sampling). W tej wersji populacja jest dzielona na grupy, a następnie losowane są nie pojedyncze
jednostki, lecz całe grupy. Losowanie grupowe jest przykładem losowania wielostopniowego (tutaj mamy 2 stopnie). W pierwszym kroku należy zdefiniować grupy, w drugim natomiast wybrać
wszystkie elementy z wylosowanych grup. Będziemy używali funkcji cluster(sampling).
Redukcja liczności zbiorów z wykorzystaniem systemu R
181
# Tworzymy pomocniczą funkcję generujacą całkowite liczby
# losowe z przedziału od 1 do 10
> genLiczba <- function() ceiling(10*runif(1))
# Generujemy przykładowe dane z 5. kategoriami
> dane <- rbind(
+
matrix(rep("A", genLiczba()), byrow=TRUE),
+
matrix(rep("B", genLiczba()), byrow=TRUE),
+
matrix(rep("C", genLiczba()), byrow=TRUE),
+
matrix(rep("D", genLiczba()), byrow=TRUE),
+
matrix(rep("E", genLiczba()), byrow=TRUE))
> dane <- cbind.data.frame(dane, round(1000*runif(nrow(dane)), digits=2))
> names(dane) <- c("wojewodztwo", "dochod")
> dane
wojewodztwo
dochod
1
A 727.51
2
A 332.39
3
A 958.54
4
A 495.79
5
B 650.74
6
B 335.60
7
B 304.47
8
B 907.75
9
C 816.74
10
C 337.80
11
C
4.28
12
C 425.86
13
C 905.67
14
C 684.64
15
C 178.01
16
C 478.33
17
C 62.65
18
D 557.86
19
D 477.71
20
D 228.84
21
D 832.78
22
D 489.75
23
D 225.12
24
D 808.17
25
D 232.41
26
D 285.64
27
E 110.17
28
E 347.39
# Zmienna 'wojewodztwo' ma 5 kategorii; jest używana jako zmienna grupująca
# Ustalamy ilość wybieranych grup na 2
# Metoda losowania: 'srswor'
> cl <- cluster(dane, clustername=c("wojewodztwo"), size=2, method="srswor")
> getdata(dane, cl)
dochod wojewodztwo ID_unit Prob
1 958.54
A
3 0.4
2 495.79
A
4 0.4
3 727.51
A
1 0.4
4 332.39
A
2 0.4
5 110.17
E
27 0.4
6 347.39
E
28 0.4
# Jak wyżej ale losowanie metodą 'systematyczne'
182
Artur Gramacki, Jarosław Gramacki
> cl <- cluster(dane, clustername=c("wojewodztwo"), size=2,
+
method="systematic", pik=runif(5))
> getdata(dane, cl)
dochod wojewodztwo ID_unit
Prob
1 727.51
A
1 0.3179695
2 332.39
A
2 0.3179695
3 958.54
A
3 0.3179695
4 495.79
A
4 0.3179695
5 650.74
B
5 0.8891991
6 335.60
B
6 0.8891991
7 304.47
B
7 0.8891991
8 907.75
B
8 0.8891991
Kolejnym zaprezentowanym schematem losowania będzie losowanie proporcjonalne do wielkości (ang. probability proportional to size sampling). W tej wersji zakłada się, że szansa wylosowania danego przypadku jest zależna od częstości jego występowania w populacji. Metoda ta jest
często używana w omówionym wcześniej losowaniu grupowym, gdy poszczególne grupy różnią
się znacznie wielkością. Przykładowo, jeżeli w 3 miastach mieszka odpowiednio 20, 30 oraz 200
tysięcy mieszkańców, to prawdopodobieństwo wyboru danego miasta nie powinno być jednakowe, właśnie ze względu na dysproporcje w ich wielkościach. Będziemy używali funkcji
ppss(pps) oraz ppswr(pps).
> library(pps)
> rozmiaryGrup <- c(20, 30, 200)
# Uruchamiamy 3. krotnie funkcję ppss. Ponieważ ostatnia grupa jest
# największa, najczęściej jest wybierana
> (indeksy <- ppss(rozmiaryGrup, 2))
[1] 2 3
> (indeksy <- ppss(rozmiaryGrup, 2))
[1] 3 3
> (indeksy <- ppss(rozmiaryGrup, 2))
[1] 3 3
# Jak wyżej, ale wersja ze zwracaniem
> (indeksy <- ppswr(rozmiaryGrup, 2))
[1] 3 3
> (indeksy <- ppswr(rozmiaryGrup, 2))
[1] 3 3
> (indeksy <- ppswr(rozmiaryGrup, 2))
[1] 2 3
> (indeksy <- ppswr(rozmiaryGrup, 2))
[1] 3 3
# Aby potwierdzić prawidłowość działania funkji możemy wykonać ją
# wielokrotnie. 'W granicy' poszczególne grupy powinny być wybrane
# mniej więcej w proporcji jak 20/250=0.12, 30/250=0.08. 200/250=0.8
> indeksy <- 0
> wynik <- matrix(nrow=10000, ncol=1)
> for(i in 1:10000){
+ indeksy <- ppss(rozmiaryGrup, 1)
+ wynik[i,] <- indeksy
+ }
> nrow(as.matrix((wynik[,1])[wynik[,1] == 1])) / 10000
[1] 0.0771
> nrow(as.matrix((wynik[,1])[wynik[,1] == 2])) / 10000
[1] 0.1181
> nrow(as.matrix((wynik[,1])[wynik[,1] == 3])) / 10000
[1] 0.8048
Redukcja liczności zbiorów z wykorzystaniem systemu R
183
Ostatnim zaprezentowanym schematem losowania będzie losowanie latin hypercube sampling
(LHS). W dostępnej polskojęzycznej literaturze autorzy nie odnaleźli żadnego polskiego tłumaczenia, dlatego też pozostajemy przy oryginalnej nazwie angielskiej. LHS jest oszczędną techniką
próbkowania (mała próba zapewnia wymaganą dokładność), która jest szczególnie często wykorzystywana w zagadnieniach związanych z analizą ryzyka (ang. uncertainty analysis). Literatura
przedmiotu [MeBi06, Mat08, MBC79, WJI98] podaje, że metoda LHS bywa chętnie stosowana
jako alternatywna i lepsza metoda próbkowania niż próbkowanie według metodologii Monte Carlo. LHS dokonując próbkowania wykorzystuje w pewnym sensie informacje o rozkładzie prawdopodobieństwa danych i dzięki temu wylosowana próbka staje się „bardziej reprezentatywna”. LHS
jest głównie stosowana wtedy, gdy należy wybrać próbkę z danych wielowymiarowych (wiele
niezależnych od siebie atrybutów). Stąd w nazwie metody pojawia się słowo hypercube. Z kolei
słowo latin pojawia się w nazwie metody dlatego, że jest tu pewna analogia do idei kwadratu łacińskiego, w którym żaden wiersz ani żadna kolumna nie zawierają dwóch takich samych wyrazów. W przykładach będziemy używali funkcji randomLHS(lhs) oraz maximinLHS(pps).
>
#
#
>
library(lhs)
Losujemy próbę 10. elementów z rozkładu jednostajnego (przedział od 0 do 1)
Próba jest 2. wymiarowa
(out <- randomLHS(10, 2))
[,1]
[,2]
[1,] 0.08640353 0.90617740
[2,] 0.61208764 0.09191655
[3,] 0.40712008 0.69511004
[4,] 0.21590303 0.48534400
[5,] 0.39817925 0.10110830
[6,] 0.78805653 0.36892300
[7,] 0.87994959 0.58477055
[8,] 0.94889438 0.27486319
[9,] 0.11467641 0.78964572
[10,] 0.53801916 0.80483726
# Tworzymy rysunek 1
> plot(out, xlim=c(0,1), ylim=c(0,1), pch=16, xlab="", ylab="")
> abline(v=seq(0,1,length=11), lty=2, col=3)
> abline(h=seq(0,1,length=11), lty=2, col=3)
# Inny wariant algorytmu LHS (patrz opis w tekście)
> out <- maximinLHS(10, 2, 1)
Wynik losowania najwygodniej jest zaprezentować graficznie (patrz rysunek 1). LHS wybiera
próbki w taki sposób, że w danej kratce zawsze wybrany zostanie tylko jeden punkt. Przy czym
jego wartość (współrzędne x i y) jest wybierana losowo. Takie działanie LHS uzasadnia użycie
w nazwie metody słowa latin. Wylosowane próbki mają rozkład jednostajny, można jednak łatwo
dokonać ich transformacji do dowolnego innego rozkładu, używając dostępnych w R funkcji generujących tzw. odwrotne dystrybuanty (ang. quantile functions), np. qnorm(stats). Aby to zademonstrować wygenerujemy 10000 próbek z rozkładu jednostajnego a następnie „przerobimy” je
na rozkład normalny. Poprawność działania zaprezentujemy na histogramach (patrz rysunek 2).
>
>
>
>
>
par(mfrow = c(1,2), pty="m")
u <- runif(10000)
hist(u, xlab="Zmienna u", ylab="Liczności", main="")
n <- qnorm(u)
hist(n, xlab="Zmienna n", ylab="Liczności", main="")
184
0.0
0.2
0.4
0.6
0.8
1.0
Artur Gramacki, Jarosław Gramacki
0.0
0.2
0.4
0.6
0.8
1.0
1000
Liczności
300
0
0
100
500
200
Liczności
400
1500
500
2000
Rys. 1. Wynik wygenerowania próby losowej za pomocą algorytmu LHS
0.0
0.2
0.4
0.6
Zmienna u
0.8
1.0
-4
-2
0
2
4
Zmienna n
Rys. 2. Wynik konwersji rozkładu jednostajnego do rozkładu normalnego
W pakiecie pps zaimplementowano kilka różnych wariantów metody LHS. Przykładowo funkcja maximinLHS(lhs) generuje dane w tak sposób, że maksymalizowane są minimalne odległości między wylosowanymi próbkami. Istotę tej modyfikacji pokazano na rysunku 3, na którym
zamieszczono wyniki 3 losowań. W górnym rzędzie pokazano wyniki dla metody randomLHS,
natomiast w dolnym dla metody maximinLHS. Widać wyraźnie, że dla drugiej metody wylosowane próbki nie są tak „przylegające” do siebie.
185
Redukcja liczności zbiorów z wykorzystaniem systemu R
0.0
0.2
0.4
0.6
0.8
1.0
0.8
0.0
0.2
0.4
0.6
0.8
0.6
0.4
0.2
0.0
0.0
0.2
0.4
0.6
0.8
1.0
randomLHS
1.0
randomLHS
1.0
randomLHS
0.0
0.2
0.6
0.8
1.0
0.4
0.6
0.8
1.0
0.4
0.6
0.8
1.0
0.8
1.0
1.0
0.0
0.2
0.4
0.6
0.8
1.0
0.8
0.4
0.2
0.0
0.2
0.2
maximinLHS
0.6
0.8
0.6
0.4
0.2
0.0
0.0
0.0
maximinLHS
1.0
maximinLHS
0.4
0.0
0.2
0.4
0.6
0.8
1.0
0.0
0.2
0.4
0.6
Rys. 3. Wynik wygenerowania próby losowej za pomocą dwóch wersji algorytmu LHS
Na początku rozdziału 2 wspomniano, że technikę histogramów można uważać za pewną formę redukcji liczności danych. W tym przypadku redukcja nie będzie dokonywana za pomocą
próbkowania danych, a raczej dane wejściowe będą podlegały pewnej kompresji. Tworząc histogram dokonujemy swego rodzaju uśredniania danych. Dane są dzielone na określoną ilość przedziałów (ang. bins) i dla każdego przedziału wyznaczana jest jej liczność oraz wartość średnia.
Dane te można uznać za skompresowaną postać danych oryginalnych. Poniżej pokazano przykłady użycia funkcji hist(graphics). Warto również pamiętać, że kształt histogramu bardzo mocno zależy od ilości przedziałów, oraz od środków owych przedziałów. Problem ich optymalnego
wyboru nie jest łatwy i w ogólności trudno jest podać jakieś rozwiązanie optymalne dla danego
zagadnienia i posiadanych danych. Tematu tego jednak nie rozwijamy w niniejszym artykule.
Na rysunku 4 pokazano 3 różne histogramy, jakie powstały na bazie tego samego zestawu danych (rysunek 4a). Widać wyraźnie, że każdy histogram nieco inaczej je prezentuje. Przykładowo
z rysunku 4b nie widać w żaden sposób, że dane mają wyraźny rozkład dwumodalny. Kod w systemie R, który posłużył do wygenerowania rysunku 4 jest pokazany poniżej. Używamy tutaj pakietu nor1mix, z pomocą którego można łatwo generować bardzo różne (i często bardzo „udziwnione”) rozkłady danych.
>
>
#
>
>
>
>
>
>
#
library(nor1mix)
par(mfrow = c(1,4), pty="m")
Generujemy rozkład dwumodalny asymetryczny
dane <- rnorMix(5000, MW.nm8)
plot(MW.nm8, main="", xlab="(a)")
hist(dane, 10, xlim=c(-4,4), ylab="Liczności", main="", xlab="(b)")
hist(dane, 20, xlim=c(-4,4), ylab="Liczności", main="", xlab="(c)")
hist(dane, 30, xlim=c(-4,4), ylab="Liczności", main="", xlab="(d)")
(h <- hist(dane, 10, xlim=c(-4,4), ylab="Liczności", main="", xlab="(b)"))
Takie dane zwraca funkcja hist
186
Artur Gramacki, Jarosław Gramacki
-2
-1
0
(a)
1
2
3
400
-4
-2
0
(b)
2
4
100
200
Liczności
600
0
200
0
0
-3
400
Liczności
1000
Liczności
500
0.2
0.0
0.1
f(x)
0.3
300
800
1500
0.4
$breaks
[1] -3.5 -3.0 -2.5 -2.0 -1.5 -1.0 -0.5 0.0 0.5 1.0 1.5
2.0 2.5 3.0 3.5
$counts
[1]
2 16 57 153 354 551 744 708 644 899 719 136 14
3
$intensities
[1] 0.0007999998 0.0064000000 0.0228000000 0.0612000000 0.1416000000
0.2204000000 0.2976000000 0.2832000000 0.2576000000 0.3596000000
0.2876000000 0.0544000000 0.0056000000 0.0012000000
$density
[1] 0.0007999998 0.0064000000 0.0228000000 0.0612000000 0.1416000000
0.2204000000 0.2976000000 0.2832000000 0.2576000000 0.3596000000
0.2876000000 0.0544000000 0.0056000000 0.0012000000
$mids
[1] -3.25 -2.75 -2.25 -1.75 -1.25 -0.75 -0.25 0.25 0.75 1.25
1.75 2.25 2.75 3.25
-4
-2
0
2
4
(c)
-4
-2
0
2
(d)
Rys. 4. Zależność kształtu histogramu od ilości przedziałów
W pakiecie gplots znajduje się funkcja hist2d do generowania histogramów 2. wymiarowych. Poniższy kod generuje histogramy pokazane na rysunku 5. Pierwszy histogram podzielony
jest na zaledwie 5 kubełków, aby na wydruku poniżej zmieściły się wszystkie wygenerowane dane. W praktycznych zastosowaniach liczba kubełków powinna być oczywiście większa, aby wykres sensownie pokazywał rozkład danych. Drugi histogram utworzono w taki sposób, że każda oś
podzielna jest na inną liczbę kubełków. Do wykonania dwóch ostatnich wykresów użyto funkcji
persp(graphics) oraz filled.contour(graphics).
> x <- rnorm(2000, sd=4)
> y <- rnorm(2000, sd=1)
> (dane <- hist2d(x,y, nbins=c(5,5), col=rainbow(10), xlab="", ylab=""))
$counts
[-3.85,-2.48] (-2.48,-1.11] (-1.11,0.263] (0.263,1.63] (1.63,3]
[-13.8,-7.82]
0
4
17
14
2
(-7.82,-1.81]
4
67
297
186
24
(-1.81,4.2]
9
144
501
379
54
(4.2,10.2]
7
39
126
102
14
(10.2,16.2]
0
2
4
4
0
$x
[1] -13.822330 -7.815735 -1.809140
4.197455 10.204051
$y
[1] -3.8484948 -2.4778419 -1.1071891 0.2634638 1.6341166
# Ilość kubełków w każdej osi może być różna.
4
187
Redukcja liczności zbiorów z wykorzystaniem systemu R
-4
-3
-3
-2
-2
-1
-1
0
0
1
1
2
2
3
> (dane <- hist2d(x,y, nbins=c(10,30), col=rainbow(10), xlab="", ylab=""))
> persp(dane$x, dane$y, dane$counts, ticktype="detailed", theta=30, phi=30,
+
expand=0.5, shade=0.5, ltheta=-30, xlab="", ylab="", zlab="")
> filled.contour(dane$x, dane$y, dane$counts, nlevels=6, col=gray((6:0)/6))
-10
-5
0
-10
5
50
-5
0
5
3
60
2
50
1
40
40
30
0
20
30
2
10
-1
0
20
-10
0
-2
-5
10
0
-2
-3
0
5
-10
-5
0
5
Rys. 5. Przykładowe histogram 2D
3. Podsumowanie
W artykule, w wielkim skrócie, pokazano możliwości użycia systemu R do wykonywania zadań redukcji liczności zbiorów danych. Należy jednak zauważyć, że wiele ze znanych metod redukcji liczności jak na razie nie doczekało się implementacji w środowisku R. Wystarczy skonfrontować dostępne repozytorium R z publikacjami na temat różnych schematów losowania. Niemniej najważniejsze metody można w R odnaleźć. Wielka dynamika jego rozwoju pozwala jednak
żywić nadzieję, że w niedługim czasie coraz więcej metod zostanie tam implementowanych.
Podkreślmy na koniec, że redukcja liczności zbiorów jest ważnym elementem analizy danych
wykonywanych w ramach szeroko pojętej eksploracji danych, zwłaszcza gdy analizowanych danych jest bardzo dużo. Wówczas na wstępnych etapach ich analizy, celem na przykład zaoszczędzenia czasu, można rozważyć redukcję ich liczności. Otrzymywane wówczas wyniki będą prawdopodobnie nieco przybliżone i mniej dokładne, jednak mogą okazać się w zupełności wystarczające. Oczywiście nie wszystkie dane należy automatycznie i bez zastanowienia redukować. Warto
jednak zawsze rozważyć taką możliwość i poeksperymentować.
188
Artur Gramacki, Jarosław Gramacki
Bibliografia
[Bie08]
[Gra09]
Biecek P.: Przewodnik po pakiecie R, Oficyna wydawnicza GiS, Wrocław, 2008 (pierwsze 64
strony tej książki znajduje się pod adresem http://www.biecek.pl/R/R.pdf).
Gramacki A., Gramacki J.: Wykorzystanie projektu R w zadaniach eksploracji danych, XV
Konferencja użytkowników i deweloperów ORACLE, 20-23.X.2009, Zakopane-Kościelisko.
[HaKa00]
Han J., Kamber M.: Data Mining: Concepts and Techniques, Morgan Kaufman, 2000.
[HMS05]
Hand D. J., Mannila H., Smyth P.: Pricinciples of Data Mining, MIT Press, Cambridge, MA,
2001; tłumaczenie polskie: Eksploracja danych,Wydawnictwa Naukowo-Techniczne, Warszawa, 2005.
[Kis95]
Kish L.: Survey Sampling, Wiley, 1995.
[KoMi04]
Koronacki J., Mielniczuk J.: Statystyka dla studentów kierunków technicznych i przyrodniczych,
WTN, 2004.
[KomR]
Komsta Ł.: Wprowadzenie do środowiska R,
http://cran.r-project.org/doc/contrib/Komsta-Wprowadzenie.pdf
[Loh99]
Lohr S. S.: Sampling: Design and Analysis, Duxbury Press, 1999
[Mat08]
Matala A.: Sample Size Requierement for Monte Carlo – simulations using Latin Hypercube
Sampling, Helsinki University of Technology, Department of Engineering Physics and Mathematics, Systems Analysis Laboratory, 2008.
[MBC79]
McKay M., Beckman, R., Conover, W.: A Comparison of Three Methods for Selecting Values of
Input Variables in the Analysis of Output from a Computer Code, Technometrics, vol. 21, no. 2,
May 1979.
[MeBi06]
Mease D., Bingham D.: Latin Hyperrectangle Sampling for Computer Experiments, Technometrics, November 1, 2006, 48(4): 467-477.
[RPro]
http://www.r-project.org/
[WaGa09]
Walesiak M., Gatnar E. (Ed.): Statystyczna analiza danych z wykorzystaniem programu R,
Wydawnictwo Naukowe PWN 2009.
[WJI98]
Wyss G. D., Jorgensen K. H., Iman R. L., Shortencarier M. J., Davenport J. M.: A User's
Guide to LHS: Sandia's Latin Hypercube Sampling Software, 1998.

Podobne dokumenty