Reglas de Asociación en R para Investigación de Mercados

A continuación te proporcionaré un código paso a paso para realizar análisis de reglas de asociación en R, junto con su explicación. Este método es muy útil en investigación de mercados para descubrir patrones de compra frecuentes (por ejemplo, “los clientes que compran pan y mantequilla también suelen comprar leche”).

Paso 1: Instalar y cargar los paquetes necesarios

# Instalar los paquetes si no los tienes
#install.packages("arules")      # Para reglas de asociación
#install.packages("arulesViz")   # Para visualización
#install.packages("tidyverse")   # Para manipulación de datos

# Cargar los paquetes
library(arules)
## Cargando paquete requerido: Matrix
## 
## Adjuntando el paquete: 'arules'
## The following objects are masked from 'package:base':
## 
##     abbreviate, write
library(arulesViz)
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.1     ✔ tibble    3.2.1
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.0.4
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ tidyr::expand() masks Matrix::expand()
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ✖ tidyr::pack()   masks Matrix::pack()
## ✖ dplyr::recode() masks arules::recode()
## ✖ tidyr::unpack() masks Matrix::unpack()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors

Paso 2: Preparar los datos

Necesitamos datos en formato transaccional (lista de compras por transacción). Usaré un conjunto de datos de ejemplo, pero puedes reemplazarlo con tus propios datos.

# Crear datos de ejemplo (transacciones de supermercado) 
transacciones <- list(
  c("pan", "leche", "huevos"),
  c("pan", "mantequilla", "leche", "refresco"),
  c("huevos", "refresco"),
  c("pan", "mantequilla", "refresco"),
  c("huevos", "leche"),
  c("pan", "mantequilla", "leche", "huevos"),
  c("pan", "mantequilla", "leche"),
  c("pan", "leche")
)
transacciones
## [[1]]
## [1] "pan"    "leche"  "huevos"
## 
## [[2]]
## [1] "pan"         "mantequilla" "leche"       "refresco"   
## 
## [[3]]
## [1] "huevos"   "refresco"
## 
## [[4]]
## [1] "pan"         "mantequilla" "refresco"   
## 
## [[5]]
## [1] "huevos" "leche" 
## 
## [[6]]
## [1] "pan"         "mantequilla" "leche"       "huevos"     
## 
## [[7]]
## [1] "pan"         "mantequilla" "leche"      
## 
## [[8]]
## [1] "pan"   "leche"
# Convertir a formato transaccional
transacciones <- as(transacciones, "transactions")

Si tienes datos en un dataframe (por ejemplo, CSV), puedes cargarlos así:

# Leer datos desde un archivo CSV
# datos <- read.csv("tu_archivo.csv")

# Convertir a formato transaccional (ajusta según tu estructura de datos)
# transacciones <- as(split(datos[,"producto"], datos[,"id_transaccion"]), "transactions")

Paso 3: Explorar los datos

# Resumen de las transacciones
summary(transacciones)
## transactions as itemMatrix in sparse format with
##  8 rows (elements/itemsets/transactions) and
##  5 columns (items) and a density of 0.575 
## 
## most frequent items:
##       leche         pan      huevos mantequilla    refresco     (Other) 
##           6           6           4           4           3           0 
## 
## element (itemset/transaction) length distribution:
## sizes
## 2 3 4 
## 3 3 2 
## 
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   2.000   2.000   3.000   2.875   3.250   4.000 
## 
## includes extended item information - examples:
##        labels
## 1      huevos
## 2       leche
## 3 mantequilla
# Ver las primeras transacciones
arules::inspect(head(transacciones, 3))
##     items                              
## [1] {huevos, leche, pan}               
## [2] {leche, mantequilla, pan, refresco}
## [3] {huevos, refresco}
# Frecuencia de items
itemFrequency(transacciones)
##      huevos       leche mantequilla         pan    refresco 
##       0.500       0.750       0.500       0.750       0.375
# Gráfico de frecuencia de items (top 5)
itemFrequencyPlot(transacciones, topN = 5, main = "Productos más frecuentes")

Paso 4: Generar reglas de asociación

# Aplicar el algoritmo Apriori
reglas <- apriori(transacciones, 
                  parameter = list(
                    support = 0.1,    # Soporte mínimo (frecuencia relativa)
                    confidence = 0.6, # Confianza mínima
                    minlen = 2        # Mínimo de items por regla
                  ))
