###############################################################################
#                                                                             #
#        TRABAJO DE INVESTIGACIÓN CUANTITATIVA — MANEJO DE RStudio            #
#        Maestría en Administración en Salud                                  #
#                                                                             #
#   Estudiante : Carlos David Argüello López
#                Nasly Estefania Chaucanes Guerrero                           #
#   Docente    : Carlos Mario García Diaz                                     #
#   Módulo     : Seminario de Investigación Cuantitativa                                   #
#   Fecha      : 07 de junio del 2026                                         #
#                                                                             #
#   Base de datos: "2022-standard-list-of-charges-professional.csv"           #
#   Fuente: Children's Minnesota — Transparencia de precios profesionales     #
#           (cargos profesionales por procedimiento médico, año 2022)         #
#                                                                             #
###############################################################################
#
#   ¿QUÉ CONTIENE ESTA BASE DE DATOS?
#   Para cada procedimiento médico (identificado por su código CPT) la base
#   reporta la "tarifa lista" del hospital (Master Fee Schedule) y el monto
#   que paga cada uno de los 14 aseguradores con los que el hospital negocia.
#   Es decir: el miso procedimiento tiene PRECIOS DISTINTOS según el pagador.
#   Este es un tema central en administración en salud: la TRANSPARENCIA DE
#   PRECIOS Y LA VARIACIÓN DE COSTOS ENTRE ASEGURADORES.
#
# 
#
###############################################################################



###############################################################################
# SECCIÓN 0. CONFIGURACIÓN INICIAL                                            #
#  (Preparar el entorno: ubicación del archivo y librerías necesarias)        #
###############################################################################

# --- 0.1 Definir el "directorio de trabajo" -------------------------------
# El directorio de trabajo es la CARPETA donde R buscará tu archivo .csv.
# Reemplaza la ruta de ejemplo por la carpeta donde guardaste el CSV.
# IMPORTANTE en Windows: usa barras "/" o barras dobles "\\", NUNCA una sola "\".
#
#   Ejemplo Windows: setwd("C:/Users/TuUsuario/Documents/TrabajoR")
#   Ejemplo Mac    : setwd("/Users/TuUsuario/Documents/TrabajoR")
#
# Si prefieres no escribir la ruta, en RStudio puedes usar el menú:
#   Session > Set Working Directory > Choose Directory...  (elige la carpeta)

# setwd("C:/Users/TuUsuario/Documents/TrabajoR")   # <- QUITA EL "#" Y EDITA

# getwd() muestra en qué carpeta está parado R en este momento (para verificar)
getwd()
## [1] "C:/Users/Carlos/Documents/Trabajos Maestria fucs/TrabajoR"
# --- 0.2 Instalar las librerías (SOLO la primera vez) ---------------------
# Una "librería" (o paquete) es un conjunto de funciones extra que amplían R.
# install.packages() las DESCARGA de internet. Solo se hace una vez por equipo.
# Si ya las tienes instaladas, deja estas líneas comentadas (con el "#").

# install.packages("readr")     # leer archivos .csv de forma robusta
# install.packages("dplyr")     # manipular datos (filtrar, agrupar, resumir)
# install.packages("tidyr")     # reorganizar datos (formato largo/ancho)
# install.packages("stringr")   # manipular texto (limpiar símbolos $, comas)
# install.packages("ggplot2")   # crear gráficos de alta calidad


# --- 0.3 Activar (cargar) las librerías -----------------------------------
# install.packages() instala; library() ACTIVA el paquete en esta sesión.
# Esto SÍ debe ejecutarse cada vez que abras RStudio.
library(readr)
library(dplyr)
## 
## Adjuntando el paquete: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(tidyr)
library(stringr)
library(ggplot2)



###############################################################################
# SECCIÓN 1. EXPLORACIÓN DE LA BASE DE DATOS                                  #
#  (Reconocer variables, tipos, unidades, volumen, periodo e ideas)           #
###############################################################################

# --- 1.1 Cargar el archivo CSV en un objeto llamado "datos" ---------------
# read.csv() lee un archivo de texto separado por comas y lo convierte en un
# "data.frame" (una tabla con filas y columnas, similar a una hoja de Excel).
#   - check.names = FALSE  -> conserva los nombres de columna tal cual (con
#                             espacios y símbolos), sin "arreglarlos".
#   - stringsAsFactors = FALSE -> deja el texto como texto, no como factor.
datos <- read.csv("2022-standard-list-of-charges-professional.csv",
                  check.names = FALSE,
                  stringsAsFactors = FALSE)

