APRENDIZAGEM NÃO SUPERVISIONADA · REGRAS DE ASSOCIAÇÃO

Apriori

Aprenda como o algoritmo sai de uma lista de compras e chega a regras como “quem compra Pão tende a comprar Manteiga” — entendendo exatamente o que significam suporte, confiança e lift.

COMECE PELO PROBLEMA

O que o Apriori está tentando descobrir?

Imagine que você tenha milhares de compras de supermercado. Cada compra é uma transação. O objetivo é descobrir quais produtos aparecem juntos com frequência e, a partir desses padrões, gerar regras de associação.

Itemset: combinação sem direção

Um itemset é apenas um conjunto de itens que aparecem juntos.

{ Pão, Manteiga }

Aqui ainda não estamos dizendo que um produto leva ao outro. Estamos apenas perguntando: “quantas compras contêm os dois?”

Regra: associação com direção

Depois que encontramos combinações frequentes, podemos criar regras:

Pão ⇒ Manteiga

A leitura é: “entre as compras que contêm Pão, com que frequência também aparece Manteiga?”

Separe as duas etapas: primeiro o Apriori procura itemsets frequentes. Depois esses itemsets podem ser transformados em regras de associação, que são avaliadas por métricas como confiança e lift.

NOSSA BASE DIDÁTICA

Vamos trabalhar com apenas 10 compras

Com uma base pequena conseguimos contar tudo manualmente. Depois o algoritmo faz exatamente a mesma lógica em milhares ou milhões de transações.

Observe quatro contagens

Para estudar a regra Pão ⇒ Manteiga, precisamos saber:

Total10compras
·
Pão6compras
Manteiga5compras
·
Pão + Manteiga4compras
Quase toda a matemática de suporte, confiança e lift será construída a partir dessas contagens.

ANTES DO ALGORITMO

Entenda os três números que aparecem no resultado

Clique em cada métrica. As próprias transações serão destacadas para mostrar exatamente qual é o numerador e qual é o denominador.

1 · SUPORTE

Suporte responde: “isso acontece bastante na base inteira?”

O suporte mede a frequência do itemset completo. Para Pão e Manteiga, contamos quantas das 10 compras contêm os dois ao mesmo tempo.

support({Pão, Manteiga}) = 4 / 10 = 0,40 = 40%

Interpretação: em 40% de todas as compras da base, Pão e Manteiga aparecem juntos.

Por que o Apriori precisa do suporte?

Porque ele não quer perder tempo explorando combinações muito raras. Definimos um suporte mínimo, por exemplo 30%.

Se um conjunto aparece em menos de 30% das compras, ele não é considerado frequente e pode ser descartado.

2 · CONFIANÇA

Confiança muda o denominador

Agora não olhamos mais para todas as 10 compras. Olhamos apenas para as compras que já possuem o antecedente da regra.

confidence(Pão ⇒ Manteiga) = 4 / 6 = 0,667 = 66,7%

Existem 6 compras com Pão. Em 4 delas também existe Manteiga.

Logo: entre quem comprou Pão, 66,7% também comprou Manteiga.

A direção importa

Pão ⇒ Manteiga = 4 / 6 = 66,7%
Manteiga ⇒ Pão = 4 / 5 = 80%

O itemset é o mesmo {Pão, Manteiga}, mas a confiança muda porque o denominador é o antecedente.

3 · LIFT

Lift pergunta: “isso é relação ou só popularidade?”

Uma confiança alta pode enganar quando o consequente já é muito popular. O lift compara a confiança da regra com a frequência normal do consequente.

Pão ⇒ Manteiga

Manteiga aparece normalmente em 50% da base. Mas entre as compras com Pão, aparece em 66,7%.

lift = 66,7% / 50% = 1,33
Manteiga na base
50%
Manteiga quando há Pão
66,7%

Leitura: Pão e Manteiga aparecem juntos cerca de 1,33 vez o esperado sob independência.

Por que confiança sozinha não basta?

Considere Café ⇒ Leite. Entre as 5 compras com Café, 4 têm Leite:

confidence = 4 / 5 = 80%

Parece uma ótima regra. Mas Leite já aparece em 90% de todas as compras.