## Apriori
## 
## Parameter specification:
##  confidence minval smax arem  aval originalSupport maxtime support minlen
##         0.6    0.1    1 none FALSE            TRUE       5     0.1      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: 0 
## 
## set item appearances ...[0 item(s)] done [0.00s].
## set transactions ...[5 item(s), 8 transaction(s)] done [0.00s].
## sorting and recoding items ... [5 item(s)] done [0.00s].
## creating transaction tree ... done [0.00s].
## checking subsets of size 1 2 3 4 done [0.00s].
## writing ... [23 rule(s)] done [0.00s].
## creating S4 object  ... done [0.00s].
# Resumen de las reglas encontradas
summary(reglas)
## set of 23 rules
## 
## rule length distribution (lhs + rhs):sizes
##  2  3  4 
##  8 11  4 
## 
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   2.000   2.000   3.000   2.826   3.000   4.000 
## 
## summary of quality measures:
##     support        confidence        coverage           lift       
##  Min.   :0.125   Min.   :0.6000   Min.   :0.1250   Min.   :0.8889  
##  1st Qu.:0.125   1st Qu.:0.7500   1st Qu.:0.1250   1st Qu.:1.1111  
##  Median :0.250   Median :1.0000   Median :0.3750   Median :1.3333  
##  Mean   :0.288   Mean   :0.8775   Mean   :0.3533   Mean   :1.3130  
##  3rd Qu.:0.375   3rd Qu.:1.0000   3rd Qu.:0.5000   3rd Qu.:1.3333  
##  Max.   :0.625   Max.   :1.0000   Max.   :0.7500   Max.   :2.0000  
##      count      
##  Min.   :1.000  
##  1st Qu.:1.000  
##  Median :2.000  
##  Mean   :2.304  
##  3rd Qu.:3.000  
##  Max.   :5.000  
## 
## mining info:
##           data ntransactions support confidence
##  transacciones             8     0.1        0.6
##                                                                                          call
##  apriori(data = transacciones, parameter = list(support = 0.1, confidence = 0.6, minlen = 2))