# --- 1.2 Tamaño y volumen de la base --------------------------------------
dim(datos)        # devuelve: número de FILAS y número de COLUMNAS
## [1] 14373    22
nrow(datos)       # solo el número de filas (registros / observaciones)
## [1] 14373
ncol(datos)       # solo el número de columnas (variables)
## [1] 22
# --- 1.3 Nombres de las variables (columnas) ------------------------------
names(datos)      # lista los nombres de todas las columnas
##  [1] "index"                       "Procedure Code"             
##  [3] "Procedure Description"       "CPT Code"                   
##  [5] "Revenue Code"                "Standard Modifiers"         
##  [7] "Fee Schedule Effective Date" "Master Fee Schedule"        
##  [9] "Blue Cross Blue Shield MN"   "HealthPartners"             
## [11] "Medica Choice"               "Medica Elect"               
## [13] "Medica Narrow"               "Preferred One"              
## [15] "Preferred One PPO"           "United Health"              
## [17] "Allina Aetna"                "America's PPO"              
## [19] "Laborcare"                   "MultiPlan"                  
## [21] "SelectCare"                  "Ucare QHP"
# --- 1.4 Primeras y últimas filas para "ver" los datos --------------------
head(datos, 5)    # muestra las primeras 5 filas
##   index Procedure Code
## 1     0          56810
## 2     1          61600
## 3     2          95933
## 4     3          61333
## 5     4          70370
##                                         Procedure Description CPT Code
## 1                         56810 Perineoplasty-Non Obstetrical    56810
## 2       61600 RESCJ/EXC LES BASE ANT CRANIAL FOSSA EXTRADURAL    61600
## 3                                     95933 Blink Reflex Test    95933
## 4 61333 Explore Orbit Transcranial Approach W/ Removal Lesion    61333
## 5               70370 PF FL Neck Lateral Airway & Fluoroscopy    70370
##   Revenue Code Standard Modifiers Fee Schedule Effective Date
## 1          960                                       01-01-22
## 2          960                                       01-01-22
## 3          960                                       01-01-22
## 4          960                                       01-01-22
## 5          960                                       01-01-22
##   Master Fee Schedule Blue Cross Blue Shield MN HealthPartners Medica Choice
## 1                1125                    793.84         671.97    534.279264
## 2                6353                   6340.73        5284.19   4200.828503
## 3                 290                    252.95          212.4    169.173358
## 4                6264                   5945.81         4729.4   3758.987793
## 5                  66                     42.99          37.53     30.163763
##   Medica Elect Medica Narrow Preferred One Preferred One PPO United Health
## 1          675       1023.75        559.39            783.73         486.5
## 2       3811.8       5781.23       4446.15           6229.27       3901.99
## 3          174         263.9        168.83            236.54        154.05
## 4       3758.4       5700.24       4231.66           5928.77       3526.52
## 5     24.65827       17417.4         30.43             42.64         31.55
##   Allina Aetna America's PPO   Laborcare MultiPlan  SelectCare   Ucare QHP
## 1      499.114        753.75  503.189708   $323.51  503.189708  457.134503
## 2      4038.58       4256.51 4176.406302 $2,594.75 4176.406302 3651.318663
## 3        62.23         194.3  151.571391   $103.29  151.571391  145.661246
## 4    3589.4068       4196.88  4063.75189 $2,345.08  4063.75189 3423.902884
## 5       31.556         44.22   33.263776    $16.43   33.263776   24.756654
tail(datos, 5)    # muestra las últimas 5 filas
##       index Procedure Code                         Procedure Description
## 14369 14368         93010A 93010 EKG 12 Lead Interp And Report Only-CHMN
## 14370 14369          90649                90649 Vaccine HPV Quadrivalent
## 14371 14370          92586      92586 Auditory Evoked Potentials Limited
## 14372 14371                                                             
## 14373 14372                                                             
##       CPT Code Revenue Code Standard Modifiers Fee Schedule Effective Date
## 14369    93010          960                                       01-01-22
## 14370    90649          960                                       01-01-22
## 14371    92586          960                                       01-01-22
## 14372                                                                     
## 14373                                                                     
##       Master Fee Schedule Blue Cross Blue Shield MN HealthPartners
## 14369                 103                      9.52          21.05
## 14370                 320                    163.41         163.39
## 14371                 255                    140.25         247.18
## 14372                                                             
## 14373                                                             
##       Medica Choice Medica Elect Medica Narrow Preferred One Preferred One PPO
## 14369     17.060554         61.8         93.73         17.39             24.36
## 14370    207.264374          192         291.2        153.64            153.64
## 14371           153          153        232.05        194.19            272.07
## 14372                                                                         
## 14373                                                                         
##       United Health Allina Aetna America's PPO  Laborcare MultiPlan SelectCare
## 14369         15.73      16.4836         69.01  16.386096    $10.55  16.386096
## 14370        181.48     319.9504         214.4  213.24368   $163.24  213.24368
## 14371           102     173.4404        170.85 172.736765   $118.29 172.736765
## 14372                                                                         
## 14373                                                                         
##       Ucare QHP
## 14369         0
## 14370         0
## 14371         0
## 14372          
## 14373
# --- 1.5 Estructura: tipo de cada variable y ejemplos ---------------------
# str() (de "structure") es la función MÁS útil para explorar: indica el tipo
# de dato de cada columna (chr = texto, int = entero, num = decimal, etc.)
str(datos)
## 'data.frame':    14373 obs. of  22 variables:
##  $ index                      : int  0 1 2 3 4 5 6 7 8 9 ...
##  $ Procedure Code             : chr  "56810" "61600" "95933" "61333" ...
##  $ Procedure Description      : chr  "56810 Perineoplasty-Non Obstetrical" "61600 RESCJ/EXC LES BASE ANT CRANIAL FOSSA EXTRADURAL" "95933 Blink Reflex Test" "61333 Explore Orbit Transcranial Approach W/ Removal Lesion" ...
##  $ CPT Code                   : chr  "56810" "61600" "95933" "61333" ...
##  $ Revenue Code               : chr  "960" "960" "960" "960" ...
##  $ Standard Modifiers         : chr  "" "" "" "" ...
##  $ Fee Schedule Effective Date: chr  "01-01-22" "01-01-22" "01-01-22" "01-01-22" ...
##  $ Master Fee Schedule        : chr  "1125" "6353" "290" "6264" ...
##  $ Blue Cross Blue Shield MN  : chr  "793.84" "6340.73" "252.95" "5945.81" ...
##  $ HealthPartners             : chr  "671.97" "5284.19" "212.4" "4729.4" ...
##  $ Medica Choice              : chr  "534.279264" "4200.828503" "169.173358" "3758.987793" ...
##  $ Medica Elect               : chr  "675" "3811.8" "174" "3758.4" ...
##  $ Medica Narrow              : chr  "1023.75" "5781.23" "263.9" "5700.24" ...
##  $ Preferred One              : chr  "559.39" "4446.15" "168.83" "4231.66" ...
##  $ Preferred One PPO          : chr  "783.73" "6229.27" "236.54" "5928.77" ...
##  $ United Health              : chr  "486.5" "3901.99" "154.05" "3526.52" ...
##  $ Allina Aetna               : chr  "499.114" "4038.58" "62.23" "3589.4068" ...
##  $ America's PPO              : chr  "753.75" "4256.51" "194.3" "4196.88" ...
##  $ Laborcare                  : chr  "503.189708" "4176.406302" "151.571391" "4063.75189" ...
##  $ MultiPlan                  : chr  "$323.51" "$2,594.75" "$103.29" "$2,345.08" ...
##  $ SelectCare                 : chr  "503.189708" "4176.406302" "151.571391" "4063.75189" ...
##  $ Ucare QHP                  : chr  "457.134503" "3651.318663" "145.661246" "3423.902884" ...
# --- 1.6 Resumen estadístico rápido de todas las columnas -----------------
summary(datos)
##      index         Procedure Code  Procedure Description      CPT Code    
##  Min.   :    0   Length   :14373   Length   :14373       Length   :14373  
##  1st Qu.: 3593   N.unique : 3920   N.unique : 3920       N.unique : 3641  
##  Median : 7186   N.blank  :   10   N.blank  :   10       N.blank  :   16  
##  Mean   : 7186   Min.nchar:    0   Min.nchar:    0       Min.nchar:    0  
##  3rd Qu.:10779   Max.nchar:   14   Max.nchar:  102       Max.nchar:    8  
##  Max.   :14372                                                            
##     Revenue Code   Standard Modifiers Fee Schedule Effective Date
##  Length   :14373   Length   :14373    Length   :14373            
##  N.unique :    4   N.unique :   16    N.unique :    3            
##  N.blank  :   54   N.blank  :14275    N.blank  :   10            
##  Min.nchar:    0   Min.nchar:    0    Min.nchar:    0            
##  Max.nchar:   12   Max.nchar:   18    Max.nchar:   27            
##                                                                  
##  Master Fee Schedule Blue Cross Blue Shield MN   HealthPartners 
##  Length   :14373     Length   :14373           Length   :14373  
##  N.unique : 2203     N.unique : 7016           N.unique : 2501  
##  N.blank  :   10     N.blank  :   10           N.blank  :   10  
##  Min.nchar:    0     Min.nchar:    0           Min.nchar:    0  
##  Max.nchar:   31     Max.nchar:   25           Max.nchar:   14  
##                                                                 
##    Medica Choice      Medica Elect     Medica Narrow     Preferred One  
##  Length   :14373   Length   :14373   Length   :14373   Length   :14373  
##  N.unique : 3333   N.unique : 2546   N.unique : 2648   N.unique : 2421  
##  N.blank  :   10   N.blank  :   10   N.blank  :   10   N.blank  :   10  
##  Min.nchar:    0   Min.nchar:    0   Min.nchar:    0   Min.nchar:    0  
##  Max.nchar:   13   Max.nchar:   12   Max.nchar:   18   Max.nchar:   13  
##                                                                         
##  Preferred One PPO   United Health      Allina Aetna     America's PPO  
##  Length   :14373   Length   :14373   Length   :14373   Length   :14373  
##  N.unique : 2415   N.unique : 4123   N.unique : 4065   N.unique : 2201  
##  N.blank  :   10   N.blank  :   10   N.blank  :   10   N.blank  :   10  
##  Min.nchar:    0   Min.nchar:    0   Min.nchar:    0   Min.nchar:    0  
##  Max.nchar:   17   Max.nchar:   13   Max.nchar:   21   Max.nchar:   13  
##                                                                         
##      Laborcare         MultiPlan         SelectCare        Ucare QHP    
##  Length   :14373   Length   :14373   Length   :14373   Length   :14373  
##  N.unique : 2982   N.unique : 3831   N.unique : 2982   N.unique : 2238  
##  N.blank  :   10   N.blank  :   10   N.blank  :   10   N.blank  :   10  
##  Min.nchar:    0   Min.nchar:    0   Min.nchar:    0   Min.nchar:    0  
##  Max.nchar:   12   Max.nchar:    9   Max.nchar:   12   Max.nchar:   11  
## 
# --- 1.7 Lectura interpretativa de la exploración -------------------------
# A partir de los resultados anteriores documentamos lo siguiente:
#
#  * NÚMERO DE VARIABLES  : 22 columnas.
#  * VOLUMEN DE DATOS     : ~14.373 filas (registros de procedimientos).
#  * TIPO DE VARIABLES    : en bruto casi todas se leen como texto (chr),
#                           porque los precios traen símbolos "$" y comas.
#                           Las corregiremos en las Secciones 2 y 3.
#  * UNIDADES DE MEDIDA   :
#       - "Procedure Code", "CPT Code", "Revenue Code": CÓDIGOS (identificadores,
#         no son cantidades; aunque parezcan números NO se suman ni promedian).
#       - "Fee Schedule Effective Date": FECHA (formato día-mes-año).
#       - "Master Fee Schedule" y las 14 columnas de aseguradores:
#         DINERO en DÓLARES estadounidenses (USD).
#  * PERIODO DE TIEMPO    : tarifario vigente desde el 01-01-2022, calculado
#                           con la población de pacientes del año 2021.
#  * IDEAS DE INVESTIGACIÓN posibles a partir de esta base:
#       1. ¿Qué tanto VARÍA el precio de un mismo procedimiento entre
#          aseguradores? (dispersión de precios = equidad/transparencia)
#       2. ¿Qué asegurador paga, en promedio, más cerca o más lejos de la
#          tarifa lista del hospital? (poder de negociación)
#       3. ¿Cuáles son los procedimientos más costosos del portafolio?
#       4. ¿Existen procedimientos cuyo precio es altamente inconsistente
#          entre pagadores? (señal de poca transparencia)



