Teoría

El Market Basket Analysis es una técnica en el ámbito de análisis y minería de datos en el campo del comercio. Su objetivo principal es descubrir patrones de asociación entre productos que suelen ser comprados juntos por los clientes.

Las 3 métricas principales para evaluar reglas de asociación son:

  • Confidence (Confianza): Probabilidad de comprar B sabiendo que se compró A. Por ejemplo, Pan –> Mantequilla = 0.8 significa que de cada 100 clientes que compraron pan, 80 compraron mantequilla también.

  • Lift (Elevación): Cuánto más probable es comprar B cuando se compra A en comparación con la probabilidad de comprar B sin saber si se compró A.

    • Lift > 1: comprar A impulsa la compra de B.
    • Lift = 1: los productos no tienen relación.
    • Lift < 1: comprar A reduce la probabilidad de comprar B.
  • Support (Soporte): Popularidad de una combinación dentro de todas las transacciones. Por ejemplo, Pan y Mantequilla = 0.05 significa que el 5% de todas las transacciones incluyeron ambos productos.

Contexto

Una cadena de tiendas de conveniencia tiene 5 tiendas ubicadas en distintas ciudades de México. La base de datos “abarrotes” contiene 1 mes de transacciones, pero presenta errores de calidad que impiden realizar análisis confiables.

El objetivo es limpiar la base de datos de forma estratégica y posteriormente aplicar Market Basket Analysis para descubrir patrones de compra y diseñar promociones que aumenten las ventas.

Instalar paquetes y llamar librerías

Las siguientes líneas de instalación solamente se deben ejecutar una vez desde la consola de RStudio:

install.packages("tidyverse")
install.packages("janitor")
install.packages("arules")
install.packages("arulesViz")
library(tidyverse)
library(janitor)
library(arules)
library(arulesViz)

Importar la base de datos

df <- read.csv(
  "https://drive.google.com/uc?export=download&id=1dT7BdzhN0VNfGKzWvxiz_xhv1pR6n3aC",
  stringsAsFactors = FALSE,
  check.names = TRUE
)

summary(df)
##     ClaveTienda          DescGiro      Codigo.Barras            PLU        
##  Length   :200625   Length   :200625   Min.   :8.347e+05   Min.   : 1.000  
##  N.unique :     5   N.unique :     3   1st Qu.:7.501e+12   1st Qu.: 1.000  
##  N.blank  :     0   N.blank  :     0   Median :7.501e+12   Median : 1.000  
##  Min.nchar:     5   Min.nchar:     8   Mean   :5.950e+12   Mean   : 2.112  
##  Max.nchar:     5   Max.nchar:    10   3rd Qu.:7.501e+12   3rd Qu.: 1.000  
##                                        Max.   :1.750e+13   Max.   :30.000  
##                                                            NAs    :199188  
##        Fecha               Hora              Marca            Fabricante    
##  Length   :200625   Length   :200625   Length   :200625   Length   :200625  
##  N.unique :   195   N.unique : 52145   N.unique :   540   N.unique :   241  
##  N.blank  :     0   N.blank  :     0   N.blank  :     0   N.blank  :     0  
##  Min.nchar:    10   Min.nchar:     8   Min.nchar:     3   Min.nchar:     3  
##  Max.nchar:    10   Max.nchar:     8   Max.nchar:    30   Max.nchar:    40  
##                                                                             
##                                                                             
##       Producto          Precio          Ult.Costo         Unidades     
##  Length   :200625   Min.   :-147.00   Min.   :  0.38   Min.   : 0.200  
##  N.unique :  3406   1st Qu.:  11.00   1st Qu.:  8.46   1st Qu.: 1.000  
##  N.blank  :     0   Median :  16.00   Median : 12.31   Median : 1.000  
##  Min.nchar:     4   Mean   :  19.42   Mean   : 15.31   Mean   : 1.262  
##  Max.nchar:    40   3rd Qu.:  25.00   3rd Qu.: 19.23   3rd Qu.: 1.000  
##                     Max.   :1000.00   Max.   :769.23   Max.   :96.000  
##                                                                        
##     F.Ticket      NombreDepartamento   NombreFamilia     NombreCategoria  
##  Min.   :     1   Length   :200625   Length   :200625   Length   :200625  
##  1st Qu.: 33964   N.unique :     9   N.unique :    51   N.unique :   174  
##  Median :105993   N.blank  :     0   N.blank  :     0   N.blank  :     0  
##  Mean   :193990   Min.nchar:     6   Min.nchar:     3   Min.nchar:     2  
##  3rd Qu.:383005   Max.nchar:    20   Max.nchar:    25   Max.nchar:    37  
##  Max.   :450040                                                           
##                                                                           
##        Estado           Mts.2        Tipo.ubicación          Giro       
##  Length   :200625   Min.   :47.0   Length   :200625   Length   :200625  
##  N.unique :     5   1st Qu.:53.0   N.unique :     3   N.unique :     2  
##  N.blank  :     0   Median :60.0   N.blank  :     0   N.blank  :     0  
##  Min.nchar:     7   Mean   :56.6   Min.nchar:     7   Min.nchar:     9  
##  Max.nchar:    12   3rd Qu.:60.0   Max.nchar:    12   Max.nchar:    10  
##                     Max.   :62.0                                        
##                                                                         
##     Hora.inicio        Hora.cierre    
##  Length   :200625   Length   :200625  
##  N.unique :     3   N.unique :     3  
##  N.blank  :     0   N.blank  :     0  
##  Min.nchar:     5   Min.nchar:     5  
##  Max.nchar:     5   Max.nchar:     5  
##                                       
## 
str(df)
## 'data.frame':    200625 obs. of  22 variables:
##  $ ClaveTienda       : chr  "MX001" "MX001" "MX001" "MX001" ...
##  $ DescGiro          : chr  "Abarrotes" "Abarrotes" "Abarrotes" "Abarrotes" ...
##  $ Codigo.Barras     : num  7.5e+12 7.5e+12 7.5e+12 7.5e+12 7.5e+12 ...
##  $ PLU               : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ Fecha             : chr  "19/06/2020" "19/06/2020" "19/06/2020" "19/06/2020" ...
##  $ Hora              : chr  "08:16:21" "08:23:33" "08:24:33" "08:24:33" ...
##  $ Marca             : chr  "NUTRI LECHE" "DAN UP" "BIMBO" "PEPSI" ...
##  $ Fabricante        : chr  "MEXILAC" "DANONE DE MEXICO" "GRUPO BIMBO" "PEPSI-COLA MEXICANA" ...
##  $ Producto          : chr  "Nutri Leche 1 Litro" "DANUP STRAWBERRY P/BEBER 350GR NAL" "Rebanadas Bimbo 2Pz" "Pepsi N.R. 400Ml" ...
##  $ Precio            : num  16 14 5 8 19.5 16 14 5 8 19.5 ...
##  $ Ult.Costo         : num  12.3 14 5 8 15 ...
##  $ Unidades          : num  1 1 1 1 1 1 1 1 1 1 ...
##  $ F.Ticket          : int  1 2 3 3 4 1 2 3 3 4 ...
##  $ NombreDepartamento: chr  "Abarrotes" "Abarrotes" "Abarrotes" "Abarrotes" ...
##  $ NombreFamilia     : chr  "Lacteos y Refrigerados" "Lacteos y Refrigerados" "Pan y Tortilla" "Bebidas" ...
##  $ NombreCategoria   : chr  "Leche" "Yogurt" "Pan Dulce Empaquetado" "Refrescos Plástico (N.R.)" ...
##  $ Estado            : chr  "Nuevo León" "Nuevo León" "Nuevo León" "Nuevo León" ...
##  $ Mts.2             : int  60 60 60 60 60 60 60 60 60 60 ...
##  $ Tipo.ubicación    : chr  "Esquina" "Esquina" "Esquina" "Esquina" ...
##  $ Giro              : chr  "Abarrotes" "Abarrotes" "Abarrotes" "Abarrotes" ...
##  $ Hora.inicio       : chr  "08:00" "08:00" "08:00" "08:00" ...
##  $ Hora.cierre       : chr  "22:00" "22:00" "22:00" "22:00" ...
head(df, 10)
##    ClaveTienda  DescGiro Codigo.Barras PLU      Fecha     Hora
## 1        MX001 Abarrotes  7.501021e+12  NA 19/06/2020 08:16:21
## 2        MX001 Abarrotes  7.501032e+12  NA 19/06/2020 08:23:33
## 3        MX001 Abarrotes  7.501000e+12  NA 19/06/2020 08:24:33
## 4        MX001 Abarrotes  7.501031e+12  NA 19/06/2020 08:24:33
## 5        MX001 Abarrotes  7.501026e+12  NA 19/06/2020 08:26:28
## 6        MX001 Abarrotes  7.501021e+12  NA 19/06/2020 08:16:21
## 7        MX001 Abarrotes  7.501032e+12  NA 19/06/2020 08:23:33
## 8        MX001 Abarrotes  7.501000e+12  NA 19/06/2020 08:24:33
## 9        MX001 Abarrotes  7.501031e+12  NA 19/06/2020 08:24:33
## 10       MX001 Abarrotes  7.501026e+12  NA 19/06/2020 08:26:28
##                         Marca                 Fabricante
## 1                 NUTRI LECHE                    MEXILAC
## 2                      DAN UP           DANONE DE MEXICO
## 3                       BIMBO                GRUPO BIMBO
## 4                       PEPSI        PEPSI-COLA MEXICANA
## 5  BLANCA NIEVES (DETERGENTE) FABRICA DE JABON LA CORONA
## 6                 NUTRI LECHE                    MEXILAC
## 7                      DAN UP           DANONE DE MEXICO
## 8                       BIMBO                GRUPO BIMBO
## 9                       PEPSI        PEPSI-COLA MEXICANA
## 10 BLANCA NIEVES (DETERGENTE) FABRICA DE JABON LA CORONA
##                              Producto Precio Ult.Costo Unidades F.Ticket
## 1                 Nutri Leche 1 Litro   16.0     12.31        1        1
## 2  DANUP STRAWBERRY P/BEBER 350GR NAL   14.0     14.00        1        2
## 3                 Rebanadas Bimbo 2Pz    5.0      5.00        1        3
## 4                    Pepsi N.R. 400Ml    8.0      8.00        1        3
## 5       Detergente Blanca Nieves 500G   19.5     15.00        1        4
## 6                 Nutri Leche 1 Litro   16.0     12.31        1        1
## 7  DANUP STRAWBERRY P/BEBER 350GR NAL   14.0     14.00        1        2
## 8                 Rebanadas Bimbo 2Pz    5.0      5.00        1        3
## 9                    Pepsi N.R. 400Ml    8.0      8.00        1        3
## 10      Detergente Blanca Nieves 500G   19.5     15.00        1        4
##    NombreDepartamento          NombreFamilia           NombreCategoria
## 1           Abarrotes Lacteos y Refrigerados                     Leche
## 2           Abarrotes Lacteos y Refrigerados                    Yogurt
## 3           Abarrotes         Pan y Tortilla     Pan Dulce Empaquetado
## 4           Abarrotes                Bebidas Refrescos Plástico (N.R.)
## 5           Abarrotes     Limpieza del Hogar                Lavandería
## 6           Abarrotes Lacteos y Refrigerados                     Leche
## 7           Abarrotes Lacteos y Refrigerados                    Yogurt
## 8           Abarrotes         Pan y Tortilla     Pan Dulce Empaquetado
## 9           Abarrotes                Bebidas Refrescos Plástico (N.R.)
## 10          Abarrotes     Limpieza del Hogar                Lavandería
##        Estado Mts.2 Tipo.ubicación      Giro Hora.inicio Hora.cierre
## 1  Nuevo León    60        Esquina Abarrotes       08:00       22:00
## 2  Nuevo León    60        Esquina Abarrotes       08:00       22:00
## 3  Nuevo León    60        Esquina Abarrotes       08:00       22:00
## 4  Nuevo León    60        Esquina Abarrotes       08:00       22:00
## 5  Nuevo León    60        Esquina Abarrotes       08:00       22:00
## 6  Nuevo León    60        Esquina Abarrotes       08:00       22:00
## 7  Nuevo León    60        Esquina Abarrotes       08:00       22:00
## 8  Nuevo León    60        Esquina Abarrotes       08:00       22:00
## 9  Nuevo León    60        Esquina Abarrotes       08:00       22:00
## 10 Nuevo León    60        Esquina Abarrotes       08:00       22:00
tail(df, 10)
##        ClaveTienda DescGiro Codigo.Barras PLU      Fecha     Hora
## 200616       MX005 Depósito   7.62221e+12  NA 07/08/2020 19:30:13
## 200617       MX005 Depósito   7.62221e+12  NA 25/07/2020 18:42:24
## 200618       MX005 Depósito   7.62221e+12  NA 18/07/2020 22:45:58
## 200619       MX005 Depósito   7.62221e+12  NA 12/07/2020 00:36:34
## 200620       MX005 Depósito   7.62221e+12  NA 12/07/2020 01:08:25
## 200621       MX005 Depósito   7.62221e+12  NA 23/10/2020 22:17:37
## 200622       MX005 Depósito   7.62221e+12  NA 10/10/2020 20:30:20
## 200623       MX005 Depósito   7.62221e+12  NA 10/10/2020 22:40:43
## 200624       MX005 Depósito   7.62221e+12  NA 27/06/2020 22:30:19
## 200625       MX005 Depósito   7.62221e+12  NA 26/06/2020 23:43:34
##                    Marca    Fabricante                          Producto Precio
## 200616 TRIDENT XTRA CARE CADBURY ADAMS Trident Xtracare Freshmint 16.32G      9
## 200617 TRIDENT XTRA CARE CADBURY ADAMS Trident Xtracare Freshmint 16.32G      9
## 200618 TRIDENT XTRA CARE CADBURY ADAMS Trident Xtracare Freshmint 16.32G      9
## 200619 TRIDENT XTRA CARE CADBURY ADAMS Trident Xtracare Freshmint 16.32G      9
## 200620 TRIDENT XTRA CARE CADBURY ADAMS Trident Xtracare Freshmint 16.32G      9
## 200621 TRIDENT XTRA CARE CADBURY ADAMS Trident Xtracare Freshmint 16.32G      9
## 200622 TRIDENT XTRA CARE CADBURY ADAMS Trident Xtracare Freshmint 16.32G      9
## 200623 TRIDENT XTRA CARE CADBURY ADAMS Trident Xtracare Freshmint 16.32G      9
## 200624 TRIDENT XTRA CARE CADBURY ADAMS Trident Xtracare Freshmint 16.32G      9
## 200625 TRIDENT XTRA CARE CADBURY ADAMS Trident Xtracare Freshmint 16.32G      9
##        Ult.Costo Unidades F.Ticket NombreDepartamento NombreFamilia
## 200616      6.92        1   106411          Abarrotes      Dulcería
## 200617      6.92        1   104693          Abarrotes      Dulcería
## 200618      6.92        1   103856          Abarrotes      Dulcería
## 200619      6.92        1   103087          Abarrotes      Dulcería
## 200620      6.92        1   103100          Abarrotes      Dulcería
## 200621      6.92        1   116598          Abarrotes      Dulcería
## 200622      6.92        1   114886          Abarrotes      Dulcería
## 200623      6.92        1   114955          Abarrotes      Dulcería
## 200624      6.92        1   101121          Abarrotes      Dulcería
## 200625      6.92        1   100879          Abarrotes      Dulcería
##        NombreCategoria       Estado Mts.2 Tipo.ubicación       Giro Hora.inicio
## 200616 Gomas de Mazcar Quintana Roo    58        Esquina Mini súper       08:00
## 200617 Gomas de Mazcar Quintana Roo    58        Esquina Mini súper       08:00
## 200618 Gomas de Mazcar Quintana Roo    58        Esquina Mini súper       08:00
## 200619 Gomas de Mazcar Quintana Roo    58        Esquina Mini súper       08:00
## 200620 Gomas de Mazcar Quintana Roo    58        Esquina Mini súper       08:00
## 200621 Gomas de Mazcar Quintana Roo    58        Esquina Mini súper       08:00
## 200622 Gomas de Mazcar Quintana Roo    58        Esquina Mini súper       08:00
## 200623 Gomas de Mazcar Quintana Roo    58        Esquina Mini súper       08:00
## 200624 Gomas de Mazcar Quintana Roo    58        Esquina Mini súper       08:00
## 200625 Gomas de Mazcar Quintana Roo    58        Esquina Mini súper       08:00
##        Hora.cierre
## 200616       21:00
## 200617       21:00
## 200618       21:00
## 200619       21:00
## 200620       21:00
## 200621       21:00
## 200622       21:00
## 200623       21:00
## 200624       21:00
## 200625       21:00