Partimos de 8 transacciones,¿cómo se obtuvieron 23 reglas?

  1. Combinatorias posibles:

    • Con 6 items únicos en tus datos (pan, leche, huevos, mantequilla, refresco, etc., el número de posibles combinaciones (itemsets) es 2⁶ - 1 = 63 posibles conjuntos de items no vacíos.

    • El algoritmo Apriori evalúa sistemáticamente todas las combinaciones posibles que cumplen con los umbrales de soporte mínimo (0.1 en nuestro caso).

  2. Generación de reglas:

    • Para cada itemset frecuente (como {pan, leche}), el algoritmo genera todas las posibles reglas (pan → leche y leche → pan) y luego filtra por confianza.

    • Con solo 3 items en un conjunto, ya puedes generar 6 reglas posibles (A → B, A → C, B → A, etc.).

  3. Tus parámetros actuales:

support = 0.1    # Solo requiere que aparezca en 1 transacción (8*0.1=0.8 → redondeo a 1)
confidence = 0.6  # Umbral relativamente bajo
minlen = 2         # Permite reglas de solo 2 items
  • Estos parámetros permisivos permiten muchas combinaciones.

Demostración con nuestros datos:

  1. Transacciones originales:

    # 1: {pan, leche, huevos}
    # 2: {pan, mantequilla, leche, refresco}
    # 3: {huevos, refresco}
    # 4: {pan, mantequilla, refresco}
    # 5: {huevos, leche}
    # 6: {pan, mantequilla, leche, huevos}
    # 7: {pan, mantequilla, leche}
    # 8: {pan, leche}
  2. Itemsets frecuentes (con soporte ≥ 0.1):

    • Pares: {pan,leche} (soporte 5/8), {pan,mantequilla} (3/8), {leche,huevos} (3/8), etc.

    • Tripletas: {pan,mantequilla,leche} (3/8), etc.

  3. Ejemplo de generación de múltiples reglas:
    Solo del itemset {pan, mantequilla, leche} (que aparece en 3 transacciones) se pueden generar:

    • pan → mantequilla,leche

    • mantequilla → pan,leche

    • leche → pan,mantequilla

    • pan,mantequilla → leche

    • pan,leche → mantequilla

    • mantequilla,leche → pan

Cómo reducir el número de reglas:

  1. Aumentar el soporte mínimo:

    reglas_filtradas <- apriori(transacciones, 
                              parameter = list(support = 0.3, confidence = 0.7, minlen = 2))
    ## Apriori
    ## 
    ## Parameter specification:
    ##  confidence minval smax arem  aval originalSupport maxtime support minlen
    ##         0.7    0.1    1 none FALSE            TRUE       5     0.3      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: 2 
    ## 
    ## set item appearances ...[0 item(s)] done [0.00s].
    ## set transactions ...[5 item(s), 8 transaction(s)] done [0.00s].
    ## sorting and recoding items ... [5 item(s)] done [0.00s].
    ## creating transaction tree ... done [0.00s].
    ## checking subsets of size 1 2 3 done [0.00s].
    ## writing ... [7 rule(s)] done [0.00s].
    ## creating S4 object  ... done [0.00s].
  2. Aumentar la confianza mínima:

    reglas_filtradas <- apriori(transacciones, 
                              parameter = list(support = 0.2, confidence = 0.8, minlen = 2))
    ## Apriori
    ## 
    ## Parameter specification:
    ##  confidence minval smax arem  aval originalSupport maxtime support minlen
    ##         0.8    0.1    1 none FALSE            TRUE       5     0.2      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: 1 
    ## 
    ## set item appearances ...[0 item(s)] done [0.00s].
    ## set transactions ...[5 item(s), 8 transaction(s)] done [0.00s].
    ## sorting and recoding items ... [5 item(s)] done [0.00s].
    ## creating transaction tree ... done [0.00s].
    ## checking subsets of size 1 2 3 done [0.00s].
    ## writing ... [7 rule(s)] done [0.00s].
    ## creating S4 object  ... done [0.00s].
  3. Filtrar por lift:

    reglas_interesantes <- subset(reglas, lift > 1.2)

¿Por qué tantas reglas son válidas?

  • Aunque solo tengas 8 transacciones, el bajo soporte mínimo (0.1) permite que:

    • Cualquier itemset que aparezca al menos 1 vez sea considerado (8*0.1=0.8 → redondeo a 1)

    • Muchas combinaciones aparecen exactamente 1 vez (soporte = 0.125)

Paso 5: Examinar las reglas

# Ver todas las reglas ordenadas por confianza
arules::inspect(sort(reglas, by = "confidence"))
##      lhs                               rhs           support confidence
## [1]  {mantequilla}                  => {pan}         0.500   1.0000000 
## [2]  {leche, refresco}              => {mantequilla} 0.125   1.0000000 
## [3]  {mantequilla, refresco}        => {pan}         0.250   1.0000000 
## [4]  {pan, refresco}                => {mantequilla} 0.250   1.0000000 
## [5]  {leche, refresco}              => {pan}         0.125   1.0000000 
## [6]  {huevos, mantequilla}          => {leche}       0.125   1.0000000 
## [7]  {huevos, mantequilla}          => {pan}         0.125   1.0000000 
## [8]  {huevos, pan}                  => {leche}       0.250   1.0000000 
## [9]  {leche, mantequilla}           => {pan}         0.375   1.0000000 
## [10] {leche, mantequilla, refresco} => {pan}         0.125   1.0000000 
## [11] {leche, pan, refresco}         => {mantequilla} 0.125   1.0000000 
## [12] {huevos, leche, mantequilla}   => {pan}         0.125   1.0000000 
## [13] {huevos, mantequilla, pan}     => {leche}       0.125   1.0000000 
## [14] {leche}                        => {pan}         0.625   0.8333333 
## [15] {pan}                          => {leche}       0.625   0.8333333 
## [16] {huevos}                       => {leche}       0.375   0.7500000 
## [17] {mantequilla}                  => {leche}       0.375   0.7500000 
## [18] {mantequilla, pan}             => {leche}       0.375   0.7500000 
## [19] {refresco}                     => {mantequilla} 0.250   0.6666667 
## [20] {refresco}                     => {pan}         0.250   0.6666667 
## [21] {pan}                          => {mantequilla} 0.500   0.6666667 
## [22] {huevos, leche}                => {pan}         0.250   0.6666667 
## [23] {leche, pan}                   => {mantequilla} 0.375   0.6000000 
##      coverage lift      count
## [1]  0.500    1.3333333 4    
## [2]  0.125    2.0000000 1    
## [3]  0.250    1.3333333 2    
## [4]  0.250    2.0000000 2    
## [5]  0.125    1.3333333 1    
## [6]  0.125    1.3333333 1    
## [7]  0.125    1.3333333 1    
## [8]  0.250    1.3333333 2    
## [9]  0.375    1.3333333 3    
## [10] 0.125    1.3333333 1    
## [11] 0.125    2.0000000 1    
## [12] 0.125    1.3333333 1    
## [13] 0.125    1.3333333 1    
## [14] 0.750    1.1111111 5    
## [15] 0.750    1.1111111 5    
## [16] 0.500    1.0000000 3    
## [17] 0.500    1.0000000 3    
## [18] 0.500    1.0000000 3    
## [19] 0.375    1.3333333 2    
## [20] 0.375    0.8888889 2    
## [21] 0.750    1.3333333 4    
## [22] 0.375    0.8888889 2    
## [23] 0.625    1.2000000 3
# Ver las top 5 reglas por lift
arules::inspect(head(sort(reglas, by = "lift"), 5))
##     lhs                       rhs           support confidence coverage
## [1] {leche, refresco}      => {mantequilla} 0.125   1.0000000  0.125   
## [2] {pan, refresco}        => {mantequilla} 0.250   1.0000000  0.250   
## [3] {leche, pan, refresco} => {mantequilla} 0.125   1.0000000  0.125   
## [4] {refresco}             => {mantequilla} 0.250   0.6666667  0.375   
## [5] {mantequilla}          => {pan}         0.500   1.0000000  0.500   
##     lift     count
## [1] 2.000000 1    
## [2] 2.000000 2    
## [3] 2.000000 1    
## [4] 1.333333 2    
## [5] 1.333333 4
# Filtrar reglas específicas (ejemplo: que contengan "leche")
reglas_leche <- subset(reglas, items %in% "leche")
arules::inspect(reglas_leche)
##      lhs                               rhs           support confidence
## [1]  {huevos}                       => {leche}       0.375   0.7500000 
## [2]  {mantequilla}                  => {leche}       0.375   0.7500000 
## [3]  {leche}                        => {pan}         0.625   0.8333333 
## [4]  {pan}                          => {leche}       0.625   0.8333333 
## [5]  {leche, refresco}              => {mantequilla} 0.125   1.0000000 
## [6]  {leche, refresco}              => {pan}         0.125   1.0000000 
## [7]  {huevos, mantequilla}          => {leche}       0.125   1.0000000 
## [8]  {huevos, leche}                => {pan}         0.250   0.6666667 
## [9]  {huevos, pan}                  => {leche}       0.250   1.0000000 
## [10] {leche, mantequilla}           => {pan}         0.375   1.0000000 
## [11] {mantequilla, pan}             => {leche}       0.375   0.7500000 
## [12] {leche, pan}                   => {mantequilla} 0.375   0.6000000 
## [13] {leche, mantequilla, refresco} => {pan}         0.125   1.0000000 
## [14] {leche, pan, refresco}         => {mantequilla} 0.125   1.0000000 
## [15] {huevos, leche, mantequilla}   => {pan}         0.125   1.0000000 
## [16] {huevos, mantequilla, pan}     => {leche}       0.125   1.0000000 
##      coverage lift      count
## [1]  0.500    1.0000000 3    
## [2]  0.500    1.0000000 3    
## [3]  0.750    1.1111111 5    
## [4]  0.750    1.1111111 5    
## [5]  0.125    2.0000000 1    
## [6]  0.125    1.3333333 1    
## [7]  0.125    1.3333333 1    
## [8]  0.375    0.8888889 2    
## [9]  0.250    1.3333333 2    
## [10] 0.375    1.3333333 3    
## [11] 0.500    1.0000000 3    
## [12] 0.625    1.2000000 3    
## [13] 0.125    1.3333333 1    
## [14] 0.125    2.0000000 1    
## [15] 0.125    1.3333333 1    
## [16] 0.125    1.3333333 1

Paso 6: Visualizar las reglas

# Gráfico de dispersión de soporte vs confianza
plot(reglas, method = "scatterplot", main = "Soporte vs Confianza")
## To reduce overplotting, jitter is added! Use jitter = 0 to prevent jitter.

# Gráfico de matriz (para reglas pequeñas)
plot(reglas, method = "matrix", measure = "lift")
## Itemsets in Antecedent (LHS)
##  [1] "{pan,refresco}"               "{leche,pan,refresco}"        
##  [3] "{leche,refresco}"             "{mantequilla,refresco}"      
##  [5] "{huevos,mantequilla}"         "{huevos,pan}"                
##  [7] "{leche,mantequilla}"          "{leche,mantequilla,refresco}"
##  [9] "{huevos,leche,mantequilla}"   "{huevos,mantequilla,pan}"    
## [11] "{pan}"                        "{leche,pan}"                 
## [13] "{mantequilla}"                "{refresco}"                  
## [15] "{leche}"                      "{huevos}"                    
## [17] "{mantequilla,pan}"            "{huevos,leche}"              
## Itemsets in Consequent (RHS)
## [1] "{leche}"       "{pan}"         "{mantequilla}"

# Gráfico de red (visualización más avanzada)
plot(head(sort(reglas, by = "lift"), 10), 
     method = "graph", 
     control = list(type = "items"))
## Warning: Unknown control parameters: type
## Available control parameters (with default values):
## layout    =  stress
## circular  =  FALSE
## ggraphdots    =  NULL
## edges     =  <environment>
## nodes     =  <environment>
## nodetext  =  <environment>
## colors    =  c("#EE0000FF", "#EEEEEEFF")
## engine    =  ggplot2
## max   =  100
## verbose   =  FALSE

Paso 7: Exportar los resultados

# Convertir reglas a dataframe para exportar
reglas_df <- as(reglas, "data.frame")
reglas_df
##                                    rules support confidence coverage      lift
## 1            {refresco} => {mantequilla}   0.250  0.6666667    0.375 1.3333333
## 2                    {refresco} => {pan}   0.250  0.6666667    0.375 0.8888889
## 3                    {huevos} => {leche}   0.375  0.7500000    0.500 1.0000000
## 4               {mantequilla} => {leche}   0.375  0.7500000    0.500 1.0000000
## 5                 {mantequilla} => {pan}   0.500  1.0000000    0.500 1.3333333
## 6                 {pan} => {mantequilla}   0.500  0.6666667    0.750 1.3333333
## 7                       {leche} => {pan}   0.625  0.8333333    0.750 1.1111111
## 8                       {pan} => {leche}   0.625  0.8333333    0.750 1.1111111
## 9      {leche,refresco} => {mantequilla}   0.125  1.0000000    0.125 2.0000000
## 10       {mantequilla,refresco} => {pan}   0.250  1.0000000    0.250 1.3333333
## 11       {pan,refresco} => {mantequilla}   0.250  1.0000000    0.250 2.0000000
## 12             {leche,refresco} => {pan}   0.125  1.0000000    0.125 1.3333333
## 13       {huevos,mantequilla} => {leche}   0.125  1.0000000    0.125 1.3333333
## 14         {huevos,mantequilla} => {pan}   0.125  1.0000000    0.125 1.3333333
## 15               {huevos,leche} => {pan}   0.250  0.6666667    0.375 0.8888889
## 16               {huevos,pan} => {leche}   0.250  1.0000000    0.250 1.3333333
## 17          {leche,mantequilla} => {pan}   0.375  1.0000000    0.375 1.3333333
## 18          {mantequilla,pan} => {leche}   0.375  0.7500000    0.500 1.0000000
## 19          {leche,pan} => {mantequilla}   0.375  0.6000000    0.625 1.2000000
## 20 {leche,mantequilla,refresco} => {pan}   0.125  1.0000000    0.125 1.3333333
## 21 {leche,pan,refresco} => {mantequilla}   0.125  1.0000000    0.125 2.0000000
## 22   {huevos,leche,mantequilla} => {pan}   0.125  1.0000000    0.125 1.3333333
## 23   {huevos,mantequilla,pan} => {leche}   0.125  1.0000000    0.125 1.3333333
##    count
## 1      2
## 2      2
## 3      3
## 4      3
## 5      4
## 6      4
## 7      5
## 8      5
## 9      1
## 10     2
## 11     2
## 12     1
## 13     1
## 14     1
## 15     2
## 16     2
## 17     3
## 18     3
## 19     3
## 20     1
## 21     1
## 22     1
## 23     1
# Exportar a CSV
write.csv(reglas_df, "reglas_asociacion.csv", row.names = FALSE)

Explicación de conceptos clave:

  1. Soporte (Support): Frecuencia relativa con la que aparece un conjunto de items. Ej: Soporte(pan → leche) = veces que compran pan y leche / total transacciones.

  2. Confianza (Confidence): Probabilidad de que el item consecuente sea comprado dado que se compró el antecedente. Ej: Confianza(pan → leche) = P(leche|pan).

  3. Lift: Mide cuánto más probable es que se compre el consecuente dado el antecedente, comparado con su probabilidad general. Lift > 1 indica asociación positiva.

Consejos para nuestros análisis:

  1. Ajusta los parámetros: Experimenta con diferentes valores de soporte y confianza según la densidad de tus datos.

  2. Interpreta el lift: Las reglas con lift > 1 son las más interesantes, ya que indican asociaciones reales.

  3. Filtra resultados: No todas las reglas serán útiles, enfócate en las que tienen sentido para tu investigación de mercado.

  4. Contexto de negocio: Siempre valida los hallazgos con tu conocimiento del negocio.