credit = pd.read_csv("dados/Credit.csv")
credit.shape, credit.columns.tolist()((400, 11),
['renda',
'limite',
'pontuacao',
'cartoes',
'idade',
'escolaridade',
'imovel_proprio',
'estudante',
'casado',
'regiao',
'saldo'])
Esta seção corresponde à seção 3.3.1 de James et al. (2023).
Um coeficiente de regressão multiplica um número por outro, \(\beta_j \cdot x_j\). Todo preditor usado até aqui neste capítulo era numérico — dólares, unidades, anos. Credit, o conjunto que esta seção introduz, tem quatro colunas que não são: imovel_proprio, estudante e casado valem sim ou não; regiao vale Leste, Sul ou Oeste. Nenhuma delas multiplica coeficiente nenhum do jeito que está — e é essa a pergunta que a seção resolve.
((400, 11),
['renda',
'limite',
'pontuacao',
'cartoes',
'idade',
'escolaridade',
'imovel_proprio',
'estudante',
'casado',
'regiao',
'saldo'])
São 400 clientes e onze colunas: renda, limite, pontuacao, cartoes, idade e escolaridade, numéricas; imovel_proprio, estudante e casado, sim/não; regiao, com três categorias; e saldo — a dívida média no cartão de crédito de cada cliente, em dólares, a resposta que o resto da seção tenta prever a partir de duas dessas colunas categóricas, estudante e regiao.
A pergunta mais simples é a de duas categorias: quem é estudante carrega, em média, saldo diferente de quem não é? Uma variável indicadora responde trocando a categoria por um número.
Uma variável indicadora (ou dummy) troca uma categoria por 0 ou 1: vale 1 quando a observação está no nível escolhido, 0 nos demais. Um preditor qualitativo com \(k\) níveis entra no modelo como \(k - 1\) indicadoras, nunca \(k\) — a categoria que sobra sem indicadora própria é a base, e toda previsão para ela sai só do intercepto.
['estudante_sim']
drop_first=True descarta a primeira categoria em ordem alfabética — não vem antes de sim — e fica só estudante_sim, que vale True para quem é estudante e False para quem não é. não é a base: toda observação sem a indicadora marcada pertence a ela.
(480.37, 396.46)
medias_estudante = credit.groupby("estudante")["saldo"].mean()
media_nao_estudante = round(float(medias_estudante["não"]), 2)
media_sim_estudante = round(float(medias_estudante["sim"]), 2)
diferenca_medias_estudante = round(media_sim_estudante - media_nao_estudante, 2)
estudantes_devem_mais = bool(diferenca_medias_estudante > 0)
media_nao_estudante, media_sim_estudante, diferenca_medias_estudante, estudantes_devem_mais(480.37, 876.83, 396.46, True)
(True, True)
O modelo ajustado só com essa indicadora dá um intercepto de 480,37 e um coeficiente de 396,46. São os mesmos dois números que saem direto do dado, sem ajustar modelo nenhum: 480,37 é o saldo médio de quem não é estudante, e 396,46 é a diferença entre essa média e a de quem é, 876,83 (876,83 − 480,37 = 396,46). intercepto_bate_com_media e coeficiente_bate_com_diferenca confirmam a igualdade nos dois casos. estudantes_devem_mais confirma que quem é estudante carrega, em média, mais saldo que quem não é.
Trocar qual categoria fica sem indicadora — a base — é uma escolha de quem ajusta o modelo, não um fato sobre o dado. get_dummies descarta, por padrão, a primeira categoria em ordem alfabética; para inverter, basta listar as categorias na ordem desejada antes de gerar a indicadora.
['estudante_não']
(876.83, -396.46)
Com sim como base, o intercepto salta para 876,83 — a mesma média de quem é estudante de antes — e o coeficiente muda de sinal, para -396,46: a mesma diferença de antes, contada na direção oposta.
True
previsoes_identicas confirma que, apesar do intercepto e do coeficiente mudarem, as previsões para os 400 clientes são as mesmas nos dois ajustes — a escolha da base muda a leitura dos coeficientes, não o que o modelo prevê.
regiao tem três categorias — Leste, Sul e Oeste —, não duas. Uma única indicadora não dá conta: sobra uma categoria sem representação. A regra do conceito acima vale de novo, com \(k = 3\): entram \(k - 1 = 2\) indicadoras.
['regiao_Oeste', 'regiao_Sul']
Duas indicadoras, regiao_Oeste e regiao_Sul; Leste fica sem indicadora própria e é a base. Uma terceira indicadora, para Leste, seria redundante: quem não é Oeste nem Sul só pode ser Leste, e o valor dela já está determinado pelas outras duas.
modelo_regiao = LinearRegression().fit(indicadora_regiao, credit["saldo"])
coeficientes_regiao = dict(zip(modelo_regiao.feature_names_in_, modelo_regiao.coef_))
coef_oeste = round(float(coeficientes_regiao["regiao_Oeste"]), 2)
coef_sul = round(float(coeficientes_regiao["regiao_Sul"]), 2)
intercepto_regiao = round(float(modelo_regiao.intercept_), 2)
intercepto_regiao, coef_oeste, coef_sul(531.0, -18.69, -12.5)
medias_regiao = credit.groupby("regiao")["saldo"].mean()
media_leste = round(float(medias_regiao["Leste"]), 2)
media_oeste = round(float(medias_regiao["Oeste"]), 2)
media_sul = round(float(medias_regiao["Sul"]), 2)
leste_tem_a_maior_media = bool(medias_regiao.idxmax() == "Leste")
media_leste, media_oeste, media_sul, leste_tem_a_maior_media(531.0, 512.31, 518.5, True)
O intercepto, 531,0, é o saldo médio de quem mora no Leste — a base. Os coeficientes leem-se como diferenças contra essa base: -18,69 para Oeste e -12,5 para Sul, o mesmo que a tabela de médias mostra: 512,31 − 531,0 = -18,69 e 518,5 − 531,0 = -12,5. leste_tem_a_maior_media confirma que, das três regiões, é no Leste que o saldo médio é o mais alto.
Com um único preditor qualitativo, o modelo não tem outra informação para usar: toda previsão é a média do grupo a que a observação pertence, e nada mais fino que isso. Para estudante, só existem duas previsões possíveis, 480,37 e 876,83; para regiao, só três, 531,0, 512,31 e 518,5.
rng = np.random.default_rng(8)
fig, (ax_estudante, ax_regiao) = plt.subplots(1, 2, figsize=(10, 4.3), sharey=True)
paineis = [
(ax_estudante, "estudante", ["não", "sim"], medias_estudante, "estudante"),
(ax_regiao, "regiao", ["Leste", "Sul", "Oeste"], medias_regiao, "região"),
]
for eixo, coluna, ordem, medias, rotulo in paineis:
for posicao, categoria in enumerate(ordem):
valores = credit.loc[credit[coluna] == categoria, "saldo"]
deslocamento = rng.uniform(-0.15, 0.15, size=len(valores))
eixo.scatter(posicao + deslocamento, valores, s=12, alpha=0.4, color="C0")
eixo.plot([posicao - 0.22, posicao + 0.22], [medias[categoria]] * 2, color="C1", linewidth=3)
eixo.set_xticks(range(len(ordem)))
eixo.set_xticklabels(ordem)
eixo.set_xlabel(rotulo)
ax_estudante.set_ylabel("saldo (dólares)")
plt.tight_layout()
plt.show()
O traço laranja fica na mesma altura para todo ponto do grupo: é essa reta plana, por categoria, que um preditor qualitativo sozinho consegue desenhar.