Wykresy funkcyjne – jak narysować „normalny” wykres

Transkrypt

Wykresy funkcyjne – jak narysować „normalny” wykres
Raportowanie
Wykresy funkcyjne – jak narysować
„normalny” wykres
Krzysztof Kusch
Predictive Solutions
Powracamy, po krótkiej przerwie, do tematu tworzenia wykresów w oparciu o język GPL.
W tym materiale zaprezentowana zostanie metoda pozwalająca na tworzenie wykresów
mających przedstawiać przebieg funkcji. Będzie się ona opierała o metodę „tworzenia”
danych w obrębie kodu GPL. Istnieje również druga metoda, co prawda moim zdaniem
mniej przyjazna użytkownikowi, ale za to rozszerzająca możliwości programu, która opiera
się na wygenerowaniu technicznego pliku danych, na podstawie którego stworzony zostanie wykres. Możliwości jej zastosowania były już prezentowane w materiale z [9. numeru]
]EKSPRESSu], dlatego teraz skupimy się jedynie na możliwościach, jakie daje kod GPL w tym
zakresie.
Do czego dążymy?
W dalszych częściach materiału zaprezentuję, w jaki sposób stworzyć wykres w oparciu
o równanie funkcji. Jako przykład wybrałem jedną z ulubionych funkcji w dziedzinie analizy
danych – funkcję gęstości prawdopodobieństwa rozkładu normalnego. Na początku przełożymy tę funkcję na język GPL, a następnie stworzymy dla niej wykresy. Aby trochę urozmaicić
tę ciężką pracę, dodamy do tych grafik kilka elementów, które sprawią, że wykres będzie
miał większą wartość informacyjną. Do dzieła!
Podstawowe elementy kodu
Inaczej niż to miało miejsce w poprzednich artykułach o języku GPL, tym razem nie będziemy
korzystać z Kreatora wykresów w celu stworzenia kodu będącego punktem wyjścia do dalszych modyfikacji. Są dwa powody takiego stanu rzeczy: po pierwsze, kod pozwalający nam
stworzyć podstawowy wykres jest bardzo prosty – nie ma więc powodu, aby wyręczać
się programem. Po drugie, w Kreatorze wykresów nie istnieje możliwość wygenerowania
wykresu bez posiadania odpowiednich zmiennych w zbiorze danych – my natomiast nie
będziemy korzystać w tej chwili z realnie występujących zmiennych.
Przed wywołaniem bloku kodu GPL w programie IBM SPSS Statistics potrzebne jest umieszczenie funkcji GGRAPH pozwalającej określić sposób wywołania kodu, zakres zmiennych,
traktowanie braków danych, szablon itd. Mimo, iż nie będziemy odnosić się do żadnego
realnego zbioru danych, wciąż musimy z tej funkcji skorzystać, aby uruchomić w Statistics kod
GPL. W obrębie tej funkcji musimy jeszcze powiedzieć programowi, że specyfikacja wykresu
(tzn. blok kodu GPL) znajduje się bezpośrednio po funkcji (SOURCE = INLINE):
GGRAPH
/GRAPHSPEC SOURCE=INLINE.
Po funkcji GGRAPH może znaleźć się już klamra BEGIN GPL – END GPL, w której umieścimy
kod GPL pozwalający programowi narysować żądany wykres. W dalszych częściach będziemy
omawiać już tylko kod GPL, pamiętając o tym, że jest on umieszczony w obrębie następującego kodu Syntax:
GGRAPH
/GRAPHSPEC SOURCE=INLINE.
BEGIN GPL
...
END GPL.
2
Wykresy funkcyjne – jak narysować „normalny” wykres
Dziedzina funkcji
W pierwszym kroku stworzymy zmienną, której wartości będą podzbiorem dziedziny funkcji,
dla którego chcemy stworzyć wykres. Patrząc na ten krok z perspektywy wykresu – chcemy
teraz stworzyć zmienną, którą umieścimy na osi x, i do wartości której będziemy przyporządkowywać zgodnie z równaniem funkcji wartości zmiennej, która znajdzie się na osi y.
Jeśli chcemy zaprezentować na wykresie funkcję gęstości prawdopodobieństwa klasycznego
rozkładu normalnego (o średniej równej 0 i odchyleniu standardowym równym 1) wydaje się
być rozsądne uwzględnienie przedziału, który będzie symetryczny względem zera. Ponieważ
99% wartości znajduje się w obrębie +/−3 odchylenia standardowe od średniej, przyjmiemy
na potrzeby naszego przykładu, że wartości zmiennej x na naszym wykresie będą zaczynać
się od −3 do 3. Oczywiście w zależności od potrzeb możemy zdefiniować inny przedział.
W języku GPL do tworzenia nowych zmiennych o wartościach pochodzących z określonego
przedziału służy funkcja iter, którą wywołuje się deklaracją DATA. Posiada ona następującą
składnię:
iter(<od>, <do>, <krok>)
gdzie od oznacza pierwszą wartość, która zostanie wprowadzona, do oznacza największą
wartość, która może być wprowadzona do nowej kolumny, a krok oznacza liczbę, o którą
każda kolejna wartość zmiennej będzie powiększana, aż nie zostanie osiągnięta wartość
do lub najbliższa wartość znajdująca się w zdefiniowanym przedziale. Przykładowo, określając wartość od jako 1, wartość do jako 10, a krok jako 1, otrzymamy zmienną zawierająca
następujące wartości:
1,2,3,4,5,6,7,8,9,10
Inaczej stanie się, gdy zdefiniujemy te same wartości od i do, ale wartość parametru krok
zmienimy na 2. Wtedy zmienna będzie składała się z wartości 1,3,5,7,9. Wartość parametru
do (10) nie zostanie tu umieszczona, gdyż nie może ona zostać obliczona w oparciu o poprzednią wartość (9+2=11). W pierwszym akapicie tej części zdecydowaliśmy już, że krańcami
przedziału, dla którego będziemy prezentować wykres, są wartości −3 i 3. Odpowiadają one
kolejno parametrom od i do w funkcji iter(), brakuje nam zatem tylko ostatniego parametru
funkcji. Na poniższych wykresach zaprezentowano, jak wygląda wykres stworzony w oparciu
o cztery wartości parametru krok: 1, 0,1, 0,01, 0,001.
3
Wykresy funkcyjne – jak narysować „normalny” wykres
Widać, że tylko pierwszy wykres został wygenerowany w oparciu o zdecydowanie za małą
liczbę punktów (mianowicie 7). Pozostałe wykresy nie różnią się wyglądem od siebie, co może
prowadzić do wniosku, że wygenerowanie tego wykresu w oparciu o 70 punktów (krok =
0,1) będzie właściwą decyzją. W praktyce wszystko zależy z jednej strony od tego, jak duży
wykres chcemy wygenerować, a z drugiej, jak mocnym komputerem dysponujemy – musimy
wszak pamiętać, że większa liczba wartości wygenerowanych przez funkcję iter(), to większa liczba punktów do nakreślenia na wykresie, co czasami może znacznie wydłużyć czas
renderowania wykresu. Ponieważ wygenerowanie wykresu w oparciu o 700 wartości nie
powinno stanowić żadnego wyzwania dla naszego komputera, zdefiniujmy parametr kroku
jako 0,01. Pierwsza linia kodu GPL będzie zatem wyglądać następująco:
DATA: x = iter(-3,3,0.01)
Znana nam z poprzednich materiałów deklaracja DATA pozwala zdefiniować zmienną, która
będzie używana w kodzie GPL. W przeszłości służyła nam ona jednak do odwoływania się
do istniejących w zbiorze danych zmiennych (funkcja col()). Teraz, za pomocą funkcji iter()
tworzymy nową zmienną o nazwie x.
Równanie funkcji gęstości prawdopodobieństwa
rozkładu normalnego
Skoro posiadamy już linię kodu tworzącą zmienną, którą umieścimy na osi x, czas teraz zająć
się drugą potrzebną nam zmienną – tą, która zostanie umieszczona na osi y. Zadanie tym
razem nie jest tak proste, jak to miało miejsce w przypadku poprzedniej zmiennej. W tym
wypadku będziemy musieli wyliczyć, w oparciu o istniejące już wartości zmiennej x, wartości
zmiennej y korzystając z następującego wzoru:
Gdzie µ to średnia (w naszym przykładzie jest ona równa 0), a σ to odchylenie standardowe.
Niestety, aby móc użyć tej formuły w kodzie, musimy ją przepisać używając odpowiednich
funkcji i operatorów matematycznych. Określmy najpierw, jakie działania musimy wykonać
obliczając wartość zmiennej y, a następnie zobaczmy, które mogą nam sprawić problem.
W powyższym równaniu mamy zatem mnożenie (w GPL zapisywane znakiem *), dzielenie
(znak/), odejmowanie (-), ale również pierwiastkowanie, podnoszenie do potęgi i obliczanie
liczby eulera podniesionej do potęgi zdefiniowanej przez wyrażenie (exp). Zauważmy również,
że mamy jedną stałą (liczbę pi) i dwa parametry (µ i σ). W języku GPL, aby obliczyć pierwiastek kwadratowy wyrażenia, należy umieścić je w obrębie funkcji sqrt (ang. square root).
Potęgowanie jest w GPL zapisywane w ten sam sposób, co w IBM SPSS Statistics i wyraża się
podwójną gwiazdką (**). Pewnym ułatwieniem dla nas jest to, że w GPL, w przeciwieństwie
do Statistics, występuje liczba pi, do której można się odwołać po prostu poprzez wpisanie
pi. Obliczanie eksponentu wyrażenia jest możliwe poprzez umieszczenie go w nawiasach
funkcji exp(). Po przekształceniu i zastosowaniu powyższych funkcji powyższe wyrażenie
przyjmie przedstawioną poniżej formę.
1/(s*sqrt(2*pi)))*exp(−1*(((x − m)**2)/(2*(s**2)))
Powinniśmy pamiętać, że parametry m i s, w naszym przykładzie wynoszą odpowiednio
0 i 1 – podstawmy te wartości w odpowiednie miejsca, aby uzyskać finalną formę wzoru:
1/(1*sqrt (2*pi)))*exp (−1* (((x − 0)**2)/(2* (1**2)))
Posiadamy już formułę, w oparciu o którą możliwe będzie wyliczanie wartości zmiennej y. Nie
wiemy jednak, jakich funkcji należy użyć w języku GPL, aby wyliczyć te wartości. Zajmiemy
się teraz tą kwestią. Po pierwsze, nie będziemy w tym miejscu korzystać z deklaracji DATA.
Do tworzenia nowej zmiennej, której wartości są wynikiem przekształceń, służy deklaracja
TRANS. W jej obrębie możemy skorzystać z bardzo przydatnej funkcji eval(), która dla każdej
obserwacji w zbiorze oblicza wartość wynikową w oparciu o zdefiniowane w niej wyrażenie
matematyczne lub logiczne. Przykładowo, korzystając z tych dwóch elementów kodu możemy
w definicji wykresu obliczyć różnicę dwóch zmiennych – np. pierwotnego wynagrodzenia
pracownika i aktualnego wynagrodzenia:
4
Wykresy funkcyjne – jak narysować „normalny” wykres
TRANS: roznica = eval (wynagr_aktual – wynagr_pierw)
Jak wspomniałem, funkcja eval() może być również wykorzystywana do tworzenia zmiennych prawda-fałsz, które zwracają wartość prawda w sytuacji, gdy spełniony jest warunek
logiczny umieszczony w obrębie wspomnianej funkcji. Przykładowo, zmienna dychotomiczna
flagująca pracowników zarabiających aktualnie powyżej 3000:
TRANS: akt3000 = eval (wynagr_aktual>3000)
W naszym przypadku chcemy, by na podstawie „przetłumaczonego” na język GPL wzoru
funkcji gęstości prawdopodobieństwa każdej wartości zmiennej x została przyporządkowana
wartość zmiennej y. W tym wypadku wystarczy umieścić omawiane wyrażenie w nawiasach
funkcji eval:
TRANS: y = eval ((1/(1*sqrt (2*pi)))*exp (-1* (((x – 0)**2)/(2*
(1**2)))))
Teraz brakuje nam już tylko jednej linii kodu, by stworzyć nasz pierwszy z omawianych w tym
materiale wykresów. Jest to oczywiście linia deklaracji ELEMENT, dzięki której zdefiniujemy
obiekt graficzny, który będzie reprezentował dane i sposób łączenia ze sobą poszczególnych wartości zmiennych (algebrę). Obiektem graficznym ma być (jak się domyślamy) linia,
a algebra umieszczona w funkcji postion () sprowadza się do skrzyżowania (operator *)
ze sobą zmiennej x i y:
ELEMENT: line (position (x*y))
Całość kodu GPL (wraz z pozwalającym go wywołać Syntaxem IBM SPSS) została zaprezentowana poniżej:
GGRAPH
/GRAPHSPEC SOURCE=INLINE.
BEGIN GPL
DATA: x = iter(-3,3,0.01)
TRANS: y = eval((1/(1*sqrt(2*pi)))*exp(-1*(((x - 0)**2)/(2*(1**2))) ))
ELEMENT: line(position(x*y))
END GPL.
Rozkład normalny – wykres warstwowy
W poprzedniej części materiału udało nam się stworzyć liniowy wykres dla funkcji gęstości prawdopodobieństwa rozkładu normalnego bez korzystania ze zbioru danych. Teraz
pójdziemy trochę dalej i stworzymy wykres, który będzie przedstawiał obszar pod krzywą
rozkładu i będzie on kolorowany w zależności od ustawionego progu, np. 1,96 odchylenia
standardowego. W omawianym powyżej kodzie GPL musimy wprowadzić łączenie trzy zmiany,
aby uzyskać taki efekt. Po pierwsze, musimy stworzyć w kodzie GPL nową zmienną, która
będzie dzielić obszar pod krzywą na taki, gdzie wartości rozkładu mieszczą się w zadeklaro-
5
Wykresy funkcyjne – jak narysować „normalny” wykres
wanym przedziale wokół średniej i takie, które są poza tym przedziałem. Po drugie, musimy
zmienić obiekt graficzny, który ma reprezentować dane. Po trzecie, musimy zaimplementować nowo wyliczoną zmienną do deklaracji ELEMENT, dzięki czemu będzie widoczny jej
wpływ na wykresie.
Zmiana pierwsza – nowa zmienna
Nie bez powodu omawiając funkcję eval () wspomniałem o możliwości tworzenia z jej
pomocą zmiennych prawda-fałsz. W tym miejscu skorzystamy właśnie z tej funkcjonalności.
Zacznijmy od szkieletu – nową zmienną nazwijmy color:
TRANS: color = eval ()
Teraz skonstruujemy wyrażenie, które zostanie umieszczone w nawiasach funkcji eval.
Załóżmy, że chcemy oznaczyć obszar pod krzywą w odległości ±1,96 odchylenia standardowego od średniej. Ponieważ wartości oddalone o 1,96 odchylenia standardowego mogą
występować z zarówno wśród wartości dodatnich jak i ujemnych, skorzystamy z funkcji abs()
zwracającej wartość bezwzględną:
abs (x) >1
Druga część wyrażenia będzie korzystała z konstrukcji „to … inaczej …”, którą w GPL zapisuje
się w postaci „? …: …”, wpisując w miejsce wielokropków w cudzysłowach nazwy generowanych kategorii. Nasze wyrażenie może zatem przyjąć formę:
abs (x) >1? "<= +/-1,96σ": "> +/-1,96σ"
Cała deklaracja TRANS będzie po dołączeniu tego wyrażenia wyglądała następująco:
TRANS: color = eval (abs (x) >1? "<= +/-1,96σ": "> +/-1,96σ")
Zmiana 2 – zmiana obiektu graficznego
Na poprzednim wykresie rysowanym obiektem graficznym była linia. Teraz, skoro chcemy
by obszar pod krzywą był zaznaczony, musimy skorzystać z innego obiektu – jest nim obiekt
o nazwie area, który pozwala tworzyć między innymi wykresy warstwowe. Zmiana następuje
tylko w deklaracji ELEMENT i ogranicza się jedynie do podmienienia słowa line na area:
ELEMENT: area (position (x*y))
Zmiana 3 – dołączenie zmiennej color do deklaracji ELEMENT
Ostatnią zmianą, którą dokonamy jest dodanie funkcji color(), która wskazuje, że rysowany
na wykresie obiekt graficzny będzie różnicowany kolorem. W jej obrębie umieścimy stworzoną specjalnie w tym celu zmienną color, co spowoduje, że obszar pod krzywą będzie
różnił się kolorem w zależności od przyjmowanych przez tę zmienną wartości:
ELEMENT: area (position (x*y), color (color))
Po zmianach – gotowy kod i wygenerowany wykres
Po wprowadzeniu opisanych powyżej zmian stajemy się już posiadaczami gotowego kodu
GPL, który pozwoli wygenerować żądany wykres. Całość kodu wygląda teraz następująco:
GGRAPH
/GRAPHSPEC SOURCE=INLINE.
BEGIN GPL
DATA: x = iter(-3,3,0.01)
TRANS: y = eval((1/(1*sqrt(2*pi)))*exp(-1*(((x - 0)**2)/(2*(1**2))) ))
TRANS: color = eval(abs(x)>1.96 ? "<= +/-1,96σ" : "> +/-1,96σ")
ELEMENT: area(position(x*y), color(color))
END GPL.
6
Wykresy funkcyjne – jak narysować „normalny” wykres
Wykres stworzony za jego pomocą wygląda następująco:
Zamiast zakończenia – co dalej?
W tym artykule chciałem przede wszystkim pokazać, w jaki sposób można w języku GPL
korzystać z funkcji iter() i eval(). Udało nam się, niejako przy okazji, stworzyć wykresy, które
mogą się przydać przy tworzeniu różnych materiałów (np. szkoleniowych). Mogą one również służyć za bazę do dalszych eksperymentów i mogą być dalej rozszerzane o dodatkowe
informacje. Przykładowo, możemy chcieć dodać do wykresu linię informującą o tym, jaki
przedział został zaznaczony:
Kod, którym posłużyłem się do wygenerowania go prezentuję poniżej:
GGRAPH
/GRAPHSPEC SOURCE=INLINE.
BEGIN GPL
DATA: x = iter(-3,3,0.01)
DATA: z = iter(-1.96,1.96,0.01)
TRANS: y = eval((1/(1*sqrt(2*pi)))*exp(-1*(((x - 0)**2)/(2*(1**2))) ))
TRANS: t = eval((1/(1*sqrt(2*pi)))*exp(-1*(((1.96)**2)/(2*(1**2)))))
TRANS: color = eval(abs(x)>1.96 ? "<= +/-1,96σ" : "> +/-1,96σ")
GUIDE: legend(aesthetic(aesthetic.color.interior), null())
ELEMENT: area(position(x*y), color(color))
ELEMENT: line(position(z*t), color(color.white), label("-1,96σ
<--(95%)--> 1,96σ"))
END GPL.
Wykresy funkcyjne – jak narysować „normalny” wykres
7
Nie zawsze uda nam się wygenerować wykres bez odwoływania się do pliku danych (nawet
takiego, który jest sztucznie wygenerowany). Przykładowo, aby wygenerować poniższy
wykres musiałem najpierw wygenerować zbiór danych w IBM SPSS Statistics, właściwie
w identyczny sposób, jaki był omawiany w tym artykule w odniesieniu do kodu GPL. Nie
da się jednak ukryć, że możliwość tworzenia zmiennych w obrębie kodu GPL jest bardzo
użyteczną funkcjonalnością i wydaje się ona być zdecydowanie bardziej „poręczna” niż
konieczność generowania sztucznego zbioru danych do każdego wykresu funkcyjnego, który
chcielibyśmy stworzyć.
Krzysztof Kusch
Zagadnienia wizualizacji informacji
z użyciem wykresów
omawiane są na [szkoleniu ST2a]
Predictive Solutions
ul. Racławicka 58 · 30-017 Kraków
tel. 12 636 96 80 · faks wew. 102
e-mail [[email protected]]
[www.predictivesolutions.pl]
Absolwent socjologii Akademii Górniczo-Hutniczej w Krakowie i Uniwersytetu Jagiellońskiego o specjalizacji Badania społeczne i analiza danych. W Predictive Solutions [dawniej SPSS Polska] pracuje od 2010 roku na stanowisku konsultant ds. rozwiązań analitycznych
i data mining, gdzie skupia się szczególnie na rozwiązaniach analitycznych dla sektora bankowego, ubezpieczeniowego i farmaceutycznego (CRM analityczny, prognozowanie sprzedaży),
a także na kwestiach związanych z wizualizacją danych i raportowaniem. Prowadzi szkolenia
z zakresu statystycznej analizy danych.

Podobne dokumenty