2  Tworzenie pakietów R (1)

2.1 Pakiety R

Pakiet to zorganizowany zbiór funkcji, który rozszerza możliwości R. Pakiety oprócz kodu zawierają szereg dodatkowych istotnych elementów, takich jak:

  • Informacja o wersji pakietu, jego twórcach, zależnościach, czy licencji
  • Dokumentacja
  • Przykładowe dane
  • Testy kodu

Pakiety R mogą być przechowywane i instalowane z wielu miejsc w internecie. Istnieje jednak centralne repozytorium (CRAN, ang. the Comprehensive R Archive Network), które zawiera oficjalne wersje pakietów R, np. https://cran.r-project.org/package=stringr. Wersje deweloperskie (rozwojowe) często można znaleźć na platformie GitHub, np. https://github.com/tidyverse/stringr/.

Do instalacji pakietu w R z repozytorium CRAN służy wbudowana funkcja install.packages(), np:

install.packages("stringr") #instalacja pakietu stringr

Zainstalowanie pakietu w R z platformy GitHub jest możliwe używając, np. funkcji install_github() z pakietu remotes.

# install.packages("remotes")
remotes::install_github("tidyverse/stringr")

W przypadku instalacji pakietu w R z platformy GitHub należy podać nazwę użytkownika lub organizacji, która tworzy ten pakiet (np. powyżej tidyverse) oraz nazwę pakietu (np. powyżej stringr) oddzielone znakiem /.

Podobnie jak instalowanie programów na komputerze - zainstalowanie pakietu odbywa się tylko jeden raz.

Komentarz

Istnieją dwa główne formy, w których rozpowszechniane są pakiety R - postać źródłowa (ang. source packages) i postać binarna (ang. binary packages). Postać źródłowa zawiera kod źródłowy pakietu, który musi zostać następnie skompilowany na komputerze użytkownika. Skompilowanie pakietu na podstawie kodu źródłowego może wymagać posiadania odpowiednich bibliotek na komputerze, np. Rtools dla systemu Windows. Dodatkowo, instalacja w ten sposób zabiera więcej czasu. Postać binarna została już wcześniej skompilowana na zewnętrznym komputerze (np. w repozytorium CRAN) Jest ona dostępna dla systemów Windows i Mac OS. Niestety, nie wszystkie pakiety (lub ich wersje) posiadają postać binarną i wymagana jest ich kompilacja.

Użycie wybranego pakietu wymaga dołączenia go do R za pomocą funkcji library(). Dołączenie wybranych pakietów do R robimy po każdym uruchomieniu R.

library(stringr)
Komentarz

Pakiet (ang. package) to zbiór funkcji, biblioteka (ang. library) to miejsce na dysku, w którym znajdują się pakiety.

W przypadku, gdy chcemy użyć zewnętrznej funkcji, ale nie dołączyliśmy odpowiedniego pakietu, pojawi się błąd o treści could not find function "nazwa_funkcji".

str_sub("chronologia", start = 1, end = 6)
#> Error in str_sub("chronologia", start = 1, end = 6) : 
#>  could not find function "str_sub"

Istnieją dwa możliwe rozwiązania powyższego problemu. Po pierwsze możliwe jest dołączenie pakietu poprzez library(stringr). Po drugie można bezpośrednio zdefiniować z jakiego pakietu pochodzi konkretna funkcja używając nazwy pakietu i operatora ::.

stringr::str_sub("chronologia", start = 1, end = 6)
[1] "chrono"
Komentarz

Operator :: może być też pomocny w przypadku, gdy kilka pakietów ma funkcję o tej samej nazwie. Wówczas, aby kod został poprawnie wykonany, warto podać nie tylko nazwę funkcji ale też nazwę pakietu z jakiego ona pochodzi.

2.2 Nazwa pakietu

Nazwa nowego pakietu musi spełniać kilka wymagań: składać się tylko ze znaków ASCII, cyfr i kropek, mieć co najmniej dwa znaki oraz zaczynać się od litery i nie kończyć się kropką (R Core Team 2019). Ważne jest również myślenie o nazwie pakietu tak jak o nazwach funkcji – nazwy pakietów powinny ułatwiać zrozumienie ich zawartości. Dodatkowo, z uwagi na istnienie wielu pakietów, warto najpierw sprawdzić czy pakiet o wymyślonej przez nas nazwie już nie istnieje. Można to przykładowo zrobić używając pakietu available (Ganz i in. 2019), który sprawdza przy wybrana nazwa nie jest już zajęta oraz czy nie ma ona jakiegoś niepożądanego przez nas znaczenia.

# install.packages("available")
available::available("mojpakiet", browse = FALSE)

2.3 Tworzenie szkieletu pakietu

Kolejnym krokiem jest stworzenie szkieletu pakietu, czyli zorganizowanego zbioru plików i folderów, do których później należy dodać odpowiednie informacje i funkcje. Znacznie w tym może pomóc pakiet usethis (Wickham i Bryan 2020), który zawiera szereg funkcji ułatwiających budowanie pakietów R.

library(usethis)

