En todos los lenguajes de programación se tienen diferentes tipos de datos primitivos; por ejemplo en Java se tiene int, long, char, byte, etc. Para Python: String, Float, Boolean, entre otros. En R se tienen 6 tipos de datos primitivos, también conocidos como “atómicos”. En R, en general todo estará compuesto de vectores o de estructuras hechas con vectores y los datos primitivos no son la excepción, ya que estos también son vectores.
TRUE, FALSE55.54
"word"5 + 7iraw(5)
Existen algunas funciones de R para saber el tipo de dato que se está usando: typeof(), class() y mode(). Las tres funciones tienen diferentes criterios para determinar que tipo de vector atómico es con el que se está tratando; typeof() identifica el tipo de dato visto desde el punto de vista de R y es la función más usada,mode() es compatible con S y class() identifica el tipo de datos visto desde una POO en R (específicamente el sistema S3).
cat(paste(class(TRUE),
class(5),
typeof(5.54),
typeof(5L),
mode("word"),
class(charToRaw("5")),
typeof(5 + 7i) ,sep = "\n"))logical
numeric
double
integer
character
raw
complex
En la siguiente tabla se muestra las posibles salidas con todas las variables de prueba.
| typeof() | class() | mode() | |
|---|---|---|---|
| TRUE | logical | logical | logical |
| 5 | double | numeric | numeric |
| 5.4 | double | numeric | numeric |
| 5L | integer | integer | numeric |
| word | character | character | character |
| charToRaw(“5”) | raw | raw | raw |
| 5 + 7i | complex | complex | complex |
Con lo anterior surge la siguiente pregunta: ¿Hay alguna diferencia entre un vector de tipo integer y un tipo double?
En general, cuando se están trabajando con datos de punto flotante, se esta considerando una aproximación del verdadero número, tanta aproximación como lo permita la memoria, por lo que pueden existir errores de precisión. De hecho, una manera más adecuada de comparar valores con punto decimal sería utilizando la función dplyr::near() en lugar de ==. Después se tratará el tema de las funciones.
Otro punto interesante es que, dependiendo del tipo de vector, se tiene un distinto representante para los valores faltantes (Missing values); para un entero se tiene NA pero con un vector tipo double se tienen NA, NaN, Inf y –Inf. Un ejemplo de esto se puede obtener al dividir distintos números entre 0.
Además de los vectores atómicos, se tienen otro tipo de vector llamado factor. Un factor es un tipo especial de dato en el cual se puede establecer una jerarquía en variables categóricas.
Supongase que se tiene una variable correspondiente al tamaño de un objeto donde los posibles valores son “Big”, “Little”, “Medium”, “Jumbo”. Intuitivamente se sabe que esta variable es categórica ordinal por lo que se desearía dejar establecido esta jerarquía en el vector que contenga esta información.
[1] Big Little Medium Jumbo
Levels: Big Jumbo Little Medium
Como se puede observar, se tiene un atributo (más adelante se verán los atributos de un vector) indicando los niveles del factor. Los niveles se establecieron en orden alfabético pero sin ningún orden realmente. Para establecer los niveles se puede dar este atributo directamente desde la creación o modificarlo después de la creación del factor.
[1] Big Little Medium Jumbo
Levels: Little Medium Big Jumbo
De manera análoga, se puede dar el orden a los niveles.
factor(c("Big", "Little", "Medium","Jumbo"), levels = c("Little", "Medium", "Big", "Jumbo"), ordered = TRUE)[1] Big Little Medium Jumbo
Levels: Little < Medium < Big < Jumbo
class() y typeof() con un factor?rev() en un factor?En la siguiente liga se puede ver un poco más de información sobre este tipo de datos.
En la sección 15 del libro R para ciencia de Datos (traducción al español de R for Data Science de Wickham y Grolemund) se mencionan algunas fuentes para aprender más acerca de los factores, el cual es un tema bastante útil de dominar ya que existe una librería llamada forcats con la que se puede trabajar de manera adecuada estos tipos de datos que son de gran ayuda al momento de hacer algún tipo de análisis descriptivo, graficación y creación de modelos. Aquí se enlistan los artículos correspondientes.
Otro tipo de dato con el que se cuenta es aquel que no está definido, es decir, como en Java, un objeto nulo: NULL. En las siguientes líneas de código se muestra como hacer comparaciones con este tipo de dato.
[1] TRUE
logical(0)
logical(0)
Otro punto muy común al analizar datos es encontrarse con información faltante. Aquí, como en otros lenguajes, los valores perdidos son distintos a un objeto no definido; ya que un valor perdido bien puede indicar un error humano al momento del registro de la información pero con la característica de que se puede inferir un posible valor para ese registro faltante. Un NA es una constante que puede ser asignada a cualquier tipo de vector a excepción de un vector de tipo raw.
Al tener esta ausencia de datos se desea identificarlos de manera rápida y esto puede ser complicado en una base de datos con cientos o miles de registros; al igual que si se tiene el conocimiento de la existencia de estos, se desea que no afecten en operaciones o aplicaciones de funciones en el resto de los datos. Véase lo siguiente
[1] NA
Es decir, que cuando se tiene algún valor perdido, la operación suma (más adelante se verá a detalle las operaciones en este lenguaje) queda determinada con NA. ¿Sucede lo mismo con otro tipo de operaciones?
Como se había mencionado, se desea que si se tiene conocimiento de la existencia de al menos un NA, este no afecte a las operaciones
[1] 10
Técnicamente, existen cinco missing values para cada uno de los vectores atómicos (a excepción de raw claro): NA, NA_interger_, NA_real_, NA_character_ y NA_complex_, pero eso solo es de manera interna.
Como en cualquier lenguaje de programación, existen identificadores para guardar o asignar el valor de los tipos de datos primitivos o resultados y así utilizarlos posteriormente. En el caso de R, la asociación de un nombre con un cierto valor se puede realizar mediante la función <-() y gracias a que R es un lenguaje interpretado, no es necesario declarar el tipo de dato antes de darle una instanciación. Por ejemplo, véase que en las siguientes líneas de código se asignan a algunos vectores atómicos un identificador.
Y en este caso, basta con llamar a la variable por su nombre para poder usarla.
[1] 5+7i
[1] 10
[1] NA
También se puede asignar un valor a una variable con el operador = pero esto no es nada recomendable ya que, por buenas prácticas, en este lenguaje, se acostumbra a usar el igual para dar valores a parámetros.
¿Qué pasará cuando se ejecute x <- NULL? ¿Qué pasará si antes x tenía otro tipo de dato?
Algo que hay que entender sobre R es que este lenguaje esta basado en el paradigma de programación orientada a objetos, aunque también tiene ciertas características de un lenguaje funcional. Existen muchos sistemas para hacer todo lo que se tiene conocimiento de POO en R, como lo es el encapsulamiento, herencia y polimorfismo. Aquí se evitará en la mayor medida posible el tema pero se harán menciones en la marcha de esto.
Para el caso de los vectores, se tienen ciertos atributos fijos desde su creación: la longitud y el tipo de dato. Más adelante, en las estructuras de datos, se detallará sobre este tema; por el momento, véase lo siguiente
[1] "longitud: 5.Tipo de dato: double"
Aclaremos ciertas cosas:
c() y agregando datos del mismo tipo dentro de él separados con una coma.
v_1 es un vector de un cierto tipo y se desea hacer otro vector v_2 del mismo tipo que contenga el contenido de v_1 más otros elementos ¿Se puede hacer v_2 <- c(v_1,...)?vector("type", length).
c() este puede ir variando.vector() es la ideal.double(length), interger(length), character(length), etc.:, por ejemplo: 1:5.seq_along() se puede crear también una secuencia de números; esta la podemos usar en lugar de 1:length(v) con v un vector.
y <- vector("double", 0); seq_along(y); 1:length(y).seq(): seq(1,10,by=2).Ya que R es un lenguaje de código libre y orientado a objetos, podemos ver el contenido de sus funciones. Para las funciones que están escritas en R podemos ejecutar directamente el nombre de la función omitiendo los paréntesis.
function (length = 0L)
.Internal(vector("integer", length))
<bytecode: 0x7fe28016dcc0>
<environment: namespace:base>
¿Cómo están hechos los constructores para double, character, boolean y raw? ¿Cómo están hechos los constructores para un complejo y un factor?
Muchas veces es útil comparar distintos tipos de variables o verificar si estas son de algún tipo en específico. Para ello existen ciertas funciones como is.character(), is.na(), is.double(), etc. En general se tiene este tipo de funciones para cualquier objeto, incluso se tiene is.function(). En el caso de los posibles valores perdidos de un double, hay que evita usar == para verificar los posibles valores como Inf y –Inf. En su lugar usar las funciones is.finite(), is.infinite(), e is.nan().
Es importante que existan operaciones en los lenguajes de programación, y en este caso se tienen las más comunes y algunas especiales.
5 + 7; sum(5,7).5 - 7.5*7; prod(5,7).5 / 7.5 %% 75 %/% 7.5 ^ 7; 5 ** 7.%*%Al igual que los clásicos operadores booleanos o relacionales
TRUE & TRUE.TRUE & FALSE.TRUE | TRUE.TRUE | FALSE.!TRUE.TRUE == FALSE.TRUE != FALSE.Ahora, lo poderoso de R es poder trabajar de manera vectorial, por lo que las operaciones actúan también de manera vectorial.
[1] 3 5 7 9 11
[1] 2 6 12 20 30
1:5 ^ 1:5(1:6) ^ c(2,3)s <- seq(1,10, by = 2); s + 7sqrt(s)1:10 + 1:51:10 * 1:51:10 * 10Lo que sucedió arriba se conoce como reciclaje, lo cual tiene el efecto de repetir de manera ordenada los vectores más pequeños para que ambos vectores que se están operando tengan la misma longitud, esto resulta muy útil cuando se quiere repetir ciertos parámetros en una operación cada \(n\) cantidad de pasos. Por ejemplo, si se desea obtener el cuadrado de todos los pares de números del \(1\) al \(100\), no es necesario hacer algún proceso usando condicionales y algún bucle, sino sólo operar de manera vectorial y usar reciclaje.
[1] 1 4 3 16 5 36 7 64 9 100 11 144
[13] 13 196 15 256 17 324 19 400 21 484 23 576
[25] 25 676 27 784 29 900 31 1024 33 1156 35 1296
[37] 37 1444 39 1600 41 1764 43 1936 45 2116 47 2304
[49] 49 2500 51 2704 53 2916 55 3136 57 3364 59 3600
[61] 61 3844 63 4096 65 4356 67 4624 69 4900 71 5184
[73] 73 5476 75 5776 77 6084 79 6400 81 6724 83 7056
[85] 85 7396 87 7744 89 8100 91 8464 93 8836 95 9216
[97] 97 9604 99 10000
Hay que tener cuidado en algunos casos, por ejemplo 1:4^c(2,4).
La mayoría de las bases datos contienen más de una variable de acuerdo a una cantidad de observaciones y no necesariamente los datos tiene que ser del mismo tipo de dato. En caso de que se deseara ejecutar algún algoritmo para la manipulación de estos, bien se podría tomar cada variable como un vector y ser ingeniosos y cuidadosos para relacionar todos esos vectores y obtener resultados útiles. Esta manera de trabajar no es eficiente ni la más recomendable, ya que se dedicará una mayor cantidad de tiempo a la programación y una menor a la obtención de resultados.
Existen muchas estructuras de datos y aunque todas pueden ser adaptadas a un lenguaje de programación, cada lenguaje tiene estructuras básicas para trabajar. Java tiene los arreglos y otro tipo de estructuras en ciertas librerías como listas, pilas y colas. Python tiene diccionarios y mediante la librería pandas utiliza marcos de datos (data frames). En el caso de R también se tienen esta última y otras más. La siguiente imagen resume las estructuras de datos en R.
Fuente: Hands-On Programming with R.
Ya se ha tratado esta estructura pero faltan algunas cosas por ver.
c(TRUE, 1,2,5,9.4).Lo que sucede aquí es llamado coercion. En otros lenguajes de programación esto puede ser entendido como promoción y casteo, pero en este caso no es necesario hacer una declaración explicita del tipo de dato al que se desea convertir. La coerción se aplicará en cualquier estructura que use vectores.
Fuente: Hands-On Programming with R.
En la imagen anterior quedan claras las reglas que se tienen al hacer coerción. Por ejemplo, ¿Qué tipo de vector resultará en las siguientes instrucciones?
Así como se tienen funciones para verificar si un vector es de algún tipo, también es fácil recordar las funciones para cambiar un tipo de dato a otro; en este caso en lugar de comenzar con el prefijo is., se usará as. dando así las funciones as.double(), as.character, etc. Y esto no se limita a los vectores atómicos, ya que ciertas estructuras se pueden convertir a otras, aunque hay que tener en cuenta que ciertos atributos pueden eliminarse o que en ciertas ocasiones no será posible hacer la conversión.
Finalmente, al tener una estructura que contiene datos, es de esperar una sintaxis para obtener los elementos que la componen. En el caso de los vectores, la obtención de los elementos interiores se puede hacer mediante de diferentes maneras.
(1:10)[[3]]. (1:10)[3][[ o [ ? Hint: ?'[['.(1:10)[seq(1, 10, by = 2)]; (1:10)[c(4,5)].(1:10)[(1:10)<4].Un atributo que tienen los vectores es llamado names, el cual otorga un nombre a cada uno de los elementos interiores del vector.
vec_altura <- c(1.50, 1.60, 1.72, 1.55)
names(vec_altura) <- c("José", "Raúl", "Cecilia", "Camila")
vec_altura[["José"]][1] 1.5
Por último, véase la aplicación tablas de búsqueda (Character subsetting) que otorga el subsetting en los vectores.
datos_frutas <- c("M", "Ma", "S", "M", "S", "Ma")
frutas <- c(M = "Manzana", Ma = "Mango", S = "Sandía")
frutas[datos_frutas] M Ma S M S Ma
"Manzana" "Mango" "Sandía" "Manzana" "Sandía" "Mango"
[1] "Manzana" "Mango" "Sandía" "Manzana" "Sandía" "Mango"
Las listas son una estructura de datos muy útil por la forma en la que esta codificada, ya que esta nos ayuda a tener vectores en su interior y de diferentes tipos. Comúnmente se le conoce a las listas como vectores recursivos, ya que una lista puede contener listas en su interior (las listas son otro tipo de objeto así que no hay restricciones para hacer listas de listas). Como bien indica una de las imágenes anteriores, las listas tienen una dimensión, lo cual no se debe confundir con su longitud, la cual es la cantidad de elementos que esta contiene.
list() y el tipo es list.list("nombre" = datos)[[1]]
[1] 1 5
[[2]]
[1] 1 2 3 4 5 6 7 8 9 10
[[1]]
[1] 1 2
[[2]]
[1] 1 2 3 4 5 6 7 8 9 10
[[3]]
[1] "a" "b" "c" "d" "e"
is.list() y as.list() como cualquier otra estructura de datos en R.[[1]]
[[1]][[1]]
[1] "a"
[[1]][[2]]
[1] "b"
[[1]][[3]]
[1] "c"
$numeros
$numeros[[1]]
[1] 1 2 3 4 5 6 7 8 9 10
$numeros[[2]]
[1] 1 2 3 4 5
$numeros[[3]]
[1] 10 11 12 13 14 15 16 17 18 19 20
$numeros[[4]]
[1] -3 -4 -5
Como las listas y los vectores son de una dimensión, estas estructuras tiene algunas características en común; ambas tienen una longitud y puede determinarse su tipo (typeof(vector(), typeof(list())). Además en ambas se les puede asignar nombres y otros atributos que se deseen para dar meta data a las estructuras. Para dar un nuevo atributo se puede usar la función attr().
lista_1 <- list("numeros" = c(1,2,3), "letras" = c("a","b","c"), "ambos" = c(1,2,"3", "4"))
attr(lista_1, "meta") <- "Más datos"
attr(vec_altura, "atributo_extra") <- "Más datos"
attributes(lista_1)$names
[1] "numeros" "letras" "ambos"
$meta
[1] "Más datos"
$names
[1] "José" "Raúl" "Cecilia" "Camila"
$atributo_extra
[1] "Más datos"
Al usar la función structure(), no es necesario usar la función attr() para agregar un nuevo atributo.
[1] 2 4 6 8 10 12 14 16 18 20
attr(,"paridad")
[1] "Pares"
structure(seq(2,20, by = 2), comment = "Pares")?En general, para ver la composición de una estructura, La función str() es de grán utilidad ya que dará de manera resumida esta composición.
Named num [1:4] 1.5 1.6 1.72 1.55
- attr(*, "names")= chr [1:4] "José" "Raúl" "Cecilia" "Camila"
- attr(*, "atributo_extra")= chr "Más datos"
List of 3
$ numeros: num [1:3] 1 2 3
$ letras : chr [1:3] "a" "b" "c"
$ ambos : chr [1:4] "1" "2" "3" "4"
- attr(*, "meta")= chr "Más datos"
Al poder acceder a los elementos de un vector o una lista, es sencillo cambiar el contenido de estos.
$numeros
[1] 2 3 4
$letras
[1] "a" "b" "c"
$ambos
[1] "1" "2" "3" "4"
attr(,"meta")
[1] "Más datos"
José Raúl Cecilia Camila
1.50 1.60 1.72 1.50
attr(,"atributo_extra")
[1] "Más datos"
lista_1[["numeros"]] o lista_1$numeros.lista[[1]]lista[[1]] y lista[1]?List of 2
$ numeros: int [1:3] 2 3 4
$ letras : chr [1:3] "a" "b" "c"
- attr(*, "meta")= chr "Más datos"
Lo anterior es un ejemplo de como borrar elementos de una lista.
v <- 1:10; v[5] <- "5".as.vector(lista_1) sigue siendo una lista?unlist()?Todos aquellos que ya hayan tenido algún acercamiento con algún lenguaje de programación como Java, C, C++, Python, etc. conocen lo importante que pueden ser los arreglos, los cuales son una colección de elementos con ciertas posiciones. Además del simple hecho de poder almacenar elementos en un solo objeto, es de gran importancia lograr manejar matrices para realizar distintas operaciones y obtener ciertos resultados para diferentes problemas del ámbito científico.
Como bien se indica en el diagrama donde se presentan las diferentes estructuras de datos que tenemos disponibles en R, las matrices y los arreglos solo aceptan un sólo tipo de dato como los vectores. De hecho, véase que internamente, los constructores de una matriz y un arreglo (matrix() y array()) tienen que convertir, en caso de que sea necesario, el input a un vector.
#matrix():
.
.
if (is.object(data) || !is.atomic(data))
data <- as.vector(data)
.
.
}
#array():
.
.
data <- as.vector(data)
.
.
}
Por lo que una matriz y un array los podemos considerar vectores, solo que con diferentes dimensiones. Solo para aclarar, al momento de ingresar listas o matrices a estos constructores, todo será llevado a vectores y se realizará, en caso de ser necesario, coerción y reciclaje.
matrix(list(1,2,3,4))?Considérese los siguientes puntos cuando se desea construir una matriz o cuando se trabaje con ellas.
Se tienen las correspondientes funciones is.matrix() y as.matrix().
El constructor matrix() en su primer argumento solicita los datos los cuales pueden ser cualquier vector.
[,1]
[1,] 1
[2,] 2
[3,] 3
[4,] 4
[5,] 5
[6,] 6
[7,] 7
[8,] 8
[9,] 9
[10,] 10
matrix() dispone de sus elementos en forma columnar. [,1] [,2]
[1,] 1 6
[2,] 2 7
[3,] 3 8
[4,] 4 9
[5,] 5 10
[,1] [,2]
[1,] 1 2
[2,] 3 4
[3,] 5 6
[4,] 7 8
[5,] 9 10
[1] 1
[1] 2 4 6 8 10
rownames() y colnames().m <- matrix(1:10, nrow = 5, ncol = 2, byrow = TRUE)
rownames(m) <- paste("Row", 1:5)
colnames(m) <- paste("Column", 1:2)
m Column 1 Column 2
Row 1 1 2
Row 2 3 4
Row 3 5 6
Row 4 7 8
Row 5 9 10
[1] 10
[1] 1
length() en vectores, se divide en nrow() y ncol(); además de la función dim() para obtener ambos resultadosnrow: 5
ncol: 2
dim: c( 5 2 )
dim() se puede usar para modificar la estructura de una matriz; aunque esto puede ocasionar una pérdida de atributos. [,1] [,2] [,3] [,4] [,5]
[1,] 1 5 9 4 8
[2,] 3 7 2 6 10
[,1] [,2]
[1,] 1 6
[2,] 2 7
[3,] 3 8
[4,] 4 9
[5,] 5 10
cbin() y rbind() se pueden unir matrices por columna o por renglón. first second
[1,] 1 2
[2,] 2 3
[3,] 3 4
[4,] 4 5
[5,] 5 6
[6,] 6 7
[7,] 7 8
[8,] 8 9
[9,] 9 10
[10,] 10 11
Las matrices son de uso recurrente en el álgebra lineal, por lo que se desearía realizar algunas operaciones básicas entre ellas; R proporciona estas así como algunas funciones importantes. Supongamos que \(\mathcal{A}\) y \(\mathcal{B}\) son matrices definidas en R.
+\(\mathcal{B}\).-\(\mathcal{B}\).*\(\mathcal{A}\).%*%\(\mathcal{B}\).t().diag(). Devuelve la diagonal de una matriz o crea una matriz diagonal.det().solve(). solve() también resuelve un sistema de ecuaciones dado.var().eigen().Hay que tener en cuenta la teoría sobre estas operaciones o funciones ya que será relevante las dimensiones de las matrices; por ejemplo si las matrices son cuadradas para obtener el determinante o la inversa o si las dimensiones de \(\mathcal{A}\) y \(\mathcal{B}\) son adecuadas para aplicar, por ejemplo, la multiplicación entre ellas. Para ver algunas otras funciones que se pueden aplicar en las matrices véase los enlaces 1 y 2.
Finalmente, cuando se desea crear un arreglo con un número mayor de dimensiones, se puede utilizar el constructor array() donde se pueden indicar las dimensiones como parámetro, por ejemplo array(1:16, c(2,2,4)). Las operaciones básicas funcionan correctamente en este tipo de estructura pero la mayoría de las funciones, al menos todas las mencionadas, no trabajan bien al tratar con arreglos ya que las funciones anteriores solicitan arreglos de dos dimensiones.
Al trabajar con matrices se debe estar consciente de que cada uno de sus elementos es de un solo tipo, lo cual puede resultar problemático al tratar con una base de datos donde se tengan datos numéricos y categóricos o incluso cuando se desee agregar una columna de otro tipo de objetos como lo son las gráficas. Aquella estructura de datos que dos dimensiones que acepta diferentes tipos de datos por columna se le llama Data Frame. La mayoría de las bases de datos se trabajan con un data frame y por convención a las columnas se les llama variables y a los renglones observaciones.
La construcción de un data frame se puede hacer mediante el constructor por defecto data.frame() donde el contenido de este es similar al de una lista, ya sea dando los nombres desde la creación o posteriormente. Por ejemplo:
Hay que notar lo siguiente del anterior ejemplo.
stringsAsFactors del constructor.Estos puntos son considerados en cuenta cuando se desea convertir algún otro objeto en data frame con la función as.data.frame().
rbind() y cbind() entre data frames. En general, estas funciones deben usarse si alguno de los objetos implicados es un data frame, en caso de que alguno no lo sea y ambos sean vectores, las funciones crearán primero una matriz aplicando coerción y dando problemas en la construcción del data frame.El constructor de una data frame, data.frame(), es más robusto que el constructor de una matriz, matrix(), ya que aquí se deben mantener las estructuras en las columnas; por ejemplo una columna de listas no deben convertirse en una columna de números como en el caso de una matriz para una sola lista. Si se desea tener una columna de listas y no distribuir los elementos de la lista en las columnas de data frame, se puede utilizar la función I() la cual ayuda a tratar un objeto como el mismo y así no aplicarle algún tipo de transformación.
(df <- data.frame(numbers = 1:3, listas= I(list(num_list = 1:10,letras = letters,listas = list(1,2,3,4)))))Al igual que las listas, el acceso puede ser mediante los nombres y posición además de vectores booleanos, agregando el acceso que se aplica en las matrices.
[1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s"
[20] "t" "u" "v" "w" "x" "y" "z"
[1] 1 2 3
$num_list
[1] 1 2 3 4 5 6 7 8 9 10
[1] 1 2 3 4 5 6 7 8 9 10
$letras
[1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s"
[20] "t" "u" "v" "w" "x" "y" "z"
as.matrix() con el anterior data frame?dim()?Todo el conocimiento ya obtenido no sería útil a largo plazo si no se tiene alguna manera de controlar el comportamiento del código bajo ciertas circunstancias, ya sea permitiendo ciertos bloques de código, repitiendo o ejecutando una cantidad determinada o indeterminada de veces un cierto proceso o aplicar ciertas transformaciones a los resultados. Por todas estas razones comenzaremos con la estructura de control más básica.
La estructura if() permite la ejecución de un cierto bloque de código si el parámetro booleano que recibe tiene por valor TRUE, lo cual indicaría que la condición necesaria para la ejecución del bloque es válida. Recordemos que un valor TRUE, y en general un booleano, se puede obtener mediante diferentes expresiones usando operadores lógicos.
Ahora, suponiendo que se desea tratar el simple problema de determinar si un usuario, de acuerdo a su edad, puede ver cierto contenido. Se puede actuar de la siguiente manera
if(edad>=18) {
Permitir_contenido
}
if(edad == 17){
No_permitir_contenido
}
if(edad == 16){
No_permitir_contenido
}
.
.
.
if(edad == 0){
No_permitir_contenido
}O bien de la siguiente manera
En cualquiera de los dos casos es necesario tratar el complemento con otra sentencia if() lo cual no es recomendable cuando no se tenga certeza de todos los casos contrarios a la condición en el primer if. Por lo cual, para tomar el complemento, se tiene la sentencia if-else; donde si sucede la condición, se ejecuta el código dentro de los delimitadores del if y en caso contrario (else) se ejecuta lo correspondiente para el complemento de la condición.
Existe en R la función ifelse() la cual permite trabajar de manera vectorial. Véase el siguiente ejemplo
#Se crea un vector con 15 edades de personas de manera "aleatoria"
(edades <- sample(1:90, replace = T, size = 15)) [1] 51 28 68 72 9 35 14 3 61 45 80 63 46 63 61
[1] "Mayor de edad" "Mayor de edad" "Mayor de edad" "Mayor de edad"
[5] "Menor de edad" "Mayor de edad" "Menor de edad" "Menor de edad"
[9] "Mayor de edad" "Mayor de edad" "Mayor de edad" "Mayor de edad"
[13] "Mayor de edad" "Mayor de edad" "Mayor de edad"
Es decir, que en cada entrada del vector evalúa la expresión dada y en caso de ser cierta la condición, se devuelve, en este caso, el caracter “Menor de edad” y en caso contrario “Mayor de edad”. Véase que la función ifelse() regresa un vector.
Cuando un programador ve que un proceso se debe repetir una cantidad de veces, automáticamente piensa en un bucle. En el caso de R se tienen los ciclos for(), while() y repeat().
Recordando que en Java los primeros dos bucles tenían una estructura similar a la siguiente:
for(int i = 0; i<n; i++){
Ejecución del código n veces
}
while(boolean){
Ejecución del código hasta que el booleano sea false
}En el caso de R se tendrá una estructura un poco diferente ya que se iterará directamente sobre un objeto y no usando una variable auxiliar que evalué una expresión para obtener un booleano y determine si el ciclo termino. En el caso del ciclo while se tiene la misma sintaxis.
Véase los siguientes ejemplos
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
Se esta usando directamente cada elemento dentro del vector para ejecutar un cierto bloque de código. Hay que aclarar que no es necesario usar el elemento con el que se está iterando aunque es común hacerlo.
[1] "Letra a"
[1] "Letra b"
[1] "Letra c"
[1] "Letra d"
[1] "Letra e"
En el anterior código se esta usando otro vector, el cual es no numérico, dando un mejor ejemplo de que el iterador no esta dependiendo de valores numéricos.
edad <- c(20,24,41,17,20)
n_cliente <- 1
control_parental <- TRUE
while(control_parental){
if(edad[n_cliente]>=18){
print("Apto para la película")
n_cliente <- n_cliente+1
}else{
print("Menor de edad")
control_parental <- F
}
}[1] "Apto para la película"
[1] "Apto para la película"
[1] "Apto para la película"
[1] "Menor de edad"
Aquí se están usando expresiones de control para determinar la salida del bucle.
Hay que recordar que en un ciclo while se corre el peligro de entrar en un ciclo infinito, el cual sería lo deseado en algunos casos. Un ciclo que se trata con un bucle tipo for siempre puede ser tratado como uno tipo while pero no siempre el caso contrario. Y que la elección entre un ciclo while y uno for depende de si se conoce el número de veces que se ejecutará un bloque o no.
switch-case visto en Java?Para estos dos ciclos se puede hacer uso de las sentencias break y next, las cuales permiten la interrupción de un ciclo y la exclusión de alguna iteración.
[1] "a"
[1] "b"
[1] "c"
[1] "d"
[1] "e"
Cuando se ejecuta repeat el ciclo termina sin importar la existencia de más iteraciones sobre el objeto.
[1] "a"
[1] "b"
[1] "c"
[1] "y"
[1] "z"
Para el caso de next, véase que se saltó ciertas iteraciones más no termino el ciclo.
Finalmente, R proporciona la estructura break, en la cual puede encontrarse cierta similitud con un ciclo while o do-while.
Este bucle se detiene cuando se encuentra a la sentencia break.
n_saludos <- 5
repeat{
print(rep("Hola", n_saludos))
#Se detiene el repeat cuando n_saludos sea igual a 1
if(n_saludos==1){
break
}
n_saludos <- n_saludos-1
}[1] "Hola" "Hola" "Hola" "Hola" "Hola"
[1] "Hola" "Hola" "Hola" "Hola"
[1] "Hola" "Hola" "Hola"
[1] "Hola" "Hola"
[1] "Hola"
Existe un “problema” con los ciclos en R debido a las configuraciones internas para el almacenamiento de objetos. Por el momento es suficiente saber que trabajar de manera vectorial o aplicar funciones que están en R de manera predeterminada es una mejor y más rápida forma de trabajar. Más adelante se verá en que casos los ciclos pueden ser “lentos” además de otros problemas que pueden surgir, por ejemplo en la recursión, y cuando será la mejor opción usar un ciclo que alguna función que trabaje de manera vectorial.
Como ya se había mencionado, R es un lenguaje de programación orientado a objetos aunque también tiene ciertas características de un lenguaje funcional ya que las funciones en R pueden considerarse funciones de primera clase; es decir que pueden ser asignadas a variables, ser almacenadas en ciertas estructuras como las listas, servir como parámetros de otras funciones y ser el retorno de otras funciones. Aunque estas no pueden ser consideradas, como bien dice Handley Wickham en su libro RAdvance, puras; es decir funciones de orden superior ya que, para que una función sea considerada de orden superior, de acuerdo a un input dado, se debe tener una única salida (inyectividad) lo cual no sucede con muchas funciones en R como sample(), runif(), print() y hasta en <-().
Sin importar que las funciones no sean de orden superior, lo cual otorga cierta flexibilidad, es indudable la importancia de crear funciones para optimizar el trabajo, dar estructura, limpieza e incluso claridad; ya que al usar una función, el resultado será algo esperado sin importar su comportamiento interno; en cambio al visualizar un ciclo, este puede no quedar claro en su funcionamiento.
La creación de funciones es sencilla
return() aunque si no se utiliza esta función, la última línea de código en la función es considerada como el retorno de la función. El retorno puede ser cualquier tipo de objeto e incluso pueden devolver funciones. En este último caso, a estas funciones se les llama function factory.Los parámetros de una función pueden tener valores por defecto, los cuales pueden ser reescritos cuando se den explícitamente otros valores.
[1] 10
[1] 214
Algo interesante de las funciones en R, al igual que en Python, es que se pueden dar una cantidad indeterminada de parámetros si así se indica en la función con el argumento .... Véase el siguiente ejemplo.
Es decir, que se está considerando ... como un parámetro más de la función aunque nótese que no es necesario agregar más de dos variables en la función.
[1] 13
[1] 223
El tema de las funciones es algo extenso, por lo que se dará de manera resumida el contenido necesario para entender gran parte del funcionamiento de estas en R. Una de las mejores fuentes de información la pueden encontrar en el libro Advance R, específicamente en los capítulos 6 y 7; para explorar más sobre este tema se puede consultar la segunda parte de la misma referencia. Finalmente, se verá como usar vectorización, una de las fuertes habilidades de R, y por último una serie de funciones que podrían ser de gran utilidad en ciertos temas.
Una función que crea el usuario se compone de tres partes: los formales, los cuales son los argumentos o los parámetros de una función y que pueden obtenerse con la función formals(), el cuerpo, es decir el código o las rutinas internas de las funciones que se pueden obtener con la función body(), y un ambiente, el cual se puede obtener con la función environment() y que básicamente es un lugar donde esta almacenada la información necesaria para el correcto uso de las funciones; más adelante se hablará un poco más de este importante concepto.
Como ya se mencionó, este lenguaje toma ciertas características de un lenguaje funcional, por lo que al tratar a las funciones como objetos es de esperarse la incorporación de atributos a estas. Uno de estos atributos es el llamado srcref (source reference), la cual imprime mejor el cuerpo de alguna función.
myStrangeFunction? El output es el siguientefunction(a = 1,b = 4,c,...) sum(prod(a,b), c, ...)
R contiene ciertas funciones que son añadidas al momento de iniciar su interfaz, las cuales se han agregado a las distintas versiones del lenguaje y a medida que R ha evolucionado se han optimizado estas funciones internas dando un buen rendimiento para su uso cotidiano. Estas funciones son llamadas primitivas y ya se han visto algunas de estas: sum(), prod(), [(), sqrt(), etc. Algunas de las funciones primitivas están implementadas y existen en el lenguaje C, lo cual da garantía de un buen rendimiento. Los tres componentes que se mencionaron antes no los tienen este tipo de funciones y así es posible verificar si una función primitiva existe principalmente en C.
function (..., na.rm = FALSE) .Primitive("sum")
Para utilizar, o mejor dicho llamar, a una función, basta con escribir su nombre, colocar los argumentos separados por coma dentro de unos paréntesis y ¡listo! Aunque se ha mencionado que + también es una función: '+'(), por lo que hay que discernir las diferentes formas de llamar a una función.
myFunction(arguments).a+b.colnames(df) <- names.[[, if, for, etc.Véase que las propias estructuras de control y los bucles también son funciones y R permite modificar todo aquello que sea función, aunque todas las formas especiales son funciones primitivas. No es recomendable sobre escribir alguna función que ya se tenga preestablecida en R o en alguna librería, pero siempre se tiene la libertad de hacerlo en caso de que se tenga el control y la consciencia suficiente de las consecuencias.
Solo para aclarar, véase los siguientes puntos.
myFunction(a,b,c)
`+`(a,b)
`colnames<-`(df, names)
for(name in names) print(name)
`for`(name, names, print(name))+ es una función, es posible crear operadores usando % al inicio y al final del operador. Todos los operadores de R se pueden encontrar ejecutando ?Syntax.[1] 8
Algunas funciones útiles de este estilo son %%, %*%, %/%, %in%, %o% y %x%. ¿Qué hacen cada una de ellas?
Es análogo la sintaxis para crear funciones de asignación aunque es necesario tener el formal value.
`indexNames<-` <- function(a, value) {
names(a) <- as.character(seq_along(a)+value)
a
}
x <- 1:20
indexNames(x) <- 3
x 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Ahora, véase la siguiente función
[1] "Hello!"
Sea cual sea el objeto que se de como parámetro al llamar la función, se devolverá el mismo resultado. Esto es posible porque las funciones en R trabajan de manera “perezosa” con sus argumentos, a esto se le llama lazy evaluation. Básicamente, si no es llamado en algún momento un argumento de la función, este no es utilizado y solo cuando se llame al argumento, hasta ese momento se hará el acceso a él.
Esto es gracias a la técnica de evaluación lazy evaluation que utiliza R al usar una estructura de datos llamada promise donde, básicamente las variables son evaluadas mediante un ambiente hasta que una expresión las utilice y este valor es guardado (cached), por ejemplo
y <- 7
lazilyEvaluated <- function(x, lazy_var = y*a){
print("Hi!")
y <- 50
a <- 3
print(lazy_var)
x*2
}
doubleVector <- function(x) c(x,x)
doubleVector(lazilyEvaluated(y))[1] "Hi!"
[1] 150
[1] 14 14
y tiene asociado un valor distinto y no tiene consciencia de la variable con el nombre y fuera de él.y como argumento de la función, a esta se le da el nombre de x pero se le asocia el mismo valor de y.lazy_var es llamado hasta print(lazy_var), donde antes se asignaron valores a las variables y y a, dando así una correcta evaluación a lazy_var; es decir que hasta ese momento fue necesario evaluar lazy_var por lo cual no se genera algún problema."Hi!" y el valor de lazy_var? Por el valor calculado y guardado en la promise.Otra forma de llamar a una función es usando la función do.call(), la cual permite agregar una lista de parámetros.
[1] 40
Finalmente, el tema de los ambientes (environments) de una función en R es un poco extenso por lo que hay que entender sólo algunas cosas.
var <- c(1,2)). Para crear un ambiente se utiliza la función new.env().ls() permite ver todos los elementos de un ambiente. Se utiliza la función assign() para agregar un nuevo elemento a un ambiente dado y para obtener alguno de los elementos se puede acceder con el operando $, como en una lista o con la función get() (si no se encuentra la variable con el nombre dado, get buscará en los ambientes padres). Para preguntar si existe alguna variale con un nombre dado en un ambiente se utiliza la función exists() y para eliminar una o varias referencias de variables en un ambiente se usa la función rm().[1] "new_variable"
[1] 1 2 3 4 5
[1] TRUE
character(0)
parent.env() dando como parámetro un ambiente. Véase la siguiente imagen
Fuente: Advance R.
El ambiente donde regularmente se trabaja es el R_Global_Environment también llamado workspace. Cada vez que se agrega una nueva librería, el environment para esta (package environment) es colocado entre el ambiente de la última librería cargada y el ambiente global. Para ver todos los ambientes activos de este tipo se puede usar la función search().
Recordando un poco de lo aprendido en programación orientada a objetos, existe la analogía de que las cerraduras son una manera de simular el encapsulamiento en las funciones, y ya que las funciones son de primera clase en R, son tratadas también como objetos.
Para más información acerca de los temas anteriores consúltese Advance R. Second Edition y Advance R. First Edition.
Desde las estructuras de datos es fácil ver la importancia que tienen los vectores en R y la facilidad de usar funciones y operaciones en ellos, logrando tener un buen rendimiento, evitando ciclos y ganando comprensión en los resultados esperados.
La Vectorización significa aplicar funciones optimizadas, en este caso escritas en C, para llegar a una solución del problema a tratar. Esto da soluciones más simples tratando a los vectores como unidad en lugar de pensar en una solución donde se tiene que tratar cada entrada en el vector. En este último caso se tendría que hacer algún tipo de ciclo, en el caso de la vectorización los ciclos son hechos directamente en C, los cuales son más rápidos y con menor sobre carga.
Ya se han tratado algunas funciones vectorizadas, como lo son sum(), prod() y sqrt(), las cuales todas sirven con un vector.
sum(list(1,2,3,4,4,5)), sum(matrix(1,ncol = 3,nrow = 6)) y sum(data.frame(x = 1:20, y = 2:21))?Dado lo anterior, ¿Se puede obtener la suma por renglones o columnas en una matriz? ¿Se puede hacer lo mismo en un data frame o una lista? ¿Se puede aplicar una función particular que trabaje de manera vectorial por renglones o columnas en un data frame? La respuesta a todo esto es ¡Sí! y se puede realizar todo con la familia de funciones apply.
La familia apply es un conjunto de funciones que ayudan en la aplicación vectorial de funciones sobre ciertas estructuras de datos. Para ver a detalle el funcionamiento de cada una de estas y otras funciones, puede consultarse el tutorial de DataCamp Tutorial on the R Apply Family y los enlaces que se dejan al final de esta sub-sección. Mientras tanto, véase los siguientes ejemplos y funciones.
La función apply(X, MARGIN, FUN, ...) ejecuta una función en un arreglo o una matriz X. El atributo MARGIN es utilizado para determinar si la aplicación vectorial será por renglones o columnas (MARGIN = 1 aplicará FUN sobre renglones, MARGIN = 2 sobre columnas y MARGIN=c(1,2) para ambos)
seq_matrix <- matrix(1:12, nrow = 4, ncol = 3)
sum_n_times <- function(x, n) sum(x*n)
apply(seq_matrix, MARGIN = 1, sum)[1] 15 18 21 24
[1] 50 130 210
En el código anterior se utilizó la función sum_n_times, la cual puede solo usarse para este caso y en ninguna otra ocasión. Esto puede suceder cuando se requiera un resultado especial y único, por lo que declarar y reservar un espacio en memoria para una función y todo lo que implica podría ser un uso inadecuado del espacio disponible. Para estos casos se puede utilizar una función anónima, las cuales son conocidas como funciones lambda.
seq_data_frame <- data.frame(x = 1:12, y = 2:13)
apply(seq_data_frame, MARGIN = 2, FUN = function(x) sum(x*5)) x y
390 450
Véase que se utilizo un data frame en la función apply anterior. Esto funciona pero no es la manera correcta de hacer vectorización con data frames. En otro momento se verá el porqué de esto.
La función lapply acepta arreglos, data frames, vectores y listas y tiene la peculiaridad de regresar una lista.
$a
[1] 210
$b
[1] 464
$a
$a[[1]]
[1] 2
$b
$b[[1]]
[1] 5
$c
$c[[1]]
[1] 8
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 5
[[4]]
[1] 6
$a
[1] 5
$b
[1] 6
$c
[1] 8
'[' para extraer elementos de las listas y elementos de las matrices.'[ cuando se utilizó para seq_matrix[c(1,2),c(1,2)]? Hint: Véase la descripción del argumento X en la documentación de lapply().'[ cuando se utilizó para el segundo ejemplo?Otra de las funciones más utilizadas es sapply(). Esta función devuelve una salida con la estructura más básica posible, por lo que es común obtener vectores con esta función. sapply() es una versión amigable de lapply() por el tipo de salida, aunque modificando el parámetro simplify a FALSE, sapply tiene el mismo comportamiento que lapply().
a b
14.49138 21.54066
$a
[1] 14.49138
$b
[1] 21.54066
tapply(), mapply() y vapply().replicate()?which()?Aquí se enlistan algunos enlaces útiles sobre este tema:
Antes de ver un compendio de funciones útiles, hay que tener en cuenta que reutilizar código es muy importante para no perder tiempo en la resolución de problemas; y antes de instalar librerías o usar otras funciones base de R, hay que hablar sobre el CRAN.
El CRAN (Comprehensive R Archive Network) es una red de servidores web y FTP (File Trasnfer Protocol) donde se almacenan versiones de código y documentación de R. Además proporciona los hipervínculos para muchos paquetes con diversos temas (se puede consultar todo esto en el CRAN Task Views). Como es difícil e ineficiente tener todo en un mismo lugar, se crearon copias (mirrors) al rededor del mundo donde se mantiene y da soporte a toda esta información. Se puede consultar en la siguiente enlace la lista de todos estos lugares. Para el caso de México, se tienen dos ubicaciones:
Para instalar paquetes se puede elegir alguna de las siguientes opciones:
Regularmente, los paquetes usarán en su codificación funciones de otros paquetes, por lo que será necesario instalar todas los paquetes de los que depende para su correcto uso. Para evitar instalar uno por uno los paquetes de los que dependa alguno, se agrega el argumento dependences=TRUE a la función install.packages(): install.packages(...,dependeces = TRUE).
Lo anterior sirve para aquellos paquetes que están en el CRAN, pero si no lo están o se desea instalar otra versión de los paquetes, se puede usar devtools, el cual permite instalar un paquete directamente desde su fuente, como es el caso de algunos localizados en github.
Al instalar un paquete, este es guardado en el ordenador; ya que los paquetes no son más que archivos y carpetas que pueden contener funciones en Scripts, archivos .Rsd (Bases de datos de R) entre otros.
actuar
La ubicación donde el paquete es guardado se le llama library y para identificar donde empieza la busqueda R al cargar un paquete se utiliza el comando .libPaths(). ¿Qué sucede al ejecutar lapply(.libPaths(), dir)?
Para cargar un paquete se puede utilizar las funciones library() y require. Ambas funciones cargan lo contenido en los paquetes aunque existen pequeñas diferencias entre ambas. Al querer cargar un paquete que no esta instalado, library devolverá un error y require una advertencia, además require regresa un valor booleano dependiendo si se encuentra o no la librería para cargar dicho paquete; esto puede ser útil dentro de funciones para preparar el workspace y sus enlaces. Por ejemplo, en la siguiente función se instala el paquete dplyr en caso de no se tenga la librería o se carga el paquete en caso contrario.
Al momento de querer usar una función de algún paquete se puede llamar como cualquier función, aunque puede darse el caso de que existan los mismos nombres entre dos o más librerías para una función o hasta el mismo nombre para una función que creó el usuario. En tal caso se puede especificar el paquete con el operador ::; por ejemplo stats::chisq.test().
attach() y detach?A continuación se dará una serie de funciones y paquetes con los que se puede trabajar en ciertas ocasiones. Aquí no se mencionarán funciones que “vivan” en el “tidyverse” o funciones dedicadas a elementos gráficos, así como funciones relacionadas a temas avanzados de estadística y probabilidad.
base::cumsum(): Dado un vector, la función cumsum devuelve la suma acumulada de dicho vector. También se tiene base::cummin(), base::cummax() y base::cumprod().base::unique(): Esta función elimina los elementos duplicados en un vector, data frame o un arreglo.base::sort(): La función sort regresa de manera ordenada su input, el cual debe ser un vector o factor.base::union(), base::intersect(), base::setdiff() y base::setequal(): Funciones para realizar operaciones de conjuntos en vectores.package:vctrs: Este paquete ofrece varias funciones útiles en el manejo de vectores, además de que algunas de sus funciones se pueden usar en otro tipo de estructuras de datos.base::rowSums()y base::rowMeans(): Con estas funciones se obtienen la suma o el promedio por renglón en una matriz. Existe el equivalente para las columnas y son más rápidas estas funciones que al aplicar apply.
base::rowsum()?utils::head() y utils::tail(): Funciones para obtener las primeras o últimas observaciones de un data frame; también funciona con matrices.base::split(): Dicha función separa en grupos de acuerdo a una variable.package:data.table: El paquete data.table contiene varias funciones para manipular data.tables, los cuales pueden obtenerse fácilmente con un data frame. Las funciones en este paquete son eficientes en memoria y muy rápidas.base::read.csv(), base::read.delim(), base::read.table() y base::readLines(): Diferentes funciones para leer archivos de texto en distintos formatos.base::write(), base::csv() y base::table(): Funciones para exportar datos, como un data frame o una matriz, en un formato especifico.base::saveRDS(): Función para exportar o guardar algún objeto de R. Se puede leer este objeto con la función base::readRDS(). Estas funciones no están optimizadas y existen algunas variantes para estas funciones como readr::save_rds y qs::qsave(). Aún así, este tipo de almacenamiento de datos (en forma de bytes) es de lo más confiable ya que es serializable. Se recomienda leer el post A better way of saving and loading objects in R.base::sample(): Función para obtener una muestra aleatoria de \(n\) números y permutaciones de un conjunto dado.set.seed(): Función para asignar la semilla para los métodos pseudo aleatorios posteriores.base::sd(): desviación estándar.base::var(): varianza.base::min(): mínimo.base::max(): máximo.base::median(): mediana.base::range(): rango.base::quantile(): cuantiles.set.seed(20)
data <- data.frame(v = sample(c(1:25, NA), size = 50, replace = T),
w = sample(c(1:25, NA), size = 50, replace = T),
x = sample(c(1:25, NA), size = 50, replace = T),
y = sample(c(1:25, NA), size = 50, replace = T),
z = sample(c(1:25, NA), size = 50, replace = T))
head(data)data.frame(Sd = sapply(data, sd, na.rm = T),
Varianza = sapply(data, var, na.rm=TRUE),
Min = sapply(data, min, na.rm=TRUE),
Max = sapply(data, max, na.rm=TRUE),
Mediana = sapply(data, median, na.rm=TRUE),
MissingValues = sapply(data, function(x) sum(is.na(x))))Lo anterior es un pequeño resumen cuantitativo de los datos generados, pero se pueden obtener con diferentes funciones como base::sumary(), base::fivenum() y Hmisc::describe().
Con los paquetes naniar y visdata se puede dar tratamiento a los valores perdidos, así como elementos gráficos de resumen sobre estos.
base::abs(): Valor absoluto.
base::sqrt(): Raíz cuadrada.
base::exp(): Función exponencial.
base::log(): Logaritmo Natural.
base::log2(), base::log10(), base::log(base): Logaritmo en diferente base.
base::sin(), base::cos(), base::tan(), base::asin(),..: Funciones trigonométricas.
base::integrate(): Función para encontrar el área debajo de una curva
base::uniroot(): Función para encontrar las raíces de una función.
base::optimse(): Función para encontrar el mínimo o máximo de una función.
base::lfactorial(): Esta función calcular el logaritmo natural del factorial de un número.
base::cut(): Dicha función establece intervalos en todo el rango de valores dados por un vector. ¿El resultado es un factor?
base::findInterval(): Misma finalidad de la función cut pero es más rápido que dicha función.
base::object.size(): Esta función regresa una aproximación de la memoria utilizada por un objeto.base::capture.output(): Dicha función captura la salida de una función en un caracter.base::readlines(): Lectura de una línea que el usuario ingresa desde la terminal.base::invisible() y base::withVisible(): En ciertas ocasiones se desea que el resultado de las funciones no sea impreso, en tal case se utiliza la función invisible y para poder visualizarlo la función withVisible().try(), suppressWarnnings() y suppressMessages(): Con dichas funciones se puede aplicar el manejo de excepciones.
base::on.exit() y base::stop().base::traceback(): Al momento de tener un error en alguna función, la función traceback puede ser utilizada para ver los errores en la pila de ejecución.package:lobstr: Este paquete ofrece varias funciones para el manejo de objetos; como la obtención de un identificador de la dirección de memoria que tiene un objeto, su tamaño en bits, obtener en mejor orden el resultado de ciertas funciones; por ejemplo con lobstr::cst() se obtiene el árbol de la pila de llamadas obtenido por base::traceback() , el tamaño de memoria utilizada en la sesión actual, etc.package:rlang: Las funciones de este paquete están diseñadas para trabajar con ambientes.package:profvis y package:bench: Cuando se desea optimizar el funcionamiento del código, es común utilizar funciones para evaluar la rápidez de las funciones o rutinas con las que se trabaja; estos paquetes ayudan a realizar un perfilamiento del código y a realizar microbenchmark en funciones pequeñas. Véase el siguiente enlace para más funciones.function (x, y = NULL, na.rm = FALSE, use)
{
if (missing(use))
use <- if (na.rm)
"na.or.complete"
else "everything"
na.method <- pmatch(use, c("all.obs", "complete.obs", "pairwise.complete.obs",
"everything", "na.or.complete"))
if (is.na(na.method))
stop("invalid 'use' argument")
if (is.data.frame(x))
x <- as.matrix(x)
else stopifnot(is.atomic(x))
if (is.data.frame(y))
y <- as.matrix(y)
else stopifnot(is.atomic(y))
.Call(C_cov, x, y, na.method, FALSE)
}
<bytecode: 0x7fe286308480>
<environment: namespace:stats>
La función anterior calcula la varianza de una variable, en este caso un vector. Véase que al final de la descripción se menciona la palabra bytecode, la cual resulta familiar cuando ya se trabajo en el lenguaje de programación Java. A partir de la versión 2.14 de R, todas las funciones estándar y paquetes fueron pre compilados en bytecode, lo cual otorga rapidez.
compiler::cmpfun().¿Los ciclos son lentos en R? Sí y no. Todo depende de como se utilice.
Para comprender lo anterior, primero hay que entender como R almacena sus objetos. Para tener un entendimiento completo sobre todas las estructuras, es recomendable leer el capitulo 2 de Advance R; por el momento sólo se dará una introducción sobre como se comportan los vectores y las listas.
Como ya se habia mencionado, cuando se hace algún objeto en R, como un vector o una lista, a estos se les asigna un nombre para poder identificarlos posteriormente. Véase los siguientes resultados.
[1] "<0x7fe286ab6358>"
El resultado anterior es un identificador de la dirección en formato hexadecimal del vector x en memoria.
[1] "<0x7fe286ab6358>"
Ahora el nombre “y” esta asociado con la variable x, aunque en realidad los nombres “y” y “x” apuntan hacia un mismo objeto y eso puede verse con los anteriores resultados.
¿Qué sucederá cuando se modifique algún valor en la variable y? ¿Se modificará en esa posición x?
trace_vec <- capture.output(y[[3]] <- 10)
cat(paste(c(strsplit(trace_vec[1],":")[[1]][1], strsplit(trace_vec[2],":")[[1]][1]), collapse = "\n"))tracemem[0x7fe286ab6358 -> 0x7fe2872454d8]
tracemem[0x7fe2872454d8 -> 0x7fe287261518]
3
10
Los valores del vector x no se modificaron, solo los de y y las anteriores salidas al modificar el tercer elemento en el vector y son por lo que en R se conoce como Copy-on-modify donde básicamente los vectores no son copiados hasta que estos sean modificados. La primera salida es debido a que se realizo una copia de todos los elementos de x y posteriormente se modifico en y el tercer elemento. Véase que x no fue alterado en la ubicación o el identificador en memoria de este.
[1] "<0x7fe286ab6358>"
[1] "<0x7fe287261518>"
x en lugar de y, qué hubiera sucedido?Ahora, véase el caso con las listas
[1] "<0x7fe287bd1108>"
[1] "<0x7fe287bd1108>"
Las listas creadas anteriormente tienen la misma dirección y no sólo eso, si no que cada uno de los elementos de la lista también son los mismos
cat(paste(c(tracemem(list_mem[[1]]),
tracemem(list_mem_copy[[1]]),
tracemem(list_mem[[2]]),
tracemem(list_mem_copy[[2]])), collapse = "\n"))<0x7fe282588008>
<0x7fe282588008>
<0x7fe282587f28>
<0x7fe282587f28>
¿Qué sucedera cuando se modifique algún elemento en list_mem? ¿Sucederá lo mismo que en los vectores?
tracemem[0x7fe287bd1108 -> 0x7fe2873351c8]
Solo se hizo un cambio en dirección de memoria, ya que en el caso de las listas no se copia todos los elementos, solo se cambia el puntero hacia otro objeto. Véase que en los elementos que no se realizo algúna modificación se sigue apuntando hacia el mismo objeto.
cat(paste(c(tracemem(list_mem[[1]]),
tracemem(list_mem_copy[[1]]),
tracemem(list_mem[[2]]),
tracemem(list_mem_copy[[2]])), collapse = "\n"))<0x7fe282588008>
<0x7fe282588008>
<0x7fe2873e68e8>
<0x7fe282587f28>
list_mem y list_mem_copy ahora son diferentes?En resumen, al modificar algún elemento dentro de un vector, se creó una copia de todos los valores teniendo así dos vectores con diferentes nombres y, cuando se modifica algún elemento en las listas, solo se cambia de dirección el punto correspondiente a ese objeto y los demás enlaces permanecen.
Cuando se hace algún ciclo donde se este agregando o modificando algún vector, internamente se realizarán copias de todo el vector aunque sólo un elemento sea alterado; en ese caso los ciclos iterativos son lentos, pero cuando se utiliza una lista en lugar de un vector el rendimiento mejora bastante. Hay que recordar que R no fue diseñado para ser rápido, aunque usando listas el rendimiento obtenido será mejor al usar un ciclo.
library(bench)
library(knitr)
data <- data.frame(matrix(1:100000, ncol = 100))
plus2_byVector <- function(x){
#Al usar m[[n]] cuando m es un data.frame, véase que se estan tomando vectores
for(item in 1:length(x)){
x[[item]] <- x[[item]]+2
}
x
}
kable(bench::mark(plus2_byVector(data))[c("min", "median", "mem_alloc", "total_time")], format = "markdown")| min | median | mem_alloc | total_time |
|---|---|---|---|
| 1.36ms | 1.62ms | 1.11MB | 442ms |
plus2_byList <- function(x){
#Al convertir un data.frame en una lista, se agrega un elemento por columna
x <- as.list(x)
for(item in 1:length(x)){
x[[item]] <- x[[item]]+2
}
x
}
kable(bench::mark(plus2_byList(data))[c("min", "median", "mem_alloc", "total_time")], format = "markdown")| min | median | mem_alloc | total_time |
|---|---|---|---|
| 150µs | 187µs | 813KB | 301ms |
Otro punto que hay que considerar es que no siempre existirá una manera sencilla de usar algún funcional para remplazar a un ciclo, ya sea al modificar en algún lugar un objeto o al usar recursión, ya que en este último caso se depende de índices o de elementos anteriores que con un funcional no sería sencillo de resolver.
En general, hay ocasiones en las que un ciclo debe permanecer explícitamente para dar solución a un problema, tal es el caso de un modificación en una estructura en cada iteración; esto se puede remplazar con algún funcional aunque el código puede perder legibilidad. Otra circunstancia donde los ciclos no deben ser remplazados por un funcional son las relaciones recursivas ya que estas dependen de las posiciones dentro de una estructura, lo cual si puede ser difícil de plasmar en un funcional. Finalmente, cuando se tenga una solución implementada con un ciclo while, será complicado remplazar este por funciones como de la familia apply.
En el ámbito de la recursión, se pueden tener varios problemas y uno de los principales son la manera en que estos son tratados dentro de los lenguajes no funcionales, ya que el almacenamiento en este tipo de instrucciones puede ser demasiado. La manera en que trabajan las funciones recursivas es llamando internamente a las mismas funciones hasta llegar a un caso base, y mientras se opere con los resultados de las funciones internas, estos deben ser almacenados en la memoria y la forma en que estos son almacenados es en una pila conocida como Call Stack.
Dependiendo del lenguaje de programación la administración de la pila de llamadas puede cambiar, aunque esta siempre tendrá un límite de almacenamiento. En el caso de la recursión puede ser sencillo llenar esta y obtener una excepción indicando esta situación. Para tales casos, se puede remplazar la implementación de las funciones con diferentes técnicas; una de ellas se llama Memoization en la cual se guarda, en un ambiente fuera de la pila de llamadas, los resultados que se han obtenido de las funciones recursivas. Por ejemplo, si se esta tratando el problema de tener los primeros \(n\) números de fibonacci y previamente se corrió fibonacci(m) donde \(m<n\), al momento de hacer fibonacci(n) está buscará si existen registros de llamadas previas y comenzará desde ese punto, por lo que fibonacci(n) no tendrá que calcular los \(n-1\) nuevamente, si no, solo los faltantes; por lo que, si se tiene el suficiente espacio, el problema del límite de memoria en la Call Stack quedará resuelto. En los siguientes enlaces se dejan algunos ejemplos de esto.
Otra forma de solucionar el problema de la recursión, es llevar un proceso recursivo a uno iterativo, esto puede ser complicado algunas veces y más aún cuando ya se tenga el proceso de manera recursiva implementado, por lo que una solución es tomar dicha implementación y tratarla de manera iterativa dentro de una función, a dicha técnica se le conoce como trampoline. Este término esta relacionado con el lenguaje Lisp, pero se utiliza para identificar dicha técnica. En el siguiente enlace se deja un ejemplo de esto en R.
Finalmente, hay que tener en cuenta que el almacenamiento de llamadas de una función se hace en la Call Stack por que se opera con dichos resultados, por ejemplo en fibonacci(n) se debe realizar fibonacci(n-1)+fibonacci(n-2). Si de alguna manera se implementa un método recursivo sin operar con los resultados previos se podría evitar dicho problema, a esto se le conoce como Tail Call Optimization.
La comunicación visual siempre será un recurso muy importante para presentar y aclarar la información con la que se esta trabajando así como los resultados obtenidos. R cuenta con una gran cantidad de funciones para crear elementos gráficos, principalmente en el ámbito estadístico, a partir de ciertas estructuras u objetos, las cuales se pueden personalizar y dar un mejor formato a gusto del programador.
En esta sección sólo se verán algunas funciones y estrategias para crear objetos gráficos usando las funciones que proporciona R por defecto, así como funciones de ciertos paquetes a excepción de paquetes y funciones relacionados con ggplot.
Una de las funciones más importantes es la función plot(), la cual es una función genérica por lo que dependiendo del tipo de objeto u objetos que se den como parámetros se comporta de manera distinta, esta función devolverá un gráfico relacionando dos variables (\(x\) y \(y\)) en un plano cartesiano. Tómese como ejemplo los datos pressure del paquete datasets. ¿Qué dice la documentación acerca de estos datos?
El anterior gráfico también pudo obtenerse especificando lo que se considerará como variable \(x\) y \(y\) o dando una formula:
plot(pressure$temperature, pressure$pressure)plot(pressure ~ temperature, data=pressure)La anterior gráfica contiene muchos elementos gráficos, como títulos para los ejes y hasta los propios ejes, además las observaciones en los datos son dibujados como circunferencias. Véase en la siguiente gráfica que se puede modificar todo esto y agregar unos cuantos elementos gráficos más
plot(pressure ~ temperature, data=pressure,
main = "Gráfico de dispersión y Lineas: Presión VS Temperatura", # Título principal
col.main = "#0e608f", # Color para el título
sub="Ejemplo 1", # Sub título
col.sub= "grey50", # Color para el sub título
xlab="Temperatura", # Eje x
ylab="Presión", # Eje y
col.lab = "#284c73", #Color para los nombres dados en los ejes
col.axis = "#284c73", # Color para las anotaciones en los ejes
fg = "#9fb3c9", # Color para otros elementos gráficos como los ejes
type = "o", # Tipo de gráfico
pch= 24, # símbolo para dibujar las observaciones
col= "#0b3666",# Color para los anteriores símbolos y lineas
bg= "#12b587", # Color de relleno de los anteriores elementos
lty = 3, #Tipo de líneas
lwd = 2, # Ancho de las líneas
cex.main = 1.3, # Tamaño para el título
cex.sub = .8, # Tamaño para el sub título
cex.lab = 1.1, # Tamaño para los títulos en los ejes
cex.axis = .8 # Tamaño para los ejes
#cex haría proporcional el tamaño en todo.
)
También pueden eliminarse los ejes y el marco
plot(pressure ~ temperature, data=pressure,
main = "Gráfico de dispersión y Lineas: Presión VS Temperatura", # Título principal
col.main = "#0e608f", # Color para el título
sub="Ejemplo 1", # Sub título
col.sub= "grey50", # Color para el sub título
axes = FALSE, #Eliminando ejes
xlab="Temperatura", # Eje x
ylab="Presión", # Eje y
col.lab = "#284c73", #Color para los nombres dados en los ejes
type = "o", # Tipo de gráfico
pch= 24, # símbolo para dibujar las observaciones
col= "#0b3666",# Color para los anteriores símbolos y lineas
bg= "#12b587", # Color de relleno de los anteriores elementos
lty = 3, #Tipo de líneas
lwd = 2, # Ancho de las líneas
cex.main = 1.3, # Tamaño para el título
cex.sub = .8, # Tamaño para el sub título
cex.lab = 1.1 # Tamaño para los títulos en los ejes
)
Para solo eliminar el marco se puede usar frame=FALSE y para eliminar alguno de los ejes con xaxt="n" o yaxt="n". Aún se puede dar una mejor presentación modificando el tipo de fuente
plot(pressure ~ temperature, data=pressure,
main = "Gráfico de dispersión y Lineas: Presión VS Temperatura", # Título principal
col.main = "#0e608f", # Color para el título
sub="Ejemplo 1", # Sub título
col.sub= "grey50", # Color para el sub título
xlab="Temperatura", # Eje x
ylab="Presión", # Eje y
col.lab = "#284c73", #Color para los nombres dados en los ejes
col.axis = "#284c73", # Color para las anotaciones en los ejes
fg = "#9fb3c9", # Color para otros elementos gráficos como los ejes
type = "o", # Tipo de gráfico
pch= 24, # símbolo para dibujar las observaciones
col= "#0b3666",# Color para los anteriores símbolos y lineas
bg= "#12b587", # Color de relleno de los anteriores elementos
lty = 3, #Tipo de líneas
lwd = 2, # Ancho de las líneas
cex.main = 1.3, # Tamaño para el título
cex.sub = .8, # Tamaño para el sub título
cex.lab = 1.1, # Tamaño para los títulos en los ejes
cex.axis = .8, # Tamaño para los ejes
font.axis = 1, # Formato para los ejes
font.lab = 3, # Formato para los títulos en los ejes
font.main = 4, # Formato para el título
font.sub = 3, # Formato para el sub título
family = "Fira Sans Condensed"
)
Muchos de los argumentos anteriores no son específicamente de la función plot(), ya que dicha función solo solicita la información para los ejes \(x\) y \(y\) y lo demás es opcional mediante el argumento ... en el cual se pueden agregar opciones de texto y parámetros gráficos.
De los anteriores las opciones de texto son los parámetros main, sub, xlab y ylab. El tipo de gráfico no es exclusivamente un graphical parameter para todas las gráficas, si no que es para la función plot() en general. Los demás argumentos son graphical parameters los cuales pueden ser usados en otros tipos de gráficos.
La gráfica se ve bien aunque se puede mejorar el aspecto de los ejes con la función axis(). Para usar la función axis primero se deben eliminar los ejes.
plot(pressure ~ temperature, data=pressure,
main = "Gráfico de dispersión y Lineas: Presión VS Temperatura", # Título principal
col.main = "#0e608f", # Color para el título
sub="Ejemplo 1", # Sub título
col.sub= "grey50", # Color para el sub título
xlab="Temperatura", # Eje x
ylab="Presión", # Eje y
col.lab = "#284c73", #Color para los nombres dados en los ejes
type = "o", # Tipo de gráfico
pch= 24, # símbolo para dibujar las observaciones
col= "#0b3666",# Color para los anteriores símbolos y lineas
bg= "#12b587", # Color de relleno de los anteriores elementos
lty = 3, #Tipo de líneas
lwd = 2, # Ancho de las líneas
cex.main = 1.3, # Tamaño para el título
cex.sub = .8, # Tamaño para el sub título
cex.lab = 1.1, # Tamaño para los títulos en los ejes
font.lab = 3, # Formato para los títulos en los ejes
font.main = 4, # Formato para el título
font.sub = 3, # Formato para el sub título
family = "Fira Sans Condensed",
axes=FALSE
)
axis(side = 1, #Indicando el eje al que se le aplicará diseño
at = c(-15,seq(0, 360, 45), 380), #Se indica donde se darán las etiquetas
labels = c(NA, seq(0, 360, 45), NA), # Se dan explícitamente las anotaciones en los ejes (en los tickmarks)
pos = -40,# Coordenada para indicar donde se intersecta con el otro eje
las = 0, # 0 para un eje paralelo y 2 para uno perpendicular
tck = 0.03, # Ajuste de longitud de los ticks.
col.axis = "#284c73",
fg = "#9fb3c9",
cex.axis = .8,
font.axis = 1,
family = "Fira Sans Condensed"
)
axis(side = 2, #Indicando el eje al que se le aplicará diseño
at = c(-40, seq(0,810, 162), 840), #Se indica donde se darán las etiquetas
labels = c(NA, seq(0,810, 162), NA), # Se dan explícitamente las anotaciones en los ejes (en los tickmarks)
pos = -15,# Coordenada para indicar donde se intersecta con el otro eje
las = 2, # 0 para un eje paralelo y 2 para uno perpendicular
tck = 0.03, # Ajuste de longitud de los ticks.
col.axis = "#284c73",
fg = "#9fb3c9",
cex.axis = .8,
font.axis = 1,
family = "Fira Sans Condensed"
)
Para aclarar, el argumento side hace referencia al eje que se esta modificando, 1-below, 2-left, 3-above y 4-right. La función axis tiene más argumentos como lyt para indicar el tipo de línea sobre el eje y col para el color del eje. ¿Qué pasa cuando en tck se da un valor negativo?
Ahora, si se desea agregar texto en la gráfica se puede usar la función text()
plot(pressure ~ temperature, data=pressure,
main = "Gráfico de dispersión y Lineas: Presión VS Temperatura", # Título principal
col.main = "#0e608f", # Color para el título
sub="Ejemplo 1", # Sub título
col.sub= "grey50", # Color para el sub título
xlab="Temperatura", # Eje x
ylab="Presión", # Eje y
col.lab = "#284c73", #Color para los nombres dados en los ejes
type = "o", # Tipo de gráfico
pch= 24, # símbolo para dibujar las observaciones
col= "#0b3666",# Color para los anteriores símbolos y lineas
bg= "#12b587", # Color de relleno de los anteriores elementos
lty = 3, #Tipo de líneas
lwd = 2, # Ancho de las líneas
cex.main = 1.3, # Tamaño para el título
cex.sub = .8, # Tamaño para el sub título
cex.lab = 1.1, # Tamaño para los títulos en los ejes
font.lab = 3, # Formato para los títulos en los ejes
font.main = 4, # Formato para el título
font.sub = 3, # Formato para el sub título
family = "Fira Sans Condensed",
axes=FALSE
)
axis(side = 1, #Indicando el eje al que se le aplicará diseño
at = c(-15,seq(0, 360, 45), 380), #Se indica donde se darán las etiquetas
labels = c(NA, seq(0, 360, 45), NA), # Se dan explícitamente las anotaciones en los ejes (en los tickmarks)
pos = -40,# Coordenada para indicar donde se intersecta con el otro eje
las = 0, # 0 para un eje paralelo y 2 para uno perpendicular
tck = 0.03, # Ajuste de longitud de los ticks.
col.axis = "#284c73",
fg = "#9fb3c9",
cex.axis = .8,
font.axis = 1,
family = "Fira Sans Condensed"
)
axis(side = 2, #Indicando el eje al que se le aplicará diseño
at = c(-40, seq(0,810, 162), 840), #Se indica donde se darán las etiquetas
labels = c(NA, seq(0,810, 162), NA), # Se dan explícitamente las anotaciones en los ejes (en los tickmarks)
pos = -15,# Coordenada para indicar donde se intersecta con el otro eje
las = 2, # 0 para un eje paralelo y 2 para uno perpendicular
tck = 0.03, # Ajuste de longitud de los ticks.
col.axis = "#284c73",
fg = "#9fb3c9",
cex.axis = .8,
font.axis = 1,
family = "Fira Sans Condensed"
)
text(270,810, #Posición para el texto
"Presión máxima",
family = "Fira Sans Condensed",
cex = .8,
font = 3,
col = "#7d522a"
)
La función tex() tiene otro argumento, pos, que modifican la posición del texto de acuerdo a las coordenadas dadas. Con la función mtext() se puede modificar el título de los ejes o mejor dicho en cada margen de la gráfica con la ventaja de manipular el margen entre los ejes y los correspondientes títulos.
plot(pressure ~ temperature, data=pressure,
main = "Gráfico de dispersión y Lineas: Presión VS Temperatura", # Título principal
col.main = "#0e608f", # Color para el título
sub="Ejemplo 1", # Sub título
col.sub= "grey50", # Color para el sub título
xlab=NA,
ylab=NA,
type = "o", # Tipo de gráfico
pch= 24, # símbolo para dibujar las observaciones
col= "#0b3666",# Color para los anteriores símbolos y lineas
bg= "#12b587", # Color de relleno de los anteriores elementos
lty = 3, #Tipo de líneas
lwd = 2, # Ancho de las líneas
cex.main = 1.3, # Tamaño para el título
cex.sub = .8, # Tamaño para el sub título
font.main = 4, # Formato para el título
font.sub = 3, # Formato para el sub título
family = "Fira Sans Condensed",
axes=FALSE
)
axis(side = 1, #Indicando el eje al que se le aplicará diseño
at = c(-15,seq(0, 360, 45), 380), #Se indica donde se darán las etiquetas
labels = c(NA, seq(0, 360, 45), NA), # Se dan explícitamente las anotaciones en los ejes (en los tickmarks)
pos = -40,# Coordenada para indicar donde se intersecta con el otro eje
las = 0, # 0 para un eje paralelo y 2 para uno perpendicular
tck = 0.03, # Ajuste de longitud de los ticks.
col.axis = "#284c73",
fg = "#9fb3c9",
cex.axis = .8,
font.axis = 1,
family = "Fira Sans Condensed"
)
axis(side = 2, #Indicando el eje al que se le aplicará diseño
at = c(-40, seq(0,810, 162), 840), #Se indica donde se darán las etiquetas
labels = c(NA, seq(0,810, 162), NA), # Se dan explícitamente las anotaciones en los ejes (en los tickmarks)
pos = -15,# Coordenada para indicar donde se intersecta con el otro eje
las = 2, # 0 para un eje paralelo y 2 para uno perpendicular
tck = 0.03, # Ajuste de longitud de los ticks.
col.axis = "#284c73",
fg = "#9fb3c9",
cex.axis = .8,
font.axis = 1,
family = "Fira Sans Condensed"
)
text(270,810, #Posición para el texto
"Presión máxima",
family = "Fira Sans Condensed",
cex = .8,
font = 3,
col = "#7d522a"
)
mtext(side = 1,"Temperatura",
line=2.4,
family = "Fira Sans Condensed",
cex= 1.1,
col = "#284c73",
font= 3
)
mtext(side = 2, #Al igual que con axis
"Presión",
line=2.4, #Agregar margen
family = "Fira Sans Condensed",
cex= 1.1,
col= "#284c73",
font= 3
)
Ahora, se desearía que la etiqueta de “Presión máxima” apuntará hacia el valor máximo con una flecha, además de agregar lineas para ubicar mejor las proyecciones en los ejes de dicho valor. Esto se puede lograr con las funciones arrow() y abline()
plot(pressure ~ temperature, data=pressure,
main = "Gráfico de dispersión y Lineas: Presión VS Temperatura", # Título principal
col.main = "#0e608f", # Color para el título
sub="Ejemplo 1", # Sub título
col.sub= "grey50", # Color para el sub título
xlab=NA,
ylab=NA,
type = "o", # Tipo de gráfico
pch= 24, # símbolo para dibujar las observaciones
col= "#0b3666",# Color para los anteriores símbolos y lineas
bg= "#12b587", # Color de relleno de los anteriores elementos
lty = 3, #Tipo de líneas
lwd = 2, # Ancho de las líneas
cex.main = 1.3, # Tamaño para el título
cex.sub = .8, # Tamaño para el sub título
font.main = 4, # Formato para el título
font.sub = 3, # Formato para el sub título
family = "Fira Sans Condensed",
axes=FALSE
)
axis(side = 1, #Indicando el eje al que se le aplicará diseño
at = c(-15,seq(0, 360, 45), 380), #Se indica donde se darán las etiquetas
labels = c(NA, seq(0, 360, 45), NA), # Se dan explícitamente las anotaciones en los ejes (en los tickmarks)
pos = -40,# Coordenada para indicar donde se intersecta con el otro eje
las = 0, # 0 para un eje paralelo y 2 para uno perpendicular
tck = 0.03, # Ajuste de longitud de los ticks.
col.axis = "#284c73",
fg = "#9fb3c9",
cex.axis = .8,
font.axis = 1,
family = "Fira Sans Condensed"
)
axis(side = 2, #Indicando el eje al que se le aplicará diseño
at = c(-40, seq(0,810, 162), 840), #Se indica donde se darán las etiquetas
labels = c(NA, seq(0,810, 162), NA), # Se dan explícitamente las anotaciones en los ejes (en los tickmarks)
pos = -15,# Coordenada para indicar donde se intersecta con el otro eje
las = 2, # 0 para un eje paralelo y 2 para uno perpendicular
tck = 0.03, # Ajuste de longitud de los ticks.
col.axis = "#284c73",
fg = "#9fb3c9",
cex.axis = .8,
font.axis = 1,
family = "Fira Sans Condensed"
)
text(270,810, #Posición para el texto
"Presión máxima",
family = "Fira Sans Condensed",
cex = .8,
font = 3,
col = "#7d522a",
pos = 2 # Se coloca la etiqueta a la izquierda de la posición dada.
)
mtext(side = 1,"Temperatura",
line=2.4,
family = "Fira Sans Condensed",
cex= 1.1,
col = "#284c73",
font= 3
)
mtext(side = 2, #Al igual que con axis
"Presión",
line=2.4, #Agregar margen
family = "Fira Sans Condensed",
cex= 1.1,
col= "#284c73",
font= 3
)
arrows(270,810, #Posición de inicio
max(pressure$temperature)-5, max(pressure$pressure), #Posición de término
col = "grey50",
lwd = 1.2 #Ancho de la línea
)
abline(h = max(pressure$pressure), #h es especial para líneas horizontales
col = "gray73",
lty = 3)
abline(v = max(pressure$temperature),#v es especial para líneas horizontales
col = "gray73",
lty = 3)
A la función abline() también se le puede dar la información en la forma \(ax+b\) donde el argumento a sirve para establecer la pendiente y b para el intercepto. Véase que las rectas dibujadas con la función abline() cubren todo el rango de ambos ejes y en este caso se desea que estas terminen cuando lleguen a la coordenada específica, para eso se utilizará la función segments() donde sus argumentos indican las coordenadas de inicio y de final. Además el diseño de la flecha puede mejorar con shape::Arrows.
library(shape)
plot(pressure ~ temperature, data=pressure,
main = "Gráfico de dispersión y Lineas: Presión VS Temperatura", # Título principal
col.main = "#0e608f", # Color para el título
sub="Ejemplo 1", # Sub título
col.sub= "grey50", # Color para el sub título
xlab=NA,
ylab=NA,
type = "o", # Tipo de gráfico
pch= 24, # símbolo para dibujar las observaciones
col= "#0b3666",# Color para los anteriores símbolos y lineas
bg= "#12b587", # Color de relleno de los anteriores elementos
lty = 3, #Tipo de líneas
lwd = 2, # Ancho de las líneas
cex.main = 1.3, # Tamaño para el título
cex.sub = .8, # Tamaño para el sub título
font.main = 4, # Formato para el título
font.sub = 3, # Formato para el sub título
family = "Fira Sans Condensed",
axes=FALSE
)
axis(side = 1, #Indicando el eje al que se le aplicará diseño
at = c(-15,seq(0, 360, 45), 380), #Se indica donde se darán las etiquetas
labels = c(NA, seq(0, 360, 45), NA), # Se dan explícitamente las anotaciones en los ejes (en los tickmarks)
pos = -40,# Coordenada para indicar donde se intersecta con el otro eje
las = 0, # 0 para un eje paralelo y 2 para uno perpendicular
tck = 0.03, # Ajuste de longitud de los ticks.
col.axis = "#284c73",
fg = "#9fb3c9",
cex.axis = .8,
font.axis = 1,
family = "Fira Sans Condensed"
)
axis(side = 2, #Indicando el eje al que se le aplicará diseño
at = c(-40, seq(0,810, 162), 840), #Se indica donde se darán las etiquetas
labels = c(NA, seq(0,810, 162), NA), # Se dan explícitamente las anotaciones en los ejes (en los tickmarks)
pos = -15,# Coordenada para indicar donde se intersecta con el otro eje
las = 2, # 0 para un eje paralelo y 2 para uno perpendicular
tck = 0.03, # Ajuste de longitud de los ticks.
col.axis = "#284c73",
fg = "#9fb3c9",
cex.axis = .8,
font.axis = 1,
family = "Fira Sans Condensed"
)
text(270,648, #Posición para el texto
"Presión máxima",
family = "Fira Sans Condensed",
cex = .8,
font = 3,
col = "#7d522a",
pos = 2 # Se coloca la etiqueta a la izquierda de la posición dada.
)
mtext(side = 1,"Temperatura",
line=2.4,
family = "Fira Sans Condensed",
cex= 1.1,
col = "#284c73",
font= 3
)
mtext(side = 2, #Al igual que con axis
"Presión",
line=2.4, #Agregar margen
family = "Fira Sans Condensed",
cex= 1.1,
col= "#284c73",
font= 3
)
Arrows(270,648, #Posición de inicio
max(pressure$temperature), max(pressure$pressure), #Posición de término
arr.type="triangle", #Tipo de flecha
arr.adj = 2, #Ajuste para que la punta de la flecha toque el punto seleccionado
# arr.width=0.5, #Ancho de flecha
arr.col = "gray47",#color de la flecha
col = "grey50",
lwd = 1.2
)
segments(max(pressure$temperature), -40, #Coordenadas del primer punto
max(pressure$temperature), max(pressure$pressure), #Coordenadas del segundo punto
col = "gray73",
lty = 3)
segments(-15, max(pressure$pressure), max(pressure$temperature), max(pressure$pressure),
col = "gray73",
lty = 3)
Todos los parámetros gráficos se podrían haber incluido directamente con la función par(). En el siguiente ejemplo se ve como dicha función ahorra código. Hay que tener en cuenta que aveces es bueno hacer una copia donde no se tengan los parámetros gráficos alterados, esto guardando el estado actual de estos: par_default <- par()y después llamando par(par_default).
par(col.main = "#0e608f",
col.sub= "grey50",
font.main = 4,
font.sub = 3,
family = "Fira Sans Condensed",
font.axis = 1,
cex.axis = .8
)
plot(pressure ~ temperature, data=pressure,
main = "Gráfico de dispersión y Lineas: Presión VS Temperatura", # Título principal
sub="Ejemplo 1", # Sub título
xlab=NA, ylab=NA,
type = "o", # Tipo de gráfico
pch= 24, # símbolo para dibujar las observaciones
col= "#0b3666",# Color para los anteriores símbolos y lineas
bg= "#12b587", # Color de relleno de los anteriores elementos
lty = 3, #Tipo de líneas
lwd = 2, # Ancho de las líneas
cex.main = 1.3, # Tamaño para el título
cex.sub = .8, # Tamaño para el sub título
axes=FALSE
)
axis(side = 1, #Indicando el eje al que se le aplicará diseño
at = c(-15,seq(0, 360, 45), 380), #Se indica donde se darán las etiquetas
labels = c(NA, seq(0, 360, 45), NA), # Se dan explícitamente las anotaciones en los ejes (en los tickmarks)
pos = -40,# Coordenada para indicar donde se intersecta con el otro eje
las = 0, # 0 para un eje paralelo y 2 para uno perpendicular
tck = 0.03, # Ajuste de longitud de los ticks.
col.axis = "#284c73",
fg = "#9fb3c9" #Color de los ticks y el marco de los ejes
)
axis(side = 2, #Indicando el eje al que se le aplicará diseño
at = c(-40, seq(0,810, 162), 840), #Se indica donde se darán las etiquetas
labels = c(NA, seq(0,810, 162), NA), # Se dan explícitamente las anotaciones en los ejes (en los tickmarks)
pos = -15,# Coordenada para indicar donde se intersecta con el otro eje
las = 2, # 0 para un eje paralelo y 2 para uno perpendicular
tck = 0.03, # Ajuste de longitud de los ticks.
col.axis = "#284c73",
fg = "#9fb3c9" #Color de los ticks y el marco de los ejes
)
text(270,648, #Posición para el texto
"Presión máxima",
cex = .8,
font = 3,
col = "#7d522a",
pos = 2 # Se coloca la etiqueta a la izquierda de la posición dada.
)
mtext(side = 1,"Temperatura",
line=2.4,
cex= 1.1,
col = "#284c73",
font= 3
)
mtext(side = 2, #Al igual que con axis
"Presión",
line=2.4, #Agregar margen
cex= 1.1,
col= "#284c73",
font= 3
)
Arrows(270,648, #Posición de inicio
max(pressure$temperature), max(pressure$pressure), #Posición de término
arr.type="triangle", #Tipo de flecha
arr.adj = 2, #Ajuste para que la punta de la flecha toque el punto seleccionado
# arr.width=0.5, #Ancho de flecha
arr.col = "gray47",#color de la flecha
col = "grey50",
lwd = 1.2
)
segments(max(pressure$temperature), -40, #Coordenadas del primer punto
max(pressure$temperature), max(pressure$pressure), #Coordenadas del segundo punto
col = "gray73",
lty = 3)
segments(-15, max(pressure$pressure), max(pressure$temperature), max(pressure$pressure),
col = "gray73",
lty = 3)
Se puede utilizar la función par() para algunas características especiales como cambiar el color de fondo (background) de la gráfica con bg
par(bg="#daeff5",
col.main = "#0e608f",
col.sub= "grey50",
font.main = 4,
font.sub = 3,
family = "Fira Sans Condensed",
font.axis = 1,
cex.axis = .8
)
plot(pressure ~ temperature, data=pressure,
main = "Gráfico de dispersión y Lineas: Presión VS Temperatura", # Título principal
sub="Ejemplo 1", # Sub título
xlab=NA, ylab=NA,
type = "o", # Tipo de gráfico
pch= 24, # símbolo para dibujar las observaciones
col= "#0b3666",# Color para los anteriores símbolos y lineas
bg= "#12b587", # Color de relleno de los anteriores elementos
lty = 3, #Tipo de líneas
lwd = 2, # Ancho de las líneas
cex.main = 1.3, # Tamaño para el título
cex.sub = .8, # Tamaño para el sub título
axes=FALSE
)
axis(side = 1, #Indicando el eje al que se le aplicará diseño
at = c(-15,seq(0, 360, 45), 380), #Se indica donde se darán las etiquetas
labels = c(NA, seq(0, 360, 45), NA), # Se dan explícitamente las anotaciones en los ejes (en los tickmarks)
pos = -40,# Coordenada para indicar donde se intersecta con el otro eje
las = 0, # 0 para un eje paralelo y 2 para uno perpendicular
tck = 0.03, # Ajuste de longitud de los ticks.
col.axis = "#284c73",
fg = "#9fb3c9" #Color de los ticks y el marco de los ejes
)
axis(side = 2, #Indicando el eje al que se le aplicará diseño
at = c(-40, seq(0,810, 162), 840), #Se indica donde se darán las etiquetas
labels = c(NA, seq(0,810, 162), NA), # Se dan explícitamente las anotaciones en los ejes (en los tickmarks)
pos = -15,# Coordenada para indicar donde se intersecta con el otro eje
las = 2, # 0 para un eje paralelo y 2 para uno perpendicular
tck = 0.03, # Ajuste de longitud de los ticks.
col.axis = "#284c73",
fg = "#9fb3c9" #Color de los ticks y el marco de los ejes
)
text(270,648, #Posición para el texto
"Presión máxima",
cex = .8,
font = 3,
col = "#7d522a",
pos = 2 # Se coloca la etiqueta a la izquierda de la posición dada.
)
mtext(side = 1,"Temperatura",
line=2.4,
cex= 1.1,
col = "#284c73",
font= 3
)
mtext(side = 2, #Al igual que con axis
"Presión",
line=2.4, #Agregar margen
cex= 1.1,
col= "#284c73",
font= 3
)
Arrows(270,648, #Posición de inicio
max(pressure$temperature), max(pressure$pressure), #Posición de término
arr.type="triangle", #Tipo de flecha
arr.adj = 2, #Ajuste para que la punta de la flecha toque el punto seleccionado
# arr.width=0.5, #Ancho de flecha
arr.col = "gray47",#color de la flecha
col = "grey50",
lwd = 1.2
)
segments(max(pressure$temperature), -40, #Coordenadas del primer punto
max(pressure$temperature), max(pressure$pressure), #Coordenadas del segundo punto
col = "gray73",
lty = 3)
segments(-15, max(pressure$pressure), max(pressure$temperature), max(pressure$pressure),
col = "gray73",
lty = 3)
Si se desea solo colorear el fondo del plano coordenado, véase el ejemplo que se da en el siguiente link.
También se pueden crear matrices de gráficas, esto gracias al parámetro mfrow
par(mfrow=c(1,2), oma = c(0, 0, 2, 0))
par(bg="#daeff5",
col.sub= "grey50",
font.main = 4,
font.sub = 3,
family = "Fira Sans Condensed",
font.axis = 1,
cex.axis = .8
)
plot(pressure ~ temperature, data=pressure,
sub="Ejemplo 1", # Sub título
xlab=NA, ylab=NA,
type = "o", # Tipo de gráfico
pch= 24, # símbolo para dibujar las observaciones
col= "#0b3666",# Color para los anteriores símbolos y lineas
bg= "#12b587", # Color de relleno de los anteriores elementos
lty = 3, #Tipo de líneas
lwd = 2, # Ancho de las líneas
cex.sub = .8, # Tamaño para el sub título
axes=FALSE
)
axis(side = 1, #Indicando el eje al que se le aplicará diseño
at = c(-15,seq(0, 360, 45), 380), #Se indica donde se darán las etiquetas
labels = c(NA, seq(0, 360, 45), NA), # Se dan explícitamente las anotaciones en los ejes (en los tickmarks)
pos = -40,# Coordenada para indicar donde se intersecta con el otro eje
las = 0, # 0 para un eje paralelo y 2 para uno perpendicular
tck = 0.03, # Ajuste de longitud de los ticks.
col.axis = "#284c73",
fg = "#9fb3c9" #Color de los ticks y el marco de los ejes
)
axis(side = 2, #Indicando el eje al que se le aplicará diseño
at = c(-40, seq(0,810, 162), 840), #Se indica donde se darán las etiquetas
labels = c(NA, seq(0,810, 162), NA), # Se dan explícitamente las anotaciones en los ejes (en los tickmarks)
pos = -15,# Coordenada para indicar donde se intersecta con el otro eje
las = 2, # 0 para un eje paralelo y 2 para uno perpendicular
tck = 0.03, # Ajuste de longitud de los ticks.
col.axis = "#284c73",
fg = "#9fb3c9" #Color de los ticks y el marco de los ejes
)
text(270,648, #Posición para el texto
"Presión máxima",
cex = .8,
font = 3,
col = "#7d522a",
pos = 2 # Se coloca la etiqueta a la izquierda de la posición dada.
)
mtext(side = 1,"Temperatura",
line=2.4,
cex= 1.1,
col = "#284c73",
font= 3
)
mtext(side = 2, #Al igual que con axis
"Presión",
line=2.4, #Agregar margen
cex= 1.1,
col= "#284c73",
font= 3
)
Arrows(270,648, #Posición de inicio
max(pressure$temperature), max(pressure$pressure), #Posición de término
arr.type="triangle", #Tipo de flecha
arr.adj = 2, #Ajuste para que la punta de la flecha toque el punto seleccionado
# arr.width=0.5, #Ancho de flecha
arr.col = "gray47",#color de la flecha
col = "grey50",
lwd = 1.2
)
segments(max(pressure$temperature), -40, #Coordenadas del primer punto
max(pressure$temperature), max(pressure$pressure), #Coordenadas del segundo punto
col = "gray73",
lty = 3)
segments(-15, max(pressure$pressure), max(pressure$temperature), max(pressure$pressure),
col = "gray73",
lty = 3)
plot(pressure ~ temperature, data=pressure,
sub="Ejemplo 1", # Sub título
xlab=NA, ylab=NA,
type = "o", # Tipo de gráfico
pch= 24, # símbolo para dibujar las observaciones
col= "#0b3666",# Color para los anteriores símbolos y lineas
bg= "#12b587", # Color de relleno de los anteriores elementos
lty = 3, #Tipo de líneas
lwd = 2, # Ancho de las líneas
cex.sub = .8, # Tamaño para el sub título
axes=FALSE
)
axis(side = 1, #Indicando el eje al que se le aplicará diseño
at = c(-15,seq(0, 360, 45), 380), #Se indica donde se darán las etiquetas
labels = c(NA, seq(0, 360, 45), NA), # Se dan explícitamente las anotaciones en los ejes (en los tickmarks)
pos = -40,# Coordenada para indicar donde se intersecta con el otro eje
las = 0, # 0 para un eje paralelo y 2 para uno perpendicular
tck = 0.03, # Ajuste de longitud de los ticks.
col.axis = "#284c73",
fg = "#9fb3c9" #Color de los ticks y el marco de los ejes
)
axis(side = 2, #Indicando el eje al que se le aplicará diseño
at = c(-40, seq(0,810, 162), 840), #Se indica donde se darán las etiquetas
labels = c(NA, seq(0,810, 162), NA), # Se dan explícitamente las anotaciones en los ejes (en los tickmarks)
pos = -15,# Coordenada para indicar donde se intersecta con el otro eje
las = 2, # 0 para un eje paralelo y 2 para uno perpendicular
tck = 0.03, # Ajuste de longitud de los ticks.
col.axis = "#284c73",
fg = "#9fb3c9" #Color de los ticks y el marco de los ejes
)
text(270,648, #Posición para el texto
"Presión máxima",
cex = .8,
font = 3,
col = "#7d522a",
pos = 2 # Se coloca la etiqueta a la izquierda de la posición dada.
)
mtext(side = 1,"Temperatura",
line=2.4,
cex= 1.1,
col = "#284c73",
font= 3
)
mtext(side = 2, #Al igual que con axis
"Presión",
line=2.4, #Agregar margen
cex= 1.1,
col= "#284c73",
font= 3
)
Arrows(270,648, #Posición de inicio
max(pressure$temperature), max(pressure$pressure), #Posición de término
arr.type="triangle", #Tipo de flecha
arr.adj = 2, #Ajuste para que la punta de la flecha toque el punto seleccionado
# arr.width=0.5, #Ancho de flecha
arr.col = "gray47",#color de la flecha
col = "grey50",
lwd = 1.2
)
segments(max(pressure$temperature), -40, #Coordenadas del primer punto
max(pressure$temperature), max(pressure$pressure), #Coordenadas del segundo punto
col = "gray73",
lty = 3)
segments(-15, max(pressure$pressure), max(pressure$temperature), max(pressure$pressure),
col = "gray73",
lty = 3)
mtext("Gráfico de dispersión y Lineas: Presión VS Temperatura", outer = TRUE, cex= 1.3,col = "#0e608f",font = 4 )
El parámetro oma es para modificar el margen exterior de la gráfica. Puede verse más de esto y del argumento mar en los siguientes enlaces:
Finalmente, un conjunto de funciones comunes para crear una matriz de dispersión
par(bg = "#e3e9ff", font.main = 4,
font.sub = 3,
family = "Fira Sans Condensed",
font.axis = 1,
cex.axis = .8)
pairs(iris, col = c("#865fd4", "#b34769", "#a19f4c")[ifelse(iris$Species=="setosa", yes = 1, no = ifelse(iris$Species=="versicolor", yes = 2, no = 3))])
+ Histograma, Gráfico de densidad, Pie Chart y Boxplot
library(wesanderson)
set.seed(20)
datos_fic <- c(sample(seq(1,2,by = 0.01), replace = T, size = 1000))
datos_fic_1 <- sample(1:5, replace = T, size = 100)
par(mfrow=c(2,2), bg = "#e3e9ff", font.main = 4,
font.sub = 3,
family = "Fira Sans Condensed",
font.axis = 1,
cex.axis = .8)
hist(datos_fic, freq=TRUE, col = "#2241b5", ylab = "Frecuencia", main = "Histograma de datos_fic")
plot(density(datos_fic), col="#2241b5", main = "Densidad de datos_fic", xlab = "datos_fic", ylab = "Densidad")
pie(table(datos_fic_1), col=wes_palette("Rushmore1"), clockwise=TRUE, main = "Gráfico de pastel")
boxplot(iris$Sepal.Length~iris$Species, col = c("#865fd4", "#b34769", "#a19f4c"), xlab = "Especies", ylab = "Longitud del Sépalo", main = "Boxplots por especie")manipulate para poder manipular las gráficas base de R de manera más interactiva.El siguiente vector contiene una muestra referente al genero en un grupo de personas: c('H','M','M','H','H','M','H','M','H','H','M','M','M','M','M','M','M','M','H','H','H','M','H','M','M','M', 'M','H','H','H','H','M','M','M','H','M','H','H','M','H','M','H','M','H','M','M','H','H','M','H'). Mediante la función table() determina la frecuencia de cada categoría usando un factor.
Convierte el siguiente vector de caracteres en un vector numérico y obtén la suma de todos sus elementos. c('7', '50', '7', '18', '42', '37', '20', '11', '33', '38', '28', '3', '16', '40', '3', '40', '36', '37', '1', '6', '8', '23', '25', '48', '5', '22', '21', '21', '1', '5', '8', '48', '34', '16', '4'). Guárdalo con el nombre v.
La función table() también funciona con elementos numéricos, así que crea una tabla de frecuencias de v.
Ordena el vector v de forma descendente.
Se desea categorizar a v en 3 grupos (los menores a 4, los mayores o igual a 4 pero menores a 31 y los restantes), por lo que hay que hacer lo siguiente
rep(). Los nombres serán el número de grupo (Uno, Dos y Tres).Obtén los nombres del vector que se obtuvo al final del punto 5 y guárdalo como una nueva variable. Crea un vector numérico con los datos 1, 2, 3 y con nombres “Uno”, “Dos” y “Tres”. Utiliza character subsetting para obtener un vector de 1, 2 y 3 con el vector de nombres que se menciono al inicio de este ejercicio.
Obtén la raíz cuadrada de los primeros 100 números naturales.
Crea un vector numérico y eleva cada elemento a la potencia determinada por la longitud de ese vector.
Crea un vector numérico y eleva cada uno de sus elementos al cubo sin usar el operador ^ ni **.
Con los datos del inciso 1 y con la función seq_along(), crea un vector con los datos antes mencionados pero que sus nombres sean una indexación de estos.
Con los siguientes datos c('14', '1', '12', '27', '5', '38', '9', '29', '21', '19', '34', '18', '22', '1','26', '18','36', '28', '24','39','36', '21', '36', '28', '8', '13'), crea un vector booleano determinando que elementos son menores a 26 y con ese vector booleano concluye cuantos datos son menores a 26 y cuantos no lo son.
Crea una lista con cinco elementos donde, al menos dos, deben ser listas que contengan listas a su vez.
Se desea crear una lista de 10 elementos con las siguientes secuencias de números: c(1, 2, 3, ..., 10), c(11, 12, ..., 20), ..., c(91, 92, ..., 100). Además cada elemento debe tener nombre.
En la lista anterior, cambia el vector c(40, ..., 50) por c(50, 49, ..., 40) sin escribir explícitamente ese vector.
Agrega tu nombre como un atributo a cualquier vector anterior y a la lista del punto 13 y convierte la lista a un vector.
La siguiente instrucción genera un vector con valores numéricos y NA: sample(c(1:20, rep(NA,15))). El anterior vector se debe guardar con el nombre de random_NA y Usando algún ciclo, determine cuantos valores NA hay en random_NA.
Utilizando los conocimientos de coerción, la función sum() y lo que sea necesario, determinar cuantos valores NA existen en random_NA.
Crea una función que, dado un vector, se determine la cantidad de valores perdidos en él.
Crear un función que, dado un número se cree una matriz que contenga solo ese número. El número de renglones y columnas debe también ser dado en los parámetros de la función.
Crear una matriz, sin tener que usar algún ciclo, de 0s y 1s alternados. Aquí un ejemplo
[,1] [,2] [,3] [,4]
[1,] 0 1 0 1
[2,] 1 0 1 0
[3,] 0 1 0 1
[4,] 1 0 1 0
[5,] 0 1 0 1
[,1] [,2] [,3] [,4] [,5]
[1,] 1 25 81 169 289
[2,] 4 36 100 196 324
[3,] 9 49 121 225 361
[4,] 16 64 144 256 400
Crear un data frame con 5 variables y al menos 3 observaciones. La primera variable corresponderá a nombres de una persona, las demás representarán la edad, altura, peso y nacionalidad de cada individuo. Se puede usar la función sample().
En el anterior data frame, agregar una sexta variable que represente los videojuegos que tiene cada usuario. Los datos deben ser los siguientes: list(juegos = c("Horizon-Zero-Dawn", "bloodborne")), list(juegos = c("Mario-Kart", "Mario Maker", "Mario Odyssey")), list(c("Halo", "Batman Arkham Knight", "Injustice")). Si hacen falta más datos, crearlos o replicar los ya dados.
Crear un función que regrese una lista donde cada elemento de la lista tendrá el nombre de cada una de las estructuras y los tipos de datos ya vistos y su contenido será un vector booleano indicando si, de acuerdo al parámetro de la función, se es de alguna clase de las ya vistas. Por ejemplo, si se da como input una matriz de caracteres, la lista resultante en la posición [["Matriz"]] y en [["Caracter"]] deben contener el valor TRUE.
[,1] [,2] [,3] [,4] [,5]
[1,] "a A" "e E" "i I" "m M" "q Q"
[2,] "b B" "f F" "j J" "n N" "r R"
[3,] "c C" "g G" "k K" "o O" "s S"
[4,] "d D" "h H" "l L" "p P" "t T"
Del data frame del inciso 22, obtener mediante una expresión lógica las columnas, que de acuerdo al primer renglón, contengan valores menores a 150.
Crea una lista que contenga al menos cada una de las estructuras ya vistas.
Crear una función que, dadas dos matrices, se verifique si estas son aptas para aplicarles las operaciones fundamentales suma y sustracción y regresar dichos resultados en una lista; en caso de que sean aptas para la multiplicación agregar este resultado a la lista de retorno.
Crear un ciclo dentro de una función donde, de acuerdo a un data frame de entrada, y utilizando la primera observación del data frame, se determine el tipo de variable que contiene el data frame en cada columna. Finalmente todos los resultados deben ser devueltos en un vector.
Crear un nuevo operando y utilizarlo con dos vectores.
En el siguiente enlace se puede encontrar información sobre la función replicate. Teniendo en mente que la anterior función puede remplazar a un ciclo for, realice el ejercicio 16 utilizando dicha función. Vea que sucede al utilizar el parámetro simplify con los valores FALSE y TRUE.
Aplicar las funciones min() y max() en la matriz del ejercicio 22 sobre los renglones y columnas.
Con la lista creada al momento de explicar la función lapply(): list(a = seq_matrix, b = seq_matrix+1, c = seq_matrix+3), utilizar tal lista, la función lapply() y una función anónima, para sumarle 5 al elemento [2,3] de cada matriz.
Con base en el ejercicio anterior, hacer una función que acepte una lista (como la del anterior ejercicio), un parámetro tipo String; los posibles valores de este serán “suma”, “resta”, “mutl”, “div”; también debe aceptar tres parámetros numéricos, el primero servirá como operando de las operaciones anteriores y los siguientes como coordenadas para ubicar el elemento en las matrices. De acuerdo a las operaciones anteriores, realizar pora cada caso con la función lapply y una función anónima la operación correspondiente al elemento designado por los parámetros de la función.
Observese que sucede al ejecutar (1:10)[-1]. Obtener de la lista del ejercicio 13 los últimos 5 elementos de cada vector.
Crear una función que, dada una lista, se determine que tipo de datos son sus elementos.
Imprimir una sequencia de vectores con sapply().
Crear un nuevo operando que sea capaz de concatenar un número con una letra.
Utilizar la función del ejercicio anterior para concatenar a los elementos de una matriz de caracteres un número.
Utilizar el operando anterior para concatenar un número a todos los elementos de la matriz del ejercicio 26.
Usando la función which(), obtener los resultados solicitados del ejercicio 11.
Usando alguna función de la familia apply y una función anónima, obtener los resultados solicitados del ejercicio 11.
Obtener el máximo y mínimo de cada uno de los elementos en la lista del ejercicio 13.
Crear una función que acepte una matriz y devuleva una lista con dos elementos: “StatsColumns” y “StatsRows”; los cuales deben ser listas y contener los resultados de aplicar las funciones sum(), prod(), sqrt(sum()) y cumsum() por renglones y columnas.
Comparar la función del ejercicio 20 con la función sum() con bench::mark().
Obtener la unión, intersección y diferencia del siguiente conjunto de letras.
Buscar algún data frame del paquete datasets. Guardar en una variable los primeros 10 registros y en otra los últimos 10.
Verifica el siguiente resultado usando la función integrate().
\[ \int_0^1sin(\pi x)dx = \frac{2}{\pi} \]
integrate() que la famosa función de densidad de una normal integra \(1\) en todo su rango. A continuación se deja dicha función y si se desea basta con usar los parámetros de una normal estándar (\(\sigma = 1\) y \(\mu = 0\)).\[ f(x) = \frac{1}{\sqrt{2\pi\sigma^2}}e^{-\frac{(x-\mu)^2}{2\sigma^2}} \]
Utilizar la función bench::mark() para comparar la rapidez entre las funciones rowSums() y apply(sum) en una matriz numérica.
Obtener la suma y producto acumulado de los primeros 100 números naturales.
Dentro del repositorio de este proyecto se encuentra el archivo Data_fake.csv. Este debe cargarse en la sesión actual de R y guardarse como un data frame con el nombre de Data_fake.
Del data frame Data_fake obtener por columna el mínimo, máximo y mediana con alguna función de la familia apply en aquellas columnas que sea posible.
Usar alguna de las funciones resumen en Data_fake.
Crear una función que acepte un data frame como input y devuelva una lista con dos elementos; el primero será un data frame con solo aquellas variables que sean de tipo categóricas sobre el input y la segunda entrada de la lista de retorno un data frame con las variables numéricas.
Utilizando la función anterior sobre Data_fake, obtener el data frame con solo variables numéricas y aplicar la función del inciso 45.
Análogo a lo anterior, obtener el data frame con solo variables numéricas de Data_fake y dividir cada variable en 5 intervalos.
Utilizar alguna función de la familia apply para aplicar la función table() sobre cada una de las variables del data frame Data_fake.
Con los resultados anteriores, obtener aquellos elementos que son más recurrentes.
Es algo tedioso estar copiando y pegando el mismo contenido para hacer una gráfica. Investiga una forma para guardar una gráfica como las vistas en la sección Graficación como un objeto para usarlo posteriormente.
Investiga como agregar una leyenda en la siguiente gráfica
x<-1:40; square = x^2; fourth = x^4
plot(x, square, type="b", col="#cc5252", xlab="x", ylab="y", pch=19)
lines(x, fourth, pch=19, col="#e0773f", type="b")Investiga la función expression() para agregar en la anterior gráfica las respectivas ecuaciones (\(y = x^2\), \(y = x^4\)).
Crear una gráfica donde se tenga el histograma y la gráfica de densidad para el siguiente vector numérico: rnorm(1000, 5, 0.4).
En la gráfica anterior, agrega con la función curve() la ecuación del ejercicio 50 con \(\mu = 5\) y \(\sigma = 0.4\)
Se puede agregar texto en una gráfica con el estilo de Latex, es decir, en \(esta \ forma \ junto \ con \ ecuaciones \ como \sum_{x = 20}^{40}x^2\cdot\log(x)\). Lee el siguiente post y crea una gráfica agregando alguna ecuación en ella.
Escribe el código necesario para crear la siguiente gráfica
rect(), polygon() y symbol(), crea la siguiente gráfica.\[ \begin{split} x &= 16 cos^3(t)\\ y &= 13 cos(t)-5cos(2t)-2cos(3t)-cos(4t) \end{split} \]
Grafica 1000 puntos con dos vectores de números pseudo aleatorios entre 0 y 1 y utiliza la función rainbow para otorgarles color.
Crea una representación geométricas de la integral \(\int_0^\pi sen(x)dx\)
Crea una gráfica del las funciones \(sen(x)\) y \(cos(x)\) agregando anotaciones cada \(\frac{\pi}{2}\) e indicando que ecuación corresponde a cada gráfica.
Crea una gráfica para comparar las funciones \(sen(x)\), \(2sen(x)\) y \(sen(\frac{x}{2})\).
Crea una representación geométrica de la función \(sen(x)\) y \(cos(x)\) en una circunferencia.
Wickham and Grolemund. 2017. R for Data Science. https://r4ds.had.co.nz Versión en español: https://es.r4ds.hadley.nz
Grolemund, Garrett.2014. Hands-On Programming with R. https://rstudio-education.github.io/hopr/
Mendoza, Vega. R para principiantes. https://bookdown.org/jboscomendoza/r-principiantes4/
Wickham and Grolemund. 2019. Advanced R. Second Edition. https://adv-r.hadley.nz
Chang, W. (2012). R graphics cookbook: practical recipes for visualizing data. " O’Reilly Media, Inc.".
Rahlf, T. (2019). Data Visualisation with R: 111 Examples. Springer Nature.
Murrell, P. (2018). R graphics. CRC Press.
A work by Carlos Vásquez
cvasquezfguerra@gmail.com