Datos del INE con R

Programación y manejo de datos con R (Slides 04-C)

Pedro J. Pérez

¿Qué es el INE?

  • El Instituto Nacional de Estadística (INE) es el organismo público encargado de la estadística oficial en España

  • Hace las grandes estadísticas del país: el censo y el padrón, el IPC, la EPA, la Contabilidad Nacional, …

  • Para nosotros, lo importante:
    (casi) todos sus datos son públicos y gratuitos.
    Están en INEbase, su base de datos,
    agrupados por temas
    (Economía, Mercado laboral, …)


Si necesitas un dato sobre España (población, precios, empleo, turismo, renta, …), lo más probable es que lo tenga el INE

Las operaciones estadísticas

  • El INE organiza su trabajo en operaciones estadísticas: cada una es una estadística concreta, con su metodología y su periodicidad
Operación Código ¿Qué mide? Periodicidad
Índice de Precios de Consumo IPC los precios (la inflación) mensual
Encuesta de Población Activa EPA ocupados, parados, tasa de paro, … trimestral
Contabilidad Nacional Trimestral CNTR2010 el PIB y sus componentes trimestral
Padrón municipal DPOP la población de cada municipio anual
Encuesta de Condiciones de Vida ECV renta, pobreza y desigualdad anual


  • El código de la operación lo usaremos después con el paquete ineapir … pero ojo, solo sirve para ver qué tablas tiene esa operación. Los datos se descargan con el identificador de la tabla, que es un número (lo vemos enseguida)

¿Cómo están organizados los datos?

  • Los datos del INE están organizados en operaciones y tablas. Veámoslo con la EPA:
Nivel ¿Qué es? En la EPA
Operación una estadística
se identifica con un código de letras
EPA
la Encuesta de Población Activa
Tabla dentro de cada operación hay muchas tablas
se identifican con un número
65334
tasas de paro por edad, sexo y comunidad autónoma
  • Las tablas contienen series. Por ejemplo, la tabla 65334 tiene 5 columnas:
    • tres “variables”: Sexo (3 categorías), Edad (7) y Comunidad autónoma (20)
    • el periodo (2026T3, 2026T2, …)
    • el dato (Total)
Sexo CC.AA. Edad Periodo Total
Ambos sexos Total Nacional Total 2026T3 10,18
Ambos sexos Total Nacional Total 2026T2 9,87
Ambos sexos Total Nacional Total 2026T1 10,83
  • Por lo tanto, la tabla 65334 contiene 3 × 7 × 20 = 420 series, cada una con un dato por trimestre desde 2002.

    Una serie de esas 420 es la de los valores (tasa de paro) para las Mujeres, Menores de 25 años de la Comunitat Valenciana

Tres formas de conseguir los datos

  1. Por menús: entras en la web, eliges la tabla, pinchas en el botón de descarga y guardas el fichero

  2. Con la url del fichero: descargas el fichero desde R, sin pasar por la web

  3. Con la API: le pides los datos directamente al INE desde R, con el paquete ineapir


  • Como con Eurostat, la primera forma no es reproducible: nadie sabe qué has pinchado. Con las otras dos, el script contiene todo el proceso

Veremos primero cómo hacerlo “a mano” (formas 1 y 2) y después con ineapir

Los datos, a mano

Descargar un fichero del INE (CSV o PC-Axis) y limpiarlo

Cada tabla tiene su página, su identificador … y varios formatos

  • Queremos la tasa de paro por comunidades autónomas. Buscando en INEbase (Mercado laboral → EPA → …) llegamos a la página de la tabla

  • El número del final de la url es el identificador de la tabla: 65334. Apúntalo, lo vamos a necesitar!!

  • El botón de descarga ofrece varios formatos. ¿Cuál elegimos?

Formato ¿Cómo es? ¿Sirve para R?
CSV separado por ; una fila por dato sí, es el mejor
Excel (.xlsx) la tabla tal como se ve en la web, con títulos y notas con mucho trabajo
PC-Axis (.px) los datos y sus metadatos (título, unidades, notas, …) sí, y muy bien, con el paquete pxR
Json el formato de la API mejor con ineapir

Pero … no hace falta pinchar en el botón: la url del fichero siempre tiene la misma forma. Solo cambian el formato y el identificador de la tabla:
https://www.ine.es/jaxiT3/files/t/es/csv_bdsc/65334.csv   (el PC-Axis: .../es/px/65334.px)

