1 Introducción

La limpieza de datos es una etapa crítica dentro de cualquier proceso de análisis estadístico, ciencia de datos o modelado predictivo. Una base de datos rara vez llega en condiciones perfectas para ser analizada. En muchos casos aparecen valores inválidos, categorías mal registradas, fechas inconsistentes, errores aritméticos, campos vacíos y variables numéricas almacenadas como texto. Por esta razón, antes de realizar análisis descriptivos, inferenciales o construir modelos, es indispensable desarrollar un flujo de depuración riguroso y verificable.

En este documento se trabaja con la base dirty_cafe_sales.csv, la cual contiene registros de ventas de una cafetería. El propósito es mostrar un proceso de limpieza de datos claro, estructurado y reproducible en RStudio, explicando la lógica y la importancia de cada bloque de código.

A lo largo del proceso se siguen normalmente tres fases fundamentales:

  1. Diagnóstico y transformación.
  2. Verificación y validación.
  3. Construcción de la base final.

El valor de este enfoque es que permite conservar simultáneamente tres niveles del trabajo:

2 Fase 1. Diagnóstico y transformación

2.1 1. Cargar los datos

En primer lugar se carga la base de datos. El argumento stringsAsFactors = FALSE evita que las variables de texto se conviertan automáticamente en factores, lo que facilita la limpieza posterior.

# -------------------------------------------------
# 1. Cargar los datos
# -------------------------------------------------
datos <- read.csv("dirty_cafe_sales.csv", stringsAsFactors = FALSE)

# Exploración básica
dim(datos)
## [1] 10000     8
names(datos)
## [1] "Transaction.ID"   "Item"             "Quantity"         "Price.Per.Unit"  
## [5] "Total.Spent"      "Payment.Method"   "Location"         "Transaction.Date"
str(datos)
## 'data.frame':    10000 obs. of  8 variables:
##  $ Transaction.ID  : chr  "TXN_1961373" "TXN_4977031" "TXN_4271903" "TXN_7034554" ...
##  $ Item            : chr  "Coffee" "Cake" "Cookie" "Salad" ...
##  $ Quantity        : chr  "2" "4" "4" "2" ...
##  $ Price.Per.Unit  : chr  "2.0" "3.0" "1.0" "5.0" ...
##  $ Total.Spent     : chr  "4.0" "12.0" "ERROR" "10.0" ...
##  $ Payment.Method  : chr  "Credit Card" "Cash" "Credit Card" "UNKNOWN" ...
##  $ Location        : chr  "Takeaway" "In-store" "In-store" "UNKNOWN" ...
##  $ Transaction.Date: chr  "2023-09-08" "2023-05-16" "2023-07-19" "2023-04-27" ...
head(datos)
##   Transaction.ID     Item Quantity Price.Per.Unit Total.Spent Payment.Method
## 1    TXN_1961373   Coffee        2            2.0         4.0    Credit Card
## 2    TXN_4977031     Cake        4            3.0        12.0           Cash
## 3    TXN_4271903   Cookie        4            1.0       ERROR    Credit Card
## 4    TXN_7034554    Salad        2            5.0        10.0        UNKNOWN
## 5    TXN_3160411   Coffee        2            2.0         4.0 Digital Wallet
## 6    TXN_2602893 Smoothie        5            4.0        20.0    Credit Card
##   Location Transaction.Date
## 1 Takeaway       2023-09-08
## 2 In-store       2023-05-16
## 3 In-store       2023-07-19
## 4  UNKNOWN       2023-04-27
## 5 In-store       2023-06-11
## 6                2023-03-31

2.1.1 Importancia de este bloque

Este bloque es esencial porque permite realizar un diagnóstico inicial de la base. Las funciones utilizadas tienen los siguientes propósitos:

  • read.csv(...): importa el archivo CSV.
  • dim(datos): muestra el número de filas y columnas.
  • names(datos): permite identificar los nombres de las variables.
  • str(datos): muestra la estructura y el tipo de dato de cada variable.
  • head(datos): permite observar rápidamente los primeros registros.

Sin esta revisión inicial no sería posible decidir qué variables requieren transformación.

2.2 2. Copia de seguridad y versión de trabajo

Se crean dos objetos adicionales. Uno conserva la base tal como fue importada y el otro será el espacio de trabajo donde se aplicarán las transformaciones.

