library(tidyverse)
library(knitr)
library(scales)
library(kableExtra)
library(ggrepel)
library(dplyr)
library(broom)
Observatorio Productividad Laboral en Costa Rica
Documento 2. Guia para el análisis de la base de datos
6 de agosto 2025
Preparado por Jorge Cornick para OIT
Contacto: jcornick@drpresearch.com
Comentarios preliminares
En este documento se elaboran las pautas para el análisis de una base de datos sobre productividad laboral.
Como un primer ejercicio, se trabaja con una base de datos sintética, descrita en el Documento 1. Construcción de base de datos sintética. Es decir, se trata de una base de datos cuyas características conocemos. En este documento, que servirá de guía para el análisis de bases de datos reales, se procede como si la estructura de la base de datos fuese desconocida.
De esta manera, esta guía podría utilizarse en ejercicios de entrenamiento del personal que tenga a cargo el mantenimiento del OPL-CR de la siguiente manera: en las secciones siguientes, se presenta una serie de preguntas sobre la base de datos, y se suministran respuestas a esas preguntas. Una versión del documento que excluya las preguntas, y que no de información previa sobre la base de datos, podría ser el punto de partida para que futuros usuarios analicen de manera independiente, y descubran las características, de la base de dato que se les suministre, sea la síntética que se usa aquí, sea la base de datos reales suministrada por el BCCR, cuando se disponga de ella.
Carga e inspeccion de la base de datos
Empezamos por cargar la base de datos y examinar su estructura y atributos
Examinamos primero los nombres y verificamos que la estructura sea un tibble
1
load("bd2.Rdata")
class(bd2)
[1] "tbl_df" "tbl" "data.frame"
names(bd2)
[1] "Año" "t" "Ubicacion" "Tamaño"
[5] "Exporta" "Empr" "Productividad" "Trabs"
[9] "VA"
Luego examinaos los atributos de la base de datos
attr(bd2, "variable_definitions") |>
enframe(name = "Variable", value = "Definición") |>
kable(caption = "Variables y definiciones") |>
kable_styling(
full_width = FALSE,
bootstrap_options = c("striped", "hover", "condensed")
)
Variable | Definición |
---|---|
Año | Año de la observación, del 2010 al 2020 |
t | Índice de tiempo que inicia en 0 para el año 2010 |
Ubicacion | Cantón al que corresponde la observación (C1 a C4) |
Tamaño | Clasificación de la empresa: Pequeña, Mediana, o Grande |
Exporta | Indica si la empresa exporta ('si') o no ('no') |
Empr | Número de empresas en la observación |
Productividad | Índice de productividad de la empresa |
Trabs | Número de trabajadores de la empresa |
VA | Valor Agregado Laboral (Productividad * Trabs * Empr)/1000 |
La siguiente tabla muestra los rangos de las variables numéricas
<- bd2 |> summarize(across(c(Año,t,Empr,Productividad,Trabs,VA),min))
mínimo <- bd2 |> summarize(across(c(Año,t,Empr,Productividad,Trabs,VA),max))
máximo <- rbind(mínimo,máximo)
resumen |> kable(caption = "Rangos de las variables numéricas") |>
resumen kable_styling(
full_width = FALSE,
bootstrap_options = c("striped", "hover", "condensed")
)
Año | t | Empr | Productividad | Trabs | VA |
---|---|---|---|---|---|
2010 | 0 | 121 | 0.1666262 | 6 | 1 |
2020 | 10 | 31718 | 2.6337447 | 1048 | 1351 |
Tenemos observaciones para 10 años, cuatro ubicaciones, tres tamaños de empresa (aunque en la tabla anterior solo aparecen los valores extremos y no se muestra “Mediana”) y dos posibles condiciones exportadoras, lo que resulta en 264 observaciones. El número de empresas varía entre 60 y 34,879, el número de trabajadores entre 19 y 238, y el valor agregado por empresa varia entre 1 y 284 (las unidades son arbitrarias).
Una primera característica que nos interesa de nuestra base de datos es la distinción entre variables estacionarias y variables que muestran una tendencia sea al crecimiento o la contracción. Examinaremos en primer término el comportamiento del número de empresas según tamaño, y del número
Número y distribución de los trabajadores
Total de trabajadores
Empezaremos analizando la evolución del total de trabajadores (que en nuestra base de datos es el total de personas ocupadas). Nuestra pregunta es si ese número es estable o muestra tendencias ya sea a crecer o decrecer. La pregunta es importante porque si el número de trabajadores mostrara una tendencia a crecer, los cambios en el PIB podrían tener dos orígenes, no mutuamente excluyentes: el cambio en el número de personas empleadas, o el cambio en la productividad. Si, por el contrario, la población ocupada fuese estacionaria, los cambios tendenciales en el PIB, si los hubiera, estarían necesariamente ligados a cambios en la productividad.
Los lectores que accedan a este documento en formato HTLM notarán que, por regla general, antes de cada gráfico aparece una línea con un triángulo negro seguido por la palabra “Code”. La idea es que los lectores tengan acceso inmediato a las imágenes pero que al mismo tiempo, si están interesados, puedan examinar el código, que se despliega al hacer “click” sobre el triángulo, y se puede copiar haciendo click sobre la imagen de un portapapels, que aparece arriba a la derecha, cuando el cursos se coloca entro del rectángulo gris que contiene el código.
Este último grupo de lectores notará que tras agrupar los datos en la segunda línea del código, se les desagrupa en la tercera línea. Es importante recordar esto como una buena práctica: después de cada operación de agrupación de los datos, se les debe volver a desagrupar. La razón es que si los datos se mantienen agregados, el comportamiento de código subsecuente, que no toma en cuenta esa agrupación, puede ser inesperado.
Un segundo detalle: cuando se utiliza summarize()
los datos se desagrupan dentro del mismo comando, es decir, dentro del paréntesis, usando .groups = 'drop"
. En cambio, cuando se utiliza mutate()
se debe hacer en una línea separada, después del pipe, que indique ungroup()
.
Un primer gráfico podría llevarnos a la conclusión de que la tendencia es a la reducción en el número de trabajadores:
Code
|>
bd2 group_by(Año) |>
summarize(Tot_trabs = sum(Trabs*Empr)/1000, .groups = 'drop') |>
ggplot(aes(Año, Tot_trabs)) +
theme_bw()+
#scale_color_brewer(palette = "Set3")+
geom_line(color = "skyblue", linewidth = 1)+
geom_text_repel(
aes(label = round(Tot_trabs)),
size = 2.5,
box.padding = 0.4,
max.overlaps = Inf
+
)geom_smooth(method = "lm", se = FALSE, color = "gray30", linetype = 3)+
scale_color_brewer(palette = "Set3")+
scale_x_continuous(breaks = 2010:2020) +
labs(
title = "Trabajadores, 2010-2020",
subtitle = "en miles",
x = "",
y = ""
+
) theme(
legend.position = "bottom",
axis.text.x = element_text(angle = 90, vjust = 0.5)
)
La tendencia negativa parece claramente marcada, pero esto es un efecto de escala. Las buenas prácticas en visualización de datos sugieren que la escala vertical siempre debe empezar en cero, pues de lo contrario la magnitud de los cambios en los datos se distorsiona. Vamos a añadir algún espacio también encima del valor máximo:
Code
|>
bd2 group_by(Año) |>
summarize(Tot_trabs = sum(Trabs*Empr)/1000, .groups = 'drop') |>
ggplot(aes(Año, Tot_trabs)) +
theme_bw()+
geom_line(color = "skyblue", linewidth = 1)+
coord_cartesian(ylim = c(0,10000))+
geom_text_repel(
aes(label = round(Tot_trabs)),
size = 2.5,
box.padding = 0.4,
max.overlaps = Inf
+
)geom_smooth(method = "lm", se = FALSE, color = "gray30", linetype = 3)+
scale_color_brewer(palette = "Set3")+
scale_x_continuous(breaks = 2010:2020) +
labs(
title = "Trabajadores, 2010-2020",
subtitle = "en miles",
x = "",
y = ""
+
) theme(
legend.position = "bottom",
axis.text.x = element_text(angle = 90, vjust = 0.5)
)
Este segundo gráfico muestra claramente que el número de trabajadores oscila dentro de márgenes estrechos y que no hay una tendencia clara. Sin entrar en detalles estadísticos, es intuitivamente obvio que si nuestra muestra fuese 2010-2015 podríamos pensar que hay una tendencia creciente, y que lo mismo sucedería si la muestra fuese 2017-2020. Esto sugiere que NO hay una tendencia definida. 2.
Para los lectores escépticos de una conclusión basada exclusivamente en una visualización de los datos, un modelo de regresión simple evidencia que el coeficiente de “Año” está muy lejos de ser significativo, y un intervalo de confianaa del 95% incluye valores que van de -128 a 30.
En general, en esta guía el análisis exploratorio será de caracter gráfico, pero el ejemplo que sigue sirve para ilustrar que usuarios más ambiciosos o técnicamente preparados podrían recurrir a modelos de regresión u otras técnicas estadísticas para analizar la estructura de los datos en bd2
Code
<- bd2 |>
datamod group_by(Año) |>
summarize(Tot_trabs = sum(Trabs*Empr)/1000, .groups = 'drop')
<- lm(Tot_trabs~Año, datamod)
mod kable(tidy(mod,conf.int = TRUE),
digits = 2,
caption = "<strong style='color:black;'>Modelo: Tot_trabs = bo + b1*Año") |>
kable_styling(
full_width = FALSE,
bootstrap_options = c("striped", "hover", "condensed")
)
term | estimate | std.error | statistic | p.value | conf.low | conf.high |
---|---|---|---|---|---|---|
(Intercept) | 170451.56 | 90898.67 | 1.88 | 0.09 | -35175.51 | 376078.63 |
Año | -81.93 | 45.11 | -1.82 | 0.10 | -183.98 | 20.12 |
La estacionaridad de la población ocupada en nuestra base de datos no es un accidente. Para facilitar el entrenamiento de los futuros administradores del OPL-CR, se ha creado un caso simple, en el que los cambios en el PIB solo pueden tener una causa: cambios en la productividad. Cuando se reformule el modelo usando datos reales, será necesario desentrañar otras posibles causas de cambio en la tendencia del PIB, como cambios en la población ocupada, su distribución por tamaño de empresas o su distribución por cantones.
Distribución de los trabajadores según tamaño de empresas
Examinaremos seguidamente dos visualizaciones de la distribución de los trabajadores según tamaño de la empresa. Usamos primero un gráfico de líneas, que permite visualizar el número absoluto de trabajadores por tamaño de empresa, y luego un gráfico de barras que permite visualizar con mayor facilidad su distribución porcentual.
Un detalle importante: para desplegar el número de trabajadores, en el primer gráfico, y el porcentaje de trabajadores, en el segundo, es necesario utilizar geom_text_repel()
y especificar cómo se calcularan las etiquetas con los valores. No existe opción para que ggplot()
haga esto de forma automática.
Gráfico lineas
Code
|>
bd2 group_by(Año,Tamaño) |>
summarize(Tot_trabs = sum(Trabs*Empr)/1000,.groups = 'drop') |>
ggplot(aes(Año, Tot_trabs,color = Tamaño)) +
theme_bw()+
scale_color_brewer(palette = "Dark2")+
geom_line(linewidth = 1)+
coord_cartesian(ylim = c(0,4000))+
geom_text_repel(
aes(label = round(Tot_trabs)),
size = 2.5,
box.padding = 0.4,
max.overlaps = Inf
+
)geom_smooth(method = "lm", se = FALSE, linetype = 3)+
scale_x_continuous(breaks = 2010:2020) +
labs(
title = "Trabajadores por tamaño de empresa, 2010-2020",
subtitle = "en miles",
x = "",
y = ""
+
) theme(
legend.position = "bottom",
axis.text.x = element_text(angle = 90, vjust = 0.5)
)
Gráfico de barras
Code
|>
bd2 group_by(Año,Tamaño) |>
summarize(Tot_trabs = sum(Trabs*Empr)/1000, .groups = 'drop') |>
group_by(Año) |>
mutate(share = Tot_trabs/sum(Tot_trabs)) |>
ungroup() |> # note diffference in ungrouping after summarize and mutate
ggplot(aes(x = factor(Año), y = Tot_trabs, fill = Tamaño)) +
theme_bw()+
geom_col(position = "fill")+
geom_text(
aes(label = percent(share, accuracy = 1)),
position = position_fill(vjust= 0.5),
size = 3.5
+
) scale_fill_brewer(palette = "Paired")+
scale_y_continuous(labels = percent)+
labs(
title = "Distribución de los trabajadores por tamaño de empresa, 2010-2020",
subtitle = "en porcentajes",
x = "",
y = ""
+
) theme(
legend.position = "bottom",
axis.text.x = element_text(angle = 90, vjust = 0.5)
)
Nótese que en el gráfico anterior, Año es tratado como un factor, como es requerido por geom_col()
. Si no se hiciera esa especifación, ggplot lo trataría como una variable continua, y geom_col()
no operaría correctamente.
En segundo lugar, es importante recordar que - es buena práctica desagrupar los datos después de cada operación de agrupamiento - el procedimiento correcto para hacerlo es distinto después de summarize()
y después de mutate()
En tercer lugar, en todos los gráficos hemos “piped” la base de datos completa a ggplot()
y luego realizado las operaciones necesarias para generar los gráficos que deseamos. Esto no es problemático porque nuestra base de datos es sencilla y también lo son nuestros gráficos. Sin embargo:
- mejores prácticas recomiendan crear un subconjunto de datos, solo con los que se necesitan para graficar
- “pipe” solo esos datos a
ggplot()
La razón para dividir el proceso en dos pasos es que en caso de problemas, es más fácil “despulgar” código dividido en módulos que grandes pedazos de código continuo.
En lo sustantivo, ambos gráficos permiten llegar a la misma conclusion: la distribución de los trabajadores entre empresas, según tamaño, oscila aleatoriamente, sin una tendencia definida. Era indispensable, sin embargo, dar este paso en el análisis, porque en las economías reales la distribución de la población trabajadora entre empresas según tamaño varía constantemente. Como además existe una correlación muy sólida entre productividad y tamaño de las empresas, el análisisi de los cambios en la distribución de los trabajadores entre empresas de diverso tamañao es un paso indispensable en el análisis de las causas de variaciones en la productividad agregada de un país.
Es posible que la productividad varíe también según otros criterios, entre los cuales ocupa un lugar destacado, en la literatura sobre estos temas, la distribucion sectorial de la poblacion trabajadora. En nuetra base de datos sintética no hay información sectorial, pero las mísmas técnicas que se han utilizado para analizar la distribución de los trabajadores según tamaño de empresa podrían aplicarse, con apenas modificaciones menores en el código, para analizar esa distribución según sectores, o cualquier otro criterio de segmentación incluido en la base de datos con la que estemos trabajando. Seguidamente se muestra un ejemplo en el que el criterio es la distribucion pro cantones.
Distribución de la población ocupada por cantones
La distribución de la población ocupada en el territorio también tiende a cambiar a lo largo del tiempo, y con frecuencia se pueden identificar polos de desarrollo o centros urbanos primarios que tienden a concentrar un porcentaje creciente de la población y el empleo, y otros cuya población más bien tiende a decrecer gradualmente. Veamos si a lo largo de esta dimensión hay tendencias al cambio en bd2 (nombre que usaremos, en adelante, tanto para referirnos a la base de datos propiamente dicha como a la economía imaginaria que ella representa)
Code
|>
bd2 group_by(Año,Ubicacion) |>
summarize(Tot_trabs = sum(Trabs*Empr)/1000, .groups = 'drop') |>
group_by(Año) |>
mutate(share = Tot_trabs/sum(Tot_trabs)) |>
ungroup() |> # note diffference in ungrouping after summarize and mutate
ggplot(aes(x = factor(Año), y = Tot_trabs, fill = Ubicacion)) +
theme_bw()+
geom_col(position = "fill")+
geom_text(
aes(label = percent(share, accuracy = 1)),
position = position_fill(vjust= 0.5),
size = 3.5
+
) scale_fill_brewer(palette = "Paired")+
scale_y_continuous(labels = percent)+
labs(
title = "Trabajadores por cantón, 2010-2020",
subtitle = "en porcentajes",
x = "",
y = ""
+
) theme(
legend.position = "bottom",
axis.text.x = element_text(angle = 90, vjust = 0.5)
)
La respuesta es negativa: no hay tendencias a un cambio permanente en la distribucion de los trabajadores pero, al igual que en el caso anterior, este es un paso indispensable en el análisis de la productividad laboral, ya que al trabajar con bases de datos reales inevitablemente encontraremos tendencias de cambio en la distribución geográfica del empleo que debemos identificar.
Tamaño y distribucion del parque empresarial
Total de empresas
El parque empresarial de bd2 está constituido por alrededor de 135,000 empresas, número que muestra apenas pequeñas variaciones aleatorias a lo largo del tiempo.
|>
bd2 group_by(Año) |>
summarize(Tot_empr = sum(Empr)/1000, .groups = 'drop') |>
ggplot(aes(Año, Tot_empr)) +
theme_bw()+
scale_color_brewer(palette = "Set3")+
geom_line(color = "skyblue", linewidth = 1)+
coord_cartesian(ylim = c(0,200))+
geom_text_repel(
aes(label = round(Tot_empr)),
size = 2.5,
box.padding = 0.4,
max.overlaps = Inf
+
)geom_smooth(method = "lm", se = FALSE, color = "gray30", linetype = 3)+
scale_color_brewer(palette = "Set3")+
scale_x_continuous(breaks = 2010:2020) +
labs(
title = "Número total de empresas",
subtitle = "en miles",
x = "",
y = ""
+
) theme(
legend.position = "bottom",
axis.text.x = element_text(angle = 90, vjust = 0.5)
)
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.
`geom_smooth()` using formula = 'y ~ x'
Empresas por tamaño
La distribución de empresas por tamaño se muestra en los dos siguientes gráficos. El primer gráfico resulta difícil de leer, porque la cantidad de empresas pequeñas es mucho mayor que la de las medianas y grandes. El gráfico se incluye en esta guía - a diferencia de lo que sucedería con un reporte - justamente para mostrar un problema, y como podemos solucionarlo.
|>
bd2 group_by(Año, Tamaño) |>
summarize(Tot_empr = sum(Empr)/1000, .groups = 'drop') |>
ggplot(aes(x = Año, y = Tot_empr, color = Tamaño)) +
theme_bw()+
scale_color_brewer(palette = "Paired")+
geom_line(linewidth = 1)+
coord_cartesian(ylim = c(0,150))+
geom_text_repel(
aes(label = round(Tot_empr)),
size = 3,
box.padding = 0.4,
max.overlaps = Inf
+
)geom_smooth(method = "lm", se = FALSE, linetype = 3)+
scale_x_continuous(breaks = 2010:2020) +
labs(
title = "Número total de empresas por tamaño",
subtitle = "en miles",
x = "",
y = ""
+
) theme(
legend.position = "bottom",
axis.text.x = element_text(angle = 90, vjust = 0.5)
)
`geom_smooth()` using formula = 'y ~ x'
En el siguiente gráfico, se muestra por separado, y usando escalas diferentes, la evolución del número de empresas según tamaño. La información es mucho más fácil de leer:
|>
bd2 group_by(Año, Tamaño) |>
summarize(Tot_empr = sum(Empr)/1000, .groups = 'drop') |>
ggplot(aes(Año, Tot_empr, color = Tamaño)) +
theme_bw() +
scale_color_brewer(palette = "Dark2") +
geom_line(linewidth = 1) +
geom_text_repel(
aes(label = round(Tot_empr)),
size = 3,
box.padding = 0.4,
max.overlaps = Inf
+
) geom_smooth(method = "lm", se = FALSE, linetype = 3) +
facet_wrap(~ Tamaño, scales = "free_y", ncol = 1) +
scale_x_continuous(breaks = 2010:2020) +
labs(
title = "Número total de empresas por tamaño",
subtitle = "en miles",
x = "",
y = ""
+
) theme(
legend.position = "none",
axis.text.x = element_text(angle = 90, vjust = 0.5)
)
`geom_smooth()` using formula = 'y ~ x'
Empresas por cantón
Usamos la misma técnica que en el caso anterior (facet_wrap()
) de manera que la información de cada cantón se despliegue en un panel independiente.
|>
bd2 group_by(Año, Ubicacion) |>
summarize(Tot_empr = sum(Empr)/1000, .groups = 'drop') |>
ggplot(aes(Año, Tot_empr, color = Ubicacion)) +
theme_bw() +
scale_color_brewer(palette = "Dark2") +
geom_line(linewidth = 1) +
geom_text_repel(
aes(label = round(Tot_empr)),
size = 3,
box.padding = 0.4,
max.overlaps = Inf
+
) coord_cartesian(ylim = c(0,50))+
geom_smooth(method = "lm", se = FALSE, linetype = 3) +
facet_wrap(~ Ubicacion, scales = "free_y", ncol = 2) +
scale_x_continuous(breaks = 2010:2020) +
labs(
title = "Número total de empresas por cantón",
subtitle = "en miles",
x = "",
y = ""
+
) theme(
legend.position = "none",
axis.text.x = element_text(angle = 90, vjust = 0.5)
)
`geom_smooth()` using formula = 'y ~ x'
Una vez más, encontramos que la variable es estacionaria, es decir, que la distribución de empresas por cantón no varía a lo largo del tiempo. Esto es un artificio de nuestra base de datos sintética3.
Empresas por tamaño y condición exportadora
El siguiente gráfico muestra el número de empresas por tamaño y condición exportadora. Nótese que la mayor parte de las empresas exportadoras son pequeñas, con un número semejante de empresas exportadoras grandes y medianas. ¿Debemos concluir que la probabilidad de que una empresa exporte es mayor en el caso de empresas pequeñas?
|>
bd2 group_by(Año, Tamaño, Exporta) |>
summarise(Empresas = mean(Empr), .groups = 'drop') |>
ggplot(aes(x = Año, y = Empresas, color = Tamaño, linetype = Exporta)) +
theme_bw()+
scale_color_brewer(palette = "Dark2")+
geom_line(linewidth = 1) +
geom_text_repel(
aes(label = round(Empresas)),
size = 2.5, # Text size
box.padding = 0.4, # Space between label and point
max.overlaps = Inf # Ensure all labels are shown
+
) facet_wrap(~ Tamaño, scales = "free_y", ncol = 1) +
scale_x_continuous(breaks = 2010:2020) +
labs(
title = "Número promedio de empresas según tamaño y condicion exportadora",
x = "",
y = "",
color = "Tamaño",
linetype = "Exporta"
+
) theme(
axis.text.x = element_text(angle = 90, vjust = 0.5),
legend.position = "bottom"
+
)guides(color = "none")
En el gráfico siguiente, la información se despliega en porcentajes en vez de valores absolutos, y esto nos permite responder a la pregunta anterior: la probabilidad de que una empresa exporte es 5 veces mayor en el caso de las empresas medianas que en el de las pequeñas, y 11 veces mayor en el caso de las grandes.
# Calculo del porcentaje de exportadores dentro de cada tamaño
<- bd2 |>
plot_data group_by(Año, Tamaño, Exporta) |>
summarise(Total_Empr = sum(Empr), .groups = 'drop') |>
group_by(Año, Tamaño) |>
mutate(Share = Total_Empr / sum(Total_Empr)) |>
ungroup()
# Crear los gráficos
ggplot(plot_data, aes(x = factor(Año), y = Total_Empr, fill = Exporta)) +
geom_col(position = "fill") +
geom_text(
aes(label = if_else(Share > 0.02, percent(Share, accuracy = 1), "")),
position = position_fill(vjust = 0.5),
size = 3,
color = "white"
+
)
# Crear un panel separado para cada tamaño de empresa
facet_wrap(~ Tamaño, ncol = 1) +
scale_y_continuous(labels = percent) +
scale_fill_brewer(palette = "Set1") +
labs(
title = "Distribucion porcentual de las empresas según condición exportadora",
subtitle = "Según tamaño, (2010-2020)",
x = "",
y = "",
fill = "Exporta:"
+
) theme_bw() +
theme(
axis.text.x = element_text(angle = 90, vjust = 0.5),
legend.position = "bottom"
)
Evolución de VA global y por componentes
Nótese que como el número total de empresas y trabajadores promedio se mantiene constante en nuestra base de datos, lo mismo que su distribución según tamaño de empresas y cantón, los cambios tendenciales en el PIB se explican exclusivamente por cambios en la productividad.
Este no es, por supuesto, el caso general, en el cual los cambios en la asignación de recursos entre empresas, sectores y regiones es la norma.
El gráfico siguiente muestra la evolucion del PIB. La tendencia pareciera ser ligeramente ascendente.
PIB global
El gráfico siguiente muestra la evolucion del PIB. La tendencia pareciera ser ligeramente ascendente.
<- bd2 |>
pib_l group_by(Año)|>
summarize(PIB = sum(VA)) |>
select(Año, PIB) |>
mutate(PIB_ind = round(PIB/PIB[1]*100)) |>
ungroup()
|> ggplot(aes(Año, PIB_ind))+
pib_l theme_bw()+
geom_line(color = "skyblue", linewidth = 1)+
geom_smooth(method = "lm", se = FALSE, color = "gray30", linetype = 3)+
scale_color_brewer(palette = "Set3")+
scale_x_continuous(breaks = 2010:2020) +
coord_cartesian(y=c(50,120))+
labs(
title = "Valor agregado laboral, 2010-2020",
subtile = "2010 = 100",
x = "",
y = "Índice PIB"
+
) theme(
legend.position = "bottom",
axis.text.x = element_text(angle = 90, vjust = 0.5)
)
Nuestra siguiente pregunta es de dónde viene el impulso para esa tendencia ligeramente creciente.
PIB por tamaño de empresas
Examinamos, primero la tendencia del PIB según tamaño de empresas. El gráfico siguiente sugiere que la tendencia es ascendente únicamente dentro de las empresas grandes.
<- bd2 |>
pib_ltamaño group_by(Año,Tamaño)|>
summarize(PIB = sum(VA), .groups = 'drop') |>
group_by(Tamaño) |>
mutate(PIB_Ind = PIB / PIB[Año == 2010]*100) |>
ungroup() |>
select(Año,Tamaño,PIB,PIB_Ind)
|> ggplot(aes(Año, PIB_Ind, color = Tamaño))+
pib_ltamaño theme_bw()+
scale_color_brewer(palette = "Set2")+
facet_wrap(~Tamaño, ncol=1)+
geom_line(linewidth = 1)+
geom_text(aes(label = round(PIB_Ind, 1)), vjust = -0.8, size = 3.5,
check_overlap = TRUE) +
geom_smooth(method = "lm", se = FALSE, linetype = 3)+
scale_x_continuous(breaks = 2010:2020) +
labs(
title = "Indice Valor Agregado Labora, 2010-2020 por tamaño de empresa",
subtile = "2010 = 100",
x = "",
y = "Índice PIB"
+
) theme(
legend.position = "",
axis.text.x = element_text(angle = 90, vjust = 0.5)
)
`geom_smooth()` using formula = 'y ~ x'
Sabemos, sin embargo, que dentro de cada tamañao hay tanto empresas exportadoras como empresas que no exportan, de manera que cabe preguntarnos si el comportamiento del PIB es igual para unas y otras
<- bd2 |>
pib_ltamaño2 group_by(Año,Tamaño,Exporta)|>
summarize(
PIB = sum(VA, na.rm = TRUE),
.groups = 'drop') |>
group_by(Tamaño,Exporta) |>
mutate(PIB_ind2 = (PIB/PIB[Año == 2010])*100) |>
ungroup()
|> ggplot(aes(Año, PIB_ind2, color = Tamaño, linetype = Exporta))+
pib_ltamaño2 theme_bw()+
scale_color_brewer(palette = "Set2")+
facet_wrap(~Tamaño, ncol=1)+
geom_line(linewidth = 1)+
geom_text(aes(label = round(PIB_ind2, 1)), vjust = -0.8, size = 3.5,
check_overlap = TRUE,
show.legend = FALSE) +
scale_x_continuous(breaks = 2010:2020) +
labs(
title = "Indice Valor Agregado Laboral 2010-2020, por tamaño y condición exportadora",
subtile = "2010 = 100",
x = "",
y = "Índice PIB"
+
) theme(
legend.position = "bottom",
axis.text.x = element_text(angle = 90, vjust = 0.5)
+
)guides(color = "none")
Como veremos más adelante, la razón principal por la que la contribución de las empresas medianas al PIB aumenta entre 2010 y 2023, mientras que la de las grandes disminuye, es la variación aleatoria en el número de trabajadores, que aumenta en el caso de las empresas medianas, y se reduce en el caso de las grandes. Esto no nos dice nada sobre la evolución de la productividad por tamaño de empresa y condición exportadora, que se examina en la sección siguiente.
Evolucion de la productividad por tamaño de empresa y condicion exportadora
Examinemos ahora el comportamiento de la productividad según tamaño de las empresas y condición exportadora:
Gráfico empresas pequeñas
Code
|> filter(Tamaño == "Pequeña") |>
bd2 group_by(Año, Exporta) |>
ggplot(aes(Año, Productividad, color = Exporta))+
theme_bw()+
scale_color_brewer(palette = "Set2")+
geom_point()+
geom_smooth(method = "lm", se = FALSE)+
scale_x_continuous(breaks = 2010:2020) +
labs(
title = "Evolución de la productividad de las pequeñas empresas, por condición exportadora",
subtitle = "2010-2020",
x = "",
y = "Índice de productividad"
+
) theme(
legend.position = "bottom",
axis.text.x = element_text(angle = 90, vjust = 0.5)
)
Gráfico empresas medianas
|> filter(Tamaño == "Mediana") |>
bd2 group_by(Año, Exporta) |>
ggplot(aes(Año, Productividad, color = Exporta))+
theme_bw()+
scale_color_brewer(palette = "Set2")+
geom_point()+
geom_smooth(method = "lm", se = FALSE)+
scale_x_continuous(breaks = 2010:2020) +
labs(
title = "Evolución de la productividad de las empresas medianas, por condición exportadora",
subtitle = "2010-2020",
x = "",
y = "Índice de productividad"
+
) theme(
legend.position = "bottom",
axis.text.x = element_text(angle = 90, vjust = 0.5)
)
`geom_smooth()` using formula = 'y ~ x'
Gráfico empresas grandes:
|> filter(Tamaño == "Grande") |>
bd2 group_by(Año, Exporta) |>
ggplot(aes(Año, Productividad, color = Exporta))+
theme_bw()+
scale_color_brewer(palette = "Set2")+
geom_point()+
geom_smooth(method = "lm", se = FALSE)+
scale_x_continuous(breaks = 2010:2020) +
labs(
title = "Evolución de la productividad de las empresas grandes, por condición exportadora",
subtitle = "2010-2020",
x = "",
y = "Índice de productividad"
+
) theme(
legend.position = "bottom",
axis.text.x = element_text(angle = 90, vjust = 0.5)
)
`geom_smooth()` using formula = 'y ~ x'
Gráfico todas las empresas, productividad media
En los tres gráficos anteriores, se muestran tanto las observaciones individuales como la tendencia lineal de la productividad para cada grupo de empresas.
Para tener una visión de conjunto, seguidamente se presenta la evolución de la productividad por tamaño de empresa y condición exportadora, pero en este caso, se calcula la productividad prometido para cada grupo de empresas, a fin de que el gráfico permita una lectura relativamente simple:
|>
bd2 group_by(Año, Tamaño, Exporta) |>
summarise(ProdMed = mean(Productividad), .groups = 'drop') |>
ggplot(aes(x = Año, y = ProdMed, color = Tamaño, linetype = Exporta)) +
theme_bw()+
scale_color_brewer(palette = "Dark2")+
geom_line(linewidth = 1.1) +
# Use geom_text_repel instead of geom_text
geom_text_repel(
aes(label = round(ProdMed, 2)),
size = 2.5, # Text size
box.padding = 0.5, # Space between label and point
max.overlaps = Inf # Ensure all labels are shown
+
) scale_x_continuous(breaks = 2010:2020) +
labs(
title = "Evolución de la productividad por tamaño de empresa y condición exportadora",
x = "",
y = "Índice de productividad",
color = "Tamaño",
linetype = "Exporta"
+
) theme_minimal() +
theme(
axis.text.x = element_text(angle = 90, vjust = 0.5),
legend.position = "bottom"
)
Tabla resumen
En la siguiente tabla, se resume el comportamiento de la productividad por tamaño de empresa y condicion exportadora:
# Create the summary table
<- bd2 |>
summary_table # 1. Keep only the start and end years
filter(Año %in% c(2010, 2020)) |>
# 2. Calculate mean productivity for each group
group_by(Año, Tamaño, Exporta) |>
summarise(Mean_Prod = mean(Productividad), .groups = 'drop') |>
# 3. Pivot the data to get 2010 and 2020 values in separate columns
pivot_wider(names_from = Año, values_from = Mean_Prod) |>
# 4. Calculate growth metrics and format percentage columns
mutate(
`Crecimiento Total` = percent((`2020` / `2010`) - 1, accuracy = 0.01),
`Crecimiento Anual` = percent(((`2020` / `2010`)^(1/10)) - 1, accuracy = 0.01)
|>
)
# 5. Select and rename columns for the final table
select(
`Tamaño de Empresa` = Tamaño,
`Condición Exportadora` = Exporta,
`Productividad Inicial (2010)` = `2010`,
`Productividad Final (2020)` = `2020`,
`Crecimiento Total`,
`Crecimiento Anual`
|>
)
# 6. Arrange the table for readability
arrange(`Tamaño de Empresa`, `Condición Exportadora`)
# 7. Print the final table in a clean format
kable(summary_table, digits = 2, caption = "Cambio en la Productividad Promedio (2010-2020)")|>
kable_styling(
full_width = FALSE,
bootstrap_options = c("striped", "hover", "condensed")
)
Tamaño de Empresa | Condición Exportadora | Productividad Inicial (2010) | Productividad Final (2020) | Crecimiento Total | Crecimiento Anual |
---|---|---|---|---|---|
Pequeña | si | 0.28 | 0.24 | -14.38% | -1.54% |
Pequeña | no | 0.28 | 0.24 | -14.67% | -1.57% |
Mediana | si | 0.49 | 0.62 | 26.83% | 2.41% |
Mediana | no | 0.48 | 0.60 | 24.43% | 2.21% |
Grande | si | 1.74 | 2.37 | 36.66% | 3.17% |
Grande | no | 1.58 | 1.92 | 21.55% | 1.97% |
Footnotes
Esta es una versión “moderna” de los data frame tradicionales. Por razones técnicas, es conveniente trabajar siempre con este formato↩︎
De hecho, los datos para esta base de datos sintética fueron generados usando una distribución normal truncada sin tendencia↩︎
Si la entrega de la base de datos real se demora, se podría considerar una segunda versión, más realista, de la base de datos sintética, para que pueda usarse en ejercicios de entrenamiento de las personas responsables del OPL-CR↩︎