###############################################################################
# SECCIÓN 2. LIMPIEZA DE LA BASE DE DATOS                                     #
#  (Completar, corregir o eliminar datos erróneos, vacíos o duplicados)       #
###############################################################################

# Guardamos cuántas filas tenemos al inicio para comparar al final.
filas_iniciales <- nrow(datos)
cat("Filas antes de limpiar:", filas_iniciales, "\n")
## Filas antes de limpiar: 14373
# --- 2.1 Eliminar FILAS DE CABECERA REPETIDAS dentro de los datos ---------
# Al inspeccionar la base notamos que algunas filas, en lugar de datos, repiten
# los TÍTULOS de las columnas (p. ej. la celda dice literalmente
# "Procedure Code"). Esas filas son basura y deben eliminarse.
# El operador "!=" significa "DIFERENTE DE". Conservamos solo las filas cuya
# celda NO sea igual al texto "Procedure Code".
datos <- datos[datos$`Procedure Code` != "Procedure Code", ]

# --- 2.2 Eliminar FILAS COMPLETAMENTE VACÍAS ------------------------------
# Algunas filas están vacías (sirven como separadores). is.na() detecta valores
# faltantes (NA). Conservamos las filas donde el código de procedimiento NO está
# vacío ni faltante.
datos <- datos[!is.na(datos$`Procedure Code`) &
               datos$`Procedure Code` != "", ]

