En el post de hoy vamos a continuar donde nos quedamos en Introducción a R. Presentaremos las principales estructuras de datos en R que usaremos de acá en adelante, y a la vez veremos como se declaran variables y se asignan valores a estas.
Tabla de Contenidos
Introducción
Las estructuras de datos básicas de R pueden agrupar por su dimensionalidad y según si son homogéneas (todos los elementos son del mismo tipo) o heterogéneas (hay elementos de distintos tipos). En el siguiente cuadro se resumen estas:
Homogéneas | Heterogéneas | |
---|---|---|
1d | Vector Simple | Lista |
2d | Matriz | Data Frame |
nd | Array |
La mayoría de los objetos que pueden existir en R se construyen sobre las bases de estas estructuras. Como se observa, no existen objetos de dimensionalidad 0, sino que los elementos individuales como enteros o cadenas, son realmente tratados como vectores de longitud 1. Entonces, dado un objeto, la mejor forma de entender su estructura es mediante la función str
(de ‘structure’):
str(mtcars)
'data.frame': 32 obs. of 11 variables:
$ mpg : num 21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
$ cyl : num 6 6 4 6 8 6 8 4 4 6 ...
$ disp: num 160 160 108 258 360 ...
$ hp : num 110 110 93 110 175 105 245 62 95 123 ...
$ drat: num 3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
$ wt : num 2.62 2.88 2.32 3.21 3.44 ...
$ qsec: num 16.5 17 18.6 19.4 17 ...
$ vs : num 0 0 1 1 0 1 0 1 1 1 ...
$ am : num 1 1 1 0 0 0 0 0 0 0 ...
$ gear: num 4 4 4 3 3 3 3 4 4 4 ...
$ carb: num 4 4 1 1 2 1 4 2 2 4 ...
En este ejemplo observamos como está formado el objeto mtcars
. Vemos que es un objeto de tipo data frame que cuenta con 32 observaciones y 11 variables, y a su vez podemos ver las características de cada una de estas variables.
Antes de continuar, les comento sobre el paquete datasets
de R, en el cual está mtcars
. Este se carga por defecto al iniciar una sesión en RStudio, y simplemente contiene conjuntos de datasets de distintas fuentes de datos. Estos datasets son típicamente utilizados para aprender a usar R, por lo que verán que son utilizados en la mayoría de tutoriales de R que andan dando vueltas por la red. Una descripción de los datasets incluidos la podemos ver con:
library(help = "datasets")
Information on package ‘datasets’
Description:
Package: datasets
Version: 3.2.3
Priority: base
Title: The R Datasets Package
Author: R Core Team and contributors worldwide
Maintainer: R Core Team <R-core@r-project.org>
Description: Base R datasets.
License: Part of R 3.2.3
Built: R 3.2.3; ; 2015-12-10 13:04:33 UTC; windows
Index:
AirPassengers Monthly Airline Passenger Numbers 1949-1960
BJsales Sales Data with Leading Indicator
BOD Biochemical Oxygen Demand
CO2 Carbon Dioxide Uptake in Grass Plants
ChickWeight Weight versus age of chicks on different diets
DNase Elisa assay of DNase
EuStockMarkets Daily Closing Prices of Major European Stock Indices, 1991-1998
Formaldehyde Determination of Formaldehyde
...
mtcars Motor Trend Car Road Tests
...
A lo largo del curso haremos uso de varios de estos datasets para ayudarnos a aprender distintos temas.
Ahora sí, comencemos a ver estos los tipos estructuras con más detenimiento.
Vectores
Los vectores son la estructura de datos básica en R, y se los puede clasificar en vectores simples (o atómicos, del ingles atomic vector) y listas. Poseen 3 propiedades:
- Tipo →
typeof
- Longitud (es decir, cuantos elementos contienen) →
length
- Atributos (meta-datos adicionales) →
attributes
Los vectores simples y las listas se diferencian respecto del tipo de elementos que pueden contener. Mientras que en los vectores simples todo los elementos deben ser de un mismo tipo, en las listas esto no es una restricción.
De ahora en más mencionaré a los «vectores simples» simplemente como «vectores».
Vectores
Hay cuatro tipos básicos de vectores, definidos según el tipo de elementos que lo componen: logical, integer, numeric (double) y character.
Los vectores son creados comúnmente con el comando c (del ingles combine):
v_numeric <- c(1, 2.5, 4.5) v_numeric [1] 1.0 2.5 4.5 v_integer <- c(1L, 6L, 10L) # La L se la utiliza para indicar que el elemento sea un entero, ya que por defecto R lo tratará con un numeric v_integer [1] 1 6 10 v_boolean <- c(TRUE, FALSE, T, F) v_boolean [1] TRUE FALSE TRUE FALSE v_character <- c("arbol", "casa") v_character [1] "arbol" "casa"
Entonces, dado un vector x,
se puede determinar su tipo mediante typeof(x)
, o comprobar si es de un tipo particular mediante la familia de funciones is: is.character
, is.double
, is.integer
, is.logical
, o más genéricamente con is.atomic
.
typeof(v_integer) [1] "integer" is.integer(v_integer) [1] TRUE is.atomic(v_integer) [1] TRUE typeof(v_numeric) [1] "double" is.double(v_numeric) [1] TRUE is.atomic(v_numeric) [1] TRUE
Luego, como todos los elementos de un vector deben ser del mismo tipo, si intentamos combinar distintos tipos de elementos, estos serán transformados al tipo de datos más flexible. Esto se lo llama «coerción», y es un concepto importantísimo. El orden de los tipos de datos desde el menos flexible al más flexible es: logical < integer < double < character.
Por ejemplo, si combinamos una cadena y un entero, el vector resultantes será una cadena:
str(c("Maricio", TRUE, 1))
chr [1:3] "Maricio" "TRUE" "1"
A su vez, si intentamos realizar operaciones sobre elementos en los cuales dicha operación no está definida, esta operación se intentará realizar sobre elementos más flexibles, y recién si la operación no es posible con ningún tipo de elemento se nos devuelve un error. Este hecho es especialmente útil en muchos casos que iremos viendo a los largo del curso. Por ejemplo, cuando trabajamos con las funciones sum
o mean
(las cuales, como se imaginan, calculan la sumatoria y el promedio de los elementos de un vector), la coerción nos permite utilizarlas juntos a los operadores de comparación para obtener cuantos elementos, o que porcentaje, cumplen una condición:
x <- 1:10 # Definimos una secuencia del 1 al 10 x [1] 1 2 3 4 5 6 7 8 9 10 x > 7 # Los elementos que cumplen la condición se transforman en TRUE, y los que no en FALSE [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE # Aplicamos la función de suma a este vector de booleanos. Como la operación suma no está definida para este tipo de elementos, a estos se los trata como el siguiente elemento las flexible, es decir, como enteros. Al suceder esto, los elementos TRUE se los interpreta como 1, mientras que a los FALSE como 0. sum(x > 7) # Cantidad de elementos que cumplen la condición [1] 3 mean(x > 7) # Proporción de elementos que cumplen la condición [1] 0.3
Listas
Las listas son diferentes de los vectores debido a que sus elementos pueden ser de cualquier tipo, incluyendo nuevas listas. Estas se crean con el comando list
:
x <- list(1:5, "Mauricio", c(TRUE, FALSE, TRUE), c(1.5, 2.23), list(1, "a"))
str(x)
List of 5
$ : int [1:5] 1 2 3 4 5
$ : chr "Mauricio"
$ : logi [1:3] TRUE FALSE TRUE
$ : num [1:2] 1.5 2.23
$ :List of 2
..$ : num 1
En esta última lista, los elementos no poseen nombre. Para acceder a ellos utilizamos su índice. Existen otros tipo llamadas listas nombradas, en las que cada uno de sus elementos posee un nombre con el cual se los puede acceder:
x <- list(elem_1 = 1:5, elem_2 = "Mauricio", elem_3 = c(TRUE, FALSE, TRUE), elem_4 = c(1.5, 2.23), elem_5 = list(1, "a"))
str(x)
List of 5
$ elem_1: int [1:5] 1 2 3 4 5
$ elem_2: chr "Mauricio"
$ elem_3: logi [1:3] TRUE FALSE TRUE
$ elem_4: num [1:2] 1.5 2.23
$ elem_5:List of 2
..$ : num 1
..$ : chr "a"
La forma de acceder a los elementos de una estructura de datos lo veremos en el próximo post.
Las listas son uno de los tipos de datos más importantes que posee R, por lo que las usaremos mucho de acá en adelante.
Atributos
Todos los objetos pueden tener una cantidad adicional de atributos, los cuales son usados para almacenar meta-información acerca de estos. Los atributos pueden ser pensados como una lista nombrada, y por lo tanto pueden ser accedidos individualmente con attr
, o todo juntos con el comando attributes
:
y <- 1:10
attr(y, "pais") <- "Japon"
attr(y, "pais")
[1] "Japon"
attr(y, "color") <- "Purpura"
str(attributes(y))
List of 2
$ pais : chr "Japon"
$ color: chr "Purpura"
En el caso de las listas, los tres atributos más importantes son:
- Names: es un vector tipo character en que cada uno de sus elementos es el nombre de un elemento del objeto.
- Dim: es usado para convertir vectores en matrices y arrays. Lo vemos más adelantes.
- Class: es usado para implementar objetos de tipo S3. Es un tema avanzado, así que de momento no lo veremos.
Cada uno de estos atributos tiene una función propia para leer su valor o modificarlo: names
, dim
y class
.
Names
Tanto en vectores como en listas y demás estructuras, existen tres formas de definir los nombre de los elementos:
x <- c(a = 1, b = 2, c = 3) # Cuando se los crea
x <- 1:3; names(x) <- c("a", "b", "c") # Modificando un vector existente con la función names
x <- setNames(1:3, c("a", "b", "c")) # Creando una copia modificada del objeto
x
a b c
1 2 3
Factores
Un uso importante de los atributos es la creación de variables tipo factor, el cual es un tipo de dato que solo puede contener una cantidad predefinida de elementos, y es usado para almacenar variables categóricas. Los factores están caracterizados por dos atributos: el atributo class, que toma el valor factor, y el atributo levels, que define los valores que puede tomar la variable.
x <- factor(c("argentina", "brasil", "brasil", "argentina"))
x
[1] argentina brasil brasil argentina
Levels: argentina brasil
class(x)
[1] "factor"
levels(x)
[1] "argentina" "brasil"
x[2] <- "chile" # No se puede utilizar valores que no están en el atributo levels
Warning message:
In `[<-.factor`(`*tmp*`, 2, value = "chile") :
invalid factor level, NA generated
x
[1] argentina <NA> brasil argentina
Levels: argentina brasil
La utilidad de las variable factor radica en poder tratar ciertos tipos de datos, de los cuales conocemos previamente que tomarán únicamente una cantidad finita de valores, como si fuesen de alguna manera etiquetas. Ya los iremos usando a los largo del curso y quedará más claro.
Matrices and arrays
Los arrays y las matrices son creadas, o bien modificando el atributo dim de vectores, o usando los comandos matrix
y array
:
a <- c(1,2,3,4,5,6)
a
[1] 1 2 3 4 5 6
dim(a)
NULL
dim(a) <- c(2,3)
a
[,1] [,2] [,3]
[1,] 1 3 5
[2,] 2 4 6
a <- matrix(1:6, ncol = 3, nrow = 2)
a
[,1] [,2] [,3]
[1,] 1 3 5
[2,] 2 4 6
b <- array(1:12, c(2, 3, 2))
b
, , 1
[,1] [,2] [,3]
[1,] 1 3 5
[2,] 2 4 6
, , 2
[,1] [,2] [,3]
[1,] 7 9 11
[2,] 8 10 12
En el caso de estos tipos de datos, se generalizan las funciones length
y names
que vimos previamente para vectores de la siguiente forma:
length →
se generaliza connrow
yncol
para matrices, y condim
para arrays.names
→
se generaliza conrownames
ycolnames
para matrices, y condimnames
, para arrays.
length(a) [1] 6 nrow(a) [1] 2 ncol(a) [1] 3 rownames(a) <- c("A", "B") colnames(a) <- c("a", "b", "c") a a b c A 1 3 5 B 2 4 6 length(b) [1] 12 dim(b) [1] 2 3 2 dimnames(b) <- list(c("one", "two"), c("a", "b", "c"), c("A", "B")) b , , A a b c one 1 3 5 two 2 4 6 , , B a b c one 7 9 11 two 8 10 12
Luego, c
se generaliza con cbind
y rbind
para matrices, y con abind
para arrays.
Data Frames
Los data frame son la estructura de datos más importante en R. Son usados para almacenar información en forma tabular y realizar análisis sobre la misma. Similar a las listas, los data frames pueden almacenar valores de distintos tipos, y al tener dos dimensiones comparten muchas de las propiedades de las matrices. Esto significa que los data frames tienen los atributos names (o colnames), rownames, ncol y nrow. Para crear un data frame se usa la función data.frame:
df <- data.frame(x = 1:3, y = c("a", "b", "c"))
str(df)
'data.frame': 3 obs. of 2 variables:
$ x: int 1 2 3
$ y: Factor w/ 3 levels "a","b","c": 1 2 3
Por defecto, al crear un data frame, las cadenas son tratadas como tipo factor, por lo que si queremos que sean tratadas realmente como cadenas debemos indicar el argumento stringAsFactors = FALSE
de la siguiente forma:
df <- data.frame( x = 1:3, y = c("a", "b", "c"), stringsAsFactors = FALSE)
str(df)
'data.frame': 3 obs. of 2 variables:
$ x: int 1 2 3
$ y: chr "a" "b" "c"
Otra formas de crear un data frame es obteniendolo desde otra estructura como un vector, una lista o una matriz, mediante la función as.data.frame
.
Luego, si tenemos múltiples data frames y queremos unirlos, podemos hacerlo mediante la unión por fila o por columna con las funciones rbind
y cbind
respectivamente:
cbind(df, data.frame(z = 3:1))
x y z
1 1 a 3
2 2 b 2
3 3 c 1
rbind(df, data.frame(x = 10, y = "z"))
x y
1 1 a
2 2 b
3 3 c
4 10 z
Unos puntos a tener en cuenta a la hora de unir tablas es que en el caso de unirlos por columnas, ambas tablas tengan la misma cantidad de filas, y en el caso de unirlas por filas, ambas tablas tengan las mismas columnas.
Comentarios
Muy bueno, gracias
muy bueno..
Excelente material, muy didáctico.