Temat: Nowy interfejs R -> .NET
Nieco po czasie, ale lepiej późno niż wcale :]
Interfejs .NETowy do eRowego serwera TCP/IP znanego jako
Rserve. Początkowo klienci byli napisani jedynie w C++ i Javie, teraz doszedł .NET.
Projekt nazywa się
RserveCLI. Jest o tyle wygodny, że nie trzeba, jak w przypadku bibliotek korzystających z COM, niczego (poza RServe) instalować ani rejestrować.
Krótka prezentacja:
http://www.rinfinance.com/agenda/2011/OliverHaynold.pdf
Obowiązkowa lektura dotycząca RServe:
1. Dokumentacja:
http://www.rforge.net/Rserve/doc.html
2. FAQ:
http://www.rforge.net/Rserve/faq.html
3. Newsy:
http://www.rforge.net/Rserve/news.html
4. dla ciekawych - protokół:
http://www.rforge.net/Rserve/dev.html
--------------------
Features of Rserve
* fast - no initialization of R is necessary
* binary transport - the transport protocol sends R objects as binary data, not just R text output.
* automatic type conversion - most R data types are converted into native data types, e.g. the result of rnorm(10) will be double[10] in C/Java. Java client also provides classes for new R types such as RBool, RList etc.
* persistent - each connection has its own namespace and working directory. Every object you create is persistent until the connection is closed. The client doesn't have to fetch or store intermediate results.
* client independence - since the client is not linked to R there are no threading issues like in RSJava etc.
* security - Rserve provides some basic security by supporting encrypted user/password authentication with server challenge. Rserve can be also configured to accept local connections only.
* file transfer - the Rserve protocol allows to transfer files between the client and the server. This way Rserve can be used as a remote server even for task such as generating plot images etc.
* configurable - one configuration file is used to control settings and to enable/disable features such as authorization, remote access or file transfer.
What Rserve is NOT
* Rserve provides no callback functionality. Your application could implement callbacks via TCP/IP and the R sockets but it is not a part of Rserve.
* Rserve is not a telnet frontend to R. The printed output is not transported (except via capture.output). Rserve uses binary protocol for transport of objects for better speed.
* Rserve is thread safe across connections, but eval methods are not thread safe within one connection. This means that multiple threads should not use the same connection unless they guarantee that no eval calls are run in parallel.
--------------------
Mamy zatem już następujące metody połączenia się z R:
1. TCP/IP (RServe, RserveCLI)
2. OLE COM (StatConnector, RCOM). Niestety, StatConnector nie jest już darmowy do komercyjnych zastosowań, a i niemało kosztuje.
3. DDE (
NDDE + Tcl/Tk
4. Bezpośrednie wywołania biblioteki R.DLL (R.NET)
Zalety
1. Nareszcie zaimplementowana wielosesyjność bez potrzeby uruchamiania biblioteki w osobnych przestrzeniach adresowych (procesy potomne). Na poniższej ilustracji dwa okna aplikacji, każda z innym GUIDem użytkownika. GUID potrzebny był mi tylko do rysowania wykresu (plik wykresu trzeba "pobrać" z serwera, więc żeby się nie nadpisywał, dodawałem mu GUID do nazwy). Każda nowa instancja aplikacji dostaje swoje środowisko, podobnie, jak w COM (np. StatConnector), ale bez problemów z tym związanych.
2. Szybkość działania. Od momentu wystartowania serwera (Rserve() albo jako exeka) połączenie trwa dosłownie chwilę, zauważalnie szybciej niż w przypadku StatConnectora. Trzeba uważać na wyjątki, które mogą zamknąć serwer, dobrze mieć watchdoga. Inaczej dostaniemy znane zabawne komunikaty o "gwałtownym zakończeniu" i "aktywnej odmowie serwera" :D
EDIT: wystarczy ustawić: option(error=function() NULL)
Więcej w
FAQ
3. Możliwość współpracy z serwerem po localhoście i po sieci. Podobnie, jak w DCOM, ale bez problemu z rejestracją komponentów i uprawnieniami. Trzeba tylko uważać na firewall.
4. Łatwa integracja z kodem w C#
5. Także same "eRy" mogą ze sobą rozmawiać dzięki klientowi "RSclient".
Wady
1. Brak urządzenia graficznego, brak konsoli tekstowej. Można zapomnieć o przechwytywaniu komunikatów konsoli (błędy, ostrzeżenia) czy sformatowanych wynikach (np. summary()). Ale tak jest ze wszystkimi tego rodzaju bibliotekami (R.NET, StatConnector). Rozwiązaniem jest sink().
EDIT: Nie jest tak źle, da się to obejść:
string s=c.eval("paste(capture.output(print(summary(mymodel))),collapse='\\n')").asString();
Więcej w
FAQ
2. W zwiazku z p1. - wykresy trzeba zapisywać do pliku (png, CairoPNG, PDF) i wyświetlać w kontrolce wyświetlającej obrazki albo osadzonej przeglądarce PDF (np. MSIE, Acrobat). Tak właśnie robię w aplikacji testowej.
3. Trzeba pamiętać o podnoszeniu "upadłego serwera". Problemu tego nie było w przypadku COM ani R.DLL (R był zawsze podnoszony od zera).
EDIT: co prawda można ustawić "option(error=function() NULL)" ale serwer może "paść" z różnych powodów, dlatego watchdog zawsze powinien być brany pod uwagę.
4. Brak informacji o błędzie zgłoszonym przez R. Dostajemy wyjątek, że R ma problem i to wszystko. Trzeba kombinować z własną procedurą, która wyświetli ostatni napotkany error/warning. Błędy R można olać (albo try/catch, albo ustawić R tak, by nie zamykał się przy wyjątku), ale błędy transmisji danych najpewniej wywalą serwer. Info o błędach można odczytać także przez sink() ale to wymaga dodatkowej pracy.
EDIT: metoda na odczytanie błędu:
Problem w tym, że RServeCLI rzuca różne wyjątki. Te pochodzące z R np. dla niezdef. obiektu, są typu (nie mam pojęcia dlaczego!) WebException. Pół biedy, jak mamy aplikację okienkową, ale jeśli sieciową, to ten wyjątek może dotyczyć tysiąca stu problemów. Rzucane są także inne wyjątki, np. InvalidOperationEx. (przy problemie konswersji danych). Trzeba więc sprawdzać nie tylko rodzaj wyjątku, ale także źródło jego wyrzucenia oraz zawartość tekstową.
try
{
var r = s.Eval("alamakota <- 0; 1/alaniemakota"); // niezdef. zmienna, dziel. przez 0
Console.WriteLine(r.AsDouble); // obsłuży nieskończoność, gdyby alaniemakota = 0
}
catch(WebException webEx) // dowolny wyjątek
{
// eRowe wyjątki mają odpowiednio ustawione Source i Message
if(webEx.Source.Equals("RserveCli"))
{
if(webEx.Message.Equals("R threw an error."))
{
Console.WriteLine(s.Eval("geterrmessage()").AsString);
}
}
else
{
// oho, to był jednak błąd "sieciowy", a nie eRa
}
}
catch(inne wyjątki) { obsługa }
Więcej w
FAQ
5. Po zakończeniu pracy warto zamykać RServe, żeby nie zabierał pamięci.
6. Instancje mogą się wzajemnie blokować. Jeśli RServe czeka na odpowiedź od instancji A, a ona się powiesiła albo czeka na reakcję użytkownika, to pozostałe instancje też wiszą.
-----------------
Wielosesyjność:
Przykładowa struktura zwracanych danych:
Adrian Olszewski edytował(a) ten post dnia 31.08.12 o godzinie 01:25