EDA RUPE 2025 (agosto) — Proveedores del Estado

Code
library(tidyverse)
library(janitor)
library(skimr)
library(stringr)

1 1) Lectura

Code
# Si tu archivo está en la misma carpeta que este .qmd:
ruta <- "datos.csv"

# RUPE viene con separador ';'
df <- readr::read_csv2(ruta, locale = locale(encoding = "UTF-8"),
                       show_col_types = FALSE, guess_max = 100000) |>
  clean_names()

# Confirmar estructura esperada
stopifnot(all(c("pais_prov","identificacion_prov","denominacion_social_prov",
                "domicilio_fiscal","localidad_prov","departamento_prov",
                "estado_prov") %in% names(df)))

skimr::skim(df)
Data summary
Name df
Number of rows 110747
Number of columns 7
_______________________
Column type frequency:
character 7
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
pais_prov 0 1 4 78 0 79 0
identificacion_prov 0 1 4 34 0 110739 0
denominacion_social_prov 0 1 2 140 0 110627 0
domicilio_fiscal 0 1 1 366 0 82462 0
localidad_prov 0 1 4 29 0 991 0
departamento_prov 0 1 5 14 0 20 0
estado_prov 0 1 6 15 0 4 0
Code
names(df)
[1] "pais_prov"                "identificacion_prov"     
[3] "denominacion_social_prov" "domicilio_fiscal"        
[5] "localidad_prov"           "departamento_prov"       
[7] "estado_prov"             
Code
head(df, 3)
# A tibble: 3 × 7
  pais_prov identificacion_prov denominacion_social_prov       domicilio_fiscal 
  <chr>     <chr>               <chr>                          <chr>            
1 URUGUAY   080192190014        GIMENEZ VAZQUEZ ROBERT ALBERTO MELENDEZ, MANUEL…
2 URUGUAY   216684990015        PACHECO TROISI MARIANGEL       CARAGUATAY 2080  
3 URUGUAY   100789580019        MOORINGMEN SRL                 FRANCIA 0 20100 …
# ℹ 3 more variables: localidad_prov <chr>, departamento_prov <chr>,
#   estado_prov <chr>

2 2) Limpieza mínima

Code
df <- df |>
  mutate(across(c(pais_prov, denominacion_social_prov, domicilio_fiscal,
                  localidad_prov, departamento_prov, estado_prov),
                ~ str_squish(as.character(.)))) |>
  distinct()

nrow(df)
[1] 110747
Code
df |>
  summarise(duplicados_por_id = n() - n_distinct(identificacion_prov))
# A tibble: 1 × 1
  duplicados_por_id
              <int>
1                 8

3 3) Resúmenes básicos

Code
# Conteo por estado
por_estado <- df |>
  count(estado_prov, name = "proveedores") |>
  arrange(desc(proveedores))

por_estado
# A tibble: 4 × 2
  estado_prov     proveedores
  <chr>                 <int>
1 ACTIVO                54540
2 EN INGRESO            37546
3 BAJA DGI              18620
4 BAJA VOLUNTARIA          41
Code
# Conteo por departamento
por_depto <- df |>
  count(departamento_prov, name = "proveedores") |>
  arrange(desc(proveedores))

por_depto |> head(15)
# A tibble: 15 × 2
   departamento_prov proveedores
   <chr>                   <int>
 1 Montevideo              41853
 2 Sin dato                31973
 3 Canelones                8312
 4 Maldonado                3481
 5 Colonia                  3250
 6 Paysandú                 2283
 7 Salto                    2240
 8 San José                 2240
 9 Soriano                  1952
10 Tacuarembó               1710
11 Florida                  1555
12 Rocha                    1366
13 Rivera                   1264
14 Cerro Largo              1234
15 Lavalleja                1192

4 4) Proxy TI por keywords

Code
# Buscamos en TODAS las columnas texto (nombre, domicilio, etc.)
keywords <- "(software|inform[aá]tica|tecnolog[ií]a|datos|anal[ií]tica|inteligencia artificial|machine learning|cloud|telecom|data\\b|bi\\b)"