# -------------------------------------------------
# 2. Copia de seguridad y versión de trabajo
# -------------------------------------------------
datos_raw <- datos
datos_clean <- datos

2.2.1 Importancia de este bloque

Este paso responde a una buena práctica profesional: nunca limpiar directamente la base original. Mantener datos_raw permite auditar todo el proceso y comparar resultados. Trabajar con datos_clean permite transformar sin perder la referencia inicial.

2.3 3. Limpieza de variables numéricas

Las variables Quantity, Price.Per.Unit y Total.Spent deberían ser numéricas, pero con frecuencia llegan almacenadas como texto. En consecuencia, primero se limpian y luego se convierten a formato numérico.

# -------------------------------------------------
# 3. Limpieza de variables numéricas
# -------------------------------------------------
vars_num <- c("Quantity", "Price.Per.Unit", "Total.Spent")

for (v in vars_num) {

  # Convertir a carácter
  x <- as.character(datos_clean[[v]])

  # Quitar espacios
  x <- trimws(x)

  # Eliminar comas
  x <- gsub(",", "", x, fixed = TRUE)

  # Estandarizar para detectar inválidos
  x_upper <- toupper(x)

  # Reemplazar valores inválidos por NA
  x[x_upper == ""] <- NA
  x[x_upper == "ERROR"] <- NA
  x[x_upper == "UNKNOWN"] <- NA

  # Convertir a numérico
  datos_clean[[paste0(v, "_num")]] <- as.numeric(x)
}

# Verificación de variables numéricas
summary(datos_clean$Quantity_num)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##   1.000   2.000   3.000   3.029   4.000   5.000     479
summary(datos_clean$Price.Per.Unit_num)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##    1.00    2.00    3.00    2.95    4.00    5.00     533
summary(datos_clean$Total.Spent_num)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##   1.000   4.000   8.000   8.924  12.000  25.000     502
sum(is.na(datos_clean$Quantity_num))
## [1] 479
sum(is.na(datos_clean$Price.Per.Unit_num))
## [1] 533
sum(is.na(datos_clean$Total.Spent_num))
## [1] 502

2.3.1 Importancia de este bloque

Este bloque es clave dentro de la fase de transformación. Cada línea cumple una función específica:

  • vars_num <- c(...): define las variables que deben convertirse a formato numérico.
  • for (v in vars_num): evita repetir tres veces el mismo procedimiento.
  • as.character(...): garantiza que la variable pueda limpiarse como texto.
  • trimws(x): elimina espacios al inicio y al final.
  • gsub(",", "", ...): elimina comas que podrían impedir la conversión.
  • toupper(x): permite identificar valores inválidos sin importar si vienen en mayúsculas o minúsculas.
  • x[x_upper == "ERROR"] <- NA: transforma errores textuales en valores faltantes reales.
  • as.numeric(x): convierte la columna limpia en numérica.
  • paste0(v, "_num"): crea columnas auxiliares para no alterar todavía las variables originales.

Este enfoque es importante porque todavía estamos en una fase de diagnóstico y transformación, por lo que conviene mantener columnas auxiliares antes de construir la base final.

2.4 4. Limpieza de fecha

La variable Transaction.Date también requiere revisión. Se limpia como texto, se convierten valores inválidos a NA y luego se transforma a formato fecha.

# -------------------------------------------------
# 4. Limpieza de fecha
# -------------------------------------------------
d <- as.character(datos_clean$Transaction.Date)
d <- trimws(d)

d_upper <- toupper(d)
d[d_upper == ""] <- NA
d[d_upper == "UNKNOWN"] <- NA
d[d_upper == "ERROR"] <- NA

datos_clean$Transaction.Date <- d
datos_clean$Date <- as.Date(d)

# Verificación de fecha
summary(datos_clean$Date)
##         Min.      1st Qu.       Median         Mean      3rd Qu.         Max. 
## "2023-01-01" "2023-04-01" "2023-07-02" "2023-07-01" "2023-10-02" "2023-12-31" 
##         NA's 
##        "460"
sum(is.na(datos_clean$Date))
## [1] 460

2.4.1 Importancia de este bloque

Las fechas son fundamentales en análisis de ventas, tendencias y series temporales. Por tanto:

  • as.character(...) evita problemas si la variable entró con otro formato.
  • trimws(...) elimina espacios sobrantes.
  • la conversión de "", "UNKNOWN" y "ERROR" a NA evita errores de interpretación.
  • as.Date(d) transforma la variable a un formato que R puede usar para análisis temporal.

La nueva columna Date es una variable auxiliar validada que luego se conservará en la base final.

2.5 5. Verificación de consistencia aritmética

En una base de ventas, una verificación crítica consiste en comprobar si el gasto total coincide con la multiplicación entre cantidad y precio unitario.

# -------------------------------------------------
# 5. Verificación de consistencia aritmética
# -------------------------------------------------
datos_clean$Total_teorico <- datos_clean$Quantity_num * datos_clean$Price.Per.Unit_num
datos_clean$Dif_Total <- datos_clean$Total.Spent_num - datos_clean$Total_teorico

summary(datos_clean$Dif_Total)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##       0       0       0       0       0       0    1456
# Filas con inconsistencias
errores_total <- datos_clean[
  abs(datos_clean$Dif_Total) > 0.01 & !is.na(datos_clean$Dif_Total),
]

nrow(errores_total)
## [1] 0
errores_total[, c("Transaction.ID",
                  "Quantity_num",
                  "Price.Per.Unit_num",
                  "Total.Spent_num",
                  "Total_teorico",
                  "Dif_Total")]
## [1] Transaction.ID     Quantity_num       Price.Per.Unit_num Total.Spent_num   
## [5] Total_teorico      Dif_Total         
## <0 rows> (or 0-length row.names)
# Corregir Total.Spent cuando haya inconsistencia
datos_clean$Total.Spent_corregido <- ifelse(
  abs(datos_clean$Dif_Total) > 0.01,
  datos_clean$Total_teorico,
  datos_clean$Total.Spent_num
)

2.5.1 Importancia de este bloque

Este paso es central en la fase de verificación, porque evalúa la consistencia interna del dataset:

  • Total_teorico: calcula cuánto debería ser el total gastado.
  • Dif_Total: mide la diferencia entre el total registrado y el total esperado.
  • abs(datos_clean$Dif_Total) > 0.01: identifica discrepancias significativas.
  • errores_total: almacena las filas con inconsistencias para revisión.
  • ifelse(...): crea una versión corregida del total cuando se detecta error.

Aquí se evidencia claramente la segunda fase: verificación y validación.

2.6 6. Limpieza y estandarización de variables categóricas

Las variables categóricas clave son Item, Payment.Method y Location. Se convierten a minúsculas, se quitan espacios sobrantes y se reemplazan valores inválidos por NA.

# -------------------------------------------------
# 6. Limpieza y estandarización de variables categóricas
# -------------------------------------------------
cols_cat <- c("Item", "Payment.Method", "Location")

for (col in cols_cat) {

  x <- as.character(datos_clean[[col]])
  x <- tolower(x)
  x <- trimws(x)

  # Reducir dobles espacios
  x <- gsub("  ", " ", x, fixed = TRUE)

  # Reemplazar inválidos por NA
  x[x == ""] <- NA
  x[x == "unknown"] <- NA
  x[x == "error"] <- NA

  datos_clean[[col]] <- x
}

# Verificación de categóricas
table(datos_clean$Item, useNA = "ifany")
## 
##     cake   coffee   cookie    juice    salad sandwich smoothie      tea 
##     1139     1165     1092     1171     1148     1131     1096     1089 
##     <NA> 
##      969
table(datos_clean$Payment.Method, useNA = "ifany")
## 
##           cash    credit card digital wallet           <NA> 
##           2258           2273           2291           3178
table(datos_clean$Location, useNA = "ifany")
## 
## in-store takeaway     <NA> 
##     3017     3022     3961
unique(datos_clean$Item)
## [1] "coffee"   "cake"     "cookie"   "salad"    "smoothie" NA         "sandwich"
## [8] "juice"    "tea"
unique(datos_clean$Payment.Method)
## [1] "credit card"    "cash"           NA               "digital wallet"
unique(datos_clean$Location)
## [1] "takeaway" "in-store" NA

2.6.1 Importancia de este bloque

Las variables categóricas suelen contener errores de registro. Este bloque busca estandarizarlas:

  • tolower(...): evita que "Coffee" y "coffee" se traten como categorías diferentes.
  • trimws(...): elimina espacios invisibles que afectan tablas y conteos.
  • gsub(" ", " ", ...): reduce espacios dobles internos.
  • la sustitución por NA convierte valores inválidos en ausencias reales.
  • table(..., useNA = "ifany"): permite revisar si todavía quedan faltantes.
  • unique(...): ayuda a inspeccionar categorías finales.

Con esto se completa la fase de transformación.

3 Fase 2. Verificación y validación

3.1 7. Revisión final de calidad

En este punto se realiza una revisión general para comprobar que la limpieza aplicada sea coherente.

# -------------------------------------------------
# 7. Revisión final de calidad
# -------------------------------------------------

# NA por variable
colSums(is.na(datos_clean))
##        Transaction.ID                  Item              Quantity 
##                     0                   969                     0 
##        Price.Per.Unit           Total.Spent        Payment.Method 
##                     0                     0                  3178 
##              Location      Transaction.Date          Quantity_num 
##                  3961                   460                   479 
##    Price.Per.Unit_num       Total.Spent_num                  Date 
##                   533                   502                   460 
##         Total_teorico             Dif_Total Total.Spent_corregido 
##                   994                  1456                  1456
# Porcentaje de NA
round(colMeans(is.na(datos_clean)) * 100, 2)
##        Transaction.ID                  Item              Quantity 
##                  0.00                  9.69                  0.00 
##        Price.Per.Unit           Total.Spent        Payment.Method 
##                  0.00                  0.00                 31.78 
##              Location      Transaction.Date          Quantity_num 
##                 39.61                  4.60                  4.79 
##    Price.Per.Unit_num       Total.Spent_num                  Date 
##                  5.33                  5.02                  4.60 
##         Total_teorico             Dif_Total Total.Spent_corregido 
##                  9.94                 14.56                 14.56
# Revisar inválidos en columnas de texto
cols_text <- sapply(datos_clean, is.character)

sum(datos_clean[, cols_text] == "error", na.rm = TRUE)
## [1] 0
sum(datos_clean[, cols_text] == "unknown", na.rm = TRUE)
## [1] 0
# Revisar numéricas
summary(datos_clean$Quantity_num)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##   1.000   2.000   3.000   3.029   4.000   5.000     479
summary(datos_clean$Price.Per.Unit_num)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##    1.00    2.00    3.00    2.95    4.00    5.00     533
summary(datos_clean$Total.Spent_num)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##   1.000   4.000   8.000   8.924  12.000  25.000     502
summary(datos_clean$Total.Spent_corregido)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##   1.000   4.000   8.000   8.929  12.000  25.000    1456
# Revisar fecha
summary(datos_clean$Date)
##         Min.      1st Qu.       Median         Mean      3rd Qu.         Max. 
## "2023-01-01" "2023-04-01" "2023-07-02" "2023-07-01" "2023-10-02" "2023-12-31" 
##         NA's 
##        "460"
# Revisar duplicados
sum(duplicated(datos_clean))
## [1] 0
sum(duplicated(datos_clean$Transaction.ID))
## [1] 0

3.1.1 Importancia de este bloque

Este bloque resume la fase de validación:

  • colSums(is.na(...)): cuantifica faltantes.
  • colMeans(is.na(...)): muestra el porcentaje de faltantes.
  • sapply(..., is.character): selecciona solo columnas de texto para buscar inválidos.
  • summary(...): revisa rangos y distribuciones básicas.
  • duplicated(...): detecta filas duplicadas e identificadores repetidos.

Si este bloque arroja resultados coherentes, entonces la base ya está lista para consolidarse.

4 Fase 3. Construcción de la base final

4.1 8. Construcción de la base final limpia

En esta fase se seleccionan solo las variables limpias y relevantes para análisis.

# -------------------------------------------------
# 8. Construcción de la base final limpia
# -------------------------------------------------
datos_final <- datos_clean[, c(
  "Transaction.ID",
  "Item",
  "Quantity_num",
  "Price.Per.Unit_num",
  "Total.Spent_corregido",
  "Payment.Method",
  "Location",
  "Date"
)]

# Renombrar variables
names(datos_final) <- c(
  "Transaction_ID",
  "Item",
  "Quantity",
  "Price_Per_Unit",
  "Total_Spent",
  "Payment_Method",
  "Location",
  "Date"
)

# Verificación inicial de datos_final
str(datos_final)
## 'data.frame':    10000 obs. of  8 variables:
##  $ Transaction_ID: chr  "TXN_1961373" "TXN_4977031" "TXN_4271903" "TXN_7034554" ...
##  $ Item          : chr  "coffee" "cake" "cookie" "salad" ...
##  $ Quantity      : num  2 4 4 2 2 5 3 4 5 5 ...
##  $ Price_Per_Unit: num  2 3 1 5 2 4 3 4 3 4 ...
##  $ Total_Spent   : num  4 12 NA 10 4 20 9 16 15 20 ...
##  $ Payment_Method: chr  "credit card" "cash" "credit card" NA ...
##  $ Location      : chr  "takeaway" "in-store" "in-store" NA ...
##  $ Date          : Date, format: "2023-09-08" "2023-05-16" ...
summary(datos_final)
##  Transaction_ID         Item              Quantity     Price_Per_Unit
##  Length:10000       Length:10000       Min.   :1.000   Min.   :1.00  
##  Class :character   Class :character   1st Qu.:2.000   1st Qu.:2.00  
##  Mode  :character   Mode  :character   Median :3.000   Median :3.00  
##                                        Mean   :3.029   Mean   :2.95  
##                                        3rd Qu.:4.000   3rd Qu.:4.00  
##                                        Max.   :5.000   Max.   :5.00  
##                                        NA's   :479     NA's   :533   
##   Total_Spent     Payment_Method       Location              Date           
##  Min.   : 1.000   Length:10000       Length:10000       Min.   :2023-01-01  
##  1st Qu.: 4.000   Class :character   Class :character   1st Qu.:2023-04-01  
##  Median : 8.000   Mode  :character   Mode  :character   Median :2023-07-02  
##  Mean   : 8.929                                         Mean   :2023-07-01  
##  3rd Qu.:12.000                                         3rd Qu.:2023-10-02  
##  Max.   :25.000                                         Max.   :2023-12-31  
##  NA's   :1456                                           NA's   :460
colSums(is.na(datos_final))
## Transaction_ID           Item       Quantity Price_Per_Unit    Total_Spent 
##              0            969            479            533           1456 
## Payment_Method       Location           Date 
##           3178           3961            460
head(datos_final)
##   Transaction_ID     Item Quantity Price_Per_Unit Total_Spent Payment_Method
## 1    TXN_1961373   coffee        2              2           4    credit card
## 2    TXN_4977031     cake        4              3          12           cash
## 3    TXN_4271903   cookie        4              1          NA    credit card
## 4    TXN_7034554    salad        2              5          10           <NA>
## 5    TXN_3160411   coffee        2              2           4 digital wallet
## 6    TXN_2602893 smoothie        5              4          20    credit card
##   Location       Date
## 1 takeaway 2023-09-08
## 2 in-store 2023-05-16
## 3 in-store 2023-07-19
## 4     <NA> 2023-04-27
## 5 in-store 2023-06-11
## 6     <NA> 2023-03-31

4.1.1 Importancia de este bloque

Aquí se materializa la tercera fase del proceso: construcción de la base final.

  • Se eliminan columnas auxiliares que ya no son necesarias.
  • Se seleccionan únicamente variables limpias.
  • Se renombran las variables para que sean más claras y consistentes.

Esta fase permite pasar de un dataset transformado (datos_clean) a un dataset analítico (datos_final).

4.2 9. Imputación de variables numéricas con la mediana

Se reemplazan los valores faltantes en Quantity y Price_Per_Unit usando la mediana. Luego se recalcula Total_Spent.

# -------------------------------------------------
# 9. Imputación de variables numéricas con la mediana
# -------------------------------------------------
mediana_quantity <- median(datos_final$Quantity, na.rm = TRUE)
datos_final$Quantity[is.na(datos_final$Quantity)] <- mediana_quantity

mediana_price <- median(datos_final$Price_Per_Unit, na.rm = TRUE)
datos_final$Price_Per_Unit[is.na(datos_final$Price_Per_Unit)] <- mediana_price

# Recalcular Total_Spent a partir de Quantity y Price_Per_Unit
datos_final$Total_Spent <- datos_final$Quantity * datos_final$Price_Per_Unit

4.2.1 Importancia de este bloque

La imputación con la mediana es apropiada en variables numéricas cuando se desea conservar registros y reducir el efecto de valores extremos. Este paso:

  • evita perder observaciones,
  • mantiene la estructura de la base,
  • y garantiza que Total_Spent se mantenga consistente tras la imputación.

4.3 10. Imputación de variables categóricas

En variables categóricas, una estrategia común consiste en reemplazar NA por la categoría "unknown".

# -------------------------------------------------
# 10. Imputación de variables categóricas
# -------------------------------------------------
datos_final$Payment_Method[is.na(datos_final$Payment_Method)] <- "unknown"
datos_final$Location[is.na(datos_final$Location)] <- "unknown"
datos_final$Item[is.na(datos_final$Item)] <- "unknown"

4.3.1 Importancia de este bloque

Esta estrategia permite:

  • conservar la observación,
  • no inventar categorías inexistentes,
  • y marcar explícitamente que el valor era desconocido.

4.4 11. Eliminar filas con fecha faltante

En este caso, se opta por eliminar filas donde no se dispone de la fecha, ya que una fecha ausente afecta análisis temporal, agregaciones por día, mes o tendencias.

# -------------------------------------------------
# 11. Eliminar filas con fecha faltante
# -------------------------------------------------
datos_final <- datos_final[!is.na(datos_final$Date), ]

4.4.1 Importancia de este bloque

La fecha es una variable estructural importante para el análisis de ventas. En lugar de imputar fechas artificialmente, se opta por eliminar esos registros para preservar la validez del análisis temporal.

4.5 12. Revisión final de la base lista para análisis

Finalmente se revisa la base resultante.

# -------------------------------------------------
# 12. Revisión final de la base lista para análisis
# -------------------------------------------------
colSums(is.na(datos_final))
## Transaction_ID           Item       Quantity Price_Per_Unit    Total_Spent 
##              0              0              0              0              0 
## Payment_Method       Location           Date 
##              0              0              0
summary(datos_final)
##  Transaction_ID         Item              Quantity     Price_Per_Unit 
##  Length:9540        Length:9540        Min.   :1.000   Min.   :1.000  
##  Class :character   Class :character   1st Qu.:2.000   1st Qu.:2.000  
##  Mode  :character   Mode  :character   Median :3.000   Median :3.000  
##                                        Mean   :3.024   Mean   :2.953  
##                                        3rd Qu.:4.000   3rd Qu.:4.000  
##                                        Max.   :5.000   Max.   :5.000  
##   Total_Spent     Payment_Method       Location              Date           
##  Min.   : 1.000   Length:9540        Length:9540        Min.   :2023-01-01  
##  1st Qu.: 4.000   Class :character   Class :character   1st Qu.:2023-04-01  
##  Median : 8.000   Mode  :character   Mode  :character   Median :2023-07-02  
##  Mean   : 8.936                                         Mean   :2023-07-01  
##  3rd Qu.:12.000                                         3rd Qu.:2023-10-02  
##  Max.   :25.000                                         Max.   :2023-12-31
str(datos_final)
## 'data.frame':    9540 obs. of  8 variables:
##  $ Transaction_ID: chr  "TXN_1961373" "TXN_4977031" "TXN_4271903" "TXN_7034554" ...
##  $ Item          : chr  "coffee" "cake" "cookie" "salad" ...
##  $ Quantity      : num  2 4 4 2 2 5 3 4 5 5 ...
##  $ Price_Per_Unit: num  2 3 1 5 2 4 3 4 3 4 ...
##  $ Total_Spent   : num  4 12 4 10 4 20 9 16 15 20 ...
##  $ Payment_Method: chr  "credit card" "cash" "credit card" "unknown" ...
##  $ Location      : chr  "takeaway" "in-store" "in-store" "unknown" ...
##  $ Date          : Date, format: "2023-09-08" "2023-05-16" ...
head(datos_final)
##   Transaction_ID     Item Quantity Price_Per_Unit Total_Spent Payment_Method
## 1    TXN_1961373   coffee        2              2           4    credit card
## 2    TXN_4977031     cake        4              3          12           cash
## 3    TXN_4271903   cookie        4              1           4    credit card
## 4    TXN_7034554    salad        2              5          10        unknown
## 5    TXN_3160411   coffee        2              2           4 digital wallet
## 6    TXN_2602893 smoothie        5              4          20    credit card
##   Location       Date
## 1 takeaway 2023-09-08
## 2 in-store 2023-05-16
## 3 in-store 2023-07-19
## 4  unknown 2023-04-27
## 5 in-store 2023-06-11
## 6  unknown 2023-03-31

```