Do stworzenia szkieletu pakietu służy funkcja create_packages(), w której należy podać ścieżkę do nowego pakietu. W tej ścieżce ostatnia nazwa folderu określa również nazwę pakietu.1

usethis::create_package("~/Documents/mojpakiet")

W efekcie działania powyższej funkcji stworzony zostanie nowy folder mojpakiet zawierający kilka plików oraz otwarty zostanie nowy projekt RStudio zawierający ten pakiet. Najważniejsze nowe pliki to:

  1. mojpakiet.Rproj - plik projektu RStudio
  2. DESCRIPTION - plik zawierający podstawowe informacje o pakiecie
  3. R/ - w tym pustym folderze konieczne będzie umieszczenie nowych funkcji R
  4. NAMESPACE - ten plik określa, między innymi, jakie funkcje są dostępne w tym pakiecie. Ten plik i jego zawartość jest tworzona automatycznie

Dodatkowo w prawym górnym panelu RStudio pojawi się nowy panel “Build”.

2.4 Dokumentacja pakietu

Dokumentowanie pakietu ma miejsce na wielu poziomach, począwszy od opisu pakietu w pliku DESCRIPTION, poprzez komentowanie kodu, dokumentację funkcji wraz z przykładami jej użycia, dokumentację danych, plik README, winiety, aż po stronę internetową pakietu.

2.5 Opis pakietu

Plik DESCRIPTION zawiera opis (metadane) pakietu, w tym jego nazwę, tytuł, wersję, autorów, opis, czy licencję.

Package: mojpakiet
Title: Moje Funkcje Robiace Wszystko
Version: 0.0.1
Authors@R: 
    person(given = "Imie",
           family = "Nazwisko",
           role = c("cre", "aut"),
           email = "imie.nazwisko@example.com")
Description: Tworzenie, przeliczanie i wyliczanie wszystkiego. 
    Czasami nawet więcej.
License: CC0
Encoding: UTF-8
LazyData: true
RoxygenNote: 7.2.3

Plik DESCRIPTION należy regularnie uaktualniać, np. zmieniać numer wersji po naniesionych zmianach w kodzie, czy dodawać nowych autorów, jeżeli tacy się pojawili.

Więcej o tym później (Sekcja 4.2).

2.6 Rozwijanie pakietu

Rozwój pakietu R może opierać się na kilku poniższych krokach:

  1. Tworzenie/modyfikowanie kodu w folderze R/
  2. Używanie funkcji devtools::load_all(), która dodaje nowe/zmodyfikowane funkcje do środowiska R
  3. Sprawdzenie czy funkcja działa zgodnie z oczekiwaniami na kilku przykładach
  4. Dodanie testów jednostkowych na podstawie stworzonych przykładów (o tym więcej w Sekcja 6.2)
  5. Uaktualnienie dokumentacji tworzonego/modyfkowanego kodu
  6. Wygenerowanie plików z dokumentacją używając devtools::document()
  7. Sprawdzenie czy pakiet nie posiada żadnych problemów używając devtools::test() oraz devtools::check()
  8. Modyfikacja wersji oprogramowania w pliku DESCRIPTION
  9. Zapisanie zmian w kodzie w repozytorium (np. GitHub)
  10. Powtórzenie powyższych czynności przy kolejnych zmianach w kodzie
ZADANIA
  1. Stwórz nowy pakiet R o wybranej przez siebie nazwie: zacznij od sprawdzenia czy nazwa pakietu nie jest już zajęta.
  2. Stwórz szkielet pakietu oraz uzupełnij najważniejsze informacje w pliku DESCRIPTION.

2.7 Tworzenie i dokumentacja funkcji

konwersja_temp = function(temperatura_f){
    (temperatura_f - 32) / 1.8
}

Umieszczenie tej funkcji w nowym pakiecie R odbywa się poprzez zapisanie tego kodu jako skrypt R (np. konwersja_temp.R) w folderze R/.

Funkcje zawarte w pakietach muszą także posiadać odpowiednią dokumentację, zawierającą, między innymi, tytuł funkcji, opis jej działania, wyjaśnienie kolejnych argumentów funkcji, oraz przykłady jej działania. Linie obejmujące dokumentację funkcji rozpoczynają się od znaków #', a tworzenie dokumentacji funkcji odbywa się poprzez wypełnianie treści dla kolejnych znaczników (np. @example określa występowanie przykładu).

Przykładowy plik R/konwersja_temp.R może wyglądać następująco:

#' Konwersja temperatur
#'
#' @description Funkcja sluzaca do konwersji temperatury 
#'   ze stopni Fahrenheita do stopni Celsjusza.
#'
#' @param temperatura_f wektor zawierajacy wartosci temperatury 
#'   w stopniach Fahrenheita
#'
#' @return wektor numeryczny
#' @export
#'
#' @examples
#' konwersja_temp(75)
#' konwersja_temp(110)
#' konwersja_temp(0)
#' konwersja_temp(c(0, 75, 110))
konwersja_temp = function(temperatura_f){
  (temperatura_f - 32) / 1.8
}

