10 CLASIFICACIÓN DE OBJETOS

10.1 Clase

En R, los objetos se agrupan por clases. Todos los objetos son de una clase determinada (en ocasiones pueden pertenecer a dos o más clases). La clase indica, en sentido amplio, cuál es la naturaleza del objeto.

Las clases más comunes de los objetos contenedores de datos que se presentan en el capítulo 8 son entera (integer), numérica (numeric), compleja (complex), lógica (logical), carácter (character), matriz (matrix), arreglo (array), data frame (data.frame) y lista (list). Aunque las categorías de los contenedores matriz, arreglo, data frame y lista coinciden con sus clases, no hay coincidencia para el caso de los vectores, los cuales pueden ser de alguna de las cinco primeras clases, según la naturaleza de sus elementos. Como ayuda nemotécnica para establecer la relación entre clases y contenedores, puede pensarse cada una de las primeras cinco clases enumeradas anteriormente como si llevaran antepuesta la palabra vector, así: vector numérico, vector complejo, vector lógico, etc.

Como parte de la programación orientada a objetos, R maneja funciones genéricas, que derivan a funciones particulares, tomando en consideración la clase del argumento principal de la función. Para averiguar la clase de un objeto, se usa la función class. Es posible, con ciertas restricciones, modificar la clase de un objeto, mediante el uso de funciones tales como as.matrix, as.data.frame, as.list, as.numeric y as.character, que permiten guardar objetos como matrices, data frames, listas, vectores numéricos y vectores de caracteres, respectivamente. El comando apropos("as") presenta una lista completa de tales funciones.

10.2 Tipo

Aunque la clase define la naturaleza de los elementos que conforman un vector, no informa sobre los elementos que conforman los demás objetos atómicos. Para conocer la naturaleza de tales elementos en matrices y arreglos, debe evaluarse su tipo, mediante la función typeof. Los tipos son cuasicoincidentes con las clases de vectores: integer, double20, logical, complex y character.

Al evaluar el tipo de datos que conforman un objeto atómico se obtiene información inequívoca (entero, lógico, carácter, etc.), puesto que todos sus elementos son del mismo tipo. No obstante, cuando se pretende evaluar el tipo de datos que conforman un objeto recursivo, esto es, un data frame o una lista (cf. capítulo 8), el resultado obtenido es list, como un recordatorio de que los elementos alojados en dichos contenedores pueden ser de diferentes tipos. Este resultado es siempre el mismo, aun si se evalúan data frames o listas conformados por elementos del mismo tipo.

No obstante, de ser necesario, puede evaluarse el tipo de cada uno de los objetos que conforman un contenedor recursivo, mediante el uso de los descriptores de acceso apropiados (capítulo 13).

A nivel de usuario, la naturaleza de los elementos constituyentes de un objeto atómico (vector, matriz o arreglo) se evalúa a través de su tipo (typeof).

10.3 Modo

El modo, que se verifica con la función mode, tiene que ver con la forma en que los objetos son almacenados internamente y es manejado automáticamente por el sistema, siendo cuasicoincidente con el tipo, sin que deba constituir un motivo de preocupación para el usuario, cobrando relevancia únicamente cuando se importe o exporte código desde o hacia otros lenguajes tales como S, C, C++ o Fortran.

10.4 Clase factor

Esta clase se usa para la caracterización de variables categóricas, con base en un conjunto de niveles. La clase factor es de especial relevancia, por ser la requerida para ciertos componentes de procedimientos estadísticos tales como el análisis de varianza. Cualquier vector puede llevarse a esta clase, mediante la función factor (o también as.factor).

La naturaleza de los elementos contenidos en un objeto no determina que este sea o no un factor. Considérense los siguientes objetos.

a <- c(9, 5, 3, 6, 9)
b <- c("j", "m", "f", "m", "m", "f")

Ambos objetos son vectores: a, de la clase numeric, y b, de la clase character. Ni a ni b son factores (esto puede verificarse mediante la función is.factor). No obstante, tanto a como b pueden convertirse en factores.

a.fact <- factor(a)
b.fact <- factor(b)

Los objetos a.fact y b.fact son factores. Aunque estos contienen los mismos elementos que a y b, respectivamente, están adicionados con un atributo correspondiente a sus niveles, los cuales pueden recuperarse, mediante la función levels.