Descargamos el fichero, lo importamos … y aparece un problema

my_url  <- "https://www.ine.es/jaxiT3/files/t/es/csv_bdsc/65334.csv"
1my_ruta <- here::here("pruebas", "epa_65334.csv")
curl::curl_download(my_url, my_ruta)
1
Guardamos el fichero en la carpeta pruebas, que tiene que existir (fs::dir_create("pruebas"))
  • Antes de importarlo, míralo: ¿cómo es por dentro?
read_lines(my_ruta, n_max = 3)
#> [1] "Sexo;Comunidades y Ciudades Autónomas;Edad;Periodo;Total"
#> [2] "Ambos sexos;Total Nacional;Total;2026T3;10,18"           
#> [3] "Ambos sexos;Total Nacional;Total;2026T2;9,87"
  • Es un CSV “a la española”: las columnas se separan con ; y los decimales llevan coma. Lo importamos con read_csv2():
df_epa <- read_csv2(my_ruta)

df_epa %>% slice(1:3)
Sexo
<chr>
Comunidades y Ciudades Autónomas
<chr>
Edad
<chr>
Periodo
<chr>
Total
<chr>
Ambos sexos Total Nacional Total 2026T3 10,18
Ambos sexos Total Nacional Total 2026T2 9,87
Ambos sexos Total Nacional Total 2026T1 10,83
  • Periodo es texto (normal: 2026T2 no es un número), pero … ¡Total también es texto! Debería ser un número

¿Por qué Total no es numérica?

  • Porque el INE marca con .. los datos que no publica (grupos con muy pocas personas en la muestra).
    Basta con que haya un .. en la columna para que toda ella se importe como texto

  • Solución: decirle a read_csv2() que .. significa “no hay dato” (NA):

df_epa <- read_csv2(my_ruta, na = "..")

df_epa %>% slice(1:3)
Sexo
<chr>
Comunidades y Ciudades Autónomas
<chr>
Edad
<chr>
Periodo
<chr>
Total
<dbl>
Ambos sexos Total Nacional Total 2026T3 10.18
Ambos sexos Total Nacional Total 2026T2 9.87
Ambos sexos Total Nacional Total 2026T1 10.83
  • Si no sabes qué valor es el que molesta, puedes buscarlo: df_epa %>% filter(is.na(parse_number(Total))) %>% count(Total)

Limpiamos los datos … y ya podemos usarlos

df_epa_2 <- df_epa %>%
1  janitor::clean_names() %>%
  rename(ccaa = comunidades_y_ciudades_autonomas,
2         tasa_paro = total) %>%
3  mutate(fecha = lubridate::yq(periodo)) %>%
  mutate(ccaa = if_else(ccaa == "Total Nacional",
4                        "00 Total Nacional", ccaa)) %>%
  separate(ccaa, into = c("ccaa_cod", "ccaa"),
5           sep = " ", extra = "merge") %>%
  select(fecha, periodo, ccaa_cod, ccaa,
         sexo, edad, tasa_paro)
1
Nombres de columnas “cómodos”: en minúsculas, sin espacios y sin tildes
2
Nombres más cortos y que digan qué hay en la columna
3
2026T2 pasa a ser una fecha de verdad: 2026-04-01 (el primer día del trimestre)
4
Total Nacional es la única sin código delante: le ponemos 00, como si fuera una comunidad más
5
La columna lleva el código y el nombre juntos (10 Comunitat Valenciana): los separamos por el primer espacio (con extra = "merge", el resto va junto al nombre)
  • O, con expresiones regulares (en lugar de if_else() y separate()):
df_epa_2 <- df_epa %>%
  janitor::clean_names() %>%
  rename(ccaa = comunidades_y_ciudades_autonomas,
         tasa_paro = total) %>%
  mutate(fecha = lubridate::yq(periodo)) %>%
  mutate(ccaa_cod = str_extract(ccaa, "^[0-9]{2}"),
         ccaa = str_remove(ccaa, "^[0-9]{2} ")) %>%
  select(fecha, periodo, ccaa_cod, ccaa,
         sexo, edad, tasa_paro)
df_epa_2 %>%
  filter(sexo == "Ambos sexos", edad == "Total") %>%
  filter(fecha == max(fecha)) %>%
  ggplot(aes(x = tasa_paro, y = fct_reorder(ccaa, tasa_paro))) +
  geom_col(aes(fill = ccaa == "Total Nacional"),
           show.legend = FALSE) +
  labs(title = "Tasa de paro por CCAA (último trimestre)",
       x = "%", y = NULL, caption = "Fuente: INE (EPA)")