lift = 80% / 90% = 0,89

Ou seja: entre compradores de Café, Leite aparece até menos do que na população geral.

Lift < 1
coocorrência menor que a esperada
Lift = 1
comportamento compatível com independência
Lift > 1
coocorrência maior que a esperada
Importante: associação não é causalidade. Lift alto não prova que comprar Pão causou a compra de Manteiga.

AGORA SIM: O APRIORI

Como ele descobre quais relações vale a pena testar?

Se existem milhares de produtos, há combinações demais. O Apriori usa o suporte e uma propriedade simples para eliminar grande parte delas antes de chegar às regras.

Princípio Apriori: se um conjunto de itens já é pouco frequente, qualquer conjunto maior que o contenha também será pouco frequente. Um conjunto maior nunca pode aparecer em mais transações que um de seus próprios subconjuntos.
PASSO 1Contar itens individuais

Pão, Manteiga, Leite, Café.

PASSO 2Manter os frequentes

Aplicar o suporte mínimo.

PASSO 3Formar pares

Contar quais aparecem juntos.

PASSO 4Podar

Eliminar combinações infrequentes e seus supersets.

PASSO 5Gerar regras

Calcular confiança e lift dos padrões encontrados.

O ALGORITMO POR DENTRO

O que o Apriori faz dentro de cada for?

Antes de usar C₁, L₁ ou qualquer notação, pense no programa executando. Em cada rodada existem candidatos atuais. O Apriori percorre cada compra e pergunta, candidato por candidato: “todos os itens desta combinação aparecem neste carrinho?”

O trabalho do for interno é apenas CONTAR. Ele ainda não calcula confiança nem lift. Ele verifica se o candidato está contido na compra e, se estiver, soma +1 à contagem daquele candidato.
1. Candidatos{Pão}, {Manteiga}, ...
2. Ler compraPegar um carrinho da base.
3. TestarO candidato está inteiro no carrinho?
4. ContarSe sim, contador += 1.
5. Depois da varreduraContagem ÷ total = suporte.
Compra atual—
Candidato sendo testado—
Clique em “Avançar uma comparação”.
A cada clique, o algoritmo executará uma comparação equivalente ao if candidato ⊆ compra.

Contadores durante a varredura

Estes números começam em zero. Eles só aumentam quando todos os itens do candidato aparecem na compra atual.

O primeiro for: percorre compras

for compra in transacoes: # agora estou olhando UMA compra

Ele pega a Compra 1, depois a Compra 2, depois a Compra 3... até visitar toda a base.

O segundo for: percorre candidatos

for compra in transacoes: for candidato in candidatos: # testar uma combinação

Para a mesma compra, ele testa todos os candidatos daquela rodada. Na primeira rodada são itens; na seguinte são pares; depois podem ser trios.

O if: a combinação está inteira no carrinho?

if candidato.issubset(compra): contagem[candidato] += 1

Para {Pão, Manteiga}, não basta existir Pão. Pão e Manteiga precisam estar juntos na mesma compra.

Depois que os dois for terminam

suporte = contagem[candidato] / total if suporte >= suporte_minimo: sobreviventes.append(candidato)

Só agora a contagem vira suporte. Os candidatos abaixo do mínimo morrem; os demais serão usados para construir a rodada seguinte.

Exemplo completo de UMA compra na rodada dos pares

Suponha que os candidatos atuais sejam {Pão,Manteiga}, {Pão,Leite} e {Manteiga,Café}, e a compra atual seja:

Compra = {Pão, Manteiga, Leite}
CandidatoPergunta feita pelo ifRespostaAção
{Pão,Manteiga}Pão E Manteiga estão na compra?Simcontador += 1
{Pão,Leite}Pão E Leite estão na compra?Simcontador += 1
{Manteiga,Café}Manteiga E Café estão na compra?Nãonão altera o contador
Depois disso, o for externo passa para a próxima compra e repete exatamente os mesmos testes. Quando todas as compras terminarem, teremos a frequência de cada candidato.

E como nasce a próxima rodada?

Depois de calcular o suporte, ficamos apenas com os sobreviventes. Então o Apriori combina esses sobreviventes para tentar criar conjuntos um item maiores.