levels(a.fact)
#> [1] "3" "5" "6" "9"
levels(b.fact)
#> [1] "f" "j" "m"

Nótese que el resultado de la función levels no es otra cosa que un vector con cada una de las categorías o niveles ordenados del factor.

En ocasiones, puede requerirse cambiar el orden por defecto de los niveles (orden alfabético) por cualquier otro. Para definir un orden personalizado, se usa el argumento levels dentro de la función factor.

Considérese el siguiente vector.

mes <- c("mar", "feb", "ene", "abr")
mes.f <- factor(mes)

El orden por defecto para los niveles del factor mes.f es el alfabético:

levels(mes.f)
#> [1] "abr" "ene" "feb" "mar"

Un orden personalizado podría obtenerse incorporando el argumento levels, con el orden deseado, al definir el factor, así:

mes.f <- factor(mes, levels = c("ene", "feb", "mar", "abr"))
levels(mes.f)
#> [1] "ene" "feb" "mar" "abr"

Un caso particular de reordenamiento de los niveles de un factor surge cuando se necesita definir un nivel determinado como nivel de referencia. Teniendo en cuenta que muchas funciones toman el primer nivel del factor como nivel de referencia, bastaría con ubicar el nivel deseado en la primera posición antes de aplicar tales funciones. Para tal efecto se usa la función relevel (reference level).

Considérese nuevamente el factor b.fact definido anteriormente y supóngase que se usará como argumento de una función, mediante la cual se pretende comparar los niveles de "j" contra los demás niveles. Para tal efecto, se define "j" como el nivel de referencia, ubicándolo en la primera posición.

b.fact <- relevel(b.fact, "j")
levels(b.fact)
#> [1] "j" "f" "m"

Para averiguar el número de niveles de un factor, se usa la función nlevels.

nlevels(a.fact)
#> [1] 4
nlevels(b.fact)
#> [1] 3

Cuando se importan datos, mediante cualquiera de las estrategias presentadas en el capítulo 6, el objeto resultante es un data frame, en el que la clase de cada uno de sus vectores queda definida automáticamente por su correspondiente contenido. Las columnas que solo contienen información numérica se importan como vectores de la clase numeric, mientras que las que contienen caracteres o una combinación de números y caracteres se importan como objetos de la clase character.

A continuación, se discute el proceso interno involucrado en la definición de factores. Aunque se trata de un tecnicismo, este permite resolver un par de aspectos desconcertantes relativos a los objetos de la clase factor. En primera instancia, que los factores no son vectores, y en segunda instancia, que, sin importar cuál sea su contenido, siempre son de tipo entero.

La transformación de un vector a la clase factor se realiza en cinco pasos:

  1. El sistema determina los niveles, evaluando el conjunto de elementos únicos, mediante la función unique, o tomándolos del argumento levels, en caso de que este haya sido suministrado por el usuario.

  2. Mediante la función match, se genera un vector de índices enteros que guardan correspondencia con los niveles ordenados del vector.

  3. Se le asigna el atributo levels, al objeto que contiene los índices.

  4. Se clasifica dicho objeto como factor.

  5. Se remplaza el vector original con el objeto creado.

El tercer paso, es decir, la asignación del atributo levels al vector de índices enteros hace que este deje de ser reconocido como vector mediante la función is.vector. Esta función, más allá de lo que su nombre pueda sugerir, solo reconoce como vector a una estructura en la que todos sus elementos sean de un modo específico y que no tenga ningún atributo adicional al nombre de los elementos. Luego, aunque, en realidad, el objeto en cuestión sí sigue siendo un vector, no es reconocido como tal mediante la función is.vector, por el hecho de habérsele agregado el atributo levels.

Cuando a este vector de enteros se le asigna la clase factor, se establece una asociación entre su contenido y los correspondientes niveles, siendo estos últimos los que se muestran, pareciendo que el objeto original no se hubiera modificado. No obstante, lo que realmente queda almacenado internamente es el vector de índices enteros. Esto explica por qué el tipo de cualquier objeto de la clase factor es integer. Para recuperar el contenido del vector de índices enteros, basta con desclasificarlo o retirarle el atributo de clase, mediante la función unclass.

En el Anexo D se presenta una función para depurar los niveles de un factor.

10.5 Tipo lógico

Los objetos de este tipo están conformados por variables dicotómicas, cuyos posibles valores son falso y verdadero. TRUE o T representa el valor verdadero, mientras que FALSE o F representa el valor falso. Estas constantes se escriben siempre en mayúscula sostenida y no van entrecomilladas.

