R develpment notes¶
Espacio para seguir la guía de R Packages.
Setup¶
install.packages(c("devtools", "roxygen2", "testthat", "knitr"))
install.packages("rstudioapi")
rstudioapi::isAvailable("0.99.149") # Should be TRUE when run from RStudio
devtools::install_github("r-lib/devtools")
library(devtools)
has_devel() # Checks if build dependencies are OK
dir.create("Projects/Rdevel")
devtools::create("Projects/Rdevel/cellMagick")
dir()
# [1] "cellMagick.Rproj" "DESCRIPTION" "NAMESPACE" "R"
Workflow¶
The first practical advantage to using a package is that it’s easy to re-load your code. You can either run devtools::load_all(), or in RStudio press Ctrl/Cmd + Shift + L, which also saves all open files, saving you a keystroke.
Crear R/hw.R
helloWorld <- function() print("Hola!")
Apretar Ctrl + Shift + L
y ejecutar helloWorld()
en la consola.
The R landscape¶
There are some functions that modify global settings that you should never use because there are better alternatives.
- Don’t use library() or require()
- Never use source()
- If you modify global options() or graphics par(), save the old values and reset when you’re done.
- Avoid modifying the working directory.
Loading package side-effects¶
You might need to do some initial setup when the package loads. To do that, you can use (define) two special functions:
.onLoad()
and.onAttach()
.
CRAN notes¶
you must use only ASCII characters in your .R files [or] use the special unicode escape "\u1234" format.
DESCRIPTION¶
Existe Imports:
y Suggests:
, y se puede manejar la falta de un paquete sugerido:
# There's a fallback method if the package isn't available
my_fun <- function(a, b) {
if (requireNamespace("pkg", quietly = TRUE)) {
pkg::f()
} else {
g()
}
}
There are actually many other rarely, if ever, used fields. A complete list can be found in the “The DESCRIPTION file” section of the R extensions manual.
LICENCE¶
Me gustó LGPL porque se puede usar en otros proyectos pero no requiere que todos los proyectos donde se usa sean también libres.
Documentación¶
R provides a standard way of documenting the objects in a package: you write
.Rd
files in theman/
directory.
Workflow¶
- Add roxygen comments
'#
to your.R
files. - Run
devtools::document()
or do a full rebuild with Ctrl + Shift + B. - Preview documentation with
?
Rinse and repeat.
roxygen¶
Ver como usar comentario '#
' en http://r-pkgs.had.co.nz/man.html#roxygen-comments
#' Say hi!
#' @param dummy A dummy parameter that does nothing. It's super effective!
#' @examples
#' helloWorld(":)")
#' @export
#' @seealso \link{print}
helloWorld <- function(dummy) print("Hola!")
Funciones con puntos en el nombre (S3 confusion)¶
Las funciones con "." en sus nombres pueden exportarse como S3methods después de un devtools::document()
.
Ver: https://stackoverflow.com/a/24607763/11524079
Para prevenirlo, al lado del @export
tag hay que poner el nombre de la función en cuestión.
Documenting packages¶
There’s no object that corresponds to a package, so you need to document
NULL
.I usually put this documentation in a file called
.R.
#' foo: A package for computating the notorious bar statistic.
#'
#' The foo package provides three categories of important functions:
#' foo, bar and baz.
#'
#' @section Foo functions:
#' The foo functions ...
#'
#' @docType package
#' @name foo
NULL
NAMESPACE¶
… in this chapter you’ll learn how to generate the NAMESPACE file with roxygen2
- Add roxygen '#
comments to your .R files. You only need to learn one tag, @export
.
- Run devtools::document() (or press Ctrl + Shift + D in RStudio)
A Shiny Package¶
https://github.com/mangothecat/shinyAppDemo
Packaging C code¶
Info sobre C en la wiki:
Patience...
- http://www.biostat.jhsph.edu/~rpeng/docs/interface.pdf
- http://users.stat.umn.edu/~geyer/rc/
- https://github.com/molpopgen/devnotes/wiki/Rcpp-and-compiler-flags
Info sobre makefiles:
Ejemplos¶
Ejemplos de otros paquetes para revisar:
- nloptr
magick
usa un CONFIGURE script.- https://github.com/becarioprecario/Rpackages
Incluir libraries del sistema¶
Como encontrar los paths a las libraries¶
$ pkg-config --libs glib-2.0
-lglib-2.0
$ pkg-config --cflags glib-2.0
-I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include
Makevars de ejemplo¶
PKG_CFLAGS
se usa para incluir archivos .h
.
PKG_LIBS
se usa para incluir "C libraries", o sea, archivos .so
(librerias dinamicas) o .a
(librerias estaticas).
Entonces, para incluir glib, en el ./src/Makevars
del paquete se puede escribir:
PKG_CFLAGS = -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include
PKG_LIBS=-lglib-2.0
Pero alcanza con
PKG_CFLAGS = -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include
O también
PKG_CPPFLAGS = -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include
Para incluir libtiff también:
PKG_CFLAGS = -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include
PKG_LIBS=-lglib-2.0 -ltiff
Compilar e incluir código de libraries en el paquete¶
Si los archivos de una library están "incluidos" en el paquete, se los "incluye" usando los paths relativos correctos.
Además, hay que decirle a R que compile las libraries, agregando una tareas ("targets") al archivo src/Makevars
.
Por ejemplo para compilar libtiff
, incluirla y limpiar después se puede hacer lo siguiente.
Incluir headers y libraries¶
PKG_CPPFLAGS, PKG_LIBS y otras flags:
CC=ccache clang -Qunused-arguments
CXX=ccache clang++ -Qunused-arguments
CCACHE_CPP2=yes
# Include de archivos ".h"
PKG_CPPFLAGS= -I./tiff410/include
# Links a archivos ".so" (las libraries)
PKG_LIBS= -L./tiff410/r_build/libtiff -ltiff -ljpeg -llzma -lz
MAKEFLAGS = -j4
Crear "targets" de compilación. Los "targets" son normalmente tareas de compilación que terminan en la creación de un archivo, o simplemente tareas sin output si se las especifica como .PHONY
.
Cada target puede tener otros targets como pre-requisito, y esto es necesario para que CellID se compile después de libtiff. En este caso CellID está representado como target en la variable $(SHLIB)
.
# https://makefiletutorial.com/
# A Makefile consists of a set of rules. A rule generally looks like this:
#
# targets : prerequisites
# command
# command
# command
# Adding .PHONY to a target will prevent make from confusing the phony target with a file name.
# For example, if the file “clean” is created somehow elsewhere, "make clean" will still be run.
.PHONY: all tiflibs rbuildcleanup
all: $(SHLIB) rbuildcleanup
$(SHLIB): tiflibs
# Note that each command is run independently, that's why there is a "cd" before each one.
# These lines tell cmake to build inside the r_build directory, keeping everything else tidy, and easy to cleanup.
tiflibs:
cd tiff410/r_build && cmake --clean-first ../
cd tiff410/r_build && make all -j4 -B
cd tiff410/r_build && cmake --target clean ../
# The "cleanup" target must depend on the rest in order to be execured last in a parallel compilation
rbuildcleanup: $(SHLIB)
rm -rf tiff410/r_build/*
Conditional includes¶
CC=ccache clang -Qunused-arguments
CXX=ccache clang++ -Qunused-arguments
CCACHE_CPP2=yes
PKG_LIBS = -ltiff
# See "1.2.1 Using Makevars" at https://cran.r-project.org/doc/manuals/r-devel/R-exts.html#Using-Makevars
# Sell also: What is "all"? https://stackoverflow.com/a/22735335
.PHONY: all testing_is_fun
# May be important to add: ".DEFAULT_GOAL := all"
# If you want to create and then link to a library, say using code in a subdirectory, use something like:
all: $(SHLIB)
$(SHLIB): testing_is_fun
# Test warnings
# $(warning Makevars debug warning: value of SHLIB is "$(SHLIB)")
$(warning checking libtiff)
TIFFSTATUS := $(shell $(LD) -ltiff || echo 1)
ifeq ($(TIFFSTATUS), 1)
$(warning TIFF library not found $(PKG_LIBS))
# OBJECTS := testing.o
testing_is_fun:
mv testing.c cell.c
else
$(warning TIFF library found)
# OBJECTS := align_image.o cell.o contiguous.o date_and_time.o fft.o fft_stats.o fit.o fl_dist.o flatten.o nums.o oif.o segment.o split_and_overlap.o testing.o tif.o
testing_is_fun:
rm testing.c
endif
# $(warning the value of "OBJECTS" is: "$(OBJECTS)")
# testing_is_fun:
# echo Testing is fun!
# # the value of "SHLIB" is: "$(SHLIB)"
# # the value of ".DEFAULT_GOAL" is: "$(.DEFAULT_GOAL)"
# echo the value of "OBJECTS" is: "$(OBJECTS)"
# # the value of "LD" is: "$(LD)"
# # TIFFSTATUS is: $(TIFFSTATUS)
References¶
Referencias y recursos
https://web.archive.org/web/20190831012258/http://www.libtiff.org/index.html
http://gnuwin32.sourceforge.net/packages/tiff.htm
https://stackoverflow.com/a/57677953
https://stackoverflow.com/a/1618618
http://mingw-w64.org/doku.php/download
https://cygwin.com/install.html elegir todo lo que diga tiff con fuente e instalar cygwin
https://medium.com/@meghamohan/all-about-static-libraries-in-c-cea57990c495
https://www.geeksforgeeks.org/static-vs-dynamic-libraries/
https://stackoverflow.com/questions/58156585/build-rocksdb-static-library-inside-r-package
https://github.com/rwinlib/libtiff
"As will the simpler approach of maybe just putting the source files of libbcd into the package src/ directory -- and R will take care of the rest."
Debugfear Makefile o Makevars¶
https://www.oreilly.com/openbook/make3/book/ch12.pdf
The warning function is very useful for debugging wayward makefiles.
$(warning A top-level warning)
Descifrar código en C¶
Vale ayudarse de https://cdecl.org/
.C - Interfaz a C en R¶
Parece que R espera que la función de C no cambie la estructura de los argumentos que se definieron en el wrapper. Pasé un día entero intentando entender cómo arreglar un problema y llegué a esa conclusión.
Otros links útiles para entender el *
y el &
de C o C++ y entender qué onda:
- http://r-pkgs.had.co.nz/src.html#src-debugging
- http://r-pkgs.had.co.nz/src.html#clang
- https://cran.rstudio.com/doc/manuals/r-devel/R-exts.html#Interface-functions-_002eC-and-_002eFortran
- https://stackoverflow.com/questions/840501/how-do-function-pointers-in-c-work?rq=1
- https://stackoverflow.com/questions/2160635/is-there-a-way-to-print-out-the-type-of-a-variable-pointer-in-c
- https://stackoverflow.com/questions/54484474/segfault-when-returning-a-value-from-c-to-r-using-call-incompatible-pointer-ty
- https://www.quora.com/What-does-ampersand-operator-do-in-a-C-C++-function-argument#
- https://softwareengineering.stackexchange.com/questions/252023/why-does-c-use-the-asterisk-for-pointers
- https://stackoverflow.com/questions/2094666/pointers-in-c-when-to-use-the-ampersand-and-the-asterisk
- https://c-for-dummies.com/caio/pointer-cheatsheet.php
Non standard evaluation¶
- https://www.r-bloggers.com/non-standard-evaluation-how-tidy-eval-builds-on-base-r/
- https://rlang.r-lib.org/reference/quotation.html
arrange¶
Como convertir nombres de variables en "strings" a "símbolos" para funciones tipo-tidy.
Ver:
?as.symbol
?sym
time_colum <- "t.frame"
arrange(cdata, !!as.symbol(time_colum))
arrange(cdata, !!sym(time_colum))
Ver:
filter¶
filter_starwars <- function(...) {
F <- quos(...)
filter(starwars, !!!F)
}
filter_starwars(species == 'Human', homeworld %in% c('Tatooine', 'Alderaan'), height > 175)
The big-bang operator !!! forces-splice a list of objects.
You can supply several expressions directly, e.g. quos(foo, bar), but more importantly you can also supply dots: quos(...).