# --- 2.3 Eliminar una COLUMNA casi totalmente vacía -----------------------
# La columna "Standard Modifiers" está vacía en más del 99% de los registros,
# por lo que no aporta y la eliminamos. Asignar NULL a una columna la borra.
datos$`Standard Modifiers` <- NULL

# La columna "index" es solo un consecutivo heredado del archivo original;
# tampoco aporta al análisis, así que también la eliminamos.
datos$index <- NULL

# --- 2.4 Revisar y eliminar FILAS DUPLICADAS EXACTAS ----------------------
# duplicated() marca como TRUE las filas que son copias idénticas de otra.
# sum() cuenta cuántas hay (TRUE cuenta como 1).
cat("Filas duplicadas exactas:", sum(duplicated(datos)), "\n")
## Filas duplicadas exactas: 3599
# Nos quedamos solo con filas únicas (el "!" invierte: conserva las NO duplicadas)
datos <- datos[!duplicated(datos), ]

# --- 2.5 Verificación final de la limpieza --------------------------------
filas_finales <- nrow(datos)
cat("Filas después de limpiar:", filas_finales, "\n")
## Filas después de limpiar: 10760
cat("Filas eliminadas en total:", filas_iniciales - filas_finales, "\n")
## Filas eliminadas en total: 3613
# NOTA: muchos códigos CPT se repiten LEGÍTIMAMENTE (un mismo procedimiento
# puede aparecer con distintos códigos de ingreso/Revenue Code). Por eso NO
# eliminamos por CPT duplicado, solo las filas idénticas completas.