CONTARvarrer todas as compras
→
FILTRARaplicar suporte mínimo
→
COMBINARcriar candidatos maiores
→
PODARremover candidatos impossíveis
→
REPETIRvoltar ao for das compras
Confiança e lift ainda não entram aqui. Enquanto procura itemsets frequentes, o Apriori trabalha principalmente com contagem + suporte + poda. Confiança e lift aparecem depois, quando os itemsets frequentes são transformados em regras.

EXECUTANDO O ALGORITMO NA MÃO

Como o Apriori realmente faz a busca

Agora vamos executar o algoritmo de verdade usando as mesmas 10 compras. Definiremos suporte mínimo = 30%. Como existem 10 transações, um conjunto precisa aparecer em pelo menos 3 compras para continuar vivo.

Duas letras que aparecem muito quando se estuda Apriori:
Ck = candidatos com k itens que ainda precisam ser testados.
Lk = candidatos que sobreviveram ao suporte mínimo e viraram itemsets frequentes.

Passo 1 — testar cada produto sozinho

No começo não conhecemos nenhuma relação. Então o algoritmo simplesmente conta em quantas transações cada item aparece.

Candidato C₁ContagemCálculo do suporteSuporteResultado
{Pão}6 compras6 ÷ 1060%fica em L₁
{Manteiga}5 compras5 ÷ 1050%fica em L₁
{Leite}9 compras9 ÷ 1090%fica em L₁
{Café}5 compras5 ÷ 1050%fica em L₁
L₁ = {Pão}, {Manteiga}, {Leite}, {Café}. Todos passaram dos 30%, então todos podem participar da próxima etapa.

Passo 2 — combinar os sobreviventes dois a dois

O Apriori usa os itens de L₁ para criar os candidatos de tamanho 2, chamados C₂. Agora ele volta às transações e conta quantas vezes cada par aparece junto.

Candidato C₂Contagem conjuntaSuporteResultado
{Pão, Manteiga}4 compras4 ÷ 10 = 40%frequente
{Pão, Leite}6 compras6 ÷ 10 = 60%frequente
{Pão, Café}3 compras3 ÷ 10 = 30%frequente
{Manteiga, Leite}5 compras5 ÷ 10 = 50%frequente
{Manteiga, Café}2 compras2 ÷ 10 = 20%eliminado
{Leite, Café}4 compras4 ÷ 10 = 40%frequente
Aqui nasce a poda: {Manteiga, Café} apareceu apenas 2 vezes. Como 20% < 30%, esse par é eliminado e não entra em L₂.

Passo 3 — tentar formar trios, mas podar antes de contar

Agora o Apriori poderia tentar todos os trios possíveis. Mas antes de consultar a base, ele verifica uma regra simples: todos os pares que formam aquele trio precisam estar em L₂.

Trio que pode ser testado

{Pão, Manteiga, Leite}

Seus pares são:

{Pão, Manteiga} ✓ {Pão, Leite} ✓ {Manteiga, Leite} ✓

Todos são frequentes. Portanto o trio vale ser contado.

4 compras ÷ 10 = 40% → sobrevive

Trio eliminado antes da contagem

{Pão, Manteiga, Café}

Seus pares são:

{Pão, Manteiga} ✓ {Pão, Café} ✓ {Manteiga, Café} ✗

Como um de seus subconjuntos já falhou, o trio inteiro é podado.

não precisa contar na base → PODADO
TrioTeste AprioriContagemResultado
{Pão, Manteiga, Leite}todos os pares frequentes4/10 = 40%L₃
{Pão, Leite, Café}todos os pares frequentes3/10 = 30%L₃
{Pão, Manteiga, Café}{Manteiga,Café} falhounem precisapodado
{Manteiga, Leite, Café}{Manteiga,Café} falhounem precisapodado

Passo 4 — tentar aumentar novamente

O único conjunto de 4 itens possível seria:

{Pão, Manteiga, Leite, Café}

Mas ele contém o subconjunto {Manteiga, Café}, que já sabemos ser infrequente. Portanto ele também pode ser descartado.

Não surgiram novos candidatos frequentes. A busca por itemsets termina aqui.

