Retenção por Coorte — Como foi feito (explicado fácil)
Pensa no Spotify. Você se cadastrou em janeiro. Eles querem saber: "a turma de janeiro continua usando em fevereiro? em março? em junho?". Quando agrupam todo mundo que entrou no mesmo mês, isso é uma coorte. Aqui fiz isso pro Olist.
O que é isso, em uma frase?
É uma matriz que mostra, pra cada grupo de clientes que comprou pela 1ª vez no mesmo mês, quantos % voltaram a comprar nos meses seguintes. Trabalhei com 4.339 clientes recorrentes do e-commerce brasileiro Olist (Kaggle).
Por que importa? Porque retenção é o KPI mais traiçoeiro do mundo. É fácil olhar pro total de clientes e ficar feliz com o crescimento, sem perceber que a turma antiga tá sumindo. A coorte separa cohorts (grupos) e mostra o ritmo de "morte" de cada um — quem caiu rápido, quem aguentou.
Resposta prática que sai daqui: "quando, em média, um cliente novo deixa de comprar?" Daí saem decisões: campanha no dia 30, e-mail no dia 60, oferta de re-engajamento no dia 90.
Da tabela à matriz coorte
1 · Pegar pedido + cliente. Carrego as duas tabelas do Olist (orders + customers) e junto. Mantenho só o que importa: quando ele comprou e quem é ele (usando customer_unique_id, porque customer_id muda a cada pedido — pegadinha do dataset).
orders = pd.read_csv("olist_orders_dataset.csv",
usecols=["order_id", "customer_id", "order_purchase_timestamp"],
parse_dates=["order_purchase_timestamp"])
customers = pd.read_csv("olist_customers_dataset.csv",
usecols=["customer_id", "customer_unique_id"])
df = orders.merge(customers, on="customer_id", how="left")
df = df.rename(columns={"order_purchase_timestamp": "data",
"customer_unique_id": "cliente"})
2 · Filtrar recorrentes. Só faz sentido falar de "retenção" pra quem voltou pelo menos 1 vez. Pego só clientes com 2+ pedidos — descendo de ~96 mil pra ~4.339.
n = df.groupby("cliente")["data"].count()
recorrentes = n[n >= 2].index
df = df[df["cliente"].isin(recorrentes)]
3 · O pulo do gato: "mês de origem" e "período". Para cada cliente, descubro qual foi o mês da 1ª compra (mês de origem — define a coorte dele). Depois calculo, pra cada pedido, qual o período em meses desde a 1ª compra (0 = mesmo mês, 1 = mês seguinte, etc.).
df["mes_pedido"] = df["data"].dt.to_period("M")
df["mes_origem"] = df.groupby("cliente")["mes_pedido"].transform("min")
df["periodo"] = (df["mes_pedido"] - df["mes_origem"]).apply(lambda r: r.n)
# cliente X: comprou em jan, mar, mai
# mes_pedido = jan/26, mar/26, mai/26
# mes_origem = jan/26 (sempre o menor)
# periodo = 0, 2, 4
Sacada: o transform("min") espalha o mínimo do grupo pra todas as linhas do mesmo cliente. É como dizer "marca em todas as linhas dele a data da 1ª compra".
4 · Contar clientes únicos por (coorte, período). Quantos clientes da coorte X estavam ativos no período Y? Pivoteia.
coortes = (
df.groupby(["mes_origem", "periodo"])["cliente"]
.nunique().reset_index(name="ativos")
)
tamanho = coortes[coortes["periodo"] == 0][["mes_origem", "ativos"]]
tamanho = tamanho.rename(columns={"ativos": "tamanho"})
coortes = coortes.merge(tamanho, on="mes_origem")
coortes["retencao_pct"] = coortes["ativos"] / coortes["tamanho"] * 100
matriz = coortes.pivot(index="mes_origem", columns="periodo", values="retencao_pct")
5 · Ler a matriz. Cada linha = uma coorte. Coluna 0 = 100% (mesmo mês). Coluna 1 = % que voltou no mês seguinte. Quanto mais devagar a linha despenca, melhor a retenção daquele grupo. Visualizado como heatmap, vira aquela "escada descendo" clássica.
| coorte (exemplo) | mês 0 | mês 1 | mês 6 |
|---|---|---|---|
| 2017-01 | 100,0% | ~10% | ~3% |
| 2017-06 | 100,0% | ~12% | ~4% |
(valores ilustrativos — números reais saem do notebook).
A "escada da retenção"
Em uma linha: o que esse projeto ensina
Que "crescimento total" engana. Só vendo por coortes dá pra saber se o produto está retendo melhor com o tempo ou se está só recrutando mais pra repor quem sai. Coorte é a forma honesta de medir saúde de base de clientes.
Coisas técnicas que apareceram aqui: Period arithmetic (to_period, subtração de períodos), groupby.transform("min") (espalhar mínimo no grupo), nunique, pivot, construção da matriz coorte sem pacote externo.