###############################################################################
# SECCIÓN 3. TIPOS DE VARIABLE, OPERACIONES BÁSICAS Y GRÁFICOS                #
###############################################################################

# --- 3.1 DEFINIR EL TIPO DE CADA VARIABLE ---------------------------------
# Aquí "le decimos" a R qué es cada columna realmente, para poder analizarla.

# (a) Los CÓDIGOS deben tratarse como FACTOR o texto (categorías), NUNCA como
#     números a promediar. as.factor() los convierte en variable categórica.
datos$`Revenue Code` <- as.factor(datos$`Revenue Code`)

# (b) La FECHA: la convertimos de texto a tipo Date con as.Date().
#     El formato "%m-%d-%y" indica mes-día-año de dos dígitos (ej. 01-01-22).
datos$`Fee Schedule Effective Date` <-
  as.Date(datos$`Fee Schedule Effective Date`, format = "%m-%d-%y")

# (c) Los PRECIOS: están como texto con símbolos "$" y comas (ej. "$2,594.75").
#     Creamos una FUNCIÓN PROPIA para limpiarlos y convertirlos a número.
#     - str_remove_all() elimina TODAS las apariciones de un patrón de texto.
#     - "\\$" representa el símbolo dólar; "," representa la coma de miles.
#     - as.numeric() convierte el texto ya limpio en número decimal.
convertir_a_numero <- function(columna_texto) {
  columna_texto <- str_remove_all(columna_texto, "\\$")   # quita el $
  columna_texto <- str_remove_all(columna_texto, ",")     # quita las comas
  as.numeric(columna_texto)                               # texto -> número
}

# Aplicamos la función a la tarifa lista del hospital (Master Fee Schedule).
datos$`Master Fee Schedule` <- convertir_a_numero(datos$`Master Fee Schedule`)

# (Las 14 columnas de aseguradores se convertirán en la Sección 4 con un BUCLE,
#  para demostrar el uso de ciclos y evitar repetir el mismo código 14 veces.)

# Verificamos que la tarifa lista ya es numérica:
class(datos$`Master Fee Schedule`)   # debe responder "numeric"
## [1] "numeric"
# --- 3.2 OPERACIONES BÁSICAS ----------------------------------------------
# Estadísticos descriptivos de la tarifa lista (en dólares).
# na.rm = TRUE le pide a R que IGNORE los valores faltantes en el cálculo.
mean(datos$`Master Fee Schedule`,   na.rm = TRUE)   # media (promedio)
## [1] 1583.661
median(datos$`Master Fee Schedule`, na.rm = TRUE)   # mediana (valor central)
## [1] 715.5
min(datos$`Master Fee Schedule`,    na.rm = TRUE)   # mínimo
## [1] 0.01
max(datos$`Master Fee Schedule`,    na.rm = TRUE)   # máximo
## [1] 18078
sd(datos$`Master Fee Schedule`,     na.rm = TRUE)   # desviación estándar
## [1] 2016.679
# Operación con dplyr: TOP 10 procedimientos MÁS COSTOSOS según la tarifa lista.
# El operador "%>%" (pipe) encadena pasos y se lee como "y luego...".
top10_costosos <- datos %>%
  arrange(desc(`Master Fee Schedule`)) %>%               # ordenar de mayor a menor
  select(`CPT Code`, `Procedure Description`, `Master Fee Schedule`) %>%
  head(10)                                               # tomar los 10 primeros