Passo 5 — só agora gerar as regras

Os itemsets frequentes dizem apenas “estes itens aparecem juntos bastante”. Para produzir uma regra, dividimos um itemset frequente em dois lados.

ITEMSET FREQUENTE{Pão, Manteiga}

suporte = 40%

→
REGRAS POSSÍVEISPão ⇒ Manteiga
Manteiga ⇒ Pão

Para cada direção, o algoritmo calcula confiança e depois lift. Se você também definir uma confiança mínima, regras abaixo desse valor podem ser descartadas.

Pão ⇒ Manteiga confidence = support(Pão,Manteiga) / support(Pão) = 0,40 / 0,60 = 0,667 = 66,7% lift = confidence / support(Manteiga) = 0,667 / 0,50 = 1,33
O algoritmo completo é isto: gerar candidatos → contar suporte → eliminar os raros → usar os sobreviventes para construir candidatos maiores → repetir até não haver novos candidatos → transformar os itemsets frequentes em regras → calcular confiança e lift.

EXEMPLO COMPLETO

Suporte mínimo = 30%

Com 10 transações, um itemset precisa aparecer em pelo menos 3 compras para sobreviver.

1 item
{Pão} · 60%
{Manteiga} · 50%
{Leite} · 90%
{Café} · 50%
2 itens
{Pão, Manteiga} · 40%
{Pão, Leite} · 60%
{Pão, Café} · 30%
{Manteiga, Leite} · 50%
{Manteiga, Café} · 20%
{Leite, Café} · 40%
3 itens
{Pão, Manteiga, Leite} · 40%
{Pão, Leite, Café} · 30%
{Pão, Manteiga, Café} · PODADO
{Manteiga, Leite, Café} · PODADO
Por que os dois últimos trios foram podados? Porque ambos contêm o par {Manteiga, Café}, que só aparece em 20% das compras. Um trio que contém esse par nunca poderia aparecer em 30% ou mais das transações.

DE ITEMSET PARA REGRA

Depois dos padrões frequentes, surgem as regras

O itemset frequente {Pão, Manteiga} pode originar duas regras diferentes. O suporte é o mesmo, mas a confiança pode ser diferente porque a direção mudou.

ANTECEDENTEPão
⇒
CONSEQUENTEManteiga

Suporte = 40% · Confiança = 66,7% · Lift = 1,33

ANTECEDENTEManteiga
⇒
CONSEQUENTEPão

Suporte = 40% · Confiança = 80% · Lift = 1,33

Resumo importante: suporte pertence ao conjunto de itens e, por isso, é igual nas duas direções. Confiança é direcional. O lift, na formulação padrão de dois lados, compara a coocorrência observada com a esperada sob independência e resulta no mesmo valor ao inverter apenas A e B.

LABORATÓRIO

Troque a regra e veja de onde cada número vem

O laboratório usa as mesmas 10 compras. Escolha uma regra; ele conta antecedente, consequente e ocorrência conjunta, calcula as três métricas e destaca as transações relevantes.

Compras totais10
Contêm antecedente0
Contêm consequente0
Contêm os dois lados0

O QUE SAI NO FINAL?

O resultado não é apenas “uma relação”

Na prática, você recebe uma tabela de regras. Cada linha precisa ser interpretada observando as métricas em conjunto.

RegraSuporteConfiançaLiftLeitura
Pão ⇒ Manteiga40%66,7%1,33Associação positiva nesta base.
Manteiga ⇒ Pão40%80%1,33Mesma coocorrência, confiança maior pela direção.
Café ⇒ Leite40%80%0,89Confiança alta, mas abaixo da taxa normal de Leite.
Pão + Café ⇒ Leite30%100%1,11Todos os carrinhos com Pão+Café têm Leite, mas Leite já é muito comum.

GUARDE ESTA IDEIA

Apriori em uma frase

O Apriori primeiro encontra combinações que aparecem com frequência usando suporte e poda; depois essas combinações viram regras, e confiança e lift ajudam a dizer se a regra é realmente interessante.

Suporte

“Isso acontece bastante?”
Olha para a base inteira.

Confiança

“Quando A acontece, B aparece quanto?”
Olha apenas para casos com A.

