przejście do zawartości
Zespół Pomiaru Dydaktycznego
Narzędzia użytkownika
Zaloguj
Narzędzia witryny
Narzędzia
Pokaż stronę
Poprzednie wersje
Odnośniki
Menadżer multimediów
Zaloguj
Menadżer multimediów
Ślad:
ewdrstyle
Ta strona jest tylko do odczytu. Możesz wyświetlić źródła tej strony ale nie możesz ich zmienić.
======Dobre praktyki tworzenia kodu====== =====Zasady formatowania kodu R w projekcie EWD===== * Zasadniczo trzymamy się zasad z [[https://google.github.io/styleguide/Rguide.xml|Google's R Style Guide]] * Z następującymi wyjątkami: - jako operatora przypisania wartości używamy '=' a nie '%%<-%%'; - schematy nadawania nazw: - nazwa_funkcji(), - nazwaZmiennej, nazwaObiektu, - nazwa_kolumny_w_ramce_danych; - można wyrównywać (wcinać) kod tabulacjami, byle nie mieszać w jednym pliku z wyrównywaniem spacjami; - każda tworzona funkcja - nawet jeśli chwilowo nie jest zamykana w żadnym pakiecie - powinna być dokumentowana w notacji roxygenowej (p. [[http://adv-r.had.co.nz/Documenting-functions.html]]). * Oraz uzupełnieniami: - Przy wykonywaniu zapytań do bazy trzymamy się [[ewdsql|wytycznych]]. - Piszemy funkcje, które nie sięgają do zmiennych, które nie zostały im przekazane jako parametry. * Dotyczy to również funkcji anonimowych!<code> wektor = c('a', 'b', c') # Źle wynik = apply(1:3, function(x){ wektor[x] }) # Dobrze wynik = apply(1:3, function(x, y){ y[x] }, wektor) # Też dobrze, działa równie szybko wynik = c() for(i in 1:3){ wynik[i] = wektor[i] }</code> * W przypadku wykorzystania funkcji takich jak //subset()// czy //summarise()// używamy funkcji //get()// do oznaczenia, że chcemy wykorzystać kolumnę z obrabianego data frame'a, a nie obiekt z zewnętrznego (wobec tego data frame'a) środowiska.<code> dane = data.frame(a = rep(1:2, 5), b = 1:10) # Źle agr = ddply(dane, ~a, summarise, sr = mean(b)) podzb = subset(dane, a == 1) # Dobrze agr = ddply(dane, ~a, summarise, sr = mean(get("b"))) podzb = subset(dane, get("a") == 1)</code> - Ciało funkcji anonimowych zawsze otaczamy nawiasami klamrowymi.<code> # Źle apply(tablica, 1, funtion(x) max(x) - min(x)) # Dobrze apply(tablica, 1, function(x){ max(x) - min(x) })</code> - Unikamy tworzenia nadmiernie długich linii kodu: <code># Źle sapply(lista, function(x){ apply(x, c(1, 2), function(y){ rowSums(y) / sum(y) }) }) # Dobrze sapply(lista, function(x){ apply(x, c(1, 2), function(y){ rowSums(y) / sum(y) }) }) # Źle zupelnie_prosta_funkcja(lista[[1]]$kolumna[filtr], apply(dane, c(1, 2), function(x){ mean(x) - sd(x) })) # Dobrze arg1 = lista[[1]]$kolumna[filtr] arg2 = apply(dane, c(1, 2), function(x){ mean(x) - sd(x) }) zupelnie_prosta_funkcja(arg1, arg2) # Źle zlozona_funkcja(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14) # Dobrze zlozona_funkcja( arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14 ) </code>W szczególności: * łamiemy do nowego wiersza ciała wszelkich nietrywialnych funkcji anonimowych (tzn. zawierających coś więcej niż prostą operację arytmetyczną lub pojedyncze wywołanie funkcji, której parametrem wywołania nie jest żadna inna funkcja); * łamiemy do nowego wiersza argumenty wywołania funkcji, jeśli jest ich zbyt wiele; * przy korzystaniu z magrittr-owego operatora %>% (stosowanego w szczególności przy korzystaniu z pakietu //dplyr//) łamiemy linię po każdym jego zastosowaniu, chyba że w danym ciągu poleceń jest on wykorzystywany tylko raz (łączy ze sobą dokładnie dwie operacje; z tym że wtedy należy też stosować się do reguły o długości linii);<code> # Źle dane = select(dane, kolumna1, kolumna2) %>% filter(kolumna2 > 0) %>% summarise(koluma3 = kolumna1 / kolumna2) # Dobrze dane = select(dane, kolumna1, kolumna2) %>% filter(kolumna2 > 0) %>% summarise(koluma3 = kolumna1 / kolumna2) </code> * za regułę kciuka przyjmujemy, że nadmiernie długa linia to linia o więcej niż 80 znakach. =====Weryfikacja poprawności argumentów funkcji (run-time testing)===== ====Ogólne zasady weryfikacji argumentów funkcji==== * Generalnie przyjmujemy zasadę, że **funkcje powinny weryfikować poprawność przekazywanych do nich argumentów**. W przypadku tworzenia pakietów zasada ta powinna być bezwzględnie przestrzegana w odniesieniu do funkcji, które są eksportowane. * Jeżeli w funkcji implementowana jest opcjonalna konwersja typów argumentów (np. jeśli argument powinien być wektorem liczb całkowitych, ale jest wektorem logicznym, można skonwertować go na wektor liczb całkowitych), to **dokonanie konwersji typów powinno wiązać się z wygenerowanie ostrzeżenia widocznego dla użytkownika**. * Minimalny zakres weryfikacji obejmuje: * Zgodność typów (w przypadku argumentów o bardziej złożonej strukturze, ale posiadających przypisane klasy - zgodność klas). * Zakres dozwolonych wartości - jeśli dotyczy. * Długość (liczba elementów) - jeśli dotyczy (odnosi się to głównie do parametrów sterujących procesem, które z założenia mają być skalarami). * Ewentualne rozszerzanie zakresu weryfikacji ponad wyżej wymienione (typowo bardziej szczegółowe sprawdzanie struktury złożonych argumentów) należy rozważać w odniesieniu do: * Skutków niewykrycia niezgodności. * Szacowanego prawdopodobieństwa wystąpienia niezgodności. * Złożoności kodu weryfikującego w stosunku do złożoności całej funkcji. * Obciążenia obliczeniowego związanego z weryfikacją. ====Metody weryfikacji argumentów funkcji==== Spośród opisanych niżej metod **preferowane jest wykorzystanie funkcji pakietu //assertive//** w rozsądnym połączeniu z użyciem warunków do obsługi bardziej skomplikowanych sytuacji (w szczególności ostrzeżeń i konwersji typów, jeśli funkcja takowe wykonuje). [[pakietassert|Przewodnik po funkcjach pakietu assertive.]] ===Stosowanie warunków=== * **Zalety:** * Elastyczne. * Umożliwia zwracanie czytelnych komunikatów o błędach i/lub ostrzeżeń. * **Wady:** * Wymaga pisania bardzo dużo kodu, co jest uciążliwe i często czyni kod nieczytelnym. * **Przykład:**<code>moja_funkcja = function(x) { if (any(is.na(x)) { stop("Argument 'x' nie może zawierać braków danych!") } else { return(x) } }</code> ===Stosowanie funkcji stopifnot()=== * **Zalety:** * W typowych, niezbyt złożonych sytuacjach, krótki i czytelny kod. * **Wady:** * Niezbyt przyjazne użytkownikowi komunikaty o błędach. * Obsługa wyłącznie błędów (ale ostrzeżeń już nie). * **Przykład:**<code>moja_funkcja = function(x) { stopifnot(all(!is.na(x)) return(x) }</code> ===Stosowanie funkcji pakietu assertive=== [[pakietassert|Przewodnik po funkcjach pakietu assertive.]] * **Zalety:** * Krótki i czytelny kod w szerokim zakresie zastosowań (nieco szerszym niż //stopifnot()//). * Wiele warunków i formatów, z którymi można sprawdzać zgodność. * Automatyzacja najbardziej typowych kombinacji warunków w pojedynczych funkcjach. * Przyjazne użytkownikowi komunikaty o błędach. * **Wady:** * Nic nie będzie równie elastyczne, jak używanie //if//. * Wymaga trochę czasu, żeby zorientować się w mnogości funkcji udostępnianych przez pakiet. * **Przykład:**<code>moja_funkcja = function(x) { assert_all_are_not_na(x) return(x) }</code>
ewdrstyle.txt
· ostatnio zmienione: 2016/10/03 21:16 przez
t.zoltak
Narzędzia strony
Pokaż stronę
Poprzednie wersje
Odnośniki
Do góry