print(top10_costosos)
##    CPT Code                                        Procedure Description
## 1     39503                      39503 Rpr Neon Diaph Hernia WWO Ch Tube
## 2     39503                      39503 Rpr Neon Diaph Hernia WWO Ch Tube
## 3     39503                      39503 Rpr Neon Diaph Hernia WWO Ch Tube
## 4     33945 33945 Heart Transplant With or Without Recipient Cardiectomy
## 5     33945 33945 Heart Transplant With or Without Recipient Cardiectomy
## 6     33945 33945 Heart Transplant With or Without Recipient Cardiectomy
## 7     33945 33945 Heart Transplant With or Without Recipient Cardiectomy
## 8     33507                      33507 Rpr Anom Aor Orig Coronary Artery
## 9     33507                      33507 Rpr Anom Aor Orig Coronary Artery
## 10    33507                      33507 Rpr Anom Aor Orig Coronary Artery
##    Master Fee Schedule
## 1                18078
## 2                18078
## 3                18078
## 4                17290
## 5                17290
## 6                17290
## 7                17290
## 8                15143
## 9                15143
## 10               15143
# --- 3.3 GRÁFICOS ----------------------------------------------------------
# (a) HISTOGRAMA: cómo se distribuyen las tarifas lista.
#     Usamos un tope de 8000 USD en el eje X para que el gráfico sea legible
#     (hay valores extremos muy altos que aplastarían la figura).
ggplot(datos, aes(x = `Master Fee Schedule`)) +
  geom_histogram(bins = 40, fill = "#2c7fb8", color = "white") +
  coord_cartesian(xlim = c(0, 8000)) +
  labs(title = "Distribución de la tarifa lista del hospital",
       subtitle = "Children's Minnesota — cargos profesionales 2022",
       x = "Tarifa lista (USD)", y = "Número de procedimientos") +
  theme_minimal()

# (b) GRÁFICO DE BARRAS: los 10 procedimientos más costosos.
#     reorder() ordena las barras por precio; coord_flip() las pone horizontales.
ggplot(top10_costosos,
       aes(x = reorder(`Procedure Description`, `Master Fee Schedule`),
           y = `Master Fee Schedule`)) +
  geom_col(fill = "#d95f0e") +
  coord_flip() +
  labs(title = "Top 10 procedimientos más costosos (tarifa lista)",
       x = NULL, y = "Tarifa lista (USD)") +
  theme_minimal()

###############################################################################
# SECCIÓN 4. FUNCIONES INTERMEDIAS: LÓGICAS + BUCLE (con propósito claro)     #
###############################################################################

# OBJETIVO DE ESTA SECCIÓN:
# 1) Usar un BUCLE "for" para convertir a número las 14 columnas de aseguradores
#    de una sola vez (en lugar de escribir 14 líneas casi idénticas).
# 2) Usar FUNCIONES LÓGICAS (ifelse, %in%) para CLASIFICAR los procedimientos.
# 3) Calcular, con el mismo bucle, un indicador de gestión: qué proporción de
#    la tarifa lista paga, en promedio, cada asegurador.

# --- 4.1 Identificar las columnas de aseguradores -------------------------
# Son todas las columnas de precio EXCEPTO la tarifa lista. Las nombramos
# explícitamente para tener control total sobre qué se procesa.
aseguradores <- c("Blue Cross Blue Shield MN", "HealthPartners",
                 "Medica Choice", "Medica Elect", "Medica Narrow",
                 "Preferred One", "Preferred One PPO", "United Health",
                 "Allina Aetna", "America's PPO", "Laborcare",
                 "MultiPlan", "SelectCare", "Ucare QHP")

# --- 4.2 BUCLE "for": convertir a número cada columna de asegurador --------
# El bucle recorre la lista "aseguradores" una por una. En cada vuelta, la
# variable "aseg" toma el nombre de un asegurador y aplicamos nuestra función
# de limpieza a esa columna. PROPÓSITO: automatizar una tarea repetitiva.
for (aseg in aseguradores) {
  datos[[aseg]] <- convertir_a_numero(datos[[aseg]])
}
cat("Conversión de", length(aseguradores), "columnas de aseguradores completada.\n")
## Conversión de 14 columnas de aseguradores completada.
# --- 4.3 FUNCIÓN LÓGICA con ifelse(): clasificar por nivel de costo --------
# ifelse(condición, valor_si_VERDADERO, valor_si_FALSO) crea una nueva columna
# categórica. Usamos la mediana como umbral: por encima = "Alto costo".
umbral <- median(datos$`Master Fee Schedule`, na.rm = TRUE)
datos$Nivel_costo <- ifelse(datos$`Master Fee Schedule` > umbral,
                           "Alto costo", "Bajo costo")