Lift

“Isso é mais do que eu esperaria normalmente?”
Compara a regra com a popularidade de B.

IMPLEMENTAÇÃO DO ZERO

O mesmo algoritmo, agora em Python

Este código não chama apriori(), mlxtend nem uma implementação pronta. Observe principalmente os dois laços aninhados: para cada compra → para cada candidato → verificar se o candidato está dentro da compra → incrementar a contagem. Depois a contagem vira suporte, os sobreviventes geram novos candidatos e a varredura começa novamente.

Abrir/fechar implementação completa
# =========================================================
# APRIORI DO ZERO — SEM BIBLIOTECA DE MINERAÇÃO
# =========================================================

transacoes = [
    {'Pão', 'Manteiga', 'Leite', 'Café'},
    {'Pão', 'Manteiga', 'Leite'},
    {'Pão', 'Manteiga', 'Leite', 'Café'},
    {'Pão', 'Manteiga', 'Leite'},
    {'Pão', 'Leite', 'Café'},
    {'Pão', 'Leite'},
    {'Manteiga', 'Leite'},
    {'Leite', 'Café'},
    {'Leite'},
    {'Café'}
]

SUPORTE_MINIMO = 0.30
CONFIANCA_MINIMA = 0.60


# ---------------------------------------------------------
# 1. GERAR COMBINAÇÕES SEM itertools
# ---------------------------------------------------------
def combinacoes(lista, tamanho, inicio=0, atual=None):
    if atual is None:
        atual = []

    if len(atual) == tamanho:
        return [atual[:]]

    resultado = []

    for i in range(inicio, len(lista)):
        atual.append(lista[i])
        resultado += combinacoes(lista, tamanho, i + 1, atual)
        atual.pop()

    return resultado


# ---------------------------------------------------------
# 2. CONTAR CANDIDATOS
# ---------------------------------------------------------
def contar_candidatos(transacoes, candidatos):
    contagem = {}

    # Todos começam com zero ocorrências.
    for candidato in candidatos:
        contagem[candidato] = 0

    # FOR EXTERNO:
    # pega UMA compra por vez.
    for compra in transacoes:

        # FOR INTERNO:
        # testa TODOS os candidatos nessa compra.
        for candidato in candidatos:

            # O candidato está INTEIRO dentro da compra?
            if candidato.issubset(compra):
                contagem[candidato] += 1

    return contagem


# ---------------------------------------------------------
# 3. TRANSFORMAR CONTAGEM EM SUPORTE E FILTRAR
# ---------------------------------------------------------
def filtrar_por_suporte(candidatos, contagem, total_transacoes):
    frequentes = []
    suportes = {}

    for candidato in candidatos:
        suporte = contagem[candidato] / total_transacoes
        suportes[candidato] = suporte

        if suporte >= SUPORTE_MINIMO:
            frequentes.append(candidato)

    return frequentes, suportes


# ---------------------------------------------------------
# 4. GERAR OS PRÓXIMOS CANDIDATOS + PODA APRIORI
# ---------------------------------------------------------
def gerar_proximos_candidatos(frequentes_anteriores, novo_tamanho):
    candidatos = set()
    frequentes_set = set(frequentes_anteriores)

    # Junta sobreviventes dois a dois.
    for a in frequentes_anteriores:
        for b in frequentes_anteriores:
            candidato = a | b

            # Queremos exatamente um item a mais.
            if len(candidato) != novo_tamanho:
                continue

            # Antes de aceitar esse candidato, verificamos
            # todos os subconjuntos com tamanho novo_tamanho - 1.
            lista = list(candidato)
            subconjuntos = combinacoes(lista, novo_tamanho - 1)

            pode_testar = True

            for subconjunto_lista in subconjuntos:
                subconjunto = frozenset(subconjunto_lista)

                # Se algum subconjunto já foi infrequente,
                # o candidato maior é PODADO sem contar na base.
                if subconjunto not in frequentes_set:
                    pode_testar = False
                    break

            if pode_testar:
                candidatos.add(frozenset(candidato))

    return list(candidatos)


# ---------------------------------------------------------
# 5. CRIAR OS CANDIDATOS DE 1 ITEM
# ---------------------------------------------------------
itens = set()