Trampas habituales en los ficheros del INE

Trampa Ejemplo Solución
separador ; y coma decimal 9,87 read_csv2()
datos no disponibles .. read_csv2(..., na = "..")
código y nombre en la misma columna 10 Comunitat Valenciana separate() o str_extract()
el periodo no es una fecha 2026T2, 2026M09 lubridate::yq(), lubridate::ym()
los totales mezclados con las categorías Total Nacional, Ambos sexos filter(): cuidado al sumar

Después de importar, comprueba siempre el tipo de cada columna y cuenta sus categorías con count().
Y una regla: del INE, nunca el Excel. Mejor el CSV separado por ; o, como vamos a ver ahora, el PC-Axis

El formato PC-Axis (.px) y el paquete pxR

  • PC-Axis es un formato pensado para publicar tablas estadísticas: además de los datos, lleva sus metadatos (título, unidades, “variables”, notas, …). En R lo lee el paquete pxR
my_url_px <- "https://www.ine.es/jaxiT3/files/t/es/px/65334.px"

1px_epa <- pxR::read.px(my_url_px)
2df_px  <- px_epa %>% as.data.frame() %>% as_tibble()
1
Lee el fichero directamente de la url (o del disco). Devuelve un objeto de clase px, con los datos y los metadatos
2
Con as.data.frame() sacamos los datos del objeto px
df_px %>% slice(1:3)
Periodo
<fct>
Edad
<fct>
Comunidades.y.Ciudades.Autónomas
<fct>
Sexo
<fct>
value
<dbl>
2026T3 Total Total Nacional Ambos sexos 10.18
2026T2 Total Total Nacional Ambos sexos 9.87
2026T1 Total Total Nacional Ambos sexos 10.83
  • Sin decirle nada: una fila por dato, value ya es numérica, los .. ya son NA y las tildes están bien. Las variables llegan como factores (con las categorías en el orden de la web)

Además de los datos, los metadatos

  • El objeto px guarda todo lo que el INE cuenta sobre la tabla:
px_epa$TITLE$value
#> [1] "Tasas de paro por distintos grupos de edad, sexo y comunidad autónoma"

px_epa$UNITS$value
#> [1] "Tasas"

names(px_epa$VALUES)               #- las "variables" de la tabla
#> [1] "Sexo"                             "Comunidades.y.Ciudades.Autónomas"
#> [3] "Edad"                             "Periodo"

px_epa$VALUES$Edad                 #- los valores de una "variable"
#> [1] "Total"              "Menores de 25 años" "25 y más años"      "De 16 a 19 años"   
#> [5] "De 20 a 24 años"    "De 25 a 54 años"    "De 55 y más años"

px_epa$NOTE$value %>% str_trunc(110)
#> [1] "Los resultados de Ceuta y Melilla deben tomarse con precaución porque pueden \"\n\"estar afectados por grandes..."
  • Muy útil para poner bien el título, las unidades y la fuente de tus tablas y gráficos … y para leer las notas, que suelen avisar de cambios metodológicos

¿CSV o PC-Axis?

CSV con read_csv2() PC-Axis con pxR
separador, decimales, tildes hay que acertar con la función automático
datos no disponibles (..) na = ".." automático
título, unidades, notas no vienen sí
tipo de las variables texto factores
el periodo y los códigos hay que limpiarlos hay que limpiarlos

Un fichero del INE: léelo con pxR::read.px() (o con read_csv2(), con cuidado). Y después, comprueba y limpia

Ojo!! Usa siempre pxR::read.px() y nunca hagas library(pxR): carga el paquete plyr, que tapa varias funciones de dplyr (mutate(), summarise(), arrange(), count(), rename()). Tu código seguiría funcionando … pero haciendo otra cosa!!