# table() cuenta cuántos procedimientos quedaron en cada categoría.
table(datos$Nivel_costo)
## 
## Alto costo Bajo costo 
##       5380       5380
# --- 4.4 FUNCIÓN LÓGICA con %in%: marcar vacunas COVID-19 -----------------
# El operador "%in%" pregunta "¿está este valor dentro de esta lista?" y
# devuelve TRUE/FALSE. grepl() busca un patrón de texto dentro de una columna.
# Marcamos las filas cuya descripción CONTENGA la palabra "Covid".
datos$Es_vacuna_covid <- grepl("Covid", datos$`Procedure Description`,
                              ignore.case = TRUE)
cat("Registros de vacunas COVID-19 encontrados:",
    sum(datos$Es_vacuna_covid), "\n")
## Registros de vacunas COVID-19 encontrados: 17
# --- 4.5 BUCLE "for" con propósito de GESTIÓN -----------------------------
# Calculamos, para cada asegurador, la PROPORCIÓN PROMEDIO que paga respecto
# a la tarifa lista del hospital. Esto es un indicador del "descuento"
# negociado: una proporción de 0.60 significa que paga, en promedio, el 60%
# de la tarifa lista. Usamos la MEDIANA (más robusta que la media frente a
# valores atípicos presentes en estos datos reales).
#
# Creamos primero una tabla vacía donde iremos guardando los resultados.
resultado <- data.frame(Asegurador = character(),
                        Prop_mediana = numeric(),
                        stringsAsFactors = FALSE)

for (aseg in aseguradores) {
  # proporción pagada respecto a la tarifa lista, fila por fila
  proporcion <- datos[[aseg]] / datos$`Master Fee Schedule`
  # eliminamos infinitos/atípicos imposibles (cuando la tarifa lista es ~0)
  proporcion <- proporcion[is.finite(proporcion) & proporcion > 0 &
                           proporcion < 5]
  prop_med <- round(median(proporcion, na.rm = TRUE), 3)
  # rbind() agrega una fila nueva a la tabla de resultados
  resultado <- rbind(resultado,
                     data.frame(Asegurador = aseg, Prop_mediana = prop_med))
}

# Ordenamos del que paga MENOR proporción al que paga MAYOR proporción.
resultado <- resultado %>% arrange(Prop_mediana)
print(resultado)
##                   Asegurador Prop_mediana
## 1                  MultiPlan        0.396
## 2                  Ucare QHP        0.546
## 3              United Health        0.594
## 4               Medica Elect        0.600
## 5  Blue Cross Blue Shield MN        0.613
## 6               Allina Aetna        0.618
## 7                  Laborcare        0.645
## 8                 SelectCare        0.645
## 9              Medica Choice        0.651
## 10             America's PPO        0.670
## 11             Preferred One        0.683
## 12            HealthPartners        0.797
## 13             Medica Narrow        0.910
## 14         Preferred One PPO        0.956
# Gráfico del indicador: ranking de aseguradores por proporción pagada.
ggplot(resultado,
       aes(x = reorder(Asegurador, Prop_mediana), y = Prop_mediana)) +
  geom_col(fill = "#1b9e77") +
  geom_hline(yintercept = 1, linetype = "dashed", color = "red") +
  coord_flip() +
  labs(title = "Proporción mediana pagada respecto a la tarifa lista",
       subtitle = "Línea roja = 100% de la tarifa lista",
       x = NULL, y = "Proporción pagada (mediana)") +
  theme_minimal()

###############################################################################
# SECCIÓN 5. TAREA DISRUPTIVA (inspirada en revisión de RPubs)                #
#                                                                             #
#  ÍNDICE DE DISPERSIÓN DE PRECIOS POR PROCEDIMIENTO                          #
###############################################################################

# CONTEXTO Y JUSTIFICACIÓN:
# En múltiples publicaciones de RPubs sobre análisis de costos en salud, una
# técnica recurrente y de alto valor es medir la VARIABILIDAD de un precio
# usando el COEFICIENTE DE VARIACIÓN (CV = desviación estándar / media). Aquí
# lo aplicamos de forma original a la transparencia de precios: calculamos,
# PARA CADA PROCEDIMIENTO, qué tan disperso es su precio ENTRE los 14
# aseguradores. Un CV alto significa que el MISMO servicio se paga de forma
# muy desigual según el pagador —una señal de inequidad y de oportunidad de
# negociación—. Esto convierte una base de precios estática en un TABLERO
# que prioriza dónde enfocar políticas de transparencia.

# --- 5.1 Calcular media, desviación y CV por fila (procedimiento) ---------
# apply(matriz, 1, función) aplica una función a CADA FILA (el "1" = filas).
# Construimos una matriz solo con las columnas de aseguradores.
matriz_precios <- as.matrix(datos[, aseguradores])

datos$Precio_medio <- apply(matriz_precios, 1, function(fila) {
  mean(fila, na.rm = TRUE)
})
datos$Precio_desv <- apply(matriz_precios, 1, function(fila) {
  sd(fila, na.rm = TRUE)
})
# Coeficiente de variación, expresado en porcentaje.
datos$CV_precio <- ifelse(datos$Precio_medio > 0,
                         round(100 * datos$Precio_desv / datos$Precio_medio, 1),
                         NA)

# --- 5.2 Ranking: procedimientos con mayor INEQUIDAD de precio ------------
# Filtramos procedimientos con un precio medio razonable (> 50 USD) para evitar
# distorsiones de montos minúsculos, y tomamos los 15 de mayor dispersión.
top_dispersos <- datos %>%
  filter(Precio_medio > 50, !is.na(CV_precio)) %>%
  distinct(`CPT Code`, .keep_all = TRUE) %>%     # un registro por código CPT
  arrange(desc(CV_precio)) %>%
  select(`CPT Code`, `Procedure Description`,
         Precio_medio, CV_precio) %>%
  head(15)
print(top_dispersos)
##    CPT Code                                        Procedure Description
## 1     70390                                        70390 PF FL Sialogram
## 2     93971                  PF US Duplex Venous Extrem Right Limit Port
## 3     95930                  95930 Visual Evoked Potential Checker/Flash
## 4     95937 95937 Neuromuscular Junction Test (Repetitive Stim) Ea Nerve
## 5     88302                                88302 Level 2 - Surgical Path
## 6     88304                                88304 Level 3 - Surgical Path
## 7     88300                                88300 Level 1 - Surgical Path
## 8     93922                     PF US Doppler Art Extrem (ABI) Bilateral
## 9     88348                        88348 Electron Microscopy, Diagnostic
## 10    93454   93454 Coronary Artery Angio Inaging Supervision and Interp
## 11    94060       94060 Spirometry Pre-Post Broncho Dilation Interp Only
## 12    93923                  PF US Doppler Arteries Extrem Upper Bil Cmp
## 13    93976                     PF US Duplex Organ Art/Vein Flow Limited
## 14    73615                           73615 IR Arthro Ankle S&I-PF-73615
## 15    78472                                PF NM Cardiac Gated Blood SGL
##    Precio_medio CV_precio
## 1    24727.8682     373.6
## 2    11470.9505     371.8
## 3     8120.1118     371.6
## 4    13599.4798     371.1
## 5     1785.9988     371.0
## 6     2567.1171     370.7
## 7      951.1128     370.5
## 8     3196.3241     370.4
## 9    14225.4651     370.1
## 10   54469.0226     370.0
## 11    2895.8674     369.9
## 12    4497.5806     369.8
## 13    8065.7446     369.6
## 14    5738.9739     369.6
## 15    9098.7433     369.4
# --- 5.3 Visualización del tablero de dispersión --------------------------
ggplot(top_dispersos,
       aes(x = reorder(`Procedure Description`, CV_precio), y = CV_precio)) +
  geom_col(fill = "#7570b3") +
  coord_flip() +
  labs(title = "Procedimientos con mayor dispersión de precio entre aseguradores",
       subtitle = "Coeficiente de variación alto = mismo servicio, precios muy desiguales",
       x = NULL, y = "Coeficiente de variación (%)") +
  theme_minimal()

# --- 5.4 Conclusión analítica (interpretación para el lector) -------------
# Los procedimientos en la parte superior del gráfico son aquellos donde un
# mismo servicio se paga de manera más desigual dependiendo del asegurador.
# Desde la administración en salud, ESTOS son los procedimientos prioritarios
# para auditar contratos, exigir transparencia y renegociar tarifas, porque es
# donde la falta de estandarización de precios es más marcada.



###############################################################################
# SECCIÓN 6. GUARDAR RESULTADOS                                               #
###############################################################################

# Guardamos la base ya limpia y enriquecida en un nuevo archivo .csv, por si
# el docente desea revisar el resultado del procesamiento.
# El archivo se creará en tu directorio de trabajo (el de la Sección 0).
write.csv(datos, "base_procesada_resultado.csv", row.names = FALSE)
cat("\nArchivo 'base_procesada_resultado.csv' guardado correctamente.\n")
## 
## Archivo 'base_procesada_resultado.csv' guardado correctamente.
cat("FIN DEL SCRIPT.\n")
## FIN DEL SCRIPT.
###############################################################################
#                            FIN DEL TRABAJO                                  #
###############################################################################