Acg N. .
Temat: Współpraca .NET i R - biblioteka dla systemów analitycznych.
Gdyby ktoś miał ochotę skrobnąć coś, co korzysta z R na platformie .NET, to służę pomocą na temat pracy z biblioteką RDCOM.Co prawda kończy mi się pracowity urlop, ale spróbuję na dniach opisać tutaj projekt, którym zajmowałem się jakiś czas - spory system klas proxy (opakowujących, pośredniczących) dla celów stworzenia online'owego systemu analiz statystycznych, wykorzystującego R.
Chodziło o to, aby napisać swego rodzaju framework (.NET / C#), pozwalający pracować z R z poziomu kodu programu.
Dzięki stworzeniu odpowiedniej liczby klas dla źródeł danych (providery danych), selektorów danych, klas testów statystycznych (poukładane ładnie w przestrzeniach nazw) i tak dalej dało się z tego poukładać, jak z cegiełek jakiś tam scenariusz podstawowej analizy statystycznej.
Oczywiście każdy test statystyczny i procedura umie się sama opisać . Zarówno przez klasę wynikową (oraz klasy kolekcji), jak i przez zapis specjalnymi formatterami: do XML, do HTML (dostarczam
podstawowy formatter), a także - do tablicy stringów z wynikami analiz (otrzymuje się coś na kształt "tekstowego raportu").
Potem dorabiało się interfejs w ASP.NET - wybór zmiennej/zmiennych, okreslanie dziedziny (sql'owe "where / having") i pisało odpowiedni formater danych, korzystający z arkuszy CSS.
Niestety, jak to bywa w projektach tworzonych "na szybko", nie mam dokumentacji (czytaj - jestem jedyną instancją dokumentacji :) ), tę trzeba napisać. Chociaż wydaje mi się, że wystarczy diagram UML i seria przykładów ("learn by examples"). Inaczej musiałbym spędzić kolejne 3 urlopy przy tym :)
Oczywiście jestem autorem kodu, a w umowie z firmą, dla której to pisałem, jest zawarta klauzula o posiadaniu przeze mnie praw autorskich i majątkowych do kodu, a także o tym, że mogę ten kod dowolnie rozpowszechniać i sprzedawać. I na podobnej licencji umieszczę tutaj ten kod. Być może będzie to licencja LGPL - musiałbym się wgłębić w szczegóły licencyjne, a nie mam głowy na razie do tego.
System domyślnie współpracował z relacyjnymi bazami danych (SQL Server), w których trzymane były dane z badań klinicznych. Dlatego starałem się, aby można było podawać "selekty", których wynikiem jest układ rekordowy (np. wyniki kolejnych miesięcy terapii w kolejnych wierszach) jak i kolumnowy (kolejnie miesiące terapii w osobnych kolumnach).
Dostęp do danych załatwia ADO.NET jak również biblioteka RDOBC - przetestowana przeze mnie i w pełni przydatna w tym projekcie.
Optymalizacje - nie było czasu myśleć o takich "szczegółach", system spełniał swoje zadanie dostatecznie dobrze w takiej postaci, w jakiej został napisany.
Przykładowy scenariusz, jaki da się zrealizować w bieżącej postaci biblioteki:
Dane wejściowe:
"DataSet" (rekordset, zbiór danych - dla programistów pojęcie to powinno być czytelne: zestaw tabel wynikowych, powiązanych relacjami) będący wynikiem odpowiedniego zapytania do bazy danych.
Tabele datasetu mogą być w postaci:
- "stacked" (2 kolumny: wartość zmiennej, czynnik grupujący)
- kolumnowej; kolejne wartości atrybutu w kolejnych kolumnach, np. wyniki pomiarów hematokrytu w kolejnych miesiącach terapii znajdują się w kolejnych kolumnach tabeli.
Braki danych reprezentowane są przez "erowe" NA, a po stronie .NET - przez Double.NaN
1. Przygotowanie tabel z wynikami badań (kwerendy SQL) oraz wczytanie ich do środowiska "R". W międzyczasie następuje "tłumaczenie" zawartości obiektu DataTable na obiekt matrix R.
UWAGA: tabele mogą mieć, w tej wersji, zawartość JEDYNIE liczbową!
Żadnych literałów!
Jeśli w tabelach są literały, mozna pozamieniać je na liczby jeszcze w kwerendzie wybierającej (select), np. tak:
a najlepiej utworzyć nową tabelę ze skonwertowanymi danymi:
2. Przygotowanie selektorów danych (która tabela, która kolumna/y)
3. Sprawdzenie, czy zmienne reprezentowane selektorem mają rozkłady normalne (na razie mam tylko klasę dla testu Shapiro-Wilka, ale chyba pisałem też klasę i funkcje "R" dla testu Jarque-Bera)
4. Jeśli rozkład chociaż jednej zmiennej nie jest normalny, transformacja zmiennych jedną z funkcji (asin, sqrt, kwadrat, szescian, odwrotnosc, log, exp). Dodatkowo ciągle badana jest homogeniczność wariancji zmiennych (test Levena)
5. Jeśli żadna z transformacji nie znormalizowała rozkładu, przejście do analiz nieparametrycznych.
6. Jeśli udało się znaleźć transformatę normalizującą (pierwsza z brzegu), to dalej będzie ona używana w programie. Trzeba o tym pamiętać przy podawaniu wyników statystyk opisowych!
7. Przeprowadzane są testy "omnibus": parametryczna lub nieparametryczna, dla prób zależnych bądź niezależnych ANOVA.
8. Jeśli wynik wskaże istnienie co najmniej jednej statystycznie istotnej różnicy między zmiennymi, przeprowadzane są testy post-factum (parametryczne bądź nieparametryczne). Oczywiście każdy zaprogramuje sobie test, jakiego potrzebuje... MSD, LSD, Tukeya, t-Studenta z poprawką Bonferroniego...
Dostepne są także klasy prostych testów dla 2 prób: parametryczne, nieparametryczne, dla prób zależnych i niezależnych. Jest test korelacji i regresji liniowej wraz z istotnościami współczynników.
Oczywiście nie znajdziecie tutaj wszystkich znanych Wam testów. Nie znajdziecie ich wielu, bo po prostu nie były mi potrzebne (np. testu chi2 - trzeba sobie napisać własną klasę).
Pisanie klas testów i procedur polega na tym, że dziedziczy się po odpowiedniej klasie testu statystycznego i "zaszywa" w niej listę poleceń R. Literał ten podaje się jako argument do metody, przekazującej go z kolei, przez DCOM, do środowiska R.
Trzeba też przygotować odpowiednią klasę wynikową, czyli odczytać wyniki z tablic, zwracanych przez "R" i "powkładać" je w odpowiednie pola klasy wynikowej.
Ma to wielką wadę - jeśli kiedyś, ktoś, nie bacząc na standardy, pozmienia coś w strukturze obiektu zwrotnego R (np. zamiast p.val będzie zmienna pvalue), to nasz analizator "się sypnie".
Niestety, rozwiązanie nie jest proste. Należałoby mieć pliki konfiguracyjne (lub zapis w bazie danych), w których podawałoby się co i jak zwraca określona funkcja "R", a następnie trzeba by brać to pod uwagę w kodzie. Niestety - nie mam ani czasu, ani większej ochoty tworzyć kolejnego "kombajnu"... może kiedyś :)
Jesli to, co napisałem, zainteresuje jakiegoś statystyka-programistę, proszę o informację. A to dlatego, że już od paru miesięcy nie mogę się zmoblilizować, aby opisać to, co stworzyłem kiedyś... Wielu było zainteresowanych, ale tylko tak "pobieżnie". Jeśli kogoś zainteresuje taka biblioteka na poważnie - wówczas wyłożę jeszcze swój czas.
Być może przyda się to komuś, kto:
1. potrzebuje "silnika obliczeń" R w swoim projekcie pisanym na platformie .NET
2. pisze środowisko graficzne dla tego języka
Dla Unixów jest fajna biblioteka komunikująca się z R poprzez TCP/IP. Jest szybka i zoptymalizowana, na pewno lepsza, niż moje dzieło. Na Windows także działa, ale nie zapewnia wielosesyjności.
Niestety, póki jakiś zapaleniec nie napisze tego co ja, tylko lepiej - nie będzie zbyt wielu alternatyw :)
Niestety, moja bibliteka potrafi jeszcze wyrzucać wyjątki. Spowodowane jest to, że czasem biblioteka RDCOM dla jednego wywołania zwraca wynik o typie double[,] a czasem object[,]
Spowodowane jest to tym, że jeśli R zwrócił NA, to jest to literał ("object"), a jesli liczbę - to double. Nie wszędzie jeszcze mam to obsłużone. Zwykle wystarczyło odpowiednio dopracować zapytanie "R" :)
---------------------------------
Przykład zastosowania.
Otóż pisałem ja swego czasu system automatycznych (i jedynie bardzo ogólnych - wszak nic nie zastąpi człowieka w analizie danych) analiz dla medycznych programów naukowych.
Wyglądało to tak, że był sobie serwis www poświęcony konkretnemu programowi medycznemu. W jego skład wchodziły następujące moduły:
- elektroniczna wersja CRFa, wraz z walidacją, kreatorem wprowadzania danych oraz wyszukiwarką, etc.
- panel statystyczno-administracyjny oferujący podsumowanie realizacji programu (ile ośrodków, ilu lekarzy, ile pacjentów, ile miesięcy, etapów terapii w którym ośrodku, etc)
- panel "szybkich" analiz statystycznych.
W tym ostatnim chodziło o to, aby lekarze-badacze, zanim jeszcze zlecą konkretne analizy (cóż, pomarzyć można było o eksperymencie przygotowanym pod przyszłe analizy :) ), jeszcze w trakcie badania mieli pogląd na to, co się w nim dzieje.
Przygotowywali zatem pewne zestawy analiz, których wyniki chcieli śledzić.
Nie interesowały ich dokładne wyniki, a także cały proces wnioskowania. Chodziło o prostotę. Wybierali z listy analizę "Odpowiedź organizmu na XXXX w kolejnych miesiącach terapii" i po chwili mieli wykres, trend oraz tabelkę istotności różnic pomiędzy kolejnymi miesiącami terapii.
Dodatkowo mogli wybrać rodzaj analizy: próby zależne / niezależne.
Podejście z próbami niezależnymi (chociaż to było badanie skuteczności leku) wcale nie było takie idiotyczne, ze względu na znaczne braki w danych i długie odstępy między pomiarami, a także innymi czynnikami, których nie mogę tutaj wspominać.
Przy wyborze "próby zależne" stosowało się różne algorytmy "wypełniania" braków, np. LOCF. Oczywiście lekarze byli poinformowani i świadomi tego, co ze sobą niesie takie czy inne podejście.
Zatem - lekarz wchodził na stronę statystyk, wybierał określony ich zestaw i dostawał ogólne wyniki - ciach, i kolejne, i kolejne. Dzięki temu lekarze mieli stały pogląd na to, co się dzieje w danym programie.
Wynikiem była po prostu tabelka istotności różnic pomiędzy kolejnymi "checkpointami" programu albo tabela statystyk opisowych.
A tutaj - przykład kodu. Tak to może wyglądać :)
Nie wklejam tekstu, bo jest tego trochę, a poza tym silnik tego forum "obcina" formatowanie (wcięcia).
Przygotowanie kwerend SQL:
Pierwsza - wynik w postaci osobnych kolumn:
Druga - wynik w postaci variable-factor (rekordami):
Kod metody main:
I
II
III
Kod metody "steps" (kroki scenariusza :) )
I część:
II część:
III część:
Adrian Olszewski edytował(a) ten post dnia 20.08.08 o godzinie 13:14