Tarea: los municipios de la provincia de Valencia

  • La tabla 2903 del INE tiene la población de los municipios de la provincia de Valencia, año a año, según el Padrón
  1. Descarga desde R el fichero CSV (separado por ;) de la tabla e impórtalo
  2. Mira qué hay en cada columna. ¿De qué tipo es cada una? ¿Qué categorías tiene Sexo?
  3. Limpia los datos: nombres de columna cómodos, y el código y el nombre del municipio en dos columnas
  4. ¿Cuáles son los 5 municipios con más población en el último año?
  5. [🌶] ¿Qué 5 municipios han ganado más habitantes (en %) entre 2015 y el último año? Quédate solo con los que tenían más de 5.000 habitantes en 2015
  • 1: la url es como la de la EPA, cambiando el identificador de la tabla
  • 2: count(Sexo). Fíjate en la población: en el fichero lleva punto de miles (1.015)
  • 3: separate(): el código y el nombre están separados por el primer espacio
  • 4: cuidado, hay una fila que no es un municipio: el total de la provincia (46 Valencia/València)
  • 5: filter() + group_by() + summarise() con first() y last()
my_url <- "https://www.ine.es/jaxiT3/files/t/es/csv_bdsc/2903.csv"

1df_pob <- read_csv2(my_url) %>%
  janitor::clean_names() %>%
  rename(anyo = periodo, poblacion = total) %>%
  separate(municipios, into = c("muni_cod", "muni"),
           sep = " ", extra = "merge") %>%
2  filter(muni_cod != "46") %>%
  select(anyo, muni_cod, muni, sexo, poblacion)
1
read_csv2() también lee directamente de una url
2
Quitamos el total de la provincia (46 Valencia/València): su código es el de la provincia, 46
df_pob %>%
  slice(1:3) %>%
  gt::gt() %>%
  gt::fmt_number(columns = poblacion,
                 decimals = 0, sep_mark = ".")
anyo muni_cod muni sexo poblacion
2025 46001 Ademuz Total 1.015
2024 46001 Ademuz Total 1.004
2023 46001 Ademuz Total 1.004
df_top5 <- df_pob %>%
  filter(sexo == "Total", anyo == max(anyo)) %>%
  slice_max(poblacion, n = 5)
df_top5 %>%
  gt::gt() %>%
  gt::fmt_number(columns = poblacion,
                 decimals = 0, sep_mark = ".")
anyo muni_cod muni sexo poblacion
2025 46250 València Total 840.792
2025 46244 Torrent Total 90.928
2025 46131 Gandia Total 83.135
2025 46190 Paterna Total 76.019
2025 46220 Sagunt/Sagunto Total 73.031
df_crec <- df_pob %>%
  filter(sexo == "Total", anyo %in% c(2015, max(anyo))) %>%
  arrange(anyo) %>%
  group_by(muni) %>%
  summarise(pob_2015 = first(poblacion),
            pob_hoy  = last(poblacion)) %>%
  filter(pob_2015 > 5000) %>%
  mutate(crec = pob_hoy / pob_2015 - 1) %>%
  slice_max(crec, n = 5)
df_crec %>%
  gt::gt() %>%
  gt::fmt_number(columns = c(pob_2015, pob_hoy),
                 decimals = 0, sep_mark = ".") %>%
  gt::fmt_percent(columns = crec, decimals = 1)
muni pob_2015 pob_hoy crec
San Antonio de Benagéber 7.981 11.192 40.2%
Montserrat 7.231 10.062 39.2%
Nàquera/Náquera 6.204 8.556 37.9%
Canet d'En Berenguer 6.426 8.297 29.1%
Bétera 22.349 28.560 27.8%

Un mapa de España por provincias, con mapSpain

  • El paquete mapSpain tiene las geometrías de España (CCAA, provincias, municipios, …)
my_url <- "https://www.ine.es/jaxiT3/files/t/es/csv_bdsc/65349.csv"

1df_prov <- read_csv2(my_url, na = "..") %>%
  janitor::clean_names() %>%
  filter(sexo == "Ambos sexos", periodo == max(periodo),
         tasas == "Tasa de paro de la población") %>%
2  mutate(cpro = str_extract(provincias, "^[0-9]{2}"))


3geo_prov <- mapSpain::esp_get_prov()


mapa_prov <- geo_prov %>% left_join(df_prov, by = "cpro")
1
La tabla 65349 de la EPA: tasas de actividad, paro y empleo por provincia
2
El código de la provincia: las 2 cifras del principio (46 Valencia/València)
3
Las geometrías de las provincias. Su código está en la columna cpro
ggplot(mapa_prov) +
  geom_sf(aes(fill = total), color = "white") +
  scale_fill_viridis_c(option = "magma",
                       direction = -1) +
  labs(title = "Tasa de paro por provincias",
       fill = "%") +
  theme_void()

… y por municipios

  • Con los datos de la tarea (df_pob): ¿cuánto ha crecido la población de cada municipio de la provincia de Valencia desde 2015?