Pierwsza linia w tym pliku określa tytuł danej funkcji. Kolejny element rozpoczynający się od znacznika @description zawiera krótki opis tego, co funkcja robi. Następnie zazwyczaj wypisane są wszystkie argumenty danej funkcji używając kolejnych znaczników @param. Znacznik @return pozwala na przekazanie informacji o tym co jest zwracane jako efekt działania funkcji. Przedostatnim znacznikiem w powyższym przypadku jest @export. Oznacza on, że ta funkcja będzie widoczna dla każdego użytkownika tego pakietu po użyciu library(mojpakiet). Bez tego znacznika funkcja byłaby tylko widoczna wewnątrz pakietu. Ostatni znacznik, @examples, wypisuje kolejne przykłady działania funkcji.

Komentarz

Powyższy przykład nie wykorzystuje wszystkich możliwych znaczników. Więcej z nich można znaleźć w dyskusji na stronie https://github.com/r-lib/roxygen2/issues/792#issuecomment-705071228.

Wybór More -> Document w panelu “Build” (inaczej wywołanie funkcji devtools::document() lub użycie skrótu CTRL+SHIFT+D) spowoduje zbudowanie pliku dokumentacji w folderze man, np. man/konwersja_temp.Rd. Pliki dokumentacji będą zawsze tworzone w ten sposób - nie należy ich modyfikować ręcznie. Zbudowanie pliku dokumentacji pozwala teraz na jej podejrzenie poprzez wywołanie pliku pomocy naszej funkcji:

?konwersja_temp

2.8 Zależności

Istnieje jedna ważna różnica pomiędzy tworzeniem funkcji w skryptach a tworzeniem jej wewnątrz pakietu - w pakietach nie można używać dołączania pakietów za pomocą funkcji library(). Zamiast tego możliwe jest definiowanie każdej zewnętrznej funkcji używając operatora ::.2

Dodatkowo każda zależność z zewnętrznym pakietem musi być określona w pliku DESCRIPTION. Jest to możliwe używając wpisów Imports: oraz Suggests:, przykładowo:3

Imports:
  stringr,
  readr
Suggests:
  readxl

Imports: określa pakiety, które muszą być zainstalowane, aby tworzony pakiet mógł zadziałać. Jeżeli wymienione tutaj pakiety nie będą znajdować się na komputerze użytkownika to zostaną one automatycznie doinstalowane podczas instalacji naszego pakietu. Suggests: wymienia pakiety, które pomagają w użytkowaniu naszego pakietu, np. takie które zawierają testowe dane. Wymienione tutaj pakiety nie będą automatycznie doinstalowane podczas instalacji naszego pakietu.

2.9 Sprawdzanie pakietu

W momencie, gdy pakiet posiada już swoje podstawowe elementy, tj. pierwsze udokumentowane funkcje oraz uzupełniony opis wraz z zależnościami warto sprawdzić czy te wszystkie elementy pakietu dobrze współgrają ze sobą. Można to zrobić używając funkcji devtools::check() (inaczej wybór Check w panelu “Build” RStudio lub skrót CTRL+SHIFT+E). W efekcie tego wywołania zostanie uruchomiony szereg sprawdzeń i testów dotyczących pakietu, jego funkcji czy opisu. Na końcu zwrócone zostanie wypisanie liczby błędów (error), ostrzeżeń (warnings) i notatek (notes), poprzedzone wymienieniem każdego ich wystąpienia. Błędy oznaczają, że z jakiegoś powodu pakietu nie można zbudować, ostrzeżenia natomiast sugerują sytuację w której jakieś ważne elementy funkcji mogą wymagać poprawy. Notatki natomiast wskazują na kwestie, które użytkownik może, ale nie musi poprawić.

ZADANIA
  1. Dodaj do swojego pakietu twoją funkcję jako plik o rozszerzeniu .R w folderze R\.
  2. Dodaj dokumentację do swojej funkcji, w tym jej tytuł, opis, argumenty, zwracane wartości i przykłady jej użycia.
  3. Wygeneruj dokumentację dla swojego pakietu.
  4. Sprawdź czy twój pakiet nie posiada żadnych błędów, ostrzeżeń i notatek. W przypadku, gdy masz jakieś błędy lub ostrzeżenia, spróbuj je naprawić.

Po wykonaniu zadań będziemy mieć czas na dyskusję i pomoc w rozwiązywaniu problemów.

2.10 Instalowanie pakietu

Sprawdzony pakiet, który nie zwraca błędów można zainstalować na własnym komputerze używając funkcji devtools::install() (inaczej wybór Install and restart w panelu “Build” RStudio lub skrót CTRL+SHIFT+B). W przypadku, gdy kod źródłowy tego pakietu znajduje się na platformie GitHub, inni użytkownicy mogą go zainstalować za pomocą funkcji remotes::install_github("nazwa_uzytkownika_github/nazwa_pakietu") (Hester i in. 2020).


  1. Funkcja również create_packages() sama tworzy nowy folder, jeżeli on wcześniej nie istniał.↩︎

  2. Istnieją również inne możliwości, np. użycie znaczników @import lub @importFrom.↩︎

  3. Istnieją również inne wpisy, takie jak Depends:, LinkingTo:, czy Enhances:.↩︎