for compra in transacoes:
    for item in compra:
        itens.add(item)

candidatos = []

for item in itens:
    candidatos.append(frozenset([item]))


# ---------------------------------------------------------
# 6. LOOP PRINCIPAL DO APRIORI
# ---------------------------------------------------------
todos_frequentes = []
suportes_globais = {}
tamanho = 1

while len(candidatos) > 0:
    print('\nRODADA', tamanho)
    print('Candidatos:', [set(c) for c in candidatos])

    # A) percorre todas as compras e conta os candidatos.
    contagem = contar_candidatos(transacoes, candidatos)

    # B) contagem / número de compras = suporte.
    frequentes, suportes = filtrar_por_suporte(
        candidatos,
        contagem,
        len(transacoes)
    )

    for candidato in candidatos:
        print(
            set(candidato),
            'contagem =', contagem[candidato],
            'suporte =', round(suportes[candidato] * 100, 1), '%'
        )

    # C) guarda somente os sobreviventes.
    if len(frequentes) == 0:
        break

    todos_frequentes += frequentes
    suportes_globais.update(suportes)

    # D) cria candidatos um item maiores.
    tamanho += 1
    candidatos = gerar_proximos_candidatos(
        frequentes,
        tamanho
    )

    # O while volta para o começo.
    # Os NOVOS candidatos serão novamente testados
    # contra TODAS as compras.


# ---------------------------------------------------------
# 7. FUNÇÃO DE SUPORTE PARA ITEMSETS JÁ ENCONTRADOS
# ---------------------------------------------------------
def suporte(itemset):
    if itemset in suportes_globais:
        return suportes_globais[itemset]

    contagem = 0

    for compra in transacoes:
        if itemset.issubset(compra):
            contagem += 1

    return contagem / len(transacoes)


# ---------------------------------------------------------
# 8. SÓ AGORA GERAR REGRAS
# ---------------------------------------------------------
regras = []

for itemset in todos_frequentes:
    if len(itemset) < 2:
        continue

    itens_do_itemset = list(itemset)

    # Testa antecedentes com 1 item, 2 itens, ...
    for tamanho_antecedente in range(1, len(itemset)):
        possibilidades = combinacoes(
            itens_do_itemset,
            tamanho_antecedente
        )

        for antecedente_lista in possibilidades:
            antecedente = frozenset(antecedente_lista)
            consequente = itemset - antecedente

            suporte_itemset = suporte(itemset)
            suporte_antecedente = suporte(antecedente)
            suporte_consequente = suporte(consequente)

            confianca = suporte_itemset / suporte_antecedente
            lift = confianca / suporte_consequente

            if confianca >= CONFIANCA_MINIMA:
                regras.append({
                    'antecedente': antecedente,
                    'consequente': consequente,
                    'suporte': suporte_itemset,
                    'confianca': confianca,
                    'lift': lift
                })


# ---------------------------------------------------------
# 9. RESULTADO
# ---------------------------------------------------------
print('\nREGRAS ENCONTRADAS')

for regra in regras:
    print(
        set(regra['antecedente']),
        '=>',
        set(regra['consequente']),
        '| suporte =', round(regra['suporte'] * 100, 1), '%',
        '| confiança =', round(regra['confianca'] * 100, 1), '%',
        '| lift =', round(regra['lift'], 2)
    )

gerar_candidatos()

Combina os itemsets frequentes do nível anterior para tentar criar conjuntos um item maiores.

Teste Apriori

Antes de contar um candidato, verifica se todos os seus subconjuntos de tamanho k−1 sobreviveram no nível anterior.

while

Repete a geração e a filtragem até não existir mais nenhum conjunto frequente maior.

REFERÊNCIAS

Para aprofundar

Agrawal, R.; Srikant, R. Fast Algorithms for Mining Association Rules. VLDB, 1994.

Oracle Machine Learning. Apriori — avaliação de regras por suporte, confiança e lift.

mlxtend. Association Rules — definições e fórmulas das métricas de regras de associação.

IBM. Apriori Algorithm — visão geral de itemsets frequentes, suporte, confiança e lift.