df_crec_muni <- df_pob %>%
  filter(sexo == "Total", anyo %in% c(2015, max(anyo))) %>%
  arrange(anyo) %>%
  group_by(muni_cod) %>%
  summarise(crec = last(poblacion) / first(poblacion) - 1)


geo_muni <- mapSpain::esp_get_munic(
1              region = "Valencia/València") %>%
2  mutate(muni_cod = paste0(cpro, cmun))


mapa_muni <- geo_muni %>% left_join(df_crec_muni, by = "muni_cod")
1
Las geometrías de los municipios de la provincia de Valencia
2
El código de municipio del INE (5 cifras) es el de la provincia (cpro) más el del municipio (cmun)
ggplot(mapa_muni) +
  geom_sf(aes(fill = crec), color = "black",
          linewidth = 0.1) +
  scale_fill_gradient2(
    labels = scales::label_percent()) +
  labs(title = "Crecimiento desde 2015",
       fill = NULL) +
  theme_void()

ineapir es el paquete del INE para descargar sus datos desde R

  • Usa la API del INE (un servicio al que un programa le pide datos con una url), pero sin tener que escribir las url
library(ineapir)     #- si no lo tienes instalado: pak::pak("ineapir")
  • Ventajas frente a descargar ficheros:

    • puedes buscar operaciones y tablas desde R
    • puedes pedir solo lo que necesitas: los últimos datos, unas fechas, unas categorías
    • los datos llegan con sus metadatos
  • Tiene una web con muchos ejemplos y una cheatsheet

Para … función
ver las operaciones get_metadata_operations()
ver las tablas de una operación get_metadata_tables_operation()
ver las “variables” de una tabla get_metadata_table_groups()
ver los valores de una “variable” get_metadata_table_values()
descargar una tabla get_data_table()

El recorrido habitual: operación → tabla → (“variables” y valores) → datos

Si ya conoces el identificador de la tabla (el t= de la url), puedes ir directamente a get_data_table()

Paso 1: ¿qué operaciones hay?

operaciones <- get_metadata_operations()

operaciones %>% select(Id, Nombre, Codigo) %>% slice(1:3)
Id
<int>
Nombre
<chr>
Codigo
<chr>
6 Índice de Coste Laboral Armonizado ICLA
7 Estadística de Transmisión de Derechos de la Propiedad ETDP
8 Censos de Población y Viviendas CENSOPV
  • La API ofrece 166 operaciones. Para buscar una, filtramos por su nombre:
operaciones %>%
  filter(str_detect(Nombre, "Población Activa")) %>%
  select(Id, Nombre, Codigo)
Id
<int>
Nombre
<chr>
Codigo
<chr>
205 Flujos de la Población Activa EFPA
293 Encuesta de Población Activa (EPA) EPA
  • Lo que necesitamos es el Codigo: EPA, IPC, …

Paso 2: ¿qué tablas tiene una operación?

tablas_ipc <- get_metadata_tables_operation(operation = "IPC")

tablas_ipc %>% select(Id, Nombre, Anyo_Periodo_ini) %>% slice(1:3)
Id
<int>
Nombre
<chr>
Anyo_Periodo_ini
<chr>
24077 Índice general nacional. Series desde enero de 1961 1961
24080 Índice nacionales de grupos ECOICOP. Series desde enero de 1993 1993
35083 Índices nacionales: Componentes para el análisis de la COVID-19 2017
  • El IPC tiene 59 tablas. Buscamos la que nos interesa: el índice general y los grupos, a nivel nacional
tablas_ipc %>%
  filter(str_detect(Nombre,
                    "Índices nacionales: general")) %>%
  select(Id, Nombre)
Id
<int>
Nombre
<chr>
76125 Índices nacionales: general y de grupos ECOICOP ver.2
79181 Índices nacionales: general y de grupos ECOICOP ver.2
76144 Índices nacionales: general y de grupos ECOICOP ver.2
25333 Índices nacionales: general y de grupos ECOICOP
  • Hay varias con el mismo nombre (distintas bases del IPC). Nos quedamos con la 76125: es la que sale en INEbase

Paso 3: descargamos la tabla

  • Ya sabemos el identificador de la tabla (la 76125). La descargamos con get_data_table():