Exploración inicial

# Número de filas y columnas
dim(df)
## [1] 200625     22
# Nombre de las columnas
names(df)
##  [1] "ClaveTienda"        "DescGiro"           "Codigo.Barras"     
##  [4] "PLU"                "Fecha"              "Hora"              
##  [7] "Marca"              "Fabricante"         "Producto"          
## [10] "Precio"             "Ult.Costo"          "Unidades"          
## [13] "F.Ticket"           "NombreDepartamento" "NombreFamilia"     
## [16] "NombreCategoria"    "Estado"             "Mts.2"             
## [19] "Tipo.ubicación"     "Giro"               "Hora.inicio"       
## [22] "Hora.cierre"
# Cantidad de datos faltantes por columna
colSums(is.na(df))
##        ClaveTienda           DescGiro      Codigo.Barras                PLU 
##                  0                  0                  0             199188 
##              Fecha               Hora              Marca         Fabricante 
##                  0                  0                  0                  0 
##           Producto             Precio          Ult.Costo           Unidades 
##                  0                  0                  0                  0 
##           F.Ticket NombreDepartamento      NombreFamilia    NombreCategoria 
##                  0                  0                  0                  0 
##             Estado              Mts.2     Tipo.ubicación               Giro 
##                  0                  0                  0                  0 
##        Hora.inicio        Hora.cierre 
##                  0                  0
# Número de registros por tienda
dplyr::count(df, ClaveTienda, sort = TRUE)
##   ClaveTienda     n
## 1       MX001 96469
## 2       MX004 83455
## 3       MX005 10021
## 4       MX002  6629
## 5       MX003  4051
# Productos más frecuentes
dplyr::count(df, Producto, sort = TRUE) %>%
  head(15)
##                       Producto    n
## 1              Pepsi N.R. 1.5L 5108
## 2    Coca Cola Retornable 2.5L 3771
## 3  Caguamon Tecate Light 1.2Lt 3471
## 4             Pepsi N. R. 2.5L 2899
## 5   Cerveza Tecate Light 340Ml 2619
## 6    Cerveza Tecate Light 16Oz 2315
## 7    Coca Cola Retornable 1.5L 2124
## 8                Pepsi N.R. 3L 1832
## 9   Coca Cola Retornable 500Ml 1659
## 10             PEPSI N.R. 1.5L 1631
## 11          Cigarros Link 20pz 1581
## 12          Doritos Nachos 53G 1549
## 13            NUTRI LECHE 1 LT 1496
## 14        COCA COLA N. R. 2.5L 1485
## 15        Coca Cola N. R. 2.5L 1467
# Departamentos más frecuentes
dplyr::count(df, NombreDepartamento, sort = TRUE)
##     NombreDepartamento      n
## 1            Abarrotes 198279
## 2   Bebes e Infantiles   1483
## 3           Ferretería    377
## 4             Farmacia    255
## 5      Vinos y Licores    104
## 6            Papelería     74
## 7             Mercería     44
## 8 Productos a Eliminar      8
## 9               Carnes      1
# Categorías más frecuentes
dplyr::count(df, NombreCategoria, sort = TRUE) %>%
  head(15)
##               NombreCategoria     n
## 1   Refrescos Plástico (N.R.) 32862
## 2       Refrescos Retornables 13880
## 3                    Frituras 11082
## 4                        Lata  8150
## 5                       Leche  7054
## 6                   Cajetilla  6329
## 7                     Botella  5867
## 8     Productos sin Categoria  5455
## 9                Papas Fritas  5344
## 10           Jugos y Néctares  5295
## 11                 Lavandería  4587
## 12      Pan Dulce Empaquetado  4466
## 13            Platos y Vasos   3271
## 14      Concentrados en Polvo  3211
## 15 Energizantes e Hidratantes  3077

Tablas descriptivas

