# install.packages("janitor")
library(tidyverse)
library(kableExtra)
library(janitor)
Muchas veces nos enfrentamos a bases de datos que fueron realizadas con objetivos diferentes al propósito de análisis que tenemos y que se encuentran o en formatos poco amigables para el procesamiento o que tienen algunas dificultades en la presentación de los datos. Recorreremos algunas estrategias para modificar los datasets o la información de modo que se ajusten mejor a los objetivos que podamos tener.
Frecuentemente encontrarán que la información no está ordenada en formato Tidy. Para que eso suceda deben cumplirse las siguientes condiciones:
Cada variable debe tener su propia columna.
Cada observación debe tener su propia fila.
Cada valor debe tener su propia celda.
Para transformar estas estructuras, presentamos las valiosas funciones que nos permiten Pivotear.
Veamos la distribución de frecuencias de personas según edad y género en una población determinada:
grupo_edad_genero <- read_delim("grupoedad_genero.csv",
delim = ",")
kable(grupo_edad_genero) %>%
kable_styling(bootstrap_options = c("striped", "hover"), font_size = 12)
| genero | 0-9 | 10-19 | 20-29 | 30-39 | 40-49 | 50-59 | 60-69 | 70-79 | 80-89 | 90-100 |
|---|---|---|---|---|---|---|---|---|---|---|
| masculino | 56 | 57 | 66 | 78 | 56 | 87 | 45 | 75 | 24 | 2 |
| femenino | 45 | 55 | 76 | 89 | 76 | 24 | 45 | 34 | 44 | 3 |
Identifiquemos las variables en la tabla:
género
rango de edad
cantidad de personas
Sólo una de esas variables es una columna, los valores de rango etario está en el nombre de la columna y se organizan horizontalmente, y los valores de recuento de personas en celdas de cada una de estas columnas.
Para pivotar hacia una tabla larga, debemos identificar las columnas seleccionadas, y los nombres de las nuevas variables que contendrán sus valores.
tabla_longer <- grupo_edad_genero %>%
pivot_longer(cols = 2:11, names_to = "rango_edad", values_to = "cantidad_personas")
kable(tabla_longer) %>%
kable_styling(bootstrap_options = c("striped", "hover"), font_size = 12)
| genero | rango_edad | cantidad_personas |
|---|---|---|
| masculino | 0-9 | 56 |
| masculino | 10-19 | 57 |
| masculino | 20-29 | 66 |
| masculino | 30-39 | 78 |
| masculino | 40-49 | 56 |
| masculino | 50-59 | 87 |
| masculino | 60-69 | 45 |
| masculino | 70-79 | 75 |
| masculino | 80-89 | 24 |
| masculino | 90-100 | 2 |
| femenino | 0-9 | 45 |
| femenino | 10-19 | 55 |
| femenino | 20-29 | 76 |
| femenino | 30-39 | 89 |
| femenino | 40-49 | 76 |
| femenino | 50-59 | 24 |
| femenino | 60-69 | 45 |
| femenino | 70-79 | 34 |
| femenino | 80-89 | 44 |
| femenino | 90-100 | 3 |
# tabla_longer <- grupo_edad_genero %>%
# pivot_longer(-genero, names_to = "rango_edad", values_to = "cantidad_personas")
Esta función realiza la operación inversa a pivot longer para transformar a tablas mas “anchas”, desplegando horizontalmente en los nombres de nuevas variables los valores de la variable a expandir, y asignando valores desde otra variable seleccionada. Usaremos como ejemplo la base de personas de Ciencia y Tecnología utilizada en clases anteriores:
personas_2018 <- read_delim("personas_2018.csv",
delim = ";")
ref_sexo <- read_delim("ref_sexo.csv",
delim = ";")
ref_disciplina <- read_delim("ref_disciplina.csv",
delim = ";")
Me interesa conocer como se distribuyen las grandes areas disciplinares según género:
personas_sexo_disciplina<- personas_2018 %>%
select(persona_id, sexo_id, disciplina_id=disciplina_maximo_grado_academico_id) %>%
left_join(ref_sexo) %>%
left_join(ref_disciplina) %>%
count(sexo_descripcion, gran_area_descripcion)
kable(personas_sexo_disciplina) %>%
kable_styling(bootstrap_options = c("striped", "hover"), font_size = 12)
| sexo_descripcion | gran_area_descripcion | n |
|---|---|---|
| FEMENINO | CIENCIAS AGRÍCOLAS | 2288 |
| FEMENINO | CIENCIAS MÉDICAS Y DE LA SALUD | 4195 |
| FEMENINO | CIENCIAS NATURALES Y EXACTAS | 9468 |
| FEMENINO | CIENCIAS SOCIALES | 12376 |
| FEMENINO | HUMANIDADES | 6628 |
| FEMENINO | INGENIERÍAS Y TECNOLOGÍAS | 2653 |
| FEMENINO | SIN DATOS | 1917 |
| MASCULINO | CIENCIAS AGRÍCOLAS | 2265 |
| MASCULINO | CIENCIAS MÉDICAS Y DE LA SALUD | 2141 |
| MASCULINO | CIENCIAS NATURALES Y EXACTAS | 6925 |
| MASCULINO | CIENCIAS SOCIALES | 8031 |
| MASCULINO | HUMANIDADES | 3637 |
| MASCULINO | INGENIERÍAS Y TECNOLOGÍAS | 4480 |
| MASCULINO | SIN DATOS | 1548 |
# group_by(sexo_descripcion, gran_area_descripcion) %>%
# summarise(cantidad_personas=n())
Queremos ahora modificar la distribución de filas y columnas para visualizar con mayor claridad la distribución en la tabla. Para eso vamos a probar pivotando cada gran área como columna (nueva variable) y asignando como contenido la frecuencia para cada género:
tabla_wide <- personas_sexo_disciplina %>%
pivot_wider(names_from = gran_area_descripcion, values_from = n)
kable(tabla_wide) %>%
kable_styling(bootstrap_options = c("striped", "hover"), font_size = 12)
| sexo_descripcion | CIENCIAS AGRÍCOLAS | CIENCIAS MÉDICAS Y DE LA SALUD | CIENCIAS NATURALES Y EXACTAS | CIENCIAS SOCIALES | HUMANIDADES | INGENIERÍAS Y TECNOLOGÍAS | SIN DATOS |
|---|---|---|---|---|---|---|---|
| FEMENINO | 2288 | 4195 | 9468 | 12376 | 6628 | 2653 | 1917 |
| MASCULINO | 2265 | 2141 | 6925 | 8031 | 3637 | 4480 | 1548 |
Otro conflicto que podemos tener es contar con valores no disponibles. Veremos cómo lidiar con este tipo de dato.
Un tipo de dato que es muy común encontrar es el NA, es decir, un valor no disponible (Not Available). Son valores ausentes o desconocidos para un registro. Debemos tener cuidado con estos datos porque el criterio de cómo operar con ellos va a variar de acuerdo a la necesidad y al impacto de la definición metodológica que se tome al respecto.
Veamos un ejemplo donde evaluamos con los operadores de comparación anterior un valor NA.
5 >= NA
## [1] NA
Cuando pedimos a R que evalúe si en este caso un valor desconocido es mayor o igual a otro valor. Nos dará como resultado un valor desconocido, ya que no lo puede determinar Lo mismo sucede si quisieramos hacer una operación matemática sobre este valor.
NA + 10 * 4
## [1] NA
R tampoco podría saber cuanto es un valor desconocido después de esas operaciones. Algo con lo que tenemos que tener bastante cuidado también es con intentar evaluar si un valor desconocido es igual a otro, ya que tampoco R puede deducirlo.
NA == NA
## [1] NA
La pregunta correcta que muchas veces nos queremos hacer es si un valor es desconocido. Y para eso tenemos una función is.na()
Retomando entonces el ejemplo de nuestro data frame de vegetales, veamos algunas funciones báscicas que nos permitan conocer si contamos con NA, dónde están, cuántos son y/o qué porcentaje de la variable representan:
vegetales <- read.csv('datos-de-produccion-vegetal-de-los-naf.csv') %>%
rename(cantidad = "X")
na_vegetales <- vegetales %>% # Contiene mi data frame datos NA si o no?
count(is.na.data.frame(vegetales))
any(is.na(vegetales)) # Cuántos NA contiene mi DF?
## [1] TRUE
any(is.na(vegetales$cod_provincia)) # Contiene NA determinada variable?
## [1] FALSE
sum(is.na(vegetales$cantidad_de_producciones)) # Cuántos NA contiene determinada variable
## [1] 49
which(is.na(vegetales$cantidad_de_producciones)) # Qué registros contienen NA, poco conveniente en DF de gran volumen
## [1] 108 5027 8744 9465 9950 10113 10142 10708 11473 12875 14230 15179
## [13] 15589 15640 16758 17107 17142 17181 17624 18673 19007 19044 19927 20618
## [25] 21162 21194 21666 21694 21810 21889 21977 22093 22137 29088 29142 29149
## [37] 29235 29253 29285 29318 29432 29524 29565 29624 29747 29822 29957 30035
## [49] 31087
sin_na_vegetales <- na.omit(vegetales) # Excluyo todos los NA de mi DF
mean(is.na(vegetales)) # calculo el promedio de NA sobre mi DF
## [1] 0.0001179027
apply(is.na(vegetales), 2, mean) # Obtengo valores absolutos de NA por cada columna (2) de mi DF
## cod_pais nom_pais cod_provincia
## 0.000000000 0.000000000 0.000000000
## nom_provincia cod_depto nom_depto
## 0.000000000 0.000000000 0.000000000
## anio produccion cantidad_de_producciones
## 0.000000000 0.000000000 0.001532735
## cod_unid_superficie nom_uni_superficie superficie_total
## 0.000000000 0.000000000 0.000000000
## cantidad
## 0.000000000
apply(is.na(vegetales), 2, sum) # Obtengo porcentaje de NA por cada columna (2) de mi DF
## cod_pais nom_pais cod_provincia
## 0 0 0
## nom_provincia cod_depto nom_depto
## 0 0 0
## anio produccion cantidad_de_producciones
## 0 0 49
## cod_unid_superficie nom_uni_superficie superficie_total
## 0 0 0
## cantidad
## 0
En el último caso lo que realizamos fue una estrategia de iteración a través de columnas. En el siguientes apartado veremos más sobre como se utiliza esta lógica, en particular cuando queremos realizar alguna operación fila por fila.
La programación funcional permite organizar y optimizar sintaxis y funcionalidad del código mediante la utilización de funciones y de controles de flujo de la información. Permite crear funciones pequeñas y simples que admiten dentro otras funciones o atributos, y resuelven un problema determinado de manera controlada.
Incluye:
Estructuras de código condicionales Loops Creación de funciones a medida del usuario Librería purrr para programación funcional
Las estructuras condiconales nos permiten ejecutar una porción de código en caso de que cumplan una condición lógica
Una sentencia if (si) te permite ejecutar un código condicional. La condición debe evaluar como TRUE o FALSE. Por ejemplo:
if (condición) {
# el código que se ejecuta cuando la condición es verdadera (TRUE)
} else {
# el código que se ejecuta cuando la condición es falsa (FALSE)
}
Su funcionamiento es el siguiente:
if(condicion){codigo a ejecutar si se cumple la condición}
if( 7 > 5){
print("Mayor")
}
## [1] "Mayor"
if( 7 < 2 ){
print("Menor")
}
if (2 > 5) {
print("Mayor") # el código que se ejecuta cuando la condición es verdadera (TRUE)
} else {
print("Menor")# el código que se ejecuta cuando la condición es falsa (FALSE)
}
## [1] "Menor"
La función if_else() sirve para crear o modificar dicotómicamente un objeto/variable/vector a partir del cumplimiento de una o más condiciones lógicas. Su funcionamiento es el siguiente:
if_else(condicion,función a aplicar si se cumple la condición,función a aplicar si no se cumple la condición)
if_else(7 > 5, true = "Mayor",false = "Menor")
## [1] "Mayor"
Se pueden encadenar múltiples sentencias if juntas:
if (condicion_1) {
# hacé esto
} else if (condicion_2) {
# hacé otra cosa
} else {
# sino hacé esto otro
}
Si queda una larga serie de sentencias if encadenadas, deberías considerar reescribir el código.
Estilo del código: Es importante que el contenido de if esté entre llaves ({}). La llave de apertura nunca debe ir en su propia línea y siempre debe ir seguida de una línea nueva. Una llave de cierre siempre debe ir en su propia línea, a menos que sea seguida por else.
La iteración y las funciones permiten reducir la duplicación en el código, en lugar de copiar y pegar reiteradamente. Esto tiene tres beneficios principales:
1- Es más fácil ver el objetivo de tu código.
2- Es más simple realizar cambios. A medida que tus necesidades cambian o cuando identificas errores, solo necesitarás realizar cambios en un lugar, en vez de recordar cambiar en cada lugar donde copiaste y pegaste el código.
3- Es probable que tengas menos errores porque cada línea de código es utilizada en más lugares.
La iteración es una herramienta valiosa cuando necesitas hacer la misma tarea con múltiples entradas: repetir la misma operación en diferentes columnas o en diferentes conjuntos de datos.
Un loop es una estructura de código que nos permite aplicar iterativamente un mismo conjunto de comandos, variando el valor de una variable. Por ejemplo:
for(i in 1:10){
print(i^2)
}
## [1] 1
## [1] 4
## [1] 9
## [1] 16
## [1] 25
## [1] 36
## [1] 49
## [1] 64
## [1] 81
## [1] 100
Esto se lee como : “Recorre cada uno de los valores (i) del vector numérico 1 a 10, y para cada uno de ellos imprimí el cuadrado (i^2)”. Un loop puede iterar sobre cualquier tipo de vector, independientemente de lo que contenga. Se puede especificar la palabra que desee que tome cada uno de los valores que debe tomar. En el ejemplo anterior fue i, pero bien podría ser “Valores”.
for(Valores in 1:10){
print(Valores^2)
}
## [1] 1
## [1] 4
## [1] 9
## [1] 16
## [1] 25
## [1] 36
## [1] 49
## [1] 64
## [1] 81
## [1] 100
Si quisiéramos calcular la mediana de cada una de estas variables numéricas del dataframe, podemos hacerlo de varias maneras:
df <- tibble(
a = rnorm(10),
b = rnorm(10),
c = rnorm(10),
d = rnorm(10)
)
Copiando y pegando el siguiente código:
median(df$a)
## [1] -0.1108455
median(df$b)
## [1] -0.4029708
median(df$c)
## [1] 0.08821318
median(df$d)
## [1] 0.1505391
O podemos hacer un for loop para que itere en cada uno de los elementos:
output <- c("double", ncol(df)) # 1. output
for (i in seq(df)) { # 2. secuencia
output[[i]] <- median(df[[i]]) # 3. cuerpo
}
output
## [1] "-0.110845493877597" "-0.40297083216107" "0.0882131791622929"
## [4] "0.150539062879926"
Cada bucle tiene tres componentes:
output: antes de comenzar el loop, siempre debes asignar suficiente espacio para la salida. Una forma general es crear un vector vacío de longitud dada con dos argumentos: el tipo de vector (“logical”, “integer”, “double”, “character”, etc) y su longitud.
La secuencia: i in seq(df). Este código determina sobre qué iterar: cada ejecución del bucle for asignará a i un valor diferente de seq(df). seq() es una versión segura de 1:length(l), con una diferencia importante: si se tiene un vector de longitud cero (accidental o intencionalmente), seq() hace lo correcto.
El cuerpo: output[[i]] <- median(df[[i]]). Este es el código que hace el trabajo. Se ejecuta repetidamente, con un valor diferente para i cada vez. La primera iteración ejecutará output[[1]] <- median(df[[1]]), la segunda ejecutará output [[2]] <- median (df [[2]]), y así sucesivamente.
Además de iterar sobre los índices numéricos con for (i in seq(xs)), y extraer el valor con x [[i]] hay otras dos formas de hacerlo:
Iterar sobre los elementos: for (x in xs).
Iterar sobre los nombres: for (nm in names(xs)). Esto es útil si queremos utilizar el nombre en el título de un gráfico o en el nombre de un archivo.
Veamos otra aplicación. Vamos a utilizar un dataset público del Ministerio de Agricultura, Ganadería y Pesca sobre exportaciones argentinas de frutas en toneladas, entre los años 2013 - 2017. Está disponible en: https://datos.gob.ar/dataset/agroindustria-frutas---anuario-exportaciones
Contiene información de exportaciones argentinas por país de destino de: arándanos, ciruelas, duraznos, limones, mandarinas, manzanas, naranjas, peras, pomelos y uvas. Está expresado en dólares y por toneladas desde el año 2013 hasta 2017 (INDEC)
Vamos a cargarlo en nuestro entorno:
frutas <- read_delim("exportaciones_de_frutas.csv", delim = ";")
Utilizamos algunas funciones ya conocidas para explorar el contenido del dataframe, para conocer dimensiones, variables disponibles, tipos de datos y medidas resúmen.
names(frutas)
## [1] "producto" "pais_destino" "año" "unidad_medida"
## [5] "totales"
head(frutas)
## # A tibble: 6 x 5
## producto pais_destino año unidad_medida totales
## <chr> <chr> <dbl> <chr> <dbl>
## 1 Arandano Estados Unidos 2013 Toneladas 7352
## 2 Arandano Reino Unido 2013 Toneladas 2080
## 3 Arandano Países Bajos 2013 Toneladas 1091
## 4 Arandano Alemania 2013 Toneladas 614
## 5 Arandano Canadá 2013 Toneladas 577
## 6 Arandano Otros 2013 Toneladas 535
summary(frutas)
## producto pais_destino año unidad_medida
## Length:285 Length:285 Min. :2013 Length:285
## Class :character Class :character 1st Qu.:2014 Class :character
## Mode :character Mode :character Median :2015 Mode :character
## Mean :2015
## 3rd Qu.:2016
## Max. :2017
## totales
## Min. : 0
## 1st Qu.: 471
## Median : 4000
## Mean : 15301
## 3rd Qu.: 22446
## Max. :148206
glimpse(frutas)
## Rows: 285
## Columns: 5
## $ producto <chr> "Arandano", "Arandano", "Arandano", "Arandano", "Aran...
## $ pais_destino <chr> "Estados Unidos", "Reino Unido", "Países Bajos", "Ale...
## $ año <dbl> 2013, 2013, 2013, 2013, 2013, 2013, 2014, 2014, 2014,...
## $ unidad_medida <chr> "Toneladas", "Toneladas", "Toneladas", "Toneladas", "...
## $ totales <dbl> 7352, 2080, 1091, 614, 577, 535, 9839, 2526, 1148, 11...
unique(frutas$pais_destino)
## [1] "Estados Unidos" "Reino Unido" "Países Bajos"
## [4] "Alemania" "Canadá" "Otros"
## [7] "Brasil" "Paraguay" "Uruguay"
## [10] "Bolivia" "Rusia" "España"
## [13] "Italia" "Grecia" "Filipinas"
## [16] "Indonesia" "Emiratos Arabes Unidos"
Nos interesa conocer el total de toneladas exportadas por año y por país de destino,independientemente del producto:
frutas_exportadas_pais_anio <- frutas %>%
group_by(pais_destino, año) %>%
summarise(total_pais_anio = sum(totales)) %>%
ungroup()
Queremos hacer un gráfico individual por pais que muestre la evolucion temporal de toneladas exportadas:
# Loop
lista_graficos_frutas <- list() # creo lista vacia como contenedor
vector_países <- unique(frutas_exportadas_pais_anio$pais_destino) # armo vector de países
for (i in seq(vector_países)) { # secuencia
p <- frutas_exportadas_pais_anio %>%
filter(pais_destino == vector_países[i]) %>% # cuerpo
ggplot() +
geom_line(aes(x=año, total_pais_anio)) +
facet_wrap( ~ pais_destino) # Títulos
lista_graficos_frutas[[i]] = p
# print(p)
}
lista_graficos_frutas[3]
## [[1]]
MAP es la forma tidy de hacer loops con código mas prolijo y mucho más eficiente.
La función map toma un input, una función para aplicar, y alguna otra cosa (por ejemplo parametros que necesite la función)
map(.x, # vector o lista de entrada
.f, # funcion a aplicar
…) #otras opciones
Cuando tenemos que pasar dos input usamos map2:
map2(.x, .y, .f, …)
Si tenemos más de dos, usamos pmap
pmap(.l, .f, …)
Vamos a repetir el ejemplo de cálculo de media para cada una de las variables (recordamos que son vectores) de df:
resultado <- map(df, median)
resultado
## $a
## [1] -0.1108455
##
## $b
## [1] -0.4029708
##
## $c
## [1] 0.08821318
##
## $d
## [1] 0.1505391
La salida de los map() es una lista, no un vector. Si queremos recuperar los valores originales podemos usar unlist()
unlist(resultado)
## a b c d
## -0.11084549 -0.40297083 0.08821318 0.15053906
Media: se obtiene a partir de la suma de todos sus valores dividida entre el número de sumandos. Su valor puede ser afectado en gran medida por la presencia de incluso un solo valor extremo (una observación inusualmente grande o pequeña).
Mediana: Es el valor medio una vez que se ordenan las observaciones de la más pequeña a la más grande,el valor que parte la distribución a la mitad. Es muy insensible a los valores extremos.
Moda: es el valor que con mayor frecuencia de ocurrencia.
Podemos calcular medidas resumen anuales, como medias, mediana, moda, mínimos y máximos:
frutas_resumen <- frutas_exportadas_pais_anio %>%
group_by(año) %>%
summarise(min = min(total_pais_anio),
media = mean(total_pais_anio),
mediana = median(total_pais_anio),
max = max(total_pais_anio),
ds= sd(total_pais_anio))
frutas_resumen
## # A tibble: 5 x 6
## año min media mediana max ds
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2013 109 64873. 16786 260972 86693.
## 2 2014 19 53395. 8859 227162 74025.
## 3 2015 81 46029. 10590 173391 58048.
## 4 2016 10 48588. 9596 199906 60930.
## 5 2017 22 43625. 11988 179535 55070.
Se podrían dividir los datos en más de dos partes. Tentativamente, los cuartiles dividen el conjunto de datos en cuatro partes iguales y las observaciones arriba del tercer cuartil constituyen el cuarto superior del conjunto de datos, el segundo cuartil es idéntico a la mediana y el primer cuartil separa el cuarto inferior de los tres cuartos superiores. Asimismo, un conjunto de datos (muestra o población) puede ser incluso más finamente dividido por medio de percentiles, el 99o percentil separa el 1% más alto del 99% más bajo, y así sucesivamente.
El reporte de una medida de centro da sólo información parcial sobre un conjunto o distribución de datos. Diferentes muestras o poblaciones pueden tener medidas idénticas de centro y aún diferir entre sí.
La medida más simple de variabilidad en una muestra es el rango, el cual es la diferencia entre los valores muestrales más grande y más pequeño. Un defecto del rango, no obstante, es que depende de sólo las dos observaciones más extremas y hace caso omiso de las posiciones de los valores restantes. Las medidas principales de variabilidad implican las desviaciones de la media.
La desviación estándar muestral, denotada por s, es la raíz cuadrada (positiva) de la varianza. Ambas son no negativas. Una interpretación preliminar de la desviación estándar muestral es que es el tamaño de una desviación típica o representativa de la media muestral dentro de la muestra dada.
Dado que no toda la información que encontramos en bases de datos es de tipo númerica, es importante conocer algunas de las herramientas que son útiles para lidiar con valores de texto.
cuyo <- c("LA RIOJA",
"MENDOZA",
"SAN JUAN",
"SAN LUIS")
str_length(cuyo)
## [1] 8 7 8 8
str_c(cuyo, collapse = ",")
## [1] "LA RIOJA,MENDOZA,SAN JUAN,SAN LUIS"
str_sub(cuyo, 1, 5)
## [1] "LA RI" "MENDO" "SAN J" "SAN L"
str_count(cuyo, "[AEIOU]")
## [1] 4 3 3 3
str_detect(cuyo, "[x]")
## [1] FALSE FALSE FALSE FALSE
str_extract(cuyo, "LA")
## [1] "LA" NA NA NA
str_replace(cuyo, " ", "_")
## [1] "LA_RIOJA" "MENDOZA" "SAN_JUAN" "SAN_LUIS"
Existe una gran cantidad de funciones de texto, en el siguiente link podrán encontrar el manual de stringr resumido: http://edrub.in/CheatSheets/cheatSheetStringr.pdf
financiamiento <- read.csv2("proyectos-financiamiento-externo.csv")
glimpse(financiamiento)
## Rows: 33
## Columns: 9
## $ proyecto_id <chr> "BID N° 1868 OC/AR", "BIRF N° 7706-AR", "BID ...
## $ proyecto_nombre <chr> "Programa de Gestión Integral de Residuos Sól...
## $ proyecto_origen <chr> "BID", "BIRF", "BID", "GEF", "GEF", "GEF", "G...
## $ proyecto_fuente <int> 22, 22, 22, 21, 21, 21, 21, 21, 21, 21, 21, 2...
## $ proyecto_tipo <chr> "Préstamo", "Préstamo", "Préstamo", "Donación...
## $ proyecto_monto_USD <int> 20200000, 718032000, 100000000, 3126060, 3850...
## $ proyecto_responsable <chr> "Implementado por GIRSU (Suprograma II) y por...
## $ proyecto_estado <chr> "Cerrado en Febrero 2017", "En implementació...
## $ proyecto_localización <chr> "Provincia de Neuquén ( San Martín de los And...
financiamiento <- financiamiento %>%
mutate(
cerrado = if_else(str_detect(proyecto_estado, "Cerrado|cierre"), 1, 0),
primer_org = word(proyecto_id),
numero_proyecto = str_extract(proyecto_id, "[[:digit:]]+"))
Las expresiones regulares suelen ser complejas hasta que uno tiene mucha práctica en usarlas. En los siguientes links pueden encontrar recursos de ayuda para utilizarlas: https://www.garrickadenbuie.com/project/regexplain/ http://web.mit.edu/hackl/www/lab/turkshop/slides/regex-cheatsheet.pdf *https://stringr.tidyverse.org/articles/regular-expressions.html
Excel
library(janitor)
curso <- read.csv2("curso.csv", encoding = "UTF-8")
curso_n <- curso %>%
clean_names() %>%
remove_empty("rows") %>%
remove_constant()
duplicados <- curso_n %>%
get_dupes()
curso_n <- anti_join(curso_n, duplicados, by = "nombre_de_pila")
curso_n %>%
tabyl(carrera) %>%
adorn_totals() %>%
adorn_percentages("col") %>%
adorn_pct_formatting() %>%
adorn_ns()
## carrera n percent
## Abogacía 0.2 (3) 18.8% (0.1875)
## Actuario 0.1 (1) 6.2% (0.0625)
## Antropología 0.1 (1) 6.2% (0.0625)
## Arquitectura 0.1 (2) 12.5% (0.125)
## Economía 0.3 (5) 31.2% (0.3125)
## Ingeniería 0.1 (1) 6.2% (0.0625)
## RRHH 0.1 (1) 6.2% (0.0625)
## Sociología 0.1 (2) 12.5% (0.125)
## Total 1.0 (16) 100.0% (1)
La estadística nos enseña sobre cómo pueden realizarse los procesos de generacíon de datos de una manera eficiente, las técnicas y herramientas para reunir información, analizar resultados, sacar conclusiones inteligentes y tomar decisiones informadas ante la presencia de incertidumbre y variación.
La estadística descripptiva nos ayuda a resumir y describir características importantes de un conjunto de datos. Podemos hacerlo con medidas resúmen, tablas y elementos gráficos.
Las restricciones de tiempo, dinero y otros recursos escasos casi siempre hacen que conocer las características de una población sea poco práctico o factible. En su lugar, se selecciona un subconjunto de la población, una muestra. Por lo general nos interesan una cantidad finita y conocida de caracteristicas de los objetos en una población: éstas pueden ser categóricas, tal como el género o de naturaleza numérica como la edad.
En un problema de estadística, el experimentador dispone de las características de una muestra y esta información le permite sacar conclusiones con respecto a la población. La probabilidad discurre de la población a la muestra (razonamiento deductivo), mientras que la estadística inferencial discurre de la muestra a la población (razonamiento inductivo). Antes de que se pueda entender lo que una muestra particular pueda decir sobre la población, primero se deberá entender la incertidumbre asociada con la toma de una muestra de una población dada.
Todo a nuestro alrededor es aleatorio. La teoría de la probabilidad es la herramienta matemática que nos permite analizar los eventos aleatorios de manera estructurada. La probabilidad de un evento es un número que indica que tan factible es la ocurrencia de este. Este número siempre está entre 0 y 1, donde 0 significa imposibilidad, mientras 1 significa certidumbre.
Pensemos en el ejemplo del dado. Probabilidad es asignar a cada cara un número, el cual dará una medida precisa de la oportunidad de que salga esa cara al tirar el dado.
La distribución de probabilidad hace referencia a los posibles valores teóricos de cada uno de los resultados pertenecientes al espacio muestral.
Existen dos tipos de distribuciones, dependiendo si el espacio muestral es o no numerable.
Distribuciones discretas: el ejemplo de dado, donde el conjunto de resultados posibles es acotado.
Distribuciones continuas: el conjunto de resultados posibles es tan grande que no se puede enumerar la probabilidad de cada caso. Ej.: talla de una población. En este caso, no podemos definir en una tabla la probabilidad de cada uno de los posibles valores. Sin embargo, sí podemos definir una función de probabilidad, la densidad. Según qué función utilicemos, cambiará la forma de la curva. Los parámetros describen a la función de probabilidad. Por lo tanto hacen referencia a los atributos de la población.
Uno de los objetivos principales de la estadística es estimar parámetros desconocidos. Para aproximar estos parámetros, se escoge un estimador, es decir, una función que depende de observaciones tomadas aleatoriamente (muestra). Ej.: Media.
El estimador nos devuelve un número. Esto es una inferencia de cuál creemos que es la media. Pero no es seguro que esa sea realmente la media. Esto es lo que denominamos estimación puntual.
A diferencia de los estimadores puntuales, los intervalos de confianza estiman un parámetro especificando un rango de posibles valores. Dicho intervalo está asociado con un nivel de confianza, que se define como la probabilidad que el procedimiento usado para generar el intervalo produzca un intervalo que contenga el parámetro verdadero.
Por su parte, también podemos calcular la probabilidad de que el parámetro poblacional sea mayor, menor o igual a un cierto valor. Esto es lo que se conoce como test de hipótesis. Los tests se construyen con dos hipótesis: La hipótesis nula y la hipótesis alternativa. Lo que buscamos es ver si hay evidencia suficiente para rechazar la hipótesis nula.
Significatividad en los tests: Muchas veces decimos que algo es “estadísticamente significativo”. Detrás de esto se encuentra un test de hipótesis que indica que hay una suficiente significativdad estadística.
La significatividad estadística (alpha), es la probabilidad de rechazar la hipotesis nula cuando en realidad es cierta. Por eso, cuanto más bajo el valor, más seguros estamos de no equivocarnos. Por lo general testeamos con valores de alpha de 1%, 5% y 10%, dependiendo del área de estudio.
El p-valor es la mínima significatividad para la que rechazo el test. Es decir, cuanto más bajo es el p-valor, más seguros estamos de rechazar la hipotesis nula.
Hay una seríe de gráficos que resultan de utilidad como herramienta para la estadística descriptiva. Exploraremos algunos de ellos continuando con los datos de exportación de frutas a partir de la siguiente pregunta: ¿cuáles son los países receptores del mayor volúmen de frutas?
Para responder nuestra pregunta con esos datos, deberíamos primero agrupar por país de destino y calcular la cantidad de toneladas recibidas por cada uno.
toneladas_por_pais <- frutas %>%
group_by(pais_destino) %>%
summarise(toneladas_fruta = sum(totales)) %>%
ungroup() %>%
arrange(desc(toneladas_fruta))
head(toneladas_por_pais)
## # A tibble: 6 x 2
## pais_destino toneladas_fruta
## <chr> <dbl>
## 1 Otros 1040966
## 2 Rusia 842293
## 3 Brasil 782235
## 4 Países Bajos 350591
## 5 España 340229
## 6 Italia 316050
Ya transformamos los datos para que se ajusten a nuestra pregunta, vamos a utilizar visualizaciones para ver cómo se distribuyen.
Un gráfico de dispersión es un resumen interesante de datos numéricos cuando el conjunto de datos es razonablemente pequeño o existen pocos valores de datos distintos. Cada observación está representada por un punto. Nos da información sobre la localización, dispersión, valores extremos y brechas.
Veamos ahora un primer gráfico de dispersión (también denominados scatterplot) que nos muestre la dispersión de los datos en la variante “exportación de frutas por pais de destino”.
ggplot(data = toneladas_por_pais) +
geom_point(aes(x = pais_destino, y = toneladas_fruta)) # gráfico de puntos
Nos brinda algo de información sobre la distribucíon: hay un grupo mayoritario de países con bajos volúmenes en toneladas, otro grupo con valores intermedios y tres puntos que se destacan claramente por el volúmen recibido. Dado que no se lee bien el eje x, podemos reemplazar los puntos por etiquetas o labels con el nombre de cada país para ver rápidamente cuáles estoy observando.
ggplot(data = toneladas_por_pais) +
geom_label(aes(x = pais_destino, y = toneladas_fruta, label = pais_destino))
Ahora observamos con el nombre de Otros lo que parece ser una agrupación de países no enumerados que en conjunto recibieron la mayor cantidad de toneladas en el período evaluado.
Los histogramas son gráficos comunmente usados para mostrar la distribución de los datos de una variable continua (como precios, toneladas, etc). El histograma permite decir si los valores que toma cada observación se agrupan en torno a un valor “típico” o medio -como en el caso de la llamada distribución normal-, o en torno a dos valores frecuentes (distribución bimodal), o con dispersión sin picos ni valles, donde no hay valores típicos ni atípicos - distribución uniforme. Esto suena complicado, pero visualmente es fácil de ver.
Analicemos la distribución de los datos que estuvimos viendo.
Hacer un histograma es simple con geom_histogram() sólo hay que elegir una variable y asignarla a las x. Si no aclaramos en cuántas partes queremos que el histograma se divida, lo hará en 30 partes o “bins” por default.
ggplot(toneladas_por_pais) +
geom_histogram(aes(x = toneladas_fruta))
Como pudimos observar con el scatterplot, vemos una distibución no uniforme, con muchos países con baja cantidad de toneladas, algunos países en valores intermedios y unos pocos con un volúmen francamente mayor.
Estos gráficos nos permiten conocer con mayor precisión la distribución de los datos, visualizando el centro (mediana), la dispersión (cuartos), simetría y valores extremos.
toneladas_por_pais_anio <- frutas %>%
group_by(pais_destino, año) %>%
summarise(toneladas_fruta_anual = sum(totales)) %>%
ungroup() %>%
arrange(desc(toneladas_fruta_anual))
ggplot() +
geom_boxplot(data = toneladas_por_pais_anio, aes(x= pais_destino, y = toneladas_fruta_anual))