1df <- get_data_table(idTable = 76125,
2                     nlast = 2,
3                     unnest = TRUE,
4                     tip = "A")
1
La tabla que queremos
2
Solo los 2 últimos periodos. Sin nlast se descarga la tabla completa
3
Una fila por dato. Sin unnest = TRUE los datos llegan “escondidos” en una columna-lista
4
Nombres y fechas que se entienden ("A" de amigable), en lugar de códigos internos del INE. La fecha llega como texto: con as.Date() pasa a ser una fecha
df %>%
  mutate(fecha = as.Date(Fecha)) %>%
  select(Nombre, fecha, Valor) %>%
  slice(1:3)
Nombre
<chr>
fecha
<date>
Valor
<dbl>
Nacional. Índice general. Índice. 2026-09-01 104.98
Nacional. Índice general. Índice. 2026-08-01 104.64
Nacional. Índice general. Variación mensual. 2026-09-01 0.3

¿Qué periodos queremos?

  • Los últimos n periodos, con nlast:
df <- get_data_table(idTable = 76125, nlast = 12, unnest = TRUE, tip = "A")
  • Un rango de fechas, con dateStart y dateEnd (en formato "aaaa/mm/dd"). Si solo ponemos dateStart, llega hasta el último dato disponible:
df <- get_data_table(idTable = 76125,
        unnest = TRUE, tip = "A",
        dateStart = "2025/01/01",
        dateEnd   = "2025/12/31")

df %>%
  mutate(fecha = as.Date(Fecha)) %>%
  summarise(desde = min(fecha), hasta = max(fecha),
            filas = n())
desde
<date>
hasta
<date>
filas
<int>
2025-01-01 2025-12-01 672

Sin nlast ni fechas se descarga la tabla entera: en tablas grandes puede tardar bastante

El problema de la columna Nombre

  • En Nombre está todo junto: el territorio, el grupo y el tipo de dato
df %>% distinct(Nombre) %>% slice(1:3)
Nombre
<chr>
Nacional. Índice general. Índice.
Nacional. Índice general. Variación mensual.
Nacional. Índice general. Variación anual.
  • Podríamos separarlo con separate(), pero hay una forma “mejor”:
    pedir los metadatos con tip = "AM" y metanames = TRUE
df <- get_data_table(idTable = 76125, nlast = 2, unnest = TRUE,
1                     tip = "AM", metanames = TRUE)

df %>% as_tibble() %>%
  select(Nombre, Grupos.ECOICOP.ver.2, Tipo.de.dato, Valor) %>% slice(1:2)
1
Con metanames = TRUE se añade una columna por cada “variable” de la tabla. ¿Y «Nacional»? No tiene columna: en esta tabla todos los datos son nacionales, así que el territorio no es una “variable” de la tabla
Nombre
<chr>
Grupos.ECOICOP.ver.2
<chr>
Tipo.de.dato
<chr>
Valor
<dbl>
Nacional. Índice general. Índice. Índice general Índice 104.98
Nacional. Índice general. Índice. Índice general Índice 104.64

Antes de filtrar: ¿qué “variables” y qué valores tiene la tabla?

#- las "variables" de la tabla:
get_metadata_table_groups(idTable = 76125)
Id
<int>
Nombre
<chr>
155577 Grupos ECOICOP ver.2
155578 Tipo de dato
#- los valores de "Grupos ECOICOP" (idGroup = su Id):
get_metadata_table_values(idTable = 76125,
                          idGroup = 155577) %>%
  select(Id, Fk_Variable, Nombre)
Id
<int>
Fk_Variable
<int>
Nombre
<chr>
304092 762 Índice general
304093 762 Alimentos y bebidas no alcohólicas
Primeras 2 filas de 14
#- los de "Tipo de dato":
get_metadata_table_values(idTable = 76125,
                          idGroup = 155578) %>%
  select(Id, Fk_Variable, Nombre)
Id
<int>
Fk_Variable
<int>
Nombre
<chr>
83 3 Índice
84 3 Variación mensual
74 3 Variación anual
87 3 Variación en lo que va de año
4 filas de 4
  • La tabla 76125 contiene 56 series (14 Grupos ECOICOP × 4 tipos de dato)
  • Para poder filtrar las series que nos interesen tenemos que proporcionar los valores de Id y de Fk_Variable que nos interesen.
    Variación anual es 3 y 74; mientras que Índice general es 762 y 304092

Ojo!! Cada “variable” tiene dos números: su Id (155578) sirve para pedir sus valores; su Fk_Variable (3), para el filtro

Con filter pedimos solo la serie que queremos: la inflación en España

  • El argumento filter es una lista,
    con un elemento por cada “variable” que queremos filtrar:
my_filtro <- list("Fk_Variable" = "Id",
                  "Fk_Variable" = "Id")
  • En nuestro caso, la variación anual del índice general:
my_filtro <- list(
1  "3"   = "74",
2  "762" = "304092")

df_ipc <- get_data_table(idTable = 76125,
            unnest = TRUE, tip = "A",
            dateStart = "2019/01/01",
            filter = my_filtro)
1
“Variable” 3 (Tipo de dato), valor 74 (Variación anual)
2
“Variable” 762 (Grupos ECOICOP), valor 304092 (Índice general)
  • Para pedir varios valores de una “variable”: list("3" = c("74", "84"))
df_ipc %>%
  mutate(fecha = as.Date(Fecha)) %>%
  ggplot(aes(x = fecha, y = Valor)) +
  geom_line(linewidth = 1) +
  geom_hline(yintercept = 0, linetype = "dashed") +
  labs(title = "Variación anual del IPC (índice general)",
       x = NULL, y = "%",
       caption = "Fuente: INE (tabla 76125)")

filter con atajos

  • Buscar los identificadores es pesado. Para las “variables” más habituales hay atajos, y se puede filtrar por el nombre del valor. Por ejemplo, la inflación de la slide anterior:
#- con los números:
my_filtro <- list("3" = "74", "762" = "304092")

#- con atajos (lo mismo):
my_filtro <- list(tipodato = "Variación anual", grupo = "Índice general")
  • Los atajos disponibles (tipodato, grupo, sexo, edad, ccaa, prov, …) se ven con get_filter_shortcuts()
  • Otro ejemplo, con la tabla del paro de la EPA (65334):
df_paro <- get_data_table(idTable = 65334, nlast = 1, unnest = TRUE, tip = "AM", metanames = TRUE,
                          filter = list(sexo = "Ambos sexos", edad = "Total"))

df_paro %>% as_tibble() %>%
  select(Comunidades.y.Ciudades.Autónomas, Sexo, Edad, T3_Periodo, Anyo, Valor) %>% slice(1:3)
Comunidades.y.Ciudades.Autónomas
<chr>
Sexo
<chr>
Edad
<chr>
T3_Periodo
<chr>
Anyo
<int>
Valor
<dbl>
Total Nacional Ambos sexos Total T3 2026 10.18
Andalucía Ambos sexos Total T3 2026 14.91
Aragón Ambos sexos Total T3 2026 9.23

Cosas a tener en cuenta

  • No todo INEbase está en la API: si una operación no aparece en get_metadata_operations(), toca descargar el fichero a mano

  • El último dato puede ser un avance: el INE publica primero el índice general y días después el detalle. Mientras tanto, el resto de series tienen NA en ese periodo

  • Los identificadores cambian: cuando el INE cambia la base de una estadística crea tablas nuevas. Si un script antiguo deja de traer datos recientes, mira si hay una tabla nueva

  • Guarda en disco lo que descargues (rio::export()): así el análisis no depende de tener conexión

Tarea: ¿qué precios suben más?

  • Con la tabla 76125 del IPC (Índices nacionales: general y de grupos ECOICOP):
  1. Descarga los 3 últimos meses de la variación anual de todos los grupos, con una columna para el nombre del grupo
  2. Quédate con el último mes que tenga datos para todos los grupos
  3. Ordena los grupos de mayor a menor subida de precios. ¿Qué grupo sube más? ¿Y menos?
  4. [🌶] Haz un gráfico de barras con la variación anual de cada grupo
  • 1: filter = list("3" = "74"), con tip = "AM" y metanames = TRUE
  • 2: si el último mes es un dato avance, los grupos tendrán NA: quítalos antes de quedarte con la fecha más reciente
  • 3: arrange(desc(Valor))
  • 4: geom_col(), con fct_reorder() para ordenar las barras
df_grupos <- get_data_table(idTable = 76125, nlast = 3, unnest = TRUE,
                            tip = "AM", metanames = TRUE, filter = list("3" = "74")) %>%
  as_tibble() %>%
  mutate(fecha = as.Date(Fecha)) %>%
  select(grupo = Grupos.ECOICOP.ver.2, fecha, var_anual = Valor) %>%
1  filter(!is.na(var_anual)) %>%
  group_by(fecha) %>% filter(n() > 1) %>% ungroup() %>%
  filter(fecha == max(fecha)) %>%
  arrange(desc(var_anual))
