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.