Las constantes lógicas aparecen frecuentemente como argumentos de funciones, para discernir entre dos líneas de acción. También es común que se generen como resultado de alguna función.

Cuando se realizan operaciones aritméticas propias de variables numéricas, TRUE toma el valor de 1 y FALSE, de 0. Esto puede ser de utilidad para contabilizar el número de elementos que satisfacen una condición.

Considérese el siguiente vector.

v <- c(7, 23, 11, 8, 45, 90, 16, 21, 34, 57, 69)

Si se quisiera averiguar, por ejemplo, cuáles elementos son mayores que 20, se escribiría:

v > 20
#>  [1] FALSE  TRUE FALSE FALSE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE

Para contabilizar el número de elementos que satisfacen esa condición, basta con sumar las respuestas verdaderas (TRUE = 1).

sum(v > 20)
#> [1] 7

Debe tenerse presente que esta relación solamente aplica en una vía; es decir que, cuando sea requerido, las constantes lógicas toman valores numéricos; no obstante, no puede usarse 1 en lugar de TRUE ni 0 en lugar de FALSE.

Las funciones isTRUE e isFALSE permiten verificar si sus argumentos son verdaderos o falsos, correspondientemente.

10.6 Resumen de objetos contendores de información

En R no existen el concepto de escalares, como elementos aislados por fuera de una estructura contenedora; todos los elementos —aun siendo unitarios— están contenidos en un vector21. Los vectores son las estructuras contenedoras básicas. Todas las estructuras contenedoras complejas están conformadas en última instancia por vectores.

Todos los elementos de un vector deben ser de la misma naturaleza; esto define el tipo de vector (typeof), siendo los más comunes integer, double, logical, character y complex.

Los vectores son unidimensionales. Cada elemento ocupa una posición. Aunque eventualmente los vectores pueden hacerse corresponder con el concepto de los vectores del álgebra lineal (cf. sección 8.2), en principio son independientes de tal concepto, por lo que no se habla ni de vectores fila ni de vectores columna; simplemente de vectores.

Los vectores son susceptibles de conformar arreglos rectangulares o hiperrectangulares, el más básico de los cuales es la matriz; este arreglo está constituido por dos dimensiones: filas y columnas. Para la conformación de tales estructuras basta con ligar un atributo de dimensionalidad al vector. Tales arreglos seguirán conteniendo elementos de la misma naturaleza, por lo cual siguen siendo del mismo tipo del vector base.

A estos objetos que solamente permiten alojar elementos de naturaleza común se les denomina atómicos.

En la figura anterior se representan una serie de objetos atómicos. El panel de la izquierda representa una serie de vectores de diferentes tipos y de diferentes tamaños. No obstante, cada vector contiene elementos de un tipo común. El panel de la derecha representa un arreglo rectangular en dos dimensiones (una matriz), cuyos elementos son todos del mismo tipo.

Cualquier estructura contenedora que permita alojar elementos de diversa naturaleza se denomina recursiva. La forma más general de las estructuras recursivas es la lista. Esta estructura permite alojar objetos de diferente tipo, sin importar el tamaño de los mismos, ni que estos tengan o no tengan nombres.

Un caso particular de lista es el data frame. Esta estructura recursiva está diseñada para alojar vectores de diferentes tipos. Los vectores son los elementos básicos de esta lista. Adicionalmente debe satisfacerse que todos los vectores tengan nombre y que tengan el mismo tamaño.

En el panel izquierdo de la figura anterior se ilustra una lista con 6 elementos: dos matrices, un data frame y tres vectores, uno de los cuales es de tamaño 1. Nótese que las dos matrices contenidas en esta lista son de diferente tipo y de diferente tamaño, no obstante, cada matriz, al ser un objeto atómico, está conformada por elementos del mismo tipo. Asimismo, los vectores pueden ser de diferente tipo y de diferente tamaño, pero, al tratarse también de objetos atómicos, cada vector está conformado por elementos del mismo tipo. Vale la pena llamar la atención sobre el tercer elemento de la lista: un vector de tamaño 1. El cuarto elemento de esta lista es un data frame, el cual tiene las mismas restricciones del que se presenta en el panel derecho: está conformado por vectores del mismo tamaño, cada uno de los cuales tiene un nombre.