1
Quitamos los NA y los meses que solo tienen el índice general (los datos avance)
df_grupos %>% slice(1:4)
grupo
<chr>
fecha
<date>
var_anual
<dbl>
Transporte 2026-09-01 10.5
Vivienda, agua, electricidad, gas y otros combustibles 2026-09-01 7.2
Índice general 2026-09-01 4.9
Restaurantes y servicios de alojamiento 2026-09-01 4.9
df_grupos %>%
  mutate(grupo = str_trunc(grupo, 45)) %>%
  ggplot(aes(x = var_anual, y = fct_reorder(grupo, var_anual))) +
  geom_col(aes(fill = grupo == "Índice general"), show.legend = FALSE) +
  labs(title = "Variación anual del IPC por grupos",
       subtitle = paste("Mes:", format(max(df_grupos$fecha), "%m/%Y")), x = "%", y = NULL)

Tarea: el paro de los jóvenes

  • Con la tabla 65334 de la EPA (Tasas de paro por distintos grupos de edad, sexo y comunidad autónoma):
  1. Averigua qué “variables” tiene la tabla y qué valores toma la “variable” Edad
  2. Descarga la tasa de paro de los menores de 25 años (ambos sexos) de todas las comunidades, desde 2008
  3. ¿Qué 3 comunidades tienen la tasa de paro juvenil más alta en el último trimestre?
  4. [🌶] Dibuja la evolución de la tasa de paro juvenil en la Comunitat Valenciana y en el total nacional
  • 1: get_metadata_table_groups() y get_metadata_table_values()
  • 2: en filter puedes usar los atajos y el nombre del valor: edad = "Menores de 25 años"
  • 3: filter(fecha == max(fecha)) y slice_max()
  • 4: filter(ccaa %in% c(...)) y geom_line(aes(color = ccaa))
get_metadata_table_groups(idTable = 65334)
Id
<int>
Nombre
<chr>
133219 Sexo
133220 Comunidades y Ciudades Autónomas
133221 Edad
get_metadata_table_values(idTable = 65334,
                          idGroup = 133221) %>%
  select(Id, Fk_Variable, Nombre)
Id
<int>
Fk_Variable
<int>
Nombre
<chr>
15668 356 Total
10568 357 Menores de 25 años
10569 357 25 y más años
10553 360 De 16 a 19 años
15661 360 De 20 a 24 años
10561 360 De 25 a 54 años
10560 357 De 55 y más años
df_jov <- get_data_table(idTable = 65334, unnest = TRUE, tip = "AM", metanames = TRUE,
                         dateStart = "2008/01/01",
                         filter = list(sexo = "Ambos sexos", edad = "Menores de 25 años")) %>%
  as_tibble() %>%
  mutate(fecha = as.Date(Fecha)) %>%
  select(ccaa = Comunidades.y.Ciudades.Autónomas, fecha, tasa_paro = Valor)
df_jov %>%
  filter(fecha == max(fecha)) %>%
  slice_max(tasa_paro, n = 3)
ccaa
<chr>
fecha
<date>
tasa_paro
<dbl>
Melilla 2026-07-01 42.54
Extremadura 2026-07-01 34.24
Rioja, La 2026-07-01 30.7
df_jov %>%
  filter(ccaa %in% c("Comunitat Valenciana", "Total Nacional")) %>%
  ggplot(aes(x = fecha, y = tasa_paro, color = ccaa)) +
  geom_line(linewidth = 1) +
  labs(title = "Tasa de paro de los menores de 25 años", x = NULL, y = "%", color = NULL)

Para saber más

Resumen

Para … usamos …
encontrar una tabla INEbase: el identificador es el t= de la url
descargar su fichero https://www.ine.es/jaxiT3/files/t/es/csv_bdsc/id.csv (o .../px/id.px)
importarlo read_csv2(ruta, na = "..")
… o mejor, el fichero PC-Axis pxR::read.px(url) %>% as.data.frame()
limpiarlo janitor::clean_names(), separate() o str_extract(), lubridate::yq() / ym()
buscar operaciones y tablas desde R get_metadata_operations(), get_metadata_tables_operation()
ver qué contiene una tabla get_metadata_table_groups(), get_metadata_table_values()
descargar una tabla get_data_table(idTable, unnest = TRUE, tip = "AM", metanames = TRUE)
… solo unos periodos nlast, dateStart, dateEnd
… solo unas categorías filter = list(...)