ti <- df |>
  mutate(across(where(is.character), str_to_lower)) |>
  filter(if_any(where(is.character), ~ str_detect(., keywords)))

list(
  total_registros = nrow(df),
  empresas_ti_detectadas = nrow(ti),
  porcentaje = round(100 * nrow(ti) / nrow(df), 2)
)
$total_registros
[1] 110747

$empresas_ti_detectadas
[1] 455

$porcentaje
[1] 0.41
Code
ti |> select(identificacion_prov, denominacion_social_prov,
             departamento_prov, estado_prov) |> slice_head(n = 20)
# A tibble: 20 × 4
   identificacion_prov denominacion_social_prov    departamento_prov estado_prov
   <chr>               <chr>                       <chr>             <chr>      
 1 213958790013        colombi strazzarino juan j… montevideo        en ingreso 
 2 040408860013        telecomunicaciones opticas… colonia           en ingreso 
 3 217056790012        rosendorff bianco guillerm… montevideo        en ingreso 
 4 b86694528           melcox telecomunicaciones … sin dato          activo     
 5 219151900012        sarubbi salles maximiliano… montevideo        en ingreso 
 6 216724890016        asociacion civil red espec… montevideo        activo     
 7 140096110010        data control informatica l… sin dato          baja dgi   
 8 215224720016        informatica elabir socieda… montevideo        baja dgi   
 9 214705590014        ingeniero ruben tomasco - … montevideo        baja dgi   
10 213679380013        madakei ltda.               montevideo        en ingreso 
11 050000630019        abi rached hnos ltda        durazno           baja dgi   
12 214276020017        capacitacion profesional e… montevideo        baja dgi   
13 214721410017        estudios y datos s r l      montevideo        baja dgi   
14 213826390018        tecnologia informatica s.r… montevideo        baja dgi   
15 213981800011        tecnologia termodinamica s… montevideo        baja dgi   
16 213786140016        wingate informatica ltda    sin dato          baja dgi   
17 160002460017        balbi y lopez s r l         salto             baja dgi   
18 213384480017        ecodata s r l               montevideo        en ingreso 
19 213359420014        informatica vandos ltda.    sin dato          baja dgi   
20 216106540016        foresagro sur ltda          montevideo        en ingreso 

5 5) Gráficos

Code
library(ggplot2)

ggplot(por_estado, aes(x = reorder(estado_prov, proveedores), y = proveedores)) +
  geom_col() +
  coord_flip() +
  labs(title = "Proveedores por estado RUPE (stock agosto 2025)",
       x = "Estado", y = "Cantidad")

Code
ggplot(por_depto |> slice_max(order_by = proveedores, n = 15),
       aes(x = reorder(departamento_prov, proveedores), y = proveedores)) +
  geom_col() +
  coord_flip() +
  labs(title = "Top 15 departamentos por cantidad de proveedores",
       x = "Departamento", y = "Cantidad")

Code
# Proxy TI por departamento (top)
ti_por_depto <- ti |>
  count(departamento_prov, name = "proveedores_ti") |>
  arrange(desc(proveedores_ti))

ggplot(ti_por_depto |> slice_max(order_by = proveedores_ti, n = 15),
       aes(x = reorder(departamento_prov, proveedores_ti), y = proveedores_ti)) +
  geom_col() +
  coord_flip() +
  labs(title = "Top 15 departamentos — proveedores TI (proxy keywords)",
       x = "Departamento", y = "Cantidad")

6 6) Nota metodológica

Texto: este CSV de RUPE no incluye fecha de alta/inscripción. El análisis es de stock al mes publicado.
Para medir incremento/altas, descargar dos meses distintos, cargarlos como df_agosto y df_julio, y comparar por identificacion_prov:

Code
# Ejemplo (si tuvieras otro CSV):
# df_julio <- read_csv2("rupe-julio-2025.csv") |> clean_names()
# nuevas_altas <- anti_join(df_agosto, df_julio, by = "identificacion_prov")
# nrow(nuevas_altas)