Esta publicación tiene dos objetivos, uno personal y otro general. El objetivo personal es poder tener un apunte en limpio sobre el uso de funciones personalizadas con R, pues estas permiten automatizar tareas comunes de una manera más eficiente que copiar y pegar. El segundo objetivo, el general, es simplemente compartir con todo aquel que guste de analizar datos y tampoco esté familiarizado con el uso de funciones
No asumo que mi código sea el más eficiente o esté libre de errores, así que cualquier corrección o comentario házmelo saber por este medio o a través de mi twitter ecodiegoale.
Si te interesa profundizar sobre la programación de funciones, te recomiendo leer R for Data Science de H. Wickham y G. Grolemund, disponible en este enlace.
Además, nunca está de más revisar stackoverflow, github, rpubs o preguntarle a ChatGPT.
Aviso: Si algún link no abre con click izquierdo, trata
con el derecho y abrir en otra ventana o pestaña.
En este blog se hará uso de la Jordà-Schularick-Taylor Macrohistory
Database (JSTMD) de una manera muy sencilla utilizando
principalmente la librería de tidyverse. Lo que haremos
será calcular la inflación y tasa de interés real de largo plazo para
todos los países disponibles y escribir una función que automatice la
elaboración de las gráficas de las variables calculadas. La inspiración
de este blog surgió tras la lectura del borrador del próximo libro de Santiago Capraro, Carlo Panico
y LD Torres, Inequality and Stagnation: A monetary
interpretation, sin duda un gran libro de macroeconomía
heterodoxa.
Para más información sobre la JSTMD revisar el siguiente enlace.
Lo primero será definir tu directorio de trabajo y cargar las librerías necesarias para el análisis.
#setwd("C:/Users/tu carpeta")
rm(list = ls()) #para borrar todos los objetos en la memoria de la consola
options(scipen=999) #para desactivar la notación científica
#Si no cuentas con los paquetes, usa el comando install.packages("ejemplo")
library(tidyverse)
library(haven)
library(scales)
library(ggtext)
Conforme avancemos en el blog haré mención de cuando estemos usando las librerías cargadas.
Hay varias formas de cargar una base de datos en tu ambiente de trabajo, la más común es descargar el archivo y guardarlo en el mismo directorio donde está tu script y posteriormente llamarla de alguna forma.
En este blog no lo haré así. Lo que haré será bajar directamente de su página la JSTMD y cargarla.
Vamos paso a paso. Primero, entramos al sitio y copiamos el enlace que proporcionan para bajar la base desde STATA. Lo sé, estamos usando R, pero igual funciona (¡viva el software abierto!).
url <- "https://www.macrohistory.net/app/download/9834512469/JSTdatasetR6.dta?t=1687773948"
Ahora, descargo el archivo desde la URL:
download.file(url, "datos.dta", mode = "wb")
#wb se refiere a "write binary". Este modo es comúnmente utilizado al descargar archivos binarios, como datos de Stata, para asegurarse de que los datos se manejen correctamente en binario.
Lo que hizo esta función fue descargar el archivo directamente desde la URL que proporcionamos y guardarlo con el nombre de datos.dta en el directorio de trabajo donde estamos trabajando. La extensión .dta es porque son datos para STATA.
Si abres la carpeta donde guardaste tu script encontrarás el archivo datos.dta.
Ya con el archivo descargado y almacenado en la misma carpeta, sigue cargarlo a R.
Para conseguirlo usaremos una función de la librería haven.
datos <- haven::read_dta("datos.dta")
Vamos a dar una mirada rápida a la estructura de nuestros datos:
glimpse(datos)
## Rows: 2,718
## Columns: 59
## $ year <dbl> 1870, 1871, 1872, 1873, 1874, 1875, 1876, 187…
## $ country <chr> "Australia", "Australia", "Australia", "Austr…
## $ iso <chr> "AUS", "AUS", "AUS", "AUS", "AUS", "AUS", "AU…
## $ ifs <dbl> 193, 193, 193, 193, 193, 193, 193, 193, 193, …
## $ pop <dbl> 1775, 1675, 1722, 1769, 1822, 1874, 1929, 199…
## $ rgdpmad <dbl> 3273.239, 3298.507, 3553.426, 3823.629, 3834.…
## $ rgdpbarro <dbl> 13.83616, 13.93686, 15.04425, 16.21944, 16.26…
## $ rconsbarro <dbl> 21.44973, 19.93080, 21.08501, 23.25491, 23.45…
## $ gdp <dbl> 208.78, 211.56, 227.40, 266.54, 287.58, 300.7…
## $ iy <dbl> 0.1092656, 0.1045791, 0.1304380, 0.1249862, 0…
## $ cpi <dbl> 2.708333, 2.666667, 2.541667, 2.541667, 2.666…
## $ ca <dbl> -6.1475940, 5.2607740, 7.8676360, -11.0478328…
## $ imports <dbl> 36, 34, 38, 49, 49, 50, 48, 52, 52, 48, 46, 5…
## $ exports <dbl> 37, 46, 53, 50, 54, 52, 51, 51, 50, 44, 59, 5…
## $ narrowm <dbl> 23.3, 27.2, 36.2, 38.6, 37.9, 37.5, 38.2, 40.…
## $ money <dbl> 54.3, 59.5, 68.5, 73.7, 79.3, 88.5, 95.8, 105…
## $ stir <dbl> 4.88, 4.60, 4.60, 4.40, 4.50, 4.60, 4.60, 4.5…
## $ ltrate <dbl> 4.911817, 4.844633, 4.737350, 4.671958, 4.653…
## $ hpnom <dbl> 0.4922526, 0.4698775, 0.4847942, 0.4698775, 0…
## $ unemp <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ wage <dbl> 0.4481183, 0.4394170, 0.4524690, 0.4916250, 0…
## $ debtgdp <dbl> 0.172568, 0.191799, 0.154920, 0.142692, 0.194…
## $ revenue <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ expenditure <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ xrusd <dbl> 0.3669461, 0.3691464, 0.3692388, 0.3624048, 0…
## $ tloans <dbl> 54.792, 53.748, 55.822, 65.380, 71.478, 79.95…
## $ tmort <dbl> 1.680, 1.766, 1.470, 1.364, 1.434, 1.564, 1.8…
## $ thh <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ tbus <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ bdebt <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ lev <dbl> 25.01666, 23.98300, 22.44565, 21.00500, 20.97…
## $ ltd <dbl> 119.83204, 107.03788, 94.90955, 101.70810, 10…
## $ noncore <dbl> 32.92877, 30.21366, 26.86041, 28.64502, 28.29…
## $ crisisJST <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
## $ crisisJST_old <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
## $ peg <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
## $ peg_strict <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
## $ peg_type <chr> "PEG", "PEG", "PEG", "PEG", "PEG", "PEG", "PE…
## $ peg_base <chr> "GBR", "GBR", "GBR", "GBR", "GBR", "GBR", "GB…
## $ JSTtrilemmaIV <dbl> NA, -0.42, 1.27, 0.59, -1.08, -0.51, -1.01, 0…
## $ eq_tr <dbl> -0.004903895, 0.110192969, 0.176749304, 0.151…
## $ housing_tr <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ bond_tr <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ bill_rate <dbl> 0.0488, 0.0460, 0.0460, 0.0440, 0.0450, 0.046…
## $ rent_ipolated <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ housing_capgain_ipolated <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ housing_capgain <dbl> NA, -0.045456301, 0.031746607, -0.030769771, …
## $ housing_rent_rtn <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ housing_rent_yd <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ eq_capgain <dbl> -0.07004543, 0.04165363, 0.10894547, 0.083086…
## $ eq_dp <dbl> 0.07141703, 0.06546638, 0.06299735, 0.0644841…
## $ eq_capgain_interp <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ eq_tr_interp <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ eq_dp_interp <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ bond_rate <dbl> 0.04911817, 0.04844633, 0.04737350, 0.0467195…
## $ eq_div_rtn <dbl> 0.06641459, 0.06819329, 0.06986062, 0.0698419…
## $ capital_tr <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ risky_tr <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ safe_tr <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
Es un dataframe de 2,718 observaciones y 59 variables. Algunas variables tienen nombres muy evidentes, otras no. Para conocer a detalle te recomiendo leer la documentación. Para acceder a ella podemos descargarla de la misma manera que la base de datos.
url <- "https://www.macrohistory.net/app/download/9834516169/JST_documentationR6.pdf?t=1676279836"
download.file(url, "documentacion.pdf", mode = "wb")
Ahora, si revisas la carpeta de tus documentos encontrarás el archivo PDF de la documentación, donde se aclara qué es cada variable y sus fuentes.
#Voy a borrar el archivo .dta porque ya no lo usaremos. También borraré el objeto "url".
file.remove("datos.dta")
## [1] TRUE
rm("url")
Ya hemos dado un primer vistazo a nuestra base utilizando la función
glimpse().
Ahora veremos el periodo que abarca la base y los países incluidos:
unique(datos$country)
## [1] "Australia" "Belgium" "Canada" "Switzerland" "Germany"
## [6] "Denmark" "Spain" "Finland" "France" "UK"
## [11] "Ireland" "Italy" "Japan" "Netherlands" "Norway"
## [16] "Portugal" "Sweden" "USA"
La base incluye a 18 países (desarrollados).
unique(datos$year)
## [1] 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884
## [16] 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899
## [31] 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914
## [46] 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929
## [61] 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944
## [76] 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959
## [91] 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974
## [106] 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989
## [121] 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004
## [136] 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019
## [151] 2020
Para el periodo de 1870-2020 con frecuencia anual.
Como dije al inicio de este blog, el objetivo es calcular la tasa de interés real de largo plazo y la inflación.
La inflación no es más que la tasa de crecimiento de los precios. En este caso, los precios están expresados como un índice de precios al consumidor donde 1990=100. Podemos expresar la inflación de la siguiente manera:
\(\pi_{it}= \frac{P_{it}}{P_{it-1}}-1\)
La expresión anterior no es más que una tasa de crecimiento. Donde \(\pi_{it}\) es la inflación del país i en el periodo t, \(P_{it}\) el nivel o índice de precios del país i en el periodo t y \(P_{it-1}\) es el mismo índice pero del periodo inmediato anterior.
En la JSTMD el índice de precios viene codificado
por la variable cpi.
Ahora, la definición de la tasa de interés real es un poco más complicada que la de la inflación.
La definición clásica está dada por la Ecuación de Fisher, la cual se expresa de la siguiente manera:
\(r_{t}= i_t-\pi^E_t\)
Donde \(r_t\) es la tasa de interés real, \(i_t\) la tasa de interés nominal y \(\pi^E_t\) la tasa de inflación esperada.
“This equation says that the real interest rate is simply the nominal interest adjusted for expected inflation. It is the real interest rate that is most important for investment and saving decisions, as it represents the true cost of borrowing (and the true return on saving).” (Carlin and Soskice, 2024, p. 29).
Lo anterior nos dice que necesitaríamos contar con un indicador de inflación esperada, que refleje las expectativas de las empresas sobre la inflación futura. Estas expectativas son las que influyen en las decisiones de inversión de las firmas, y, como resultado, impactan en los niveles de producción y empleo.
Lamentablemente, la JSTMD no cuenta con un indicador
parecido, nosotros hemos calculado la inflación observada, no la
esperada, sin embargo y dado que no es el objetivo de esta entrada,
omitiremos este detalle y calcularemos la tasa de interés real como la
diferencia entre la tasa de interés de largo plazo (ltrate)
y la inflación simplemente. Para introducirse en la discusión de las
cuestiones de la tasa de interés real recomiendo el libro Macroeconomics.
Institutions, Instability, and Inequality de Wendy Carlin y David
Soskice. También recomiendo este hilo
de twitter (nunca diré X) de Jamel Sandoval, donde se ofrecen
elementos más precisos sobre este tema.
No te recomiendo buscar el libro de Carlin y Soskice en Library Genesis donde, si bien solo se encuentra la edición anterior, está de manera gratuita.
Aclaradas las cuestiones teóricas, proseguimos.
datos <- datos %>%
group_by(country) %>%
mutate(
inf = ((cpi / lag(cpi)) -1)*100,
lt_r = ltrate - inf
)
El código anterior utiliza algunos verbos y funciones comunes de
tidyverse. Primero usamos group_by(), esta
función agrupa el dataframe por la columna country.
Esto significa que las operaciones subsiguientes se realizarán por cada
grupo definido por valores únicos en la columna country (por los
países). Luego, mutate() se utiliza para crear o modificar
columnas en el dataframe. Se crea una nueva columna llamada
inf. Esta columna calcula la tasa de inflación para
cada observación en el grupo. La fórmula toma el índice de precios al
consumidor observado (cpi) , lo divide por el valor del
cpi en la observación anterior (utilizando
lag()) y le resta 1. Luego, se multiplica por 100 para
expresar la tasa como un porcentaje. Por último, se crea otra nueva
columna llamada lt_r. Esta columna calcula la tasa de
interés real a largo plazo. Resta la tasa de inflación calculada
anteriormente de la tasa de interés nominal de largo plazo
(ltrate).
Ahora, una nota importante. En la documentación se aclara que la variable ltrate viene expresada en términos porcentuales, es decir, ya viene multiplicada por 100, sin embargo, personalmente yo prefiero trabajar con las variables en nivel, no en porcentaje, por lo que dividiré entre 100 las variables que usaremos.
datos <- datos %>%
mutate(
inf = inf/100,
lt_r = lt_r/100
) %>%
ungroup()
Una vez hecho lo anterior, siempre recomiendo observar cómo lucen nuestras variables:
head(datos %>%
group_by(country) %>%
select(year, country, inf) %>%
filter(year > 1979) %>%
pivot_wider(names_from = year,
values_from = inf))
## # A tibble: 6 × 42
## # Groups: country [6]
## country `1980` `1981` `1982` `1983` `1984` `1985` `1986` `1987` `1988`
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Australia 0.101 0.0969 0.111 0.101 0.0395 0.0674 0.0908 0.0849 0.0723
## 2 Belgium 0.0665 0.0763 0.0872 0.0767 0.0634 0.0487 0.0130 0.0155 0.0116
## 3 Canada 0.102 0.125 0.108 0.0582 0.0434 0.0395 0.0417 0.0436 0.0402
## 4 Switzerland 0.0402 0.0649 0.0566 0.0297 0.0292 0.0344 0.00752 0.0145 0.0186
## 5 Germany 0.0542 0.0633 0.0527 0.0328 0.0241 0.0218 -0.00125 0.00242 0.0127
## 6 Denmark 0.123 0.117 0.101 0.0691 0.0629 0.0468 0.0368 0.0399 0.0456
## # ℹ 32 more variables: `1989` <dbl>, `1990` <dbl>, `1991` <dbl>, `1992` <dbl>,
## # `1993` <dbl>, `1994` <dbl>, `1995` <dbl>, `1996` <dbl>, `1997` <dbl>,
## # `1998` <dbl>, `1999` <dbl>, `2000` <dbl>, `2001` <dbl>, `2002` <dbl>,
## # `2003` <dbl>, `2004` <dbl>, `2005` <dbl>, `2006` <dbl>, `2007` <dbl>,
## # `2008` <dbl>, `2009` <dbl>, `2010` <dbl>, `2011` <dbl>, `2012` <dbl>,
## # `2013` <dbl>, `2014` <dbl>, `2015` <dbl>, `2016` <dbl>, `2017` <dbl>,
## # `2018` <dbl>, `2019` <dbl>, `2020` <dbl>
El código anterior acomoda nuestra base de datos de la siguiente
manera, en las columnas tenemos los años y en las filas los países, la
variable observada es la inflación que calculamos. Para más detalle del
código anterior te recomiendo copiar y pegarlo en ChatGPT y pedirle que
te explique línea por línea lo que acabo de hacer. Puedes darle un poco
de contexto antes y especificarle que estoy usando R o
tidyverse.
Ahora calculo un cuadro resumen con la inflación y tasa de interés de largo plazo promedio para el periodo 1980-2020.
datos %>%
group_by(country) %>%
filter(year > 1979) %>%
summarise(
inf.avrg = (mean(inf, na.rm = TRUE))*100,
lt_r.avrg = (mean(lt_r, na.rm = TRUE))*100,
)
## # A tibble: 18 × 3
## country inf.avrg lt_r.avrg
## <chr> <dbl> <dbl>
## 1 Australia 3.97 3.59
## 2 Belgium 2.69 3.19
## 3 Canada 3.08 3.52
## 4 Denmark 2.93 3.63
## 5 Finland 3.13 3.21
## 6 France 2.96 3.05
## 7 Germany 2.07 2.66
## 8 Ireland 3.74 3.26
## 9 Italy 4.61 3.36
## 10 Japan 0.965 1.95
## 11 Netherlands 2.23 2.77
## 12 Norway 3.69 2.66
## 13 Portugal 6.62 2.29
## 14 Spain 4.54 3.09
## 15 Sweden 3.38 3.07
## 16 Switzerland 1.59 1.43
## 17 UK 3.40 2.90
## 18 USA 3.00 2.90
En la sección anterior calculamos las variables que nos interesan, además estimamos unas medias simples. Ahora lo que queremos es graficar y observar su evolución en el tiempo. Vamos paso a paso.
En ggplot2, un tema se refiere a un conjunto de
características visuales que determinan el aspecto y la apariencia
general de un gráfico. Los temas son una forma de personalizar el estilo
de tus gráficos y hacer que se vean de la manera que desees. Puedes
aplicar un tema a todo tu gráfico o modificar elementos específicos del
tema según sea necesario. También puedes usar temas predefinidos. A
continuación yo definiré mis propios visuales. Para más información
puedes revisar mis blogs sobre ggplot2 aquí y aquí.
Igualmente te recomiendo que copies y pegues mi tema en una conversación
con ChatGPT y le pidas te explique cada elemento. Por ahora, únicamente
me detendré a explicar plot.subtitle().
La forma en que utilizo plot.subtitle() y
ggtext está basada e inspirada en el código de Albert
Rapp.
El objetivo es mejorar la presentación de gráficos al reemplazar una leyenda grande con colores en las palabras del título.
theme_jstd <- theme(panel.background = element_blank(),
panel.border = element_blank(),
legend.position = "none",
axis.line.x = element_blank(),
axis.line = element_line(
colour = "black",
linewidth= 0.8),
axis.ticks = element_blank(),
axis.text.y = element_text(
colour = "black",
size = 10),
axis.text.x = element_text(
colour = "black",
size = 10),
axis.title.y = element_blank(),
axis.title.x = element_text(size = 10),
legend.key = element_blank(), #quita el fondo gris de las leyendas
panel.grid.major.y =element_line(
color = '#D7D6D6',
linewidth = 0.8),
panel.grid.minor.y = element_line(
color = "#D7D6D6",
linewidth = 0.1,
linetype = 'dashed'),
panel.grid.major.x = element_blank(),
plot.title = element_text(
size = 14, hjust = 0,
face = "bold", colour = "black"),
plot.caption = element_text(
size = 8, hjust = 0,
colour = "black"),
plot.subtitle = ggtext::element_markdown(
size = 12, hjust = 0,
face = "bold", colour = "black"))
Lo primero y más común para graficar la evolución de nuestras variables para un solo país es crear un dataframe filtrado a partir del original, donde la condición sea el país que queremos mostrar.
datos_usa <- datos %>%
filter(year > 1969 & country == "USA")
Hemos filtrado nuestro dataframe para que solo tenga datos de Estados Unidos a partir de 1970.
Luego, sigue graficarlo:
usa <- ggplot(datos_usa, aes(x = year)) +
geom_hline(yintercept = 0, linewidth = 0.8) +
geom_line(aes(y = inf, color = "Inflación"), size = 1) +
geom_line(aes(y = lt_r, color = "Tasa Larga"), size = 1) +
labs(title = "USA",
caption = "Authored by @ecodiegoale with information from JSTDatasetR6, 2023.",
subtitle = "<span style = 'color:#FF9800;'>Inflation</span> and <span style = 'color:#B80000;'>long-term real interest rate</span>",
x = " ") +
scale_color_manual(" ",
values = c("Inflación" = "#FF9800",
"Tasa Larga" = "#B80000"))+
scale_y_continuous(label = percent)+ #librería scales
theme_jstd
usa
Reitero, no me detendré en los detalles de cómo graficar, eso lo pueden revisar en mis Apuntes de ggplot2 ya citados o en cualquier manual de R. Lo único que destacaré es lo siguiente:
Para este gráfico hemos usado el dataframe filtrado datos_usa.
En scale_y_continuous() cuando aplicamos
label = percent estamos ajustando la escala del eje
Y para que sea continua y formateando las etiquetas en formato
de porcentaje. Por ejemplo, si tienes datos en el eje Y que van
de 0 a 1, la escala se ajustará para mostrar este rango y las etiquetas
se formatearán como porcentajes (por ejemplo, 0%, 25%, 50%, 75%, 100%).
Es por esto que dividí anteriormente nuestras variables entre
100.
El parámetro subtitle dentro de la función
labs() en ggplot2 se utiliza para establecer
el subtítulo del gráfico. En el código que usé, el subtítulo se está
personalizando utilizando formato de texto HTML y colores definidos en
scale_color_manual().
Se utiliza la etiqueta HTML con el atributo de estilo (style). El atributo color se establece en #FF9800, que es un código de color naranja. Esto significa que la palabra “Inflation” en el subtítulo se mostrará con un color naranja. De manera análoga se hace para “long-term real interest rate”, que se mostrará con un color rojo oscuro.
En resumen, el subtítulo utiliza colores diferentes (naranja y
rojo oscuro) para resaltar visualmente las partes específicas del texto.
Estos colores están definidos en scale_color_manual() y se
aplican directamente en el formato HTML del subtítulo para crear un
efecto visual personalizado y coherente con la paleta de colores del
gráfico. Para más detalle revisar el código ya citado de Albert
Rapp.
Si quieres guardar tus gráficos en buena calidad puedes hacerlo en un formato PNG o TIFF de la siguiente manera:
ggsave("usa.png", plot = usa, width = 14, height = 10, units = "cm")
ggsave("usa.tiff", plot = usa, width = 14, height = 10, units = "cm", dpi = 600)
Ahora lo mismo para Canadá.
datos_can <- datos %>%
filter(year > 1969 & country == "Canada")
ggplot(datos_can, aes(x = year)) +
geom_hline(yintercept = 0, linewidth = 0.8) +
geom_line(aes(y = inf, color = "Inflación"), size = 1) +
geom_line(aes(y = lt_r, color = "Tasa Larga"), size = 1) +
labs(title = "Canada",
caption = "Authored by @ecodiegoale with information from JSTDatasetR6, 2023.",
subtitle = "<span style = 'color:#FF9800;'>Inflation</span> and <span style = 'color:#B80000;'>long-term real interest rate</span>",
x = " ") +
scale_color_manual(" ",
values = c("Inflación" = "#FF9800",
"Tasa Larga" = "#B80000"))+
scale_y_continuous(label = percent)+
theme_jstd
Hemos hecho dos gráficos para dos países distintos, para conseguirlo tuvimos que crear dos nuevos dataframes a partir de nuestra base original por medio de filtros. Ahora, si quisiéramos graficar para los 18 países disponibles en la base tendríamos que realizar esta operación 16 veces más. Copiar y pegar código no suena muy eficiente o rápido. Además, al hacer esto nos exponemos a equivocarnos, pues podríamos copiar y pegar y no reemplazar apropiadamente los parámetros necesarios. Otro inconveniente es la creación de 16 dataframes más, donde cada uno contenga la misma información pero para países diferentes.
Es aquí cuando entran en juego las funciones personalizadas.
Como dije en la introducción, estas permiten automatizar tareas comunes de una manera más eficiente que copiar y pegar. Otra ventaja es que a medida que cambian los requisitos, solo necesitas actualizar el código en un lugar en lugar de en muchos, es decir, si hay cambios en los requisitos del código, solo necesitas realizar actualizaciones en la definición de la función en lugar de tener que buscar y actualizar múltiples instancias del mismo código copiado en varios lugares.
Aclaro, no soy programador, soy economista, así que mis funciones pueden no ser las mejores o más optimizadas.
No siendo un programador, escribir funciones puede demandar tiempo y esfuerzo. Antes de embarcarte en la tarea de crear una función, es recomendable evaluar su utilidad en situaciones específicas. Por ejemplo, para nuestro caso, si la base de datos solo contuviera información para 3 países, escribir una función podría ser más tardado que solo hacer 3 gráficos. No obstante, aprender a escribir funciones puede ser beneficioso, especialmente cuando te enfrentas a grandes conjuntos de datos y tareas analíticas que tienden a volverse repetitivas. Mi intención no es enseñarte a programar funciones de manera avanzada, ya que yo mismo sigo aprendiendo. Más bien, mi objetivo es mostrar de manera práctica cómo utilizar funciones para aplicarlas de inmediato en situaciones concretas.
Recomiendo mucho R for Data Science de H. Wickham y G. Grolemund, disponible en este enlace.
La estructura básica de una función en R es la siguiente:
nombre_funcion <- function(argumento1, argumento2, ...) {
# Cuerpo de la función
# Aquí se realiza el procesamiento o las operaciones deseadas
# Puedes utilizar los argumentos dentro del cuerpo de la función
# Al final, la función devuelve un resultado opcionalmente usando return()
return(resultado)
}
Viendo lo anterior, deducimos que la operación deseada será la filtración de nuestra base por país y su graficación. Recordemos el código anterior e identifiquemos los argumentos que tendrían que cambiar para cada país y los que se mantienen:
Primero, se filtra el dataframe usando la variable country.
datos_usa <- datos %>%
filter(year > 1969 & country == "USA")
Tendríamos que cambiar “USA” por cada uno de los elementos que se muestran a continuación.
unique(datos$country)
## [1] "Australia" "Belgium" "Canada" "Switzerland" "Germany"
## [6] "Denmark" "Spain" "Finland" "France" "UK"
## [11] "Ireland" "Italy" "Japan" "Netherlands" "Norway"
## [16] "Portugal" "Sweden" "USA"
Entonces, nuestra función tendrá dos argumentos, la base de datos empleada y el país filtrado.
Ahora, en cuanto al gráfico, este tendría que cambiar el dataframe que usa y !el título!:
usa <- ggplot(datos_usa, aes(x = year)) +
geom_hline(yintercept = 0, linewidth = 0.8) +
geom_line(aes(y = inf, color = "Inflación"), size = 1) +
geom_line(aes(y = lt_r, color = "Tasa Larga"), size = 1) +
labs(title = "USA",
caption = "Authored by @ecodiegoale with information from JSTDatasetR6, 2023.",
subtitle = "<span style = 'color:#FF9800;'>Inflation</span> and <span style = 'color:#B80000;'>long-term real interest rate</span>",
x = " ") +
scale_color_manual(" ",
values = c("Inflación" = "#FF9800",
"Tasa Larga" = "#B80000"))+
scale_y_continuous(label = percent)+ #librería scales
theme_jstd
Si nos fijamos bien, en labs() está escrito el título de manera fija. Tenemos que encontrar una manera que el título cambie conforme lo hace la información del gráfico. Es decir, si se grafica la información filtrada para Alemania, el título debe cambiar.
Lo demás no es necesario cambiar, por ejemplo, los años (year) y los valores del eje Y (inf y lt_r), no cambian, pues tienen el mismo nombre siempre, solo cambian sus valores conforme el país.
La función queda como sigue:
generar_graficos <- function(base_datos, individuo) {
#Rutina del gráfico
ggplot(data = base_datos %>%
filter(country == individuo & year > 1969),
aes(x = year)) +
geom_hline(yintercept = 0, linewidth = 0.8) +
geom_line(aes(y = inf, color = "Inflación"), size = 1) +
geom_line(aes(y = lt_r, color = "Tasa Larga"), size = 1) +
#Asignar título al gráfico conforme al país
labs(title = paste0(base_datos$country[base_datos$country == individuo][1]),
caption = "Authored by @ecodiegoale with information from JSTDatasetR6, 2023.",
subtitle = "<span style = 'color:#FF9800;'>Inflation</span> and <span style = 'color:#B80000;'>long-term real interest rate</span>",
x = " ") +
scale_y_continuous(label = percent) +
scale_color_manual(" ",values = c("Inflación" = "#FF9800",
"Tasa Larga" = "#B80000"))+
theme_jstd
}
El nombre de mi función es generar_graficos. El
primer argumento es la base de datos que usamos y el segundo es el país
filtrado, yo lo llamé individuo. Luego, iniciamos con
la graficación usando la función ggplot() como de
costumbre, pero observa que en data = ya no usamos
datos_usa o datos_can o cualquier base filtrada
individualmente por país, usamos un nombre genérico
base_datos. Después, a esa base genérica le aplicamos
un filtro por medio de una pipa %>%, le decimos que esa base genérica
tiene una columna llamada country la cual debe ser
igual (==) a algún individuo. El resto del gráfico
sigue como lo hicimos para Estados Unidos.
Vamos a deternos en esta línea:
#labs(title = paste0(base_datos$country[base_datos$country == individuo][1]),
Esta línea de código utiliza la función paste0() y la
indexación de vectores para construir el título de nuestro gráfico en
ggplot2.
La función paste0() se utiliza para concatenar los
elementos proporcionados sin ningún separador. En este caso, se utiliza
para concatenar el nombre del país seleccionado al título del
gráfico.
base_datos$country[base_datos$country == individuo]: Esto crea un subconjunto de la columna country en el dataframe base_datos donde el valor de country es igual a la variable individuo. En otras palabras, selecciona todas las filas en las que el país coincide con el valor de individuo.
Esto luciría más o menos así cuando individuo es igual a Alemania, por poner un ejemplo:
datos$country[datos$country == "Germany"]
## [1] "Germany" "Germany" "Germany" "Germany" "Germany" "Germany" "Germany"
## [8] "Germany" "Germany" "Germany" "Germany" "Germany" "Germany" "Germany"
## [15] "Germany" "Germany" "Germany" "Germany" "Germany" "Germany" "Germany"
## [22] "Germany" "Germany" "Germany" "Germany" "Germany" "Germany" "Germany"
## [29] "Germany" "Germany" "Germany" "Germany" "Germany" "Germany" "Germany"
## [36] "Germany" "Germany" "Germany" "Germany" "Germany" "Germany" "Germany"
## [43] "Germany" "Germany" "Germany" "Germany" "Germany" "Germany" "Germany"
## [50] "Germany" "Germany" "Germany" "Germany" "Germany" "Germany" "Germany"
## [57] "Germany" "Germany" "Germany" "Germany" "Germany" "Germany" "Germany"
## [64] "Germany" "Germany" "Germany" "Germany" "Germany" "Germany" "Germany"
## [71] "Germany" "Germany" "Germany" "Germany" "Germany" "Germany" "Germany"
## [78] "Germany" "Germany" "Germany" "Germany" "Germany" "Germany" "Germany"
## [85] "Germany" "Germany" "Germany" "Germany" "Germany" "Germany" "Germany"
## [92] "Germany" "Germany" "Germany" "Germany" "Germany" "Germany" "Germany"
## [99] "Germany" "Germany" "Germany" "Germany" "Germany" "Germany" "Germany"
## [106] "Germany" "Germany" "Germany" "Germany" "Germany" "Germany" "Germany"
## [113] "Germany" "Germany" "Germany" "Germany" "Germany" "Germany" "Germany"
## [120] "Germany" "Germany" "Germany" "Germany" "Germany" "Germany" "Germany"
## [127] "Germany" "Germany" "Germany" "Germany" "Germany" "Germany" "Germany"
## [134] "Germany" "Germany" "Germany" "Germany" "Germany" "Germany" "Germany"
## [141] "Germany" "Germany" "Germany" "Germany" "Germany" "Germany" "Germany"
## [148] "Germany" "Germany" "Germany" "Germany"
datos$country[datos$country == "Germany"][1]
## [1] "Germany"
En resumen, cuando en el gráfico se filtra información para un país, en el título se concatena el primer elemento de la columna country cuando sus valores son iguales al país que se usó para filtrar. Si se filtra para Alemania (individuo == “Germany”), en el título se pega el primer elemento de la columna country (“Germany”)
Una vez construida la función, hay que generar los vectores que contengan los argumentos que necesita para funcionar:
#Generar un vector con los valores únicos de la variable country
individuos <- unique(datos$country)
#este es el segundo argumento de la función que creamos, el primero es la base de datos
individuos
## [1] "Australia" "Belgium" "Canada" "Switzerland" "Germany"
## [6] "Denmark" "Spain" "Finland" "France" "UK"
## [11] "Ireland" "Italy" "Japan" "Netherlands" "Norway"
## [16] "Portugal" "Sweden" "USA"
Ahora necesitamos aplicar la función que creamos a cada elemento de
la lista individuos. Para eso usaremos la función
lapply(), la cual es una función en R que se utiliza para
aplicar una función (generar_graficos) a cada elemento
de una lista (o vector) y devuelve una lista con los resultados.
Vamos a hacer un ejemplo básico de la función lapply()
antes de usarla en nuestro análisis. Supongamos que tienes una lista de
números y quieres calcular el cuadrado de cada número usando lapply:
# Creamos una lista que contenga números (nosotros ya tenemos una lista, pero esta contiene gráficos)
numeros <- list(1, 2, 3, 4, 5)
# Definimos una función que calcule el cuadrado de un número
calcular_cuadrado <- function(x) {
return(x^2)
}
# Ahora aplicamos la función a cada elemento de la lista usando lapply
resultados_cuadrados <- lapply(numeros, calcular_cuadrado)
# Vemos los resultados
print(resultados_cuadrados)
## [[1]]
## [1] 1
##
## [[2]]
## [1] 4
##
## [[3]]
## [1] 9
##
## [[4]]
## [1] 16
##
## [[5]]
## [1] 25
Ahora, retomemos nuestro análisis. El resultado de nuestra función es una lista de gráficos, donde cada elemento de la lista corresponde al resultado de aplicar la función generar_graficos() a un elemento de la lista individuos.
lista_graficos <- lapply(individuos, function(x) {
generar_graficos(datos, x)
})
Procedemos a observar el contenido de nuestra lista. En teoría, debería ser una lista con 18 elementos (18 gráficos de los países).
#View(lista_graficos)
Con la línea anterior se te desplegará una ventana que contenga los elementos y detalles de tu lista. Cada elemento está enumerado del 1 al 18. Si bien ya logramos lo que queríamos, podemos hacer que esta lista sea más cómoda de utilizar. En vez de números, sería mejor que cada elemento tenga de nombre el país del gráfico que almacena.
lista_graficos <- setNames(lista_graficos, individuos)
Ahora, si quisiéramos ver únicamente el gráfico de Dinamarca habría que extraerlo de la lista. Esto se hace de la siguiente manera:
lista_graficos["Denmark"]
## $Denmark
Puedes extraer cada uno de los elementos únicamente cambiando el nombre, sin embargo, hacer esto 18 veces tampoco es muy eficiente.
Lo que haré a continuación será un bucle for, en un próximo blog tal vez me explaye más sobre esto, por ahora con ver el siguiente código basta:
output_dir <- "inflacion" #nombre de la carpeta donde almacenaré los gráficos
# Iterar a través de los gráficos y guardarlos en el directorio
for (nombre in names(lista_graficos)) {
filename <- file.path(output_dir, paste0(nombre, ".png"))
ggsave(filename, plot = lista_graficos[[nombre]], width = 14, height = 10, units = "cm")
}
El resultado final será que en tu carpeta donde está guardado tu script aparacerá otra carpeta llamada “inflacion”, la cual contendrá los 18 gráficos en formato PNG con sus respectivos nombres:
En esta entrada hemos visto cómo manipular datos con
tidyverse, especialmente datos macroeconómicos, procuré no
dejar de lado la teoría económica y mostrar comandos útiles para bajar y
cargar bases de datos y archivos desde internet directamente al ambiente
de R. El principal objetivo fue mostrar cómo visualizar
datos y automatizar su graficación por medio de funciones. Como dije
anteriormente, no siendo programador esta tarea puede ser desafiante,
pero puede ser beneficiosa y vale la pena aprenderla, siempre con un
objetivo práctivo en mente y un problema a resolver, de nada sirve
aprender algo de lo que no podamos servirnos.
Y recuerden, cualquiera puede programar.