# Tabla de tienda y departamento
tabyl(df, ClaveTienda, NombreDepartamento)
##  ClaveTienda Abarrotes Bebes e Infantiles Carnes Farmacia Ferretería Mercería
##        MX001     95415                515      1      147        245       28
##        MX002      6590                 21      0        4         10        0
##        MX003      4026                 15      0        2          8        0
##        MX004     82234                932      0      102        114       16
##        MX005     10014                  0      0        0          0        0
##  Papelería Productos a Eliminar Vinos y Licores
##         35                    3              80
##          0                    0               4
##          0                    0               0
##         32                    5              20
##          7                    0               0
# Tabla de tienda y categoría
tabyl(df, ClaveTienda, NombreCategoria)
##  ClaveTienda Achiotes Aderezo para Ensalada Afeitado y Depilación Aguas
##        MX001       28                    18                   210  1647
##        MX002        1                     0                     6   132
##        MX003        1                     1                    10    34
##        MX004       97                    36                   182  1172
##        MX005        0                     0                     0     0
##  Aguas Saborizadas Aluminios y Envolturas Antojitos Mexicanos
##               1273                      0                  11
##                134                      0                   0
##                 89                      2                   2
##               1134                      0                  35
##                  0                      0                   0
##  Aromatizantes de Ambiente Arroz Artículos de Limpieza Atoles Atún Avena
##                         28   674                     7     54  176    22
##                          3    10                     0      9   22     0
##                          0    18                     0      5    3     1
##                         17  1345                    19     83  213    58
##                          0     0                     0      0    0     0
##  Avena y Fibra Azúcar Azúcar Glass Azucarados Barras Energéticas
##             39    307            2        128                238
##              0      0            0          5                  0
##              8      0            0          3                  0
##             20      0           17         55                 16
##              0      4            0          0                  0
##  Bicarbonato de Sodio Blanqueador de Ropa Bombónes Botella Cacahuates Café
##                     0                 549        7    2225        386  326
##                     0                  46        0      41         64   33
##                     0                  21        0       7         15   17
##                   159                 506        0     125        197  688
##                     0                   1        0    3469         80    2
##  Cajetilla Cal Hidratada Camarón Seco Carne Seca Catsup Cepillo Dental Cerillos
##       3511             4           13         12    148             61      198
##        443             0            0          0      7              0        5
##         73             0            0          0      7              1        2
##       2106            46            6          0    260             72      100
##        196             0            0          0      2              0        0
##  Charal Chicharrón de Cerdo Chicharrón de Harina Chiclosos Chile Seco
##      23                 189                  305         4        109
##       0                  37                   22         0          0
##       0                  10                   15         0          2
##       0                  59                  286         2        231
##       0                  44                    0         0         25
##  Chiles Envasados Chocolate Chocolate Macizo Chocolates Chorizo
##               456       588               13        799      48
##                35        30                0          5       0
##                35        67                1         46      10
##              1599       278               33         47     243
##                 0         0                0          0       0
##  Comida para Gatos Comida para Perros Concentrados en Polvo Consome Cosméticos
##                 41                259                  1900    1090          8
##                  1                  8                    44       0          0
##                 18                 18                    49       9          0
##                107                426                  1218     505          1
##                  0                  0                     0       0          0
##  Cremas Cremas de Nieve Cucharas, Tenedores y Cuchillos  Cuidado de la Piel
##     797             183                                0                142
##      11              25                                5                 14
##      56              15                                4                  3
##    1583              82                               51                285
##       0               0                                0                  0
##  Cuidado del Cabello Desodorantes  Dulce Macizo Empanizador Encendedores
##                  290            73           79         271           66
##                   23             5            0           4            3
##                    4             2            0          17            0
##                  340            39            4         565           31
##                    0             0            0           0           83
##  Energizantes e Hidratantes Especias Extractos Fibras
##                        1741        0         9      1
##                         141        0         0      0
##                          36        0         0      0
##                        1159     1761        16      0
##                           0        0         0      0
##  Fideos, Spaguetti, Tallarines Finas Flan en Polvo Frijol Bayo Frijol Varios
##                            262    21             1         214           235
##                             10     0             0           2             5
##                             12     4             0           2             0
##                           1120    22             1          78            96
##                              0     0             0           0             0
##  Frijoles en Lata Frituras Fruta Acidulada Frutas Envasadas Frutas secas
##               480     6920              64               10            0
##                17      668              12                0            0
##                51      234               6                0            0
##               397     2732              60               15          102
##                 4      528               6                0            0
##  Gelatinas en Polvo y Ligth Gomas Gomas de Mazcar Harina para Pastel Helados
##                          14     6             176                 60       0
##                           0     0              17                  0       0
##                           2     0              24                  0       0
##                          45     0             113                105      96
##                           0     0             185                  0       0
##  Higiene Femenina Hojuelas de Maíz Hot Cakes Insecticidas y Venenos
##               319               38        43                     80
##                26                1         0                      9
##                11                2         2                      2
##               643               16        69                    235
##                 0                0         0                      0
##  Jamón de Cerdo Jamón de Pavo Jugos Jugos y Néctares Lata Lavandería
##              71           187    49             2634 2419       1790
##               0             0     1              389  155        149
##              18            53     6              176   19         79
##              97           231    73             2069  916       2559
##               0             0     0               27 4641         10
##  Lavatrastes Leche Leche en Polvo Leche Saborizada Leches y Cremas Lentejas
##          534  3070             84              716              73       14
##           20   106              5                5               0        1
##           19   185              0               54               3        0
##          386  3692            192              784              36        0
##            3     1              0                0               0        0
##  Light Limpiadores Líquidos Limpieza de Calzado Limpieza de Cocina Maíz
##      1                  759                  20                 23  103
##      0                   68                   2                  0    9
##      0                   51                   4                  0   10
##      0                  723                  23                 18  406
##      0                    2                   0                  0    0
##  Maíz Rocero Malvavisco Mantequilla Margarina Mayonesa  Mazapán Mermeladas
##           26        272          27        59       308      40         42
##            0         31           2         0        10       0          0
##            0         21           3         7        20       2          0
##            0        347         127       232       481      40         38
##            0          0           0         0         1       0          0
##  Mezclas para Cerveza Miel Mole Mortadela Mostaza Nachos Paletas
##                    31   20  167         0      29    332     309
##                    18    3    4         0       0     18       5
##                     0    2   10         0       1     13      20
##                    37   41  440        11      60    117      66
##                    78    0    0         0       0      0      71
##  Palillos de Madera Palomitas Palomitas para Microondas Pan de Barra Blanco
##                   0       116                       266                 481
##                   0        15                         1                   3
##                   2         7                        12                  32
##                  23       111                        50                 438
##                   0         9                         0                   0
##  Pan de Barra Integral Pan Dulce Empaquetado
##                    274                  2876
##                      0                    26
##                     21                   121
##                    239                  1443
##                      0                     0
##  Pan para Hamburguesa, HotDog y Tortas Pan Tostado y Crotones Pañal para Adulto
##                                    575                    114                 0
##                                      2                      1                 0
##                                     16                     11                 0
##                                    543                    156                 1
##                                      0                      0                 0
##  Papas Fritas Papel Higiénico Papillas Pasta Dental Pastas de Figuritas
##          3214             636      103          181                 287
##           306              28        0           13                  10
##           119               4        7            1                   5
##          1286            1350      224          383                 941
##           419              17        0            0                   0
##  Pastelillos Pastillas Pistaches Pizzas Platos y Vasos  Polvo para Hornear
##         1150       142         6      3             729                 59
##            3         5         0      0              33                  1
##           48         3         0      2              16                  6
##          961       112         0     21            2493                 89
##            0        31         0      0               0                  0
##  Polvos Populares Postres Refrigerados Productos del mar Envasados
##      32       236                  369                          18
##       0        11                    0                           1
##       1         9                   22                           1
##      16       659                  295                          96
##       2         0                    0                           0
##  Productos sin Categoria Puré de Papa Quesos Refrescos en Lata
##                     1927           17    884               203
##                       77            4      7                44
##                       43            3    133                 0
##                     3399           54   1888               236
##                        9            0      0                 0
##  Refrescos Plástico (N.R.) Refrescos Retornables Rellenas Repostería
##                      19834                  9118      189         15
##                       1709                   785        5          0
##                        779                   277       20          1
##                      10540                  3700      323         10
##                          0                     0        0          0
##  Saborizante para Leche Sal Sal Combinada Saladas Salchicha HotDog y Viena
##                      55 286            98     784                       94
##                       0  24             4      50                        0
##                       4  11             0      45                       35
##                      83 484             1     933                      302
##                       0   1             0       0                        0
##  Salchicha para Asar Salsa Botanera Salsa Casera Salsa para Spaguetti
##                   25            111            7                  651
##                    0             16            0                   15
##                   12              0            0                   28
##                  543            187           10                 1791
##                    0              4            0                    0
##  Salsa Picante Salsas Varias Sandwich Sazonadores Semillas Servilletas
##            339             7      453         209      222          80
##             19             1       15          20       46           0
##             12             0       29           8        3           1
##            496             0      223         469      111          21
##              8             0        0           0       29           0
##  Sopas Instantáneas Surtido Sustituto de Crema Tamarindos Te Té helado
##                 731     685                  2         50  4       115
##                  45       3                  0          0  0        17
##                  20      24                  0          6  0        16
##                 688     147                  3         26 16       233
##                  10       0                  0         12  0         0
##  Tendederos y Pinzas Tocino Tortillas de Harina e Integrales Tortillas de Maíz
##                    1     26                               26                 0
##                    0      1                                0                 0
##                    0     15                                0                27
##                    7    101                               32                 0
##                    0      0                                0                 0
##  Tostadas Tradicionales Trigo Tropicales Varios Verduras y Vegetales Vinagre
##       375           574   138         46     56                  327      41
##        14            52     6         21      1                   15       4
##        46            35    21          4      7                   15       1
##       628           892   404         43     50                 1009      72
##         7             0     0          0      0                    0       0
##  Yogurt
##     859
##       8
##      39
##    1468
##       0

Limpiar la base de datos

Técnica 1. Eliminar valores irrelevantes

Para Market Basket Analysis necesitamos identificar las transacciones y los productos comprados. Algunas variables descriptivas no son necesarias para generar las reglas.

Primero se crea un identificador de transacción.

NOTA: como la base proporcionada no muestra una columna específica de ticket o folio, se utiliza la combinación de tienda, fecha y hora como identificador de compra.

df$ClaveTienda <- as.character(df$ClaveTienda)
df$Fecha <- as.character(df$Fecha)
df$Hora <- as.character(df$Hora)
df$Producto <- as.character(df$Producto)

# Crear identificador de transacción
df$Transaccion <- paste(
  df$ClaveTienda,
  df$Fecha,
  df$Hora,
  sep = "_"
)

head(df[, c("Transaccion", "ClaveTienda", "Fecha", "Hora", "Producto")], 10)
##                  Transaccion ClaveTienda      Fecha     Hora
## 1  MX001_19/06/2020_08:16:21       MX001 19/06/2020 08:16:21
## 2  MX001_19/06/2020_08:23:33       MX001 19/06/2020 08:23:33
## 3  MX001_19/06/2020_08:24:33       MX001 19/06/2020 08:24:33
## 4  MX001_19/06/2020_08:24:33       MX001 19/06/2020 08:24:33
## 5  MX001_19/06/2020_08:26:28       MX001 19/06/2020 08:26:28
## 6  MX001_19/06/2020_08:16:21       MX001 19/06/2020 08:16:21
## 7  MX001_19/06/2020_08:23:33       MX001 19/06/2020 08:23:33
## 8  MX001_19/06/2020_08:24:33       MX001 19/06/2020 08:24:33
## 9  MX001_19/06/2020_08:24:33       MX001 19/06/2020 08:24:33
## 10 MX001_19/06/2020_08:26:28       MX001 19/06/2020 08:26:28
##                              Producto
## 1                 Nutri Leche 1 Litro
## 2  DANUP STRAWBERRY P/BEBER 350GR NAL
## 3                 Rebanadas Bimbo 2Pz
## 4                    Pepsi N.R. 400Ml
## 5       Detergente Blanca Nieves 500G
## 6                 Nutri Leche 1 Litro
## 7  DANUP STRAWBERRY P/BEBER 350GR NAL
## 8                 Rebanadas Bimbo 2Pz
## 9                    Pepsi N.R. 400Ml
## 10      Detergente Blanca Nieves 500G

Eliminar transacciones que contienen únicamente un producto, ya que no pueden generar asociaciones entre productos.

tam_transaccion <- df %>%
  dplyr::count(Transaccion, name = "CantidadProductos")

summary(tam_transaccion$CantidadProductos)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   1.000   1.000   1.000   1.521   2.000  23.000
df <- df %>%
  filter(
    Transaccion %in%
      tam_transaccion$Transaccion[
        tam_transaccion$CantidadProductos > 1
      ]
  )

Técnica 2. Eliminar valores repetidos

# Número de registros antes
nrow(df)
## [1] 111518
# Eliminar filas completamente duplicadas
df <- distinct(df)

# Número de registros después
nrow(df)
## [1] 111513

También se eliminan productos repetidos dentro de una misma transacción para evitar que un producto aparezca dos veces dentro del mismo basket.

df_mba <- df %>%
  select(Transaccion, Producto) %>%
  distinct(Transaccion, Producto)

Técnica 3. Corregir errores tipográficos y similares

Se eliminan espacios adicionales, comas y comillas en los nombres de productos, ya que la coma será utilizada posteriormente como separador dentro del basket.

df_mba$Producto <- stringr::str_squish(df_mba$Producto)

df_mba$Producto <- gsub(
  "[,\"']",
  " ",
  df_mba$Producto
)

df_mba$Producto <- stringr::str_squish(df_mba$Producto)

head(df_mba, 10)
##                  Transaccion                                 Producto
## 1  MX001_19/06/2020_08:16:21                      Nutri Leche 1 Litro
## 2  MX001_19/06/2020_08:23:33       DANUP STRAWBERRY P/BEBER 350GR NAL
## 3  MX001_19/06/2020_08:24:33                      Rebanadas Bimbo 2Pz
## 4  MX001_19/06/2020_08:24:33                         Pepsi N.R. 400Ml
## 5  MX001_19/06/2020_08:26:28            Detergente Blanca Nieves 500G
## 6  MX001_19/06/2020_08:26:28            Flash Xtra Brisa Marina 500Ml
## 7  MX001_19/06/2020_08:26:28      Danone Bipack Fresa Chocoarroz 130G
## 8  MX001_19/06/2020_08:26:28                     Jabon Zote Rosa 200G
## 9  MX001_19/06/2020_08:26:28 T Femenina Princess Nocturna C/Alas 8Pza
## 10 MX001_19/06/2020_15:24:02          Jugo De Durazno Del Valle 413Ml

Técnica 4. Convertir tipos de datos

df_mba$Transaccion <- as.character(df_mba$Transaccion)
df_mba$Producto <- as.character(df_mba$Producto)

str(df_mba)
## 'data.frame':    111507 obs. of  2 variables:
##  $ Transaccion: chr  "MX001_19/06/2020_08:16:21" "MX001_19/06/2020_08:23:33" "MX001_19/06/2020_08:24:33" "MX001_19/06/2020_08:24:33" ...
##  $ Producto   : chr  "Nutri Leche 1 Litro" "DANUP STRAWBERRY P/BEBER 350GR NAL" "Rebanadas Bimbo 2Pz" "Pepsi N.R. 400Ml" ...

Técnica 5. Tratar valores faltantes

# Revisar valores faltantes
colSums(is.na(df_mba))
## Transaccion    Producto 
##           0           0
# Eliminar productos con NA
df_mba <- df_mba[
  !is.na(df_mba$Producto),
]

# Eliminar productos vacíos
df_mba <- df_mba[
  df_mba$Producto != "",
]

colSums(is.na(df_mba))
## Transaccion    Producto 
##           0           0

Técnica 6. Herramientas estadísticas

Se analiza la cantidad de productos que existen dentro de cada transacción.

tam_transaccion_limpia <- df_mba %>%
  dplyr::count(
    Transaccion,
    name = "CantidadProductos"
  )

summary(tam_transaccion_limpia$CantidadProductos)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   1.000   2.000   2.000   2.605   3.000  23.000
boxplot(
  tam_transaccion_limpia$CantidadProductos,
  horizontal = TRUE,
  main = "Productos por transacción",
  xlab = "Cantidad de productos"
)

Generar Basket

Primero se ordena la base por transacción.

df_mba <- df_mba[
  order(df_mba$Transaccion),
]

Después se agrupan todos los productos pertenecientes a una misma transacción.

basket <- df_mba %>%
  group_by(Transaccion) %>%
  summarise(
    Producto = paste(
      unique(Producto),
      collapse = ","
    ),
    .groups = "drop"
  )

head(basket, 10)
## # A tibble: 10 × 2
##    Transaccion               Producto                                           
##    <chr>                     <chr>                                              
##  1 MX001_01/05/2020_00:02:43 Doritos Nachos 53G,Doritos Pizzerolas 47G,Cheetos …
##  2 MX001_01/05/2020_00:04:49 Coca Cola Retornable 500Ml,Cheetos Flaming Hot 48g…
##  3 MX001_01/05/2020_00:06:07 Palomitas Poffets Queso 26G,Cheetos Jumbo Colmillo…
##  4 MX001_01/05/2020_00:09:07 Cheetos Torciditos 48g,Coca Cola Retornable 2.5L,P…
##  5 MX001_01/05/2020_00:18:08 Paketaxo Mezcladito 170g Sabritas,Pake Taxo Sabrit…
##  6 MX001_01/05/2020_00:18:33 Maravillas Vainilla Gamesa Tubo 116gr,Leche Lala P…
##  7 MX001_01/05/2020_00:23:17 Cremax De Nieve Fresa 90G,Polvorones Marinela 113G…
##  8 MX001_01/05/2020_00:24:29 Lechita Fresa Santa Clara 200ml,Coca Cola Retornab…
##  9 MX001_01/05/2020_00:46:23 Jugo Bida Uva 500Ml,Valle Frut Citrus Punch 600Ml  
## 10 MX001_01/05/2020_00:58:59 Jumex Nectar De Guayaba 440Ml,Manzanita Sol N.R. 2…

Se elimina el identificador de transacción, ya que el archivo que utilizará arules únicamente necesita los productos pertenecientes a cada basket.

basket_exportar <- basket %>%
  select(Producto)

head(basket_exportar)
## # A tibble: 6 × 1
##   Producto                                                                      
##   <chr>                                                                         
## 1 Doritos Nachos 53G,Doritos Pizzerolas 47G,Cheetos Flaming Hot 48g,Coca Cola N…
## 2 Coca Cola Retornable 500Ml,Cheetos Flaming Hot 48g,Joya Sabores R. 355Ml      
## 3 Palomitas Poffets Queso 26G,Cheetos Jumbo Colmillos 27g,Orbit Menta 5.6G      
## 4 Cheetos Torciditos 48g,Coca Cola Retornable 2.5L,PAPEL HIGIENICO MEGA JBO 500…
## 5 Paketaxo Mezcladito 170g Sabritas,Pake Taxo Sabritas Botanero 65G,Pepsi N. R.…
## 6 Maravillas Vainilla Gamesa Tubo 116gr,Leche Lala Premium 1L

Exportar el basket.

write.table(
  basket_exportar,
  "basket.csv",
  sep = "\t",
  quote = FALSE,
  row.names = FALSE,
  col.names = FALSE
)

Market Basket Analysis

Ahora se convierte el archivo generado al formato de transacciones utilizado por el paquete arules.

tr <- read.transactions(
  "basket.csv",
  format = "basket",
  sep = ",",
  cols = NULL
)

tr
## transactions in sparse format with
##  42811 transactions (rows) and
##  3207 items (columns)

Revisar un resumen de las transacciones.

summary(tr)
## transactions as itemMatrix in sparse format with
##  42811 rows (elements/itemsets/transactions) and
##  3207 columns (items) and a density of 0.0008121716 
## 
## most frequent items:
##           Pepsi N.R. 1.5L Coca Cola Retornable 2.5L        Doritos Nachos 53G 
##                      1992                      1340                      1137 
##          Pepsi N. R. 2.5L          NUTRI LECHE 1 LT                   (Other) 
##                      1014                       929                    105095 
## 
## element (itemset/transaction) length distribution:
## sizes
##     1     2     3     4     5     6     7     8     9    10    11    12    13 
##     4 27733  9302  3404  1260   542   218   124    92    44    26    13    16 
##    14    15    16    17    18    19    20    23 
##     6    14     3     4     2     2     1     1 
## 
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   1.000   2.000   2.000   2.605   3.000  23.000 
## 
## includes extended item information - examples:
##                    labels
## 1 123 maxi efecto cn 900g
## 2              7.0646E+25
## 3             7.50044E+12

Reglas de Asociación

Se utiliza el algoritmo Apriori para identificar productos que aparecen juntos dentro de las transacciones.

reglas.asociacion <- apriori(
  tr,
  parameter = list(
    supp = 0.001,
    conf = 0.2,
    minlen = 2,
    maxlen = 10
  )
)
## Apriori
## 
## Parameter specification:
##  confidence minval smax arem  aval originalSupport maxtime support minlen
##         0.2    0.1    1 none FALSE            TRUE       5   0.001      2
##  maxlen target  ext
##      10  rules TRUE
## 
## Algorithmic control:
##  filter tree heap memopt load sort verbose
##     0.1 TRUE TRUE  FALSE TRUE    2    TRUE
## 
## Absolute minimum support count: 42 
## 
## set item appearances ...[0 item(s)] done [0.00s].
## set transactions ...[3207 item(s), 42811 transaction(s)] done [0.02s].
## sorting and recoding items ... [596 item(s)] done [0.00s].
## creating transaction tree ... done [0.01s].
## checking subsets of size 1 2 3 done [0.00s].
## writing ... [26 rule(s)] done [0.00s].
## creating S4 object  ... done [0.00s].

Cantidad total de reglas encontradas:

length(reglas.asociacion)
## [1] 26

Resumen de las reglas:

summary(reglas.asociacion)
## set of 26 rules
## 
## rule length distribution (lhs + rhs):sizes
##  2 
## 26 
## 
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##       2       2       2       2       2       2 
## 
## summary of quality measures:
##     support           confidence        coverage             lift       
##  Min.   :0.001028   Min.   :0.2080   Min.   :0.001915   Min.   :  7.83  
##  1st Qu.:0.001180   1st Qu.:0.2442   1st Qu.:0.003667   1st Qu.: 27.15  
##  Median :0.001402   Median :0.3160   Median :0.004999   Median : 43.15  
##  Mean   :0.001684   Mean   :0.3577   Mean   :0.005046   Mean   : 70.71  
##  3rd Qu.:0.002050   3rd Qu.:0.4198   3rd Qu.:0.005729   3rd Qu.: 73.04  
##  Max.   :0.003434   Max.   :0.7708   Max.   :0.009811   Max.   :239.29  
##      count       
##  Min.   : 44.00  
##  1st Qu.: 50.50  
##  Median : 60.00  
##  Mean   : 72.08  
##  3rd Qu.: 87.75  
##  Max.   :147.00  
## 
## mining info:
##  data ntransactions support confidence
##    tr         42811   0.001        0.2
##                                                                                     call
##  apriori(data = tr, parameter = list(supp = 0.001, conf = 0.2, minlen = 2, maxlen = 10))

Ordenar las reglas

Las reglas pueden evaluarse utilizando confidence, lift y support.

  • Confidence: indica qué tan probable es comprar el producto B después de comprar A.
  • Lift: indica qué tan fuerte es la relación entre ambos productos.
  • Support: indica qué tan frecuente es la combinación dentro de todas las compras.

Top 10 por Confidence

reglas_confidence <- sort(
  reglas.asociacion,
  by = "confidence",
  decreasing = TRUE
)

top10_confidence <- head(
  reglas_confidence,
  n = 10
)

inspect(top10_confidence)
##      lhs                                     rhs                                      support confidence    coverage      lift count
## [1]  {Detergente Salvo Microesferas 250G} => {Fabuloso Fresca Lavanda 500Ml}      0.002592792  0.7708333 0.003363621 220.00097   111
## [2]  {Fabuloso Fresca Lavanda 500Ml}      => {Detergente Salvo Microesferas 250G} 0.002592792  0.7400000 0.003503772 220.00097   111
## [3]  {MARLBORO GOLD 100´S 20PZ}           => {MARLBORO 100´S ROJO FT 20PZ}        0.003433697  0.5464684 0.006283432  55.70204   147
## [4]  {la lechera 387g}                    => {CARNETION CLAVEL 339ML}             0.001027773  0.5365854 0.001915396 239.28913    44
## [5]  {CARNETION CLAVEL 339ML}             => {la lechera 387g}                    0.001027773  0.4583333 0.002242414 239.28913    44
## [6]  {AJO MOLIDO 20G LA ARDILLA}          => {COMINO MOLIDO 20G LA ARDILLA}       0.002242414  0.4485981 0.004998715  85.73632    96
## [7]  {COMINO MOLIDO 20G LA ARDILLA}       => {AJO MOLIDO 20G LA ARDILLA}          0.002242414  0.4285714 0.005232300  85.73632    96
## [8]  {Caguamon Cerveza Indio 1.2 L}       => {Caguamon Tecate Light 1.2Lt}        0.001121207  0.3934426 0.002849735  33.55313    48
## [9]  {PIMIENTA MOLIDA PZ}                 => {COMINO MOLIDO 20G LA ARDILLA}       0.001401509  0.3821656 0.003667282  73.03970    60
## [10] {MARLBORO 100´S ROJO FT 20PZ}        => {MARLBORO GOLD 100´S 20PZ}           0.003433697  0.3500000 0.009810563  55.70204   147

Top 10 por Lift

reglas_lift <- sort(
  reglas.asociacion,
  by = "lift",
  decreasing = TRUE
)

top10_lift <- head(
  reglas_lift,
  n = 10
)

inspect(top10_lift)
##      lhs                                     rhs                                      support confidence    coverage      lift count
## [1]  {la lechera 387g}                    => {CARNETION CLAVEL 339ML}             0.001027773  0.5365854 0.001915396 239.28913    44
## [2]  {CARNETION CLAVEL 339ML}             => {la lechera 387g}                    0.001027773  0.4583333 0.002242414 239.28913    44
## [3]  {Detergente Salvo Microesferas 250G} => {Fabuloso Fresca Lavanda 500Ml}      0.002592792  0.7708333 0.003363621 220.00097   111
## [4]  {Fabuloso Fresca Lavanda 500Ml}      => {Detergente Salvo Microesferas 250G} 0.002592792  0.7400000 0.003503772 220.00097   111
## [5]  {AJO MOLIDO 20G LA ARDILLA}          => {COMINO MOLIDO 20G LA ARDILLA}       0.002242414  0.4485981 0.004998715  85.73632    96
## [6]  {COMINO MOLIDO 20G LA ARDILLA}       => {AJO MOLIDO 20G LA ARDILLA}          0.002242414  0.4285714 0.005232300  85.73632    96
## [7]  {PIMIENTA MOLIDA PZ}                 => {COMINO MOLIDO 20G LA ARDILLA}       0.001401509  0.3821656 0.003667282  73.03970    60
## [8]  {COMINO MOLIDO 20G LA ARDILLA}       => {PIMIENTA MOLIDA PZ}                 0.001401509  0.2678571 0.005232300  73.03970    60
## [9]  {AJO MOLIDO 20G LA ARDILLA}          => {PIMIENTA MOLIDA PZ}                 0.001214641  0.2429907 0.004998715  66.25906    52
## [10] {PIMIENTA MOLIDA PZ}                 => {AJO MOLIDO 20G LA ARDILLA}          0.001214641  0.3312102 0.003667282  66.25906    52

Top 10 por Support

reglas_support <- sort(
  reglas.asociacion,
  by = "support",
  decreasing = TRUE
)

top10_support <- head(
  reglas_support,
  n = 10
)

inspect(top10_support)
##      lhs                                     rhs                                      support confidence    coverage      lift count
## [1]  {MARLBORO GOLD 100´S 20PZ}           => {MARLBORO 100´S ROJO FT 20PZ}        0.003433697  0.5464684 0.006283432  55.70204   147
## [2]  {MARLBORO 100´S ROJO FT 20PZ}        => {MARLBORO GOLD 100´S 20PZ}           0.003433697  0.3500000 0.009810563  55.70204   147
## [3]  {Detergente Salvo Microesferas 250G} => {Fabuloso Fresca Lavanda 500Ml}      0.002592792  0.7708333 0.003363621 220.00097   111
## [4]  {Fabuloso Fresca Lavanda 500Ml}      => {Detergente Salvo Microesferas 250G} 0.002592792  0.7400000 0.003503772 220.00097   111
## [5]  {AJO MOLIDO 20G LA ARDILLA}          => {COMINO MOLIDO 20G LA ARDILLA}       0.002242414  0.4485981 0.004998715  85.73632    96
## [6]  {COMINO MOLIDO 20G LA ARDILLA}       => {AJO MOLIDO 20G LA ARDILLA}          0.002242414  0.4285714 0.005232300  85.73632    96
## [7]  {PASTA SPAGHETTI YEMINA 200G}        => {PURE DE TOMATE DEL FUERTE 345G}     0.002148980  0.3161512 0.006797318  27.56568    92
## [8]  {charola 855}                        => {VASOS DES CONVERMEX 12OZ 25PZ}      0.001751886  0.2232143 0.007848450  27.14780    75
## [9]  {VASOS DES CONVERMEX 12OZ 25PZ}      => {charola 855}                        0.001751886  0.2130682 0.008222186  27.14780    75
## [10] {MANTECA VEGETAL INCA 250G}          => {HARINA DE TRIGO SELECTA 1KG}        0.001681811  0.2962963 0.005676111  43.14538    72

Visualización de las principales reglas

if(length(reglas.asociacion) > 0){

  top10reglas <- head(
    reglas_lift,
    n = min(10, length(reglas_lift))
  )

  plot(
    top10reglas,
    method = "graph",
    engine = "htmlwidget"
  )
}

Los 15 productos que más se venden

itemFrequencyPlot(
  tr,
  topN = 15,
  type = "relative",
  main = "Los 15 productos que más se compran"
)

Tabla de las mejores reglas

if(length(reglas.asociacion) > 0){

  tabla_reglas <- as(
    reglas_lift,
    "data.frame"
  )

  head(tabla_reglas, 10)
}
##                                                                      rules
## 2                            {la lechera 387g} => {CARNETION CLAVEL 339ML}
## 3                            {CARNETION CLAVEL 339ML} => {la lechera 387g}
## 4  {Detergente Salvo Microesferas 250G} => {Fabuloso Fresca Lavanda 500Ml}
## 5  {Fabuloso Fresca Lavanda 500Ml} => {Detergente Salvo Microesferas 250G}
## 18           {AJO MOLIDO 20G LA ARDILLA} => {COMINO MOLIDO 20G LA ARDILLA}
## 19           {COMINO MOLIDO 20G LA ARDILLA} => {AJO MOLIDO 20G LA ARDILLA}
## 13                  {PIMIENTA MOLIDA PZ} => {COMINO MOLIDO 20G LA ARDILLA}
## 14                  {COMINO MOLIDO 20G LA ARDILLA} => {PIMIENTA MOLIDA PZ}
## 12                     {AJO MOLIDO 20G LA ARDILLA} => {PIMIENTA MOLIDA PZ}
## 11                     {PIMIENTA MOLIDA PZ} => {AJO MOLIDO 20G LA ARDILLA}
##        support confidence    coverage      lift count
## 2  0.001027773  0.5365854 0.001915396 239.28913    44
## 3  0.001027773  0.4583333 0.002242414 239.28913    44
## 4  0.002592792  0.7708333 0.003363621 220.00097   111
## 5  0.002592792  0.7400000 0.003503772 220.00097   111
## 18 0.002242414  0.4485981 0.004998715  85.73632    96
## 19 0.002242414  0.4285714 0.005232300  85.73632    96
## 13 0.001401509  0.3821656 0.003667282  73.03970    60
## 14 0.001401509  0.2678571 0.005232300  73.03970    60
## 12 0.001214641  0.2429907 0.004998715  66.25906    52
## 11 0.001214641  0.3312102 0.003667282  66.25906    52

Recomendaciones

1. Utilizar las reglas con mayor Lift

Las reglas con Lift mayor a 1 indican que existe una asociación positiva entre los productos. Estas combinaciones son especialmente útiles para diseñar promociones, ya que la compra de un producto incrementa la probabilidad de comprar el otro.

if(length(reglas.asociacion) > 0){

  mejor_lift <- head(
    reglas_lift,
    1
  )

  inspect(mejor_lift)
}
##     lhs                  rhs                      support     confidence
## [1] {la lechera 387g} => {CARNETION CLAVEL 339ML} 0.001027773 0.5365854 
##     coverage    lift     count
## [1] 0.001915396 239.2891 44

Por lo tanto, una estrategia sería colocar cerca los productos pertenecientes a las reglas con mayor lift o incluirlos dentro de una promoción conjunta.

2. Analizar las reglas con mayor Confidence

Las reglas con mayor Confidence representan aquellas donde, después de comprar un producto, existe una probabilidad alta de que el cliente compre también el producto asociado.

if(length(reglas.asociacion) > 0){

  mejor_confidence <- head(
    reglas_confidence,
    1
  )

  inspect(mejor_confidence)
}
##     lhs                                     rhs                                 support confidence    coverage    lift count
## [1] {Detergente Salvo Microesferas 250G} => {Fabuloso Fresca Lavanda 500Ml} 0.002592792  0.7708333 0.003363621 220.001   111

Estas reglas pueden utilizarse para crear recomendaciones de productos o promociones del tipo “si compras A, lleva también B”.

3. Considerar los productos con mayor Support

Los productos y combinaciones con mayor Support aparecen frecuentemente dentro de las compras.

if(length(reglas.asociacion) > 0){

  mejor_support <- head(
    reglas_support,
    1
  )

  inspect(mejor_support)
}
##     lhs                           rhs                               support confidence    coverage     lift count
## [1] {MARLBORO GOLD 100´S 20PZ} => {MARLBORO 100´S ROJO FT 20PZ} 0.003433697  0.5464684 0.006283432 55.70204   147

Estos productos pueden utilizarse como productos de atracción, ya que forman parte de una proporción importante de las transacciones.

4. Diferenciar popularidad de asociación

Una regla puede tener un Support alto simplemente porque sus productos son muy populares. Sin embargo, esto no necesariamente significa que exista una asociación fuerte entre ellos.

Por esta razón, para diseñar promociones es recomendable revisar principalmente el Lift y complementarlo con Confidence y Support.

5. Diseñar promociones simples

Las reglas de asociación permiten detectar pares o pequeños grupos de productos que los clientes ya compran juntos.

Una estrategia recomendable es crear promociones de dos o tres productos relacionados en lugar de promociones demasiado grandes, ya que son más fáciles de entender para los clientes y están respaldadas por los patrones observados en las transacciones.

Conclusiones

El Market Basket Analysis permitió identificar relaciones entre los productos comprados dentro de las tiendas.

El Support permitió identificar las combinaciones más frecuentes, el Confidence mostró la probabilidad de que un cliente comprara un segundo producto después de comprar el primero y el Lift permitió conocer qué asociaciones fueron más fuertes de lo esperado por casualidad.

Las reglas con un Lift mayor a 1 son las más útiles para desarrollar promociones cruzadas, acomodo estratégico de productos y recomendaciones de compra.

Por lo tanto, la cadena de tiendas puede utilizar los patrones encontrados para diseñar promociones basadas en el comportamiento real de sus clientes y aumentar las oportunidades de venta.

Tabla de Tienda y Departamento

janitor::tabyl(df, ClaveTienda, NombreDepartamento)
##  ClaveTienda Abarrotes Bebes e Infantiles Carnes Farmacia Ferretería Mercería
##        MX001     52024                239      1       72         58       11
##        MX002      2765                  5      0        1          0        0
##        MX003      3080                  8      0        2          7        0
##        MX004     50997                460      0       32         32        9
##        MX005      1649                  0      0        0          0        0
##  Papelería Productos a Eliminar Vinos y Licores
##          8                    2              25
##          0                    0               2
##          0                    0               0
##         12                    1              10
##          1                    0               0

Tabla de estados y hora de inicio

janitor::tabyl(df, Estado, Hora.inicio)
##        Estado 07:00 08:00 09:00
##       Chiapas  3097     0     0
##       Jalisco     0     0  2773
##    Nuevo León     0 52440     0
##  Quintana Roo     0  1650     0
##       Sinaloa 51553     0     0

Limpiar la base de datos

Limpiar la base de datos

Técnica 1. Eliminar valores irrelevantes

# Eliminar columnas
df <- subset(df, select = -c(PLU))

# Eliminar renglones
df <- df[df$Precio > 0, ]

Técnica 2. Eliminar valores repetidos

df <- dplyr::distinct(df)

Técnica 3. Corregir errores tipográficos y similares

df$Unidades <- ceiling(df$Unidades)

Técnica 4. Corregir formato de fecha

df$Fecha <- as.Date(df$Fecha, format = "%d/%m/%Y")

Técnica 5. Tratar valores faltantes

# Borrar todos los NA's
# df <- na.omit(df)

# Reemplazar los NA's con CEROS
# df[is.na(df)] <- 0

# Reemplazar los NA's con el PROMEDIO
# df$altura[is.na(df$altura)] <- mean(df$altura, na.rm = TRUE)

Técnica 6. Herramientas Estadísticas

boxplot(df$Precio, horizontal = TRUE)

boxplot(df$Unidades, horizontal = TRUE)

LS0tCnRpdGxlOiAiS2FtaWxhaE1hcmtldEJhc2tldCIKYXV0aG9yOiAiS2FtaWxhaCBDaGFpZGV6IEEwMTc0MTk0MyIKZGF0ZTogIjIwMjYtMDgtMTgiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRoZW1lOiBjb3NtbwotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgd2FybmluZyA9IEZBTFNFLAogIG1lc3NhZ2UgPSBGQUxTRQopCmBgYAoKIyA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5UZW9yw61hPC9zcGFuPgoKRWwgKipNYXJrZXQgQmFza2V0IEFuYWx5c2lzKiogZXMgdW5hIHTDqWNuaWNhIGVuIGVsIMOhbWJpdG8gZGUgYW7DoWxpc2lzIHkgbWluZXLDrWEgZGUgZGF0b3MgZW4gZWwgY2FtcG8gZGVsIGNvbWVyY2lvLiBTdSBvYmpldGl2byBwcmluY2lwYWwgZXMgZGVzY3VicmlyIHBhdHJvbmVzIGRlIGFzb2NpYWNpw7NuIGVudHJlIHByb2R1Y3RvcyBxdWUgc3VlbGVuIHNlciBjb21wcmFkb3MganVudG9zIHBvciBsb3MgY2xpZW50ZXMuCgpMYXMgMyBtw6l0cmljYXMgcHJpbmNpcGFsZXMgcGFyYSBldmFsdWFyIHJlZ2xhcyBkZSBhc29jaWFjacOzbiBzb246CgoqICoqQ29uZmlkZW5jZSAoQ29uZmlhbnphKToqKiBQcm9iYWJpbGlkYWQgZGUgY29tcHJhciBCIHNhYmllbmRvIHF1ZSBzZSBjb21wcsOzIEEuIFBvciBlamVtcGxvLCBQYW4gLS0+IE1hbnRlcXVpbGxhID0gMC44IHNpZ25pZmljYSBxdWUgZGUgY2FkYSAxMDAgY2xpZW50ZXMgcXVlIGNvbXByYXJvbiBwYW4sIDgwIGNvbXByYXJvbiBtYW50ZXF1aWxsYSB0YW1iacOpbi4KCiogKipMaWZ0IChFbGV2YWNpw7NuKToqKiBDdcOhbnRvIG3DoXMgcHJvYmFibGUgZXMgY29tcHJhciBCIGN1YW5kbyBzZSBjb21wcmEgQSBlbiBjb21wYXJhY2nDs24gY29uIGxhIHByb2JhYmlsaWRhZCBkZSBjb21wcmFyIEIgc2luIHNhYmVyIHNpIHNlIGNvbXByw7MgQS4gIAogICogTGlmdCA+IDE6IGNvbXByYXIgQSBpbXB1bHNhIGxhIGNvbXByYSBkZSBCLgogICogTGlmdCA9IDE6IGxvcyBwcm9kdWN0b3Mgbm8gdGllbmVuIHJlbGFjacOzbi4KICAqIExpZnQgPCAxOiBjb21wcmFyIEEgcmVkdWNlIGxhIHByb2JhYmlsaWRhZCBkZSBjb21wcmFyIEIuCgoqICoqU3VwcG9ydCAoU29wb3J0ZSk6KiogUG9wdWxhcmlkYWQgZGUgdW5hIGNvbWJpbmFjacOzbiBkZW50cm8gZGUgdG9kYXMgbGFzIHRyYW5zYWNjaW9uZXMuIFBvciBlamVtcGxvLCBQYW4geSBNYW50ZXF1aWxsYSA9IDAuMDUgc2lnbmlmaWNhIHF1ZSBlbCA1JSBkZSB0b2RhcyBsYXMgdHJhbnNhY2Npb25lcyBpbmNsdXllcm9uIGFtYm9zIHByb2R1Y3Rvcy4KCiFbXShodHRwczovL2Fycy5lbHMtY2RuLmNvbS9jb250ZW50L2ltYWdlLzMtczIuMC1COTc4MDEyMzgxNDc5MTAwMDA2WC1mMDYtMDEtOTc4MDEyMzgxNDc5MS5qcGcpCgojIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPkNvbnRleHRvPC9zcGFuPgoKVW5hIGNhZGVuYSBkZSB0aWVuZGFzIGRlIGNvbnZlbmllbmNpYSB0aWVuZSA1IHRpZW5kYXMgdWJpY2FkYXMgZW4gZGlzdGludGFzIGNpdWRhZGVzIGRlIE3DqXhpY28uIExhIGJhc2UgZGUgZGF0b3MgImFiYXJyb3RlcyIgY29udGllbmUgMSBtZXMgZGUgdHJhbnNhY2Npb25lcywgcGVybyBwcmVzZW50YSBlcnJvcmVzIGRlIGNhbGlkYWQgcXVlIGltcGlkZW4gcmVhbGl6YXIgYW7DoWxpc2lzIGNvbmZpYWJsZXMuCgpFbCBvYmpldGl2byBlcyBsaW1waWFyIGxhIGJhc2UgZGUgZGF0b3MgZGUgZm9ybWEgZXN0cmF0w6lnaWNhIHkgcG9zdGVyaW9ybWVudGUgYXBsaWNhciBNYXJrZXQgQmFza2V0IEFuYWx5c2lzIHBhcmEgZGVzY3VicmlyIHBhdHJvbmVzIGRlIGNvbXByYSB5IGRpc2XDsWFyIHByb21vY2lvbmVzIHF1ZSBhdW1lbnRlbiBsYXMgdmVudGFzLgoKIyA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5JbnN0YWxhciBwYXF1ZXRlcyB5IGxsYW1hciBsaWJyZXLDrWFzPC9zcGFuPgoKTGFzIHNpZ3VpZW50ZXMgbMOtbmVhcyBkZSBpbnN0YWxhY2nDs24gc29sYW1lbnRlIHNlIGRlYmVuIGVqZWN1dGFyIHVuYSB2ZXogZGVzZGUgbGEgY29uc29sYSBkZSBSU3R1ZGlvOgoKYGBge3IgZXZhbD1GQUxTRX0KaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikKaW5zdGFsbC5wYWNrYWdlcygiamFuaXRvciIpCmluc3RhbGwucGFja2FnZXMoImFydWxlcyIpCmluc3RhbGwucGFja2FnZXMoImFydWxlc1ZpeiIpCmBgYAoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGphbml0b3IpCmxpYnJhcnkoYXJ1bGVzKQpsaWJyYXJ5KGFydWxlc1ZpeikKYGBgCgojIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPkltcG9ydGFyIGxhIGJhc2UgZGUgZGF0b3M8L3NwYW4+CgpgYGB7cn0KZGYgPC0gcmVhZC5jc3YoCiAgImh0dHBzOi8vZHJpdmUuZ29vZ2xlLmNvbS91Yz9leHBvcnQ9ZG93bmxvYWQmaWQ9MWRUN0JkemhOMFZOZkdLeld2eGl6X3hodjFwUjZuM2FDIiwKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsCiAgY2hlY2submFtZXMgPSBUUlVFCikKCnN1bW1hcnkoZGYpCgpzdHIoZGYpCgpoZWFkKGRmLCAxMCkKCnRhaWwoZGYsIDEwKQpgYGAKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+RXhwbG9yYWNpw7NuIGluaWNpYWw8L3NwYW4+CgpgYGB7cn0KIyBOw7ptZXJvIGRlIGZpbGFzIHkgY29sdW1uYXMKZGltKGRmKQoKIyBOb21icmUgZGUgbGFzIGNvbHVtbmFzCm5hbWVzKGRmKQoKIyBDYW50aWRhZCBkZSBkYXRvcyBmYWx0YW50ZXMgcG9yIGNvbHVtbmEKY29sU3Vtcyhpcy5uYShkZikpCgojIE7Dum1lcm8gZGUgcmVnaXN0cm9zIHBvciB0aWVuZGEKZHBseXI6OmNvdW50KGRmLCBDbGF2ZVRpZW5kYSwgc29ydCA9IFRSVUUpCgojIFByb2R1Y3RvcyBtw6FzIGZyZWN1ZW50ZXMKZHBseXI6OmNvdW50KGRmLCBQcm9kdWN0bywgc29ydCA9IFRSVUUpICU+JQogIGhlYWQoMTUpCgojIERlcGFydGFtZW50b3MgbcOhcyBmcmVjdWVudGVzCmRwbHlyOjpjb3VudChkZiwgTm9tYnJlRGVwYXJ0YW1lbnRvLCBzb3J0ID0gVFJVRSkKCiMgQ2F0ZWdvcsOtYXMgbcOhcyBmcmVjdWVudGVzCmRwbHlyOjpjb3VudChkZiwgTm9tYnJlQ2F0ZWdvcmlhLCBzb3J0ID0gVFJVRSkgJT4lCiAgaGVhZCgxNSkKYGBgCgojIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPlRhYmxhcyBkZXNjcmlwdGl2YXM8L3NwYW4+CgpgYGB7cn0KIyBUYWJsYSBkZSB0aWVuZGEgeSBkZXBhcnRhbWVudG8KdGFieWwoZGYsIENsYXZlVGllbmRhLCBOb21icmVEZXBhcnRhbWVudG8pCmBgYAoKYGBge3J9CiMgVGFibGEgZGUgdGllbmRhIHkgY2F0ZWdvcsOtYQp0YWJ5bChkZiwgQ2xhdmVUaWVuZGEsIE5vbWJyZUNhdGVnb3JpYSkKYGBgCgojIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPkxpbXBpYXIgbGEgYmFzZSBkZSBkYXRvczwvc3Bhbj4KCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPlTDqWNuaWNhIDEuIEVsaW1pbmFyIHZhbG9yZXMgaXJyZWxldmFudGVzPC9zcGFuPgoKUGFyYSBNYXJrZXQgQmFza2V0IEFuYWx5c2lzIG5lY2VzaXRhbW9zIGlkZW50aWZpY2FyIGxhcyB0cmFuc2FjY2lvbmVzIHkgbG9zIHByb2R1Y3RvcyBjb21wcmFkb3MuIEFsZ3VuYXMgdmFyaWFibGVzIGRlc2NyaXB0aXZhcyBubyBzb24gbmVjZXNhcmlhcyBwYXJhIGdlbmVyYXIgbGFzIHJlZ2xhcy4KClByaW1lcm8gc2UgY3JlYSB1biBpZGVudGlmaWNhZG9yIGRlIHRyYW5zYWNjacOzbi4KCioqTk9UQToqKiBjb21vIGxhIGJhc2UgcHJvcG9yY2lvbmFkYSBubyBtdWVzdHJhIHVuYSBjb2x1bW5hIGVzcGVjw61maWNhIGRlIHRpY2tldCBvIGZvbGlvLCBzZSB1dGlsaXphIGxhIGNvbWJpbmFjacOzbiBkZSB0aWVuZGEsIGZlY2hhIHkgaG9yYSBjb21vIGlkZW50aWZpY2Fkb3IgZGUgY29tcHJhLgoKYGBge3J9CmRmJENsYXZlVGllbmRhIDwtIGFzLmNoYXJhY3RlcihkZiRDbGF2ZVRpZW5kYSkKZGYkRmVjaGEgPC0gYXMuY2hhcmFjdGVyKGRmJEZlY2hhKQpkZiRIb3JhIDwtIGFzLmNoYXJhY3RlcihkZiRIb3JhKQpkZiRQcm9kdWN0byA8LSBhcy5jaGFyYWN0ZXIoZGYkUHJvZHVjdG8pCgojIENyZWFyIGlkZW50aWZpY2Fkb3IgZGUgdHJhbnNhY2Npw7NuCmRmJFRyYW5zYWNjaW9uIDwtIHBhc3RlKAogIGRmJENsYXZlVGllbmRhLAogIGRmJEZlY2hhLAogIGRmJEhvcmEsCiAgc2VwID0gIl8iCikKCmhlYWQoZGZbLCBjKCJUcmFuc2FjY2lvbiIsICJDbGF2ZVRpZW5kYSIsICJGZWNoYSIsICJIb3JhIiwgIlByb2R1Y3RvIildLCAxMCkKYGBgCgpFbGltaW5hciB0cmFuc2FjY2lvbmVzIHF1ZSBjb250aWVuZW4gw7puaWNhbWVudGUgdW4gcHJvZHVjdG8sIHlhIHF1ZSBubyBwdWVkZW4gZ2VuZXJhciBhc29jaWFjaW9uZXMgZW50cmUgcHJvZHVjdG9zLgoKYGBge3J9CnRhbV90cmFuc2FjY2lvbiA8LSBkZiAlPiUKICBkcGx5cjo6Y291bnQoVHJhbnNhY2Npb24sIG5hbWUgPSAiQ2FudGlkYWRQcm9kdWN0b3MiKQoKc3VtbWFyeSh0YW1fdHJhbnNhY2Npb24kQ2FudGlkYWRQcm9kdWN0b3MpCgpkZiA8LSBkZiAlPiUKICBmaWx0ZXIoCiAgICBUcmFuc2FjY2lvbiAlaW4lCiAgICAgIHRhbV90cmFuc2FjY2lvbiRUcmFuc2FjY2lvblsKICAgICAgICB0YW1fdHJhbnNhY2Npb24kQ2FudGlkYWRQcm9kdWN0b3MgPiAxCiAgICAgIF0KICApCmBgYAoKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+VMOpY25pY2EgMi4gRWxpbWluYXIgdmFsb3JlcyByZXBldGlkb3M8L3NwYW4+CgpgYGB7cn0KIyBOw7ptZXJvIGRlIHJlZ2lzdHJvcyBhbnRlcwpucm93KGRmKQoKIyBFbGltaW5hciBmaWxhcyBjb21wbGV0YW1lbnRlIGR1cGxpY2FkYXMKZGYgPC0gZGlzdGluY3QoZGYpCgojIE7Dum1lcm8gZGUgcmVnaXN0cm9zIGRlc3B1w6lzCm5yb3coZGYpCmBgYAoKVGFtYmnDqW4gc2UgZWxpbWluYW4gcHJvZHVjdG9zIHJlcGV0aWRvcyBkZW50cm8gZGUgdW5hIG1pc21hIHRyYW5zYWNjacOzbiBwYXJhIGV2aXRhciBxdWUgdW4gcHJvZHVjdG8gYXBhcmV6Y2EgZG9zIHZlY2VzIGRlbnRybyBkZWwgbWlzbW8gYmFza2V0LgoKYGBge3J9CmRmX21iYSA8LSBkZiAlPiUKICBzZWxlY3QoVHJhbnNhY2Npb24sIFByb2R1Y3RvKSAlPiUKICBkaXN0aW5jdChUcmFuc2FjY2lvbiwgUHJvZHVjdG8pCmBgYAoKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+VMOpY25pY2EgMy4gQ29ycmVnaXIgZXJyb3JlcyB0aXBvZ3LDoWZpY29zIHkgc2ltaWxhcmVzPC9zcGFuPgoKU2UgZWxpbWluYW4gZXNwYWNpb3MgYWRpY2lvbmFsZXMsIGNvbWFzIHkgY29taWxsYXMgZW4gbG9zIG5vbWJyZXMgZGUgcHJvZHVjdG9zLCB5YSBxdWUgbGEgY29tYSBzZXLDoSB1dGlsaXphZGEgcG9zdGVyaW9ybWVudGUgY29tbyBzZXBhcmFkb3IgZGVudHJvIGRlbCBiYXNrZXQuCgpgYGB7cn0KZGZfbWJhJFByb2R1Y3RvIDwtIHN0cmluZ3I6OnN0cl9zcXVpc2goZGZfbWJhJFByb2R1Y3RvKQoKZGZfbWJhJFByb2R1Y3RvIDwtIGdzdWIoCiAgIlssXCInXSIsCiAgIiAiLAogIGRmX21iYSRQcm9kdWN0bwopCgpkZl9tYmEkUHJvZHVjdG8gPC0gc3RyaW5ncjo6c3RyX3NxdWlzaChkZl9tYmEkUHJvZHVjdG8pCgpoZWFkKGRmX21iYSwgMTApCmBgYAoKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+VMOpY25pY2EgNC4gQ29udmVydGlyIHRpcG9zIGRlIGRhdG9zPC9zcGFuPgoKYGBge3J9CmRmX21iYSRUcmFuc2FjY2lvbiA8LSBhcy5jaGFyYWN0ZXIoZGZfbWJhJFRyYW5zYWNjaW9uKQpkZl9tYmEkUHJvZHVjdG8gPC0gYXMuY2hhcmFjdGVyKGRmX21iYSRQcm9kdWN0bykKCnN0cihkZl9tYmEpCmBgYAoKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+VMOpY25pY2EgNS4gVHJhdGFyIHZhbG9yZXMgZmFsdGFudGVzPC9zcGFuPgoKYGBge3J9CiMgUmV2aXNhciB2YWxvcmVzIGZhbHRhbnRlcwpjb2xTdW1zKGlzLm5hKGRmX21iYSkpCgojIEVsaW1pbmFyIHByb2R1Y3RvcyBjb24gTkEKZGZfbWJhIDwtIGRmX21iYVsKICAhaXMubmEoZGZfbWJhJFByb2R1Y3RvKSwKXQoKIyBFbGltaW5hciBwcm9kdWN0b3MgdmFjw61vcwpkZl9tYmEgPC0gZGZfbWJhWwogIGRmX21iYSRQcm9kdWN0byAhPSAiIiwKXQoKY29sU3Vtcyhpcy5uYShkZl9tYmEpKQpgYGAKCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPlTDqWNuaWNhIDYuIEhlcnJhbWllbnRhcyBlc3RhZMOtc3RpY2FzPC9zcGFuPgoKU2UgYW5hbGl6YSBsYSBjYW50aWRhZCBkZSBwcm9kdWN0b3MgcXVlIGV4aXN0ZW4gZGVudHJvIGRlIGNhZGEgdHJhbnNhY2Npw7NuLgoKYGBge3J9CnRhbV90cmFuc2FjY2lvbl9saW1waWEgPC0gZGZfbWJhICU+JQogIGRwbHlyOjpjb3VudCgKICAgIFRyYW5zYWNjaW9uLAogICAgbmFtZSA9ICJDYW50aWRhZFByb2R1Y3RvcyIKICApCgpzdW1tYXJ5KHRhbV90cmFuc2FjY2lvbl9saW1waWEkQ2FudGlkYWRQcm9kdWN0b3MpCgpib3hwbG90KAogIHRhbV90cmFuc2FjY2lvbl9saW1waWEkQ2FudGlkYWRQcm9kdWN0b3MsCiAgaG9yaXpvbnRhbCA9IFRSVUUsCiAgbWFpbiA9ICJQcm9kdWN0b3MgcG9yIHRyYW5zYWNjacOzbiIsCiAgeGxhYiA9ICJDYW50aWRhZCBkZSBwcm9kdWN0b3MiCikKYGBgCgojIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPkdlbmVyYXIgQmFza2V0PC9zcGFuPgoKUHJpbWVybyBzZSBvcmRlbmEgbGEgYmFzZSBwb3IgdHJhbnNhY2Npw7NuLgoKYGBge3J9CmRmX21iYSA8LSBkZl9tYmFbCiAgb3JkZXIoZGZfbWJhJFRyYW5zYWNjaW9uKSwKXQpgYGAKCkRlc3B1w6lzIHNlIGFncnVwYW4gdG9kb3MgbG9zIHByb2R1Y3RvcyBwZXJ0ZW5lY2llbnRlcyBhIHVuYSBtaXNtYSB0cmFuc2FjY2nDs24uCgpgYGB7cn0KYmFza2V0IDwtIGRmX21iYSAlPiUKICBncm91cF9ieShUcmFuc2FjY2lvbikgJT4lCiAgc3VtbWFyaXNlKAogICAgUHJvZHVjdG8gPSBwYXN0ZSgKICAgICAgdW5pcXVlKFByb2R1Y3RvKSwKICAgICAgY29sbGFwc2UgPSAiLCIKICAgICksCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKQoKaGVhZChiYXNrZXQsIDEwKQpgYGAKClNlIGVsaW1pbmEgZWwgaWRlbnRpZmljYWRvciBkZSB0cmFuc2FjY2nDs24sIHlhIHF1ZSBlbCBhcmNoaXZvIHF1ZSB1dGlsaXphcsOhIGBhcnVsZXNgIMO6bmljYW1lbnRlIG5lY2VzaXRhIGxvcyBwcm9kdWN0b3MgcGVydGVuZWNpZW50ZXMgYSBjYWRhIGJhc2tldC4KCmBgYHtyfQpiYXNrZXRfZXhwb3J0YXIgPC0gYmFza2V0ICU+JQogIHNlbGVjdChQcm9kdWN0bykKCmhlYWQoYmFza2V0X2V4cG9ydGFyKQpgYGAKCkV4cG9ydGFyIGVsIGJhc2tldC4KCmBgYHtyfQp3cml0ZS50YWJsZSgKICBiYXNrZXRfZXhwb3J0YXIsCiAgImJhc2tldC5jc3YiLAogIHNlcCA9ICJcdCIsCiAgcXVvdGUgPSBGQUxTRSwKICByb3cubmFtZXMgPSBGQUxTRSwKICBjb2wubmFtZXMgPSBGQUxTRQopCmBgYAoKIyA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5NYXJrZXQgQmFza2V0IEFuYWx5c2lzPC9zcGFuPgoKQWhvcmEgc2UgY29udmllcnRlIGVsIGFyY2hpdm8gZ2VuZXJhZG8gYWwgZm9ybWF0byBkZSB0cmFuc2FjY2lvbmVzIHV0aWxpemFkbyBwb3IgZWwgcGFxdWV0ZSBgYXJ1bGVzYC4KCmBgYHtyfQp0ciA8LSByZWFkLnRyYW5zYWN0aW9ucygKICAiYmFza2V0LmNzdiIsCiAgZm9ybWF0ID0gImJhc2tldCIsCiAgc2VwID0gIiwiLAogIGNvbHMgPSBOVUxMCikKCnRyCmBgYAoKUmV2aXNhciB1biByZXN1bWVuIGRlIGxhcyB0cmFuc2FjY2lvbmVzLgoKYGBge3J9CnN1bW1hcnkodHIpCmBgYAoKIyA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5SZWdsYXMgZGUgQXNvY2lhY2nDs248L3NwYW4+CgpTZSB1dGlsaXphIGVsIGFsZ29yaXRtbyAqKkFwcmlvcmkqKiBwYXJhIGlkZW50aWZpY2FyIHByb2R1Y3RvcyBxdWUgYXBhcmVjZW4ganVudG9zIGRlbnRybyBkZSBsYXMgdHJhbnNhY2Npb25lcy4KCmBgYHtyfQpyZWdsYXMuYXNvY2lhY2lvbiA8LSBhcHJpb3JpKAogIHRyLAogIHBhcmFtZXRlciA9IGxpc3QoCiAgICBzdXBwID0gMC4wMDEsCiAgICBjb25mID0gMC4yLAogICAgbWlubGVuID0gMiwKICAgIG1heGxlbiA9IDEwCiAgKQopCmBgYAoKQ2FudGlkYWQgdG90YWwgZGUgcmVnbGFzIGVuY29udHJhZGFzOgoKYGBge3J9Cmxlbmd0aChyZWdsYXMuYXNvY2lhY2lvbikKYGBgCgpSZXN1bWVuIGRlIGxhcyByZWdsYXM6CgpgYGB7cn0Kc3VtbWFyeShyZWdsYXMuYXNvY2lhY2lvbikKYGBgCgojIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPk9yZGVuYXIgbGFzIHJlZ2xhczwvc3Bhbj4KCkxhcyByZWdsYXMgcHVlZGVuIGV2YWx1YXJzZSB1dGlsaXphbmRvICoqY29uZmlkZW5jZSoqLCAqKmxpZnQqKiB5ICoqc3VwcG9ydCoqLgoKKiAqKkNvbmZpZGVuY2U6KiogaW5kaWNhIHF1w6kgdGFuIHByb2JhYmxlIGVzIGNvbXByYXIgZWwgcHJvZHVjdG8gQiBkZXNwdcOpcyBkZSBjb21wcmFyIEEuCiogKipMaWZ0OioqIGluZGljYSBxdcOpIHRhbiBmdWVydGUgZXMgbGEgcmVsYWNpw7NuIGVudHJlIGFtYm9zIHByb2R1Y3Rvcy4KKiAqKlN1cHBvcnQ6KiogaW5kaWNhIHF1w6kgdGFuIGZyZWN1ZW50ZSBlcyBsYSBjb21iaW5hY2nDs24gZGVudHJvIGRlIHRvZGFzIGxhcyBjb21wcmFzLgoKIyA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5Ub3AgMTAgcG9yIENvbmZpZGVuY2U8L3NwYW4+CgpgYGB7cn0KcmVnbGFzX2NvbmZpZGVuY2UgPC0gc29ydCgKICByZWdsYXMuYXNvY2lhY2lvbiwKICBieSA9ICJjb25maWRlbmNlIiwKICBkZWNyZWFzaW5nID0gVFJVRQopCgp0b3AxMF9jb25maWRlbmNlIDwtIGhlYWQoCiAgcmVnbGFzX2NvbmZpZGVuY2UsCiAgbiA9IDEwCikKCmluc3BlY3QodG9wMTBfY29uZmlkZW5jZSkKYGBgCgojIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPlRvcCAxMCBwb3IgTGlmdDwvc3Bhbj4KCmBgYHtyfQpyZWdsYXNfbGlmdCA8LSBzb3J0KAogIHJlZ2xhcy5hc29jaWFjaW9uLAogIGJ5ID0gImxpZnQiLAogIGRlY3JlYXNpbmcgPSBUUlVFCikKCnRvcDEwX2xpZnQgPC0gaGVhZCgKICByZWdsYXNfbGlmdCwKICBuID0gMTAKKQoKaW5zcGVjdCh0b3AxMF9saWZ0KQpgYGAKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+VG9wIDEwIHBvciBTdXBwb3J0PC9zcGFuPgoKYGBge3J9CnJlZ2xhc19zdXBwb3J0IDwtIHNvcnQoCiAgcmVnbGFzLmFzb2NpYWNpb24sCiAgYnkgPSAic3VwcG9ydCIsCiAgZGVjcmVhc2luZyA9IFRSVUUKKQoKdG9wMTBfc3VwcG9ydCA8LSBoZWFkKAogIHJlZ2xhc19zdXBwb3J0LAogIG4gPSAxMAopCgppbnNwZWN0KHRvcDEwX3N1cHBvcnQpCmBgYAoKIyA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5WaXN1YWxpemFjacOzbiBkZSBsYXMgcHJpbmNpcGFsZXMgcmVnbGFzPC9zcGFuPgoKYGBge3J9CmlmKGxlbmd0aChyZWdsYXMuYXNvY2lhY2lvbikgPiAwKXsKCiAgdG9wMTByZWdsYXMgPC0gaGVhZCgKICAgIHJlZ2xhc19saWZ0LAogICAgbiA9IG1pbigxMCwgbGVuZ3RoKHJlZ2xhc19saWZ0KSkKICApCgogIHBsb3QoCiAgICB0b3AxMHJlZ2xhcywKICAgIG1ldGhvZCA9ICJncmFwaCIsCiAgICBlbmdpbmUgPSAiaHRtbHdpZGdldCIKICApCn0KYGBgCgojIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPkxvcyAxNSBwcm9kdWN0b3MgcXVlIG3DoXMgc2UgdmVuZGVuPC9zcGFuPgoKYGBge3J9Cml0ZW1GcmVxdWVuY3lQbG90KAogIHRyLAogIHRvcE4gPSAxNSwKICB0eXBlID0gInJlbGF0aXZlIiwKICBtYWluID0gIkxvcyAxNSBwcm9kdWN0b3MgcXVlIG3DoXMgc2UgY29tcHJhbiIKKQpgYGAKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+VGFibGEgZGUgbGFzIG1lam9yZXMgcmVnbGFzPC9zcGFuPgoKYGBge3J9CmlmKGxlbmd0aChyZWdsYXMuYXNvY2lhY2lvbikgPiAwKXsKCiAgdGFibGFfcmVnbGFzIDwtIGFzKAogICAgcmVnbGFzX2xpZnQsCiAgICAiZGF0YS5mcmFtZSIKICApCgogIGhlYWQodGFibGFfcmVnbGFzLCAxMCkKfQpgYGAKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+UmVjb21lbmRhY2lvbmVzPC9zcGFuPgoKIyMgMS4gVXRpbGl6YXIgbGFzIHJlZ2xhcyBjb24gbWF5b3IgTGlmdAoKTGFzIHJlZ2xhcyBjb24gKipMaWZ0IG1heW9yIGEgMSoqIGluZGljYW4gcXVlIGV4aXN0ZSB1bmEgYXNvY2lhY2nDs24gcG9zaXRpdmEgZW50cmUgbG9zIHByb2R1Y3Rvcy4gRXN0YXMgY29tYmluYWNpb25lcyBzb24gZXNwZWNpYWxtZW50ZSDDunRpbGVzIHBhcmEgZGlzZcOxYXIgcHJvbW9jaW9uZXMsIHlhIHF1ZSBsYSBjb21wcmEgZGUgdW4gcHJvZHVjdG8gaW5jcmVtZW50YSBsYSBwcm9iYWJpbGlkYWQgZGUgY29tcHJhciBlbCBvdHJvLgoKYGBge3J9CmlmKGxlbmd0aChyZWdsYXMuYXNvY2lhY2lvbikgPiAwKXsKCiAgbWVqb3JfbGlmdCA8LSBoZWFkKAogICAgcmVnbGFzX2xpZnQsCiAgICAxCiAgKQoKICBpbnNwZWN0KG1lam9yX2xpZnQpCn0KYGBgCgpQb3IgbG8gdGFudG8sIHVuYSBlc3RyYXRlZ2lhIHNlcsOtYSBjb2xvY2FyIGNlcmNhIGxvcyBwcm9kdWN0b3MgcGVydGVuZWNpZW50ZXMgYSBsYXMgcmVnbGFzIGNvbiBtYXlvciBsaWZ0IG8gaW5jbHVpcmxvcyBkZW50cm8gZGUgdW5hIHByb21vY2nDs24gY29uanVudGEuCgojIyAyLiBBbmFsaXphciBsYXMgcmVnbGFzIGNvbiBtYXlvciBDb25maWRlbmNlCgpMYXMgcmVnbGFzIGNvbiBtYXlvciAqKkNvbmZpZGVuY2UqKiByZXByZXNlbnRhbiBhcXVlbGxhcyBkb25kZSwgZGVzcHXDqXMgZGUgY29tcHJhciB1biBwcm9kdWN0bywgZXhpc3RlIHVuYSBwcm9iYWJpbGlkYWQgYWx0YSBkZSBxdWUgZWwgY2xpZW50ZSBjb21wcmUgdGFtYmnDqW4gZWwgcHJvZHVjdG8gYXNvY2lhZG8uCgpgYGB7cn0KaWYobGVuZ3RoKHJlZ2xhcy5hc29jaWFjaW9uKSA+IDApewoKICBtZWpvcl9jb25maWRlbmNlIDwtIGhlYWQoCiAgICByZWdsYXNfY29uZmlkZW5jZSwKICAgIDEKICApCgogIGluc3BlY3QobWVqb3JfY29uZmlkZW5jZSkKfQpgYGAKCkVzdGFzIHJlZ2xhcyBwdWVkZW4gdXRpbGl6YXJzZSBwYXJhIGNyZWFyIHJlY29tZW5kYWNpb25lcyBkZSBwcm9kdWN0b3MgbyBwcm9tb2Npb25lcyBkZWwgdGlwbyAqKiJzaSBjb21wcmFzIEEsIGxsZXZhIHRhbWJpw6luIEIiKiouCgojIyAzLiBDb25zaWRlcmFyIGxvcyBwcm9kdWN0b3MgY29uIG1heW9yIFN1cHBvcnQKCkxvcyBwcm9kdWN0b3MgeSBjb21iaW5hY2lvbmVzIGNvbiBtYXlvciAqKlN1cHBvcnQqKiBhcGFyZWNlbiBmcmVjdWVudGVtZW50ZSBkZW50cm8gZGUgbGFzIGNvbXByYXMuCgpgYGB7cn0KaWYobGVuZ3RoKHJlZ2xhcy5hc29jaWFjaW9uKSA+IDApewoKICBtZWpvcl9zdXBwb3J0IDwtIGhlYWQoCiAgICByZWdsYXNfc3VwcG9ydCwKICAgIDEKICApCgogIGluc3BlY3QobWVqb3Jfc3VwcG9ydCkKfQpgYGAKCkVzdG9zIHByb2R1Y3RvcyBwdWVkZW4gdXRpbGl6YXJzZSBjb21vIHByb2R1Y3RvcyBkZSBhdHJhY2Npw7NuLCB5YSBxdWUgZm9ybWFuIHBhcnRlIGRlIHVuYSBwcm9wb3JjacOzbiBpbXBvcnRhbnRlIGRlIGxhcyB0cmFuc2FjY2lvbmVzLgoKIyMgNC4gRGlmZXJlbmNpYXIgcG9wdWxhcmlkYWQgZGUgYXNvY2lhY2nDs24KClVuYSByZWdsYSBwdWVkZSB0ZW5lciB1biAqKlN1cHBvcnQgYWx0byoqIHNpbXBsZW1lbnRlIHBvcnF1ZSBzdXMgcHJvZHVjdG9zIHNvbiBtdXkgcG9wdWxhcmVzLiBTaW4gZW1iYXJnbywgZXN0byBubyBuZWNlc2FyaWFtZW50ZSBzaWduaWZpY2EgcXVlIGV4aXN0YSB1bmEgYXNvY2lhY2nDs24gZnVlcnRlIGVudHJlIGVsbG9zLgoKUG9yIGVzdGEgcmF6w7NuLCBwYXJhIGRpc2XDsWFyIHByb21vY2lvbmVzIGVzIHJlY29tZW5kYWJsZSByZXZpc2FyIHByaW5jaXBhbG1lbnRlIGVsICoqTGlmdCoqIHkgY29tcGxlbWVudGFybG8gY29uICoqQ29uZmlkZW5jZSoqIHkgKipTdXBwb3J0KiouCgojIyA1LiBEaXNlw7FhciBwcm9tb2Npb25lcyBzaW1wbGVzCgpMYXMgcmVnbGFzIGRlIGFzb2NpYWNpw7NuIHBlcm1pdGVuIGRldGVjdGFyIHBhcmVzIG8gcGVxdWXDsW9zIGdydXBvcyBkZSBwcm9kdWN0b3MgcXVlIGxvcyBjbGllbnRlcyB5YSBjb21wcmFuIGp1bnRvcy4KClVuYSBlc3RyYXRlZ2lhIHJlY29tZW5kYWJsZSBlcyBjcmVhciBwcm9tb2Npb25lcyBkZSBkb3MgbyB0cmVzIHByb2R1Y3RvcyByZWxhY2lvbmFkb3MgZW4gbHVnYXIgZGUgcHJvbW9jaW9uZXMgZGVtYXNpYWRvIGdyYW5kZXMsIHlhIHF1ZSBzb24gbcOhcyBmw6FjaWxlcyBkZSBlbnRlbmRlciBwYXJhIGxvcyBjbGllbnRlcyB5IGVzdMOhbiByZXNwYWxkYWRhcyBwb3IgbG9zIHBhdHJvbmVzIG9ic2VydmFkb3MgZW4gbGFzIHRyYW5zYWNjaW9uZXMuCgojIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPkNvbmNsdXNpb25lczwvc3Bhbj4KCkVsIE1hcmtldCBCYXNrZXQgQW5hbHlzaXMgcGVybWl0acOzIGlkZW50aWZpY2FyIHJlbGFjaW9uZXMgZW50cmUgbG9zIHByb2R1Y3RvcyBjb21wcmFkb3MgZGVudHJvIGRlIGxhcyB0aWVuZGFzLgoKRWwgKipTdXBwb3J0KiogcGVybWl0acOzIGlkZW50aWZpY2FyIGxhcyBjb21iaW5hY2lvbmVzIG3DoXMgZnJlY3VlbnRlcywgZWwgKipDb25maWRlbmNlKiogbW9zdHLDsyBsYSBwcm9iYWJpbGlkYWQgZGUgcXVlIHVuIGNsaWVudGUgY29tcHJhcmEgdW4gc2VndW5kbyBwcm9kdWN0byBkZXNwdcOpcyBkZSBjb21wcmFyIGVsIHByaW1lcm8geSBlbCAqKkxpZnQqKiBwZXJtaXRpw7MgY29ub2NlciBxdcOpIGFzb2NpYWNpb25lcyBmdWVyb24gbcOhcyBmdWVydGVzIGRlIGxvIGVzcGVyYWRvIHBvciBjYXN1YWxpZGFkLgoKTGFzIHJlZ2xhcyBjb24gdW4gKipMaWZ0IG1heW9yIGEgMSoqIHNvbiBsYXMgbcOhcyDDunRpbGVzIHBhcmEgZGVzYXJyb2xsYXIgcHJvbW9jaW9uZXMgY3J1emFkYXMsIGFjb21vZG8gZXN0cmF0w6lnaWNvIGRlIHByb2R1Y3RvcyB5IHJlY29tZW5kYWNpb25lcyBkZSBjb21wcmEuCgpQb3IgbG8gdGFudG8sIGxhIGNhZGVuYSBkZSB0aWVuZGFzIHB1ZWRlIHV0aWxpemFyIGxvcyBwYXRyb25lcyBlbmNvbnRyYWRvcyBwYXJhIGRpc2XDsWFyIHByb21vY2lvbmVzIGJhc2FkYXMgZW4gZWwgY29tcG9ydGFtaWVudG8gcmVhbCBkZSBzdXMgY2xpZW50ZXMgeSBhdW1lbnRhciBsYXMgb3BvcnR1bmlkYWRlcyBkZSB2ZW50YS4KCiMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+VGFibGEgZGUgVGllbmRhIHkgRGVwYXJ0YW1lbnRvPC9zcGFuPgoKYGBge3J9CgpqYW5pdG9yOjp0YWJ5bChkZiwgQ2xhdmVUaWVuZGEsIE5vbWJyZURlcGFydGFtZW50bykKCmBgYAoKIyA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5UYWJsYSBkZSBlc3RhZG9zIHkgaG9yYSBkZSBpbmljaW88L3NwYW4+CgpgYGB7cn0KCmphbml0b3I6OnRhYnlsKGRmLCBFc3RhZG8sIEhvcmEuaW5pY2lvKQoKYGBgCgojIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPkxpbXBpYXIgbGEgYmFzZSBkZSBkYXRvczwvc3Bhbj4KCiMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+TGltcGlhciBsYSBiYXNlIGRlIGRhdG9zPC9zcGFuPgoKIyA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5Uw6ljbmljYSAxLiBFbGltaW5hciB2YWxvcmVzIGlycmVsZXZhbnRlczwvc3Bhbj4KCmBgYHtyfQoKIyBFbGltaW5hciBjb2x1bW5hcwpkZiA8LSBzdWJzZXQoZGYsIHNlbGVjdCA9IC1jKFBMVSkpCgojIEVsaW1pbmFyIHJlbmdsb25lcwpkZiA8LSBkZltkZiRQcmVjaW8gPiAwLCBdCgpgYGAKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+VMOpY25pY2EgMi4gRWxpbWluYXIgdmFsb3JlcyByZXBldGlkb3M8L3NwYW4+CgpgYGB7cn0KCmRmIDwtIGRwbHlyOjpkaXN0aW5jdChkZikKCmBgYAoKIyA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5Uw6ljbmljYSAzLiBDb3JyZWdpciBlcnJvcmVzIHRpcG9ncsOhZmljb3MgeSBzaW1pbGFyZXM8L3NwYW4+CgpgYGB7cn0KCmRmJFVuaWRhZGVzIDwtIGNlaWxpbmcoZGYkVW5pZGFkZXMpCgpgYGAKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+VMOpY25pY2EgNC4gQ29ycmVnaXIgZm9ybWF0byBkZSBmZWNoYTwvc3Bhbj4KCmBgYHtyfQoKZGYkRmVjaGEgPC0gYXMuRGF0ZShkZiRGZWNoYSwgZm9ybWF0ID0gIiVkLyVtLyVZIikKCmBgYAoKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+VMOpY25pY2EgNS4gVHJhdGFyIHZhbG9yZXMgZmFsdGFudGVzPC9zcGFuPgoKYGBge3J9CgojIEJvcnJhciB0b2RvcyBsb3MgTkEncwojIGRmIDwtIG5hLm9taXQoZGYpCgojIFJlZW1wbGF6YXIgbG9zIE5BJ3MgY29uIENFUk9TCiMgZGZbaXMubmEoZGYpXSA8LSAwCgojIFJlZW1wbGF6YXIgbG9zIE5BJ3MgY29uIGVsIFBST01FRElPCiMgZGYkYWx0dXJhW2lzLm5hKGRmJGFsdHVyYSldIDwtIG1lYW4oZGYkYWx0dXJhLCBuYS5ybSA9IFRSVUUpCgpgYGAKCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPlTDqWNuaWNhIDYuIEhlcnJhbWllbnRhcyBFc3RhZMOtc3RpY2FzPC9zcGFuPgoKYGBge3J9Cgpib3hwbG90KGRmJFByZWNpbywgaG9yaXpvbnRhbCA9IFRSVUUpCgpib3hwbG90KGRmJFVuaWRhZGVzLCBob3Jpem9udGFsID0gVFJVRSkKCmBgYAo=