8  S3

Es posible que haya notado que los resultados de su máquina tragamonedas no se ven como prometí. Sugerí que la máquina tragamonedas mostraría sus resultados así:

play()
## 0 0 DD
## $0

Pero la máquina actual muestra sus resultados en un formato menos bonito:

play()
## "0"  "0" "DD" 
## 0

Además, la máquina tragamonedas usa un truco para mostrar símbolos (llamamos print desde dentro de play). Como resultado, los símbolos no siguen la salida de su premio si lo guarda:

a_play <- play()
## "B" "0" "B" 

a_play
## 0

Puede solucionar ambos problemas con el sistema S3 de R.

8.1 El Sistema S3

S3 se refiere a un sistema de clases integrado en R. El sistema rige cómo R maneja objetos de diferentes clases. Ciertas funciones de R buscarán la clase S3 de un objeto y luego se comportarán de manera diferente en respuesta.

La función print es así. Cuando imprime un vector numérico, print mostrará un número:

num <- 1000000000
print(num)
## 1000000000

Pero si le da ese número a la clase S3 POSIXct seguida de POSIXt, print mostrará una hora:

class(num) <- c("POSIXct", "POSIXt")
print(num)
## "2001-09-08 19:46:40 CST"

Si usa objetos con clases, y lo hace, se encontrará con el sistema S3 de R. El comportamiento de S3 puede parecer extraño al principio, pero es fácil de predecir una vez que se familiariza con él.

El sistema S3 de R se basa en tres componentes: atributos (especialmente el atributo class), funciones genéricas y métodos.

8.2 Atributos

En Atributos, aprendió que muchos objetos de R vienen con atributos, piezas de información adicional a las que se les da un nombre y se agregan al objeto. Los atributos no afectan los valores del objeto, pero se adhieren al objeto como un tipo de metadatos que R puede usar para manejar el objeto. Por ejemplo, un data frame almacena sus nombres de fila y columna como atributos. Los data frame también almacenan su clase, "data.frame", como un atributo.

Puede ver los atributos de un objeto con attribute. Si ejecuta attribute en el data frame mazo que creó en Proyecto 2: Baraja de Cartas, verá:

attributes(mazo)
## $names
## [1] "cara"  "palo"  "valor"
## 
## $class
## [1] "data.frame"
## 
## $row.names
##  [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 
## [20] 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
## [37] 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52

R viene con muchas funciones auxiliares que le permiten establecer y acceder a los atributos más comunes utilizados en R. Ya conoce las funciones names, dim y class, cada una de las cuales funciona con un atributo con nombre homónimo. Sin embargo, R también tiene row.names, levels y muchas otras funciones auxiliares basadas en atributos. Puede utilizar cualquiera de estas funciones para recuperar el valor de un atributo:

row.names(mazo)
##  [1] "1"  "2"  "3"  "4"  "5"  "6"  "7"  "8"  "9"  "10" "11" "12" "13"
## [14] "14" "15" "16" "17" "18" "19" "20" "21" "22" "23" "24" "25" "26"
## [27] "27" "28" "29" "30" "31" "32" "33" "34" "35" "36" "37" "38" "39"
## [40] "40" "41" "42" "43" "44" "45" "46" "47" "48" "49" "50" "51" "52"

o para cambiar el valor de un atributo:

row.names(mazo) <- 101:152

o para dar a un objeto un atributo completamente nuevo:

levels(mazo) <- c("level 1", "level 2", "level 3")

attributes(mazo)
## $names
## [1] "cara"  "palo"  "valor"
## 
## $class
## [1] "data.frame"
## 
## $row.names
##  [1] 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
## [18] 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
## [35] 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
## [52] 152
## 
## $levels
## [1] "level 1" "level 2" "level 3"

R es muy libre cuando se trata de atributos. Le permitirá agregar cualquier atributo que desee a un objeto (y luego generalmente los ignorará). La única vez que R se quejará es cuando una función necesita encontrar un atributo y no está allí.

Puede agregar cualquier atributo general a un objeto con attr; también puede usar attr para buscar el valor de cualquier atributo de un objeto. Veamos cómo funciona esto con a_play, el resultado de jugar nuestra máquina tragamonedas una vez:

a_play <- play()
a_play
## 0

attributes(a_play)
## NULL

attr toma dos argumentos: un objeto de R y el nombre de un atributo (como una cadena de caracteres). Para darle al objeto de R un atributo del nombre especificado, guarde un valor en la salida de attr. Démosle a a_play un atributo llamado simbolos que contiene un vector de cadenas de caracteres:

attr(a_play, "simbolos") <- c("B", "0", "B")

attributes(a_play)
## $simbolos
## [1] "B" "0" "B"

Para buscar el valor de cualquier atributo, dale a attr un objeto R y el nombre del atributo que te gustaría buscar:

attr(a_play, "simbolos")
## "B" "0" "B"

Si asigna un atributo a un vector atómico, como a_play, R generalmente mostrará el atributo debajo de los valores del vector. Sin embargo, si el atributo cambia la clase del vector, R puede mostrar toda la información en el vector de una nueva forma (como vimos con los objetos POSIXct):

a_play
## [1] 0
## attr(,"simbolos")
## [1] "B" "0" "B"

R generalmente ignorará los atributos de un objeto a menos que le des un nombre que busca una función de R, como names o class. Por ejemplo, R ignorará el atributo simbolos de a_play mientras manipulas a_play:

a_play + 1
##  1
## attr(,"simbolos")
##  "B" "0" "B"

Ejercicio 11.1 (Añade un Atributo) Modifique play para devolver un premio que contenga los símbolos asociados a él como un atributo denominado simbolos. Elimine la llamada redundante a print(simbolos):

play <- function() {
  simbolos <- obt_simbolos()
  print(simbolos)
  puntuacion(simbolos)
}

Solución. Puede crear una nueva versión de play capturando la salida de puntuacion(simbolos) y asignándole un atributo. play puede devolver la versión mejorada de la salida:

play <- function() {
  simbolos <- obt_simbolos()
  premio <- puntuacion(simbolos)
  attr(premio, "simbolos") <- simbolos
  premio
}

Ahora play devuelve tanto el premio como los símbolos asociados con el premio. Puede que los resultados no se vean bonitos, pero los símbolos se quedan con el premio cuando lo copiamos a un nuevo objeto. Podemos trabajar en ordenar la pantalla en un minuto:

play()
## [1] 0
## attr(,"simbolos")
## [1] "B"  "BB" "0" 
 
b_play <- play()
 
b_play
## [1] 0
## attr(,"simbolos")
## [1] "0" "B" "0"

También puede generar un premio y establecer sus atributos en un solo paso con la función structure. structure crea un objeto con un conjunto de atributos. El primer argumento de structure debe ser un objeto de R o un conjunto de valores, y los argumentos restantes deben ser atributos con nombre para que structure se agregue al objeto. Puede dar a estos argumentos cualquier nombre de argumento que desee. structure agregará los atributos al objeto bajo los nombres que proporcione como nombres de argumento:

play <- function() {
  simbolos <- get_simbolos()
  structure(puntuacion(simbolos), simbolos = simbolos)
}

c_play <- play()
c_play
##  0
##  attr(,"simbolos")
##  "0"  "BB" "B" 

Ahora que su salida play contiene un atributo simbolos, ¿qué puede hacer con él? Puede escribir sus propias funciones que busquen y usen el atributo. Por ejemplo, la siguiente función buscará el atributo simbolos de a_play y lo usará para mostrar a_play de una manera bonita. Usaremos esta función para mostrar los resultados de nuestra tragamonedas, así que tomemos un momento para estudiar lo que hace:

mostrat_tragamoneda <- function(premio){

  # extraer simbolos
  simbolos <- attr(premio, "simbolos")

  # colapsar símbolos en una sola cadena de caracteres
  simbolos <- paste(simbolos, collapse = " ")

  # combinar símbolo con premio como una cadena de caracteres
  # \n es una secuencia de escape especial para una nueva línea (i.e. return or enter)
  texto <- paste(simbolos, premio, sep = "\n$")

  # mostrar cadena de caracteres en la consola sin comillas
  cat(texto)
}

mostrat_tragamoneda(a_play)
## B 0 B
## $0

La función espera un objeto como a_play que tiene tanto un valor numérico como un atributo de simbolos. La primera línea de la función buscará el valor del atributo simbolos y lo guardará como un objeto llamado simbolos. Hagamos un objeto simbolos de ejemplo para que podamos ver lo que hace el resto de la función. Podemos usar el atributo simbolos de a_play para hacer el trabajo. simbolos será un vector de cadenas de tres caracteres:

simbolos <- attr(a_play, "simbolos")

simbolos
## "B" "0" "B"

A continuación, mostrat_tragamoneda usa paste para colapsar las tres cadenas en simbolos en una cadena de un solo carácter. paste colapsa un vector de cadenas de caracteres en una sola cadena cuando le das el argumento collapse. paste usará el valor de collapse para separar las cadenas anteriormente distintas. Por lo tanto, simbolos se convierte en B 0 B las tres cadenas separadas por un espacio:

simbolos <- paste(simbolos, collapse = " ")

simbolos
## "B 0 B"

Nuestra función luego usa paste de una nueva forma para combinar simbolos con el valor de premio. paste combina objetos separados en una cadena de caracteres cuando le das un argumento sep. Por ejemplo, aquí paste combinará la cadena en simbolos, B 0 B, con el número en premio, 0. paste usará el valor del argumento sep para separar las entradas en el cadena nueva. Aquí, ese valor es \n$, por lo que nuestro resultado se verá como "B 0 B\n$0":

premio <- a_play
texto <- paste(simbolos, premio, sep = "\n$")

texto
## "B 0 B\n$0"

La última línea de mostrat_tragamoneda llama a cat en la nueva cadena. cat es como print; muestra su entrada en la línea de comando. Sin embargo, cat no encierra su salida entre comillas. cat también reemplaza cada \n con una nueva línea o salto de línea. El resultado es lo que vemos. Tenga en cuenta que se ve exactamente como sugerí que nuestra salida play debería verse en Programas:

cat(texto)
## B 0 B
## $0

Puedes usar mostrat_tragamoneda para limpiar manualmente la salida de play:

mostrat_tragamoneda(play())
## C B 0
## $2

mostrat_tragamoneda(play())
## 7 0 BB
## $0

Este método de limpieza de la salida requiere que intervengas manualmente en tu sesión de R (para llamar a mostrat_tragamoneda). Hay una función que puede usar para limpiar automáticamente la salida de play cada vez que se muestra. Esta función es print, y es una función genérica.

8.3 Funciones Genéricas

R usa print con más frecuencia de lo que piensas; R llama a print cada vez que muestra un resultado en la ventana de su consola. Esta llamada ocurre en segundo plano, por lo que no la nota; pero la llamada explica cómo la salida llega a la ventana de la consola (recuerde que print siempre imprime su argumento en la ventana de la consola). Esta llamada print también explica por qué la salida de print siempre coincide con lo que ve cuando muestra un objeto en la línea de comando:

print(pi)
## 3.141593

pi
## 3.141593


print(head(mazo))
##  cara  palo valor
##   rey picas    13
## reina picas    12
##  jota picas    11
##  diez picas    10
## nueve picas     9
##  ocho picas     8

head(mazo)
##  cara  palo valor
##   rey picas    13
## reina picas    12
##  jota picas    11
##  diez picas    10
## nueve picas     9
##  ocho picas     8


print(play())
##  5
## attr(,"simbolos")
##  "B"  "BB" "B" 

play()
##  5
## attr(,"simbolos")
##  "B"  "BB" "B" 

Puede cambiar la forma en que R muestra la salida de su maquina tragamonedas reescribiendo print para que se vea como mostrat_tragamoneda. Entonces R mostraría la salida en el formato que hemos creado. Sin embargo, este método tendría efectos secundarios negativos. No desea que R llame a mostrat_tragamoneda cuando muestre un data frame, un vector numérico o cualquier otro objeto.

Afortunadamente, print no es una función normal; es una función genérica. Esto significa que print está escrito de una manera que le permite hacer cosas diferentes en casos diferentes. Ya has visto este comportamiento en acción (aunque es posible que no te hayas dado cuenta). print hizo una cosa cuando miramos la versión sin clase de num:

num <- 1000000000
print(num)
## 1000000000

y una cosa diferente cuando le dimos a num una clase:

class(num) <- c("POSIXct", "POSIXt")
print(num)
## "2001-09-08 19:46:40 CST"

Eche un vistazo al código dentro de print para ver cómo lo hace. Puede imaginar que print busca el atributo de clase de su entrada y luego usa un árbol +if+ para elegir qué salida mostrar. Si esto te ocurrió, ¡buen trabajo! print hace algo muy similar, pero mucho más simple.

8.4 Métodos

Cuando llamas a print, print llama a una función especial, UseMethod:

print
## function (x, ...) 
## UseMethod("print")
## <bytecode: 0x7ffee4c62f80>
## <environment: namespace:base>

UseMethod examina la clase de la entrada que proporcionas para el primer argumento de print y luego pasa todos tus argumentos a una nueva función diseñada para manejar esa clase de entrada. Por ejemplo, cuando le das a print un objeto POSIXct, UseMethod pasará todos los argumentos de print a print.POSIXct. R luego ejecutará print.POSIXct y devolverá los resultados:

print.POSIXct
## function (x, ...) 
## {
##     max.print <- getOption("max.print", 9999L)
##     if (max.print < length(x)) {
##         print(format(x[seq_len(max.print)], usetz = TRUE), ...)
##         cat(" [ reached getOption(\"max.print\") -- omitted", 
##             length(x) - max.print, "entries ]\n")
##     }
##     else print(format(x, usetz = TRUE), ...)
##     invisible(x)
## }
## <bytecode: 0x7fa948f3d008>
## <environment: namespace:base>

Si le das a print un objeto de factor, UseMethod pasará todos los argumentos de print a print.factor. R luego ejecutará print.factor y devolverá los resultados:

print.factor
## function (x, quote = FALSE, max.levels = NULL, width = getOption("width"), 
##     ...) 
## {
##     ord <- is.ordered(x)
##     if (length(x) == 0L) 
##         cat(if (ord) 
##             "ordered"
## ...
##         drop <- n > maxl
##         cat(if (drop) 
##             paste(format(n), ""), T0, paste(if (drop) 
##             c(lev[1L:max(1, maxl - 1)], "...", if (maxl > 1) lev[n])
##         else lev, collapse = colsep), "\n", sep = "")
##     }
##     invisible(x)
## }
## <bytecode: 0x7fa94a64d470>
## <environment: namespace:base>

print.POSIXct y print.factor se denominan métodos de print. Por sí mismos, print.POSIXct y print.factor funcionan como funciones regulares de R. Sin embargo, cada uno fue escrito específicamente para que ‘UseMethod’ pudiera llamarlo para manejar una clase específica de entrada de print.

Tenga en cuenta que print.POSIXct y print.factor hacen dos cosas diferentes (también tenga en cuenta que compendié la mitad de print.factor: es una función larga). Así es como print se las arregla para hacer diferentes cosas en diferentes casos. print llama a UseMethod, que llama a un método especializado basado en la clase del primer argumento de print.

Puede ver qué métodos existen para una función genérica llamando a methods en la función. Por ejemplo, print tiene casi 200 métodos (lo que le da una idea de cuántas clases existen en R):

methods(print)
##   [1] print.acf*                                   
##   [2] print.anova                                  
##   [3] print.aov*                                   
##  ...                      
## [176] print.xgettext*                              
## [177] print.xngettext*                             
## [178] print.xtabs*
##
##  Nonvisible functions are asterisked

Este sistema de funciones genéricas, métodos y despacho basado en clases se conoce como S3 porque se originó en la tercera versión de S, el lenguaje de programación que se convertiría en S-PLUS y R. Muchas funciones comunes de R son genéricas de S3 que funcionan con un conjunto de métodos de clase. Por ejemplo, summary y head también llaman a UseMethod. Las funciones más básicas, como c, +, -, < y otras también se comportan como funciones genéricas, aunque llaman a .primitive en lugar de UseMethod.

El sistema S3 permite que las funciones de R se comporten de diferentes maneras para diferentes clases. Puede usar S3 para formatear la salida de su tragamonedas. Primero, dé a su salida su propia clase. Luego escriba un método de impresión para esa clase. Para hacer esto de manera eficiente, necesitará saber un poco acerca de cómo UseMethod selecciona una función de método para usar.

8.4.1 Selección de Método

UseMethod utiliza un sistema muy simple para emparejar métodos con funciones.

Cada método S3 tiene un nombre de dos partes. La primera parte del nombre se referirá a la función con la que trabaja el método. La segunda parte se referirá a la clase. Estas dos partes estarán separadas por un punto. Entonces, por ejemplo, el método de print que funciona con funciones se llamará print.function. El método de summary que trabaja con matrices se llamará summary.matrix. Y así sucesivamente.

Cuando UseMethod necesita llamar a un método, busca una función de R con el nombre de estilo S3 correcto. La función no tiene que ser especial de ninguna manera; solo necesita tener el nombre correcto.

Puede participar en este sistema escribiendo su propia función y dándole un nombre de estilo S3 válido. Por ejemplo, demos a a_play una clase propia. No importa cómo llames a la clase; R almacenará cualquier cadena de caracteres en el atributo de clase:

class(a_play) <- "tragamonedas"

Ahora escribamos un método de impresión S3 para la clase +tragamonedas+. El método no necesita hacer nada especial, ni siquiera necesita imprimir a_play. Pero necesita llamarse print.tragamonedas; de lo contrario, UseMethod no lo encontrará. El método también debería tomar los mismos argumentos que print; de lo contrario, R dará un error cuando intente pasar los argumentos a print.tragamonedas:

args(print)
## function (x, ...) 
## NULL

print.tragamonedas <- function(x, ...) {
  cat("Estoy usando el método print.tragamonedas")
}

¿Funciona nuestro método? Sí, y no solo eso; R usa el método de impresión para mostrar el contenido de a_play. Este método no es muy útil, así que voy a eliminarlo. Tendrás la oportunidad de escribir uno mejor en un minuto:

print(a_play)
## Estoy usando el método print.tragamonedas

a_play
## Estoy usando el método print.tragamonedas

rm(print.tragamonedas)

Algunos objetos de R tienen múltiples clases. Por ejemplo, la salida de Sys.time tiene dos clases. ¿Qué clase usará UseMethod para encontrar un método de impresión?

ahora <- Sys.time()
attributes(ahora)
## $class
## [1] "POSIXct" "POSIXt" 

UseMethod primero buscará un método que coincida con la primera clase listada en el vector de clase del objeto. Si UseMethod no puede encontrar uno, buscará el método que coincida con la segunda clase (y así sucesivamente si hay más clases en el vector de clase de un objeto).

Si le das a print un objeto cuya clase o clases no tienen un método de impresión, UseMethod llamará a print.default, un método especial escrito para manejar casos generales.

Usemos este sistema para escribir un mejor método de impresión para la salida de la máquina tragamonedas.

Ejercicio 11.2 (Hacer un Método de print) Escriba un nuevo método de print para la clase de tragamonedas. El método debería llamar a mostrat_tragamoneda para devolver una salida de máquina tragamonedas bien formateada.

¿Qué nombre debe usar para este método?

Solución. Es sorprendentemente fácil escribir un buen método print.tragamonedas porque ya hicimos todo el trabajo duro cuando escribimos mostrat_tragamoneda. Por ejemplo, el siguiente método funcionará. Solo asegúrese de que el método se llame print.tragamonedas para que UseMethod pueda encontrarlo, y asegúrese de que toma los mismos argumentos que print para que UseMethod pueda pasar esos argumentos a print.tragamonedas sin ningún problema:

print.tragamonedas <- function(x, ...) {
  mostrat_tragamoneda(x)
}

Ahora R usará automáticamente mostrat_tragamoneda para mostrar objetos de clase +tragamonedas+ (y solo objetos de clase “tragamonedas”):

a_play
## B 0 B
## $0

Asegurémonos de que cada salida de la máquina tragamonedas tenga la clase tragamonedas.

Ejercicio 11.3 (Añadir una Clase) Modifique la función play para que asigne tragamonedas al atributo class de su salida:

play <- function() {
  simbolos <- get_simbolos()
  structure(puntuacion(simbolos), simbolos = simbolos)
}

Solución. Puede establecer el atributo class de la salida al mismo tiempo que establece el atributo +simbolos+. Simplemente agregue class = "tragamonedas" a la llamada structure:

play <- function() {
  simbolos <- get_simbolos()
  structure(puntuacion(simbolos), simbolos = simbolos, class = "tragamonedas")
}

Ahora cada uno de nuestras salidas de play tendrá la clase tragamonedas:

class(play())
## "tragamonedas"

Como resultado, R los mostrará en el formato de máquina tragamonedas correcto:

play()
## BB BB BBB
## $5

play()
## BB 0 0
## $0

8.5 Clases

Puede usar el sistema S3 para crear una nueva clase sólida de objetos en R. Luego, R tratará los objetos de su clase de manera consistente y sensata. Para hacer una clase:

  • Elija un nombre para su clase.
  • Asigne a cada instancia de su clase un atributo +class+.
  • Escriba métodos de clase para cualquier función genérica que pueda usar objetos de su clase.

Muchos paquetes de R se basan en clases que se han creado de manera similar. Si bien este trabajo es simple, puede que no sea fácil. Por ejemplo, considere cuántos métodos existen para clases predefinidas.

Puede llamar a methods en una clase con el argumento class, que toma una cadena de caracteres. methods devolverá todos los métodos escritos para la clase. Tenga en cuenta que methods no podrá mostrarle los métodos que vienen en un paquete R descargado:

methods(class = "factor")
##  [1] [.factor             [[.factor           
##  [3] [[<-.factor          [<-.factor          
##  [5] all.equal.factor     as.character.factor 
##  [7] as.data.frame.factor as.Date.factor      
##  [9] as.list.factor       as.logical.factor   
## [11] as.POSIXlt.factor    as.vector.factor    
## [13] droplevels.factor    format.factor       
## [15] is.na<-.factor       length<-.factor     
## [17] levels<-.factor      Math.factor         
## [19] Ops.factor           plot.factor*        
## [21] print.factor         relevel.factor*     
## [23] relist.factor*       rep.factor          
## [25] summary.factor       Summary.factor      
## [27] xtfrm.factor        
## 
##    Nonvisible functions are asterisked

Esta salida indica cuánto trabajo se requiere para crear una clase robusta y de buen comportamiento. Por lo general, necesitará escribir un método de class para cada operación básica de R.

Considere dos desafíos que enfrentará de inmediato. Primero, R descarta atributos (como class) cuando combina objetos en un vector:

play1 <- play()
play1
## B BBB BBB
## $5

play2 <- play()
play2
## 0 B 0
## $0

c(play1, play2)
## [1] 5 0

Aquí, R deja de usar print.tragamonedas para mostrar el vector porque el vector c(play1, play2) ya no tiene un atributo +class+ “tragamonedas”.

A continuación, R eliminará los atributos de un objeto (como class) cuando subjunte el objeto:

play1[1]
## [1] 5

Puede evitar este comportamiento escribiendo un método c.tragamonedas y un método [.tragamonedas, pero luego se acumularán dificultades rápidamente. ¿Cómo combinaría los atributos de simbolos de múltiples jugadas en un vector de atributos de símbolos? ¿Cómo cambiarías print.tragamonedas para manejar vectores de salidas? Estos desafíos están abiertos para que los explores. Sin embargo, normalmente no tendrá que intentar este tipo de programación a gran escala como científico de datos.

En nuestro caso, es muy útil dejar que los objetos tragamonedas vuelvan a tener valores de premios únicos cuando combinamos grupos de ellos en un vector.

8.6 S3 y Depuración

S3 puede ser molesto si está tratando de comprender las funciones de R. Es difícil saber qué hace una función si su cuerpo de código contiene una llamada a UseMethod. Ahora que sabe que UseMethod llama a un método específico de clase, puede buscar y examinar el método directamente. Será una función cuyo nombre siga la sintaxis <function.class>, o posiblemente <function.default>. También puede usar la función methods para ver qué métodos están asociados con una función o una clase.

8.7 S4 y R5

R también contiene otros dos sistemas que crean un comportamiento específico de clase. Estos se conocen como S4 y R5 (o clases de referencia). Cada uno de estos sistemas es mucho más difícil de usar que S3 y quizás, como consecuencia, más raro. Sin embargo, ofrecen garantías que S3 no ofrece. Si desea obtener más información sobre estos sistemas, incluido cómo escribir y usar sus propias funciones genéricas, le recomiendo el libro Advanced R Programming de Hadley Wickham.

8.8 Resumen

Los valores no son el único lugar para almacenar información en R, y las funciones no son la única forma de crear un comportamiento único. También puede hacer ambas cosas con el sistema S3 de R. El sistema S3 proporciona una forma sencilla de crear comportamientos específicos de objetos en R. En otras palabras, es la versión de programación orientada a objetos (OOP) de R. El sistema se implementa mediante funciones genéricas. Estas funciones examinan el atributo de clase de su entrada y llaman a un método específico de clase para generar salida. Muchos métodos de S3 buscarán y utilizarán información adicional que se almacena en los atributos de un objeto. Muchas funciones comunes de R son genéricas de S3.

El sistema S3 de R es más útil para las tareas de informática que para las tareas de ciencia de datos, pero comprender S3 puede ayudarlo a solucionar problemas en su trabajo en R como científico de datos

Ahora sabe bastante sobre cómo escribir código de R que realiza tareas personalizadas, pero ¿cómo podría repetir estas tareas? Como científico de datos, a menudo repetirá tareas, a veces miles o incluso millones de veces. ¿Por qué? Porque la repetición te permite simular resultados y estimar probabilidades. Bucles te mostrará cómo automatizar la repetición con las funciones for y while de R. Usará for para simular varios juegos de máquinas tragamonedas y para calcular la tasa de pago de su máquina tragamonedas.