Skip to content

Exercicio 1 Arvore de decisao

Esse exercicio é baseado no dataset Crash Car do Kaggle. O objetivo é analisar a base de dados limpá-la, e no final construir um modelo de árvore de decisão

Etapa 1 - Analise e instalação dos dados

O dataset foi carregado a partir de um arquivo Excel, contendo informações como tipo de colisão, tipo de lesão, dia da semana, fatores primários do acidente, data, hora e localização.

Carregando os dados

Formato do dataset: (53943, 11)

import pandas as pd
# Ler o arquivo corretamente (apenas )
df = pd.read_excel("docs/arvore-decisao/crashcar.xlsx")

# Visualização inicial da base de dados
print('Formato do dataset:', df.shape)

O dataset possui 53943 linha e 11 colunas.

As bibiliotecas utilizadas

    import matplotlib.pyplot as plt
    import pandas as pd
    from io import StringIO, BytesIO
    from sklearn.model_selection import train_test_split
    from sklearn.tree import DecisionTreeClassifier, plot_tree
    from sklearn.metrics import confusion_matrix, accuracy_score
    from sklearn.preprocessing import StandardScaler
    from sklearn import tree

Explicação dos tipos de dados

Year : int64

Month : int64

Day : int64

Weekend? : object

Hour : float64

Collision Type : object

Injury Type : object

Primary Factor : object

Reported_Location : object

Latitude : float64

Longitude : float64

    for col in df:
    print(col, ":", df[col].dtype, "\n")

Explicação

Int64: Dados numéricos inteiros, como Year, Month, Day, Hour e Latitude.
Object: Dados categóricos ou textuais, como Collision Type, Injury Type, Weekend?, Primary Factor e Time.
FLoat64: Dados numéricos com casas decimais, como Longitude, Latitude e Hora.

Estatisticas descritivas dos numéricos

Formato do dataset: (53943, 11) Estatísticas das colunas numéricas:

Year Month Day Hour Latitude
18941 2011 2 6 1600.0 39.164240
34616 2007 7 3 1800.0 39.166640
18593 2011 8 3 1600.0 39.164700
3916 2015 11 6 1000.0 39.164240
14110 2012 10 1 100.0 39.167191
31033 2008 10 6 1300.0 39.164304
27957 2009 10 5 700.0 39.135552
13273 2012 7 4 1700.0 39.179040
34111 2007 1 6 2300.0 39.171680
45416 2004 12 4 1800.0 39.194144
# Estatísticas descritivas das colunas numéricas
print('Estatísticas das colunas numéricas:')
print(df[['Year', 'Month', 'Day', 'Hour', 'Latitude']].describe())

Visualização dos dados

2025-09-26T12:34:25.642771 image/svg+xml Matplotlib v3.10.6, https://matplotlib.org/ 2025-09-26T12:34:25.707631 image/svg+xml Matplotlib v3.10.6, https://matplotlib.org/

Visualizações: Foram criados gráficos de barras para o número de acidentes por tipo de veículo e por gravidade (fatal/não fatal), facilitando a compreensão dos dados.

Exploração inicial dos dados

import matplotlib.pyplot as plt
import pandas as pd
df = pd.read_excel("docs/arvore-decisao/crashcar.xlsx")

# Visualização inicial da base de dados
print(f'Formato do dataset: {df.shape}<br>')

# Explicação de tipo de dado em cada coluna
for col in df:
    print(df[col].head(1), "<br>")
print('Valores nulos por coluna:')
print(df.isnull().sum(), "<br>")


################################################
# Estatísticas descritivas das colunas numéricas
print('Estatísticas das colunas numéricas:')
print(df[['Year', 'Month', 'Day', 'Hour', 'Latitude']].describe())

Etapa 2

Normalização de dados categóricos em numéricos

Collision Type Collision Type Num
0 2-Car 1
1 2-Car 1
2 2-Car 1
3 2-Car 1
4 2-Car 1
5 2-Car 1
6 2-Car 1
7 1-Car 1
8 2-Car 1
9 1-Car 1
def collision_to_num(collision):
    if collision == '1-Car' or collision == '2-Car' or collision == '3+ Cars':
        return 1
    elif collision == 'Moped/Motorcycle':
        return 2
    elif collision == 'Bus':
        return 3
    elif collision == 'Pedestrian':
        return 4
    elif collision == 'Cyclist':
        return 5
    else:
        return 0
df['Collision Type Num'] = df['Collision Type'].apply(collision_to_num)
Injury Type Injury Type Num
0 No injury/unknown 0
1 No injury/unknown 0
2 Non-incapacitating 0
3 Non-incapacitating 0
4 No injury/unknown 0
5 No injury/unknown 0
6 No injury/unknown 0
7 Incapacitating 0
8 No injury/unknown 0
9 No injury/unknown 0
def injury_to_num(injury):
    if injury == 'Fatal':
        return 1
    else:
        return 0

df['Injury Type Num'] = df['Injury Type'].apply(injury_to_num)
Weekend? Weekend Num
0 Weekday 2
1 Weekday 2
2 Weekend 1
3 Weekend 1
4 Weekend 1
5 Weekday 2
6 Weekday 2
7 Weekday 2
8 Weekend 1
9 Weekend 1
def weekend_to_num(value):
    if str(value).lower() == 'weekend':
        return 1
    elif str(value).lower() == 'weekday':
        return 2
    else:
        return 0
df['Weekend Num'] = df['Weekend?'].apply(weekend_to_num)
Primary Factor Primary Factor Num
0 OTHER (DRIVER) - EXPLAIN IN NARRATIVE 0
1 FOLLOWING TOO CLOSELY 1
2 DISREGARD SIGNAL/REG SIGN 1
3 FAILURE TO YIELD RIGHT OF WAY 1
4 FAILURE TO YIELD RIGHT OF WAY 1
5 FAILURE TO YIELD RIGHT OF WAY 1
6 DRIVER DISTRACTED - EXPLAIN IN NARRATIVE 5
7 ENGINE FAILURE OR DEFECTIVE 3
8 FOLLOWING TOO CLOSELY 1
9 RAN OFF ROAD RIGHT 1


1 = Erros de julgamento do motorista
2 = Velocidade / comportamento arriscado
3 = Falhas mecânicas
4 = Condições da estrada / ambientais
5 = Distrações
6 = Uso de Substâncias
7 = Fatores diversos
8 = Outros

def primary_factor_to_num(factor):
    if factor == 'Erros de julgamento do motorista':
        return 1
    elif factor == 'Velocidade / comportamento arriscado':
        return 2
    elif factor == 'Falhas mecânicas':
        return 3
    elif factor == 'Condições da estrada / ambientais':
        return 4
    elif factor == 'Distrações':
        return 5
    elif factor == 'Uso de Substâncias':
        return 6
    elif factor == 'Fatores diversos':
        return 7
    else:
        return 0  # Outros / não especificado
df['Primary Factor Num'] = df['Primary Factor'].apply(primary_factor_to_num)

Conversão de variáveis categóricas em numéricas: tipo de colisão, tipo de lesão, dia da semana e fator primário foram transformados em variáveis numéricas para facilitar os proximos processos e estabelecer uma normalização.

Limpeza

Tamanho do dataset antes remoção de valores ausentes (53943, 11)
Tamanho do dataset após remoção de valores ausentes (52582, 11)

print(df.shape)
print("Tamanho do dataset antes remoção de valores ausentes")
df = df.dropna()
print(df.shape)
print("Tamanho do dataset após remoção de valores ausentes")

Todos os registros com valores ausentes foram removidos, garantindo a integridade dos dados para o treinamento do modelo

Etapa 3

Separação em treino e teste / Arvore de Decisão

Os dados foram divididos em conjuntos de treino 80% e teste 20% de forma estratificada, preservando a proporção das classes. A distribuição das classes foi feita para ambos os conjuntos garantindo que o modelo fosse treinado e avaliado de forma justa.

(53943, 15)
Treino: 42065 amostras
Teste: 10517 amostras
Proporção: 80.0% treino, 20.0% teste

Distribuição das classes - Treino:

Collision Type Num count
1 39744
2 799
3 673
4 479
5 370

Distribuição das classes - Teste:

Collision Type Num count
1 9936
2 200
3 168
4 120
5 93
import matplotlib.pyplot as plt
import pandas as pd
from io import StringIO
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier

df = pd.read_excel("docs/arvore-decisao/crashcar.xlsx")
#Transformando Collision Type em numérico
def collision_to_num(collision):
    if collision == '1-Car' or collision == '2-Car' or collision == '3+ Cars':
        return 1
    elif collision == 'Moped/Motorcycle':
        return 2
    elif collision == 'Bus':
        return 3
    elif collision == 'Pedestrian':
        return 4
    elif collision == 'Cyclist':
        return 5
    else:
        return 0
df['Collision Type Num'] = df['Collision Type'].apply(collision_to_num)
#Transformando Injury Type em numérico
def injury_to_num(injury):
    if injury == 'Fatal':
        return 1
    else:
        return 0
df['Injury Type Num'] = df['Injury Type'].apply(injury_to_num)
#Transformando Weekend? em numérico 
def weekend_to_num(value):
    if str(value).lower() == 'weekend':
        return 1
    elif str(value).lower() == 'weekday':
        return 2
    else:
        return 0
df['Weekend Num'] = df['Weekend?'].apply(weekend_to_num)
def primary_factor_to_num(factor):
    if factor == "FAILURE TO YIELD RIGHT OF WAY" or \
        factor == "FOLLOWING TOO CLOSELY" or \
        factor == "IMPROPER TURNING" or \
        factor == "UNSAFE BACKING" or \
        factor == "RAN OFF ROAD RIGHT" or \
        factor == "DISREGARD SIGNAL/REG SIGN" or \
        factor == "LEFT OF CENTER" or \
        factor == "IMPROPER LANE USAGE" or \
        factor == "UNSAFE LANE MOVEMENT" or \
        factor == "OVERCORRECTING/OVERSTEERING" or \
        factor == "IMPROPER PASSING" or \
        factor == "WRONG WAY ON ONE WAY" or \
        factor == "VIOLATION OF LICENSE RESTRICTION":
        return 1

    elif factor == 'SPEED TOO FAST FOR WEATHER CONDITIONS' or \
            factor == 'UNSAFE SPEED':
        return 2

    elif factor == "BRAKE FAILURE OR DEFECTIVE" or \
            factor == "TIRE FAILURE OR DEFECTIVE" or \
            factor == "ACCELERATOR FAILURE OR DEFECTIVE" or \
            factor == "STEERING FAILURE" or \
            factor == "ENGINE FAILURE OR DEFECTIVE" or \
            factor == "HEADLIGHT DEFECTIVE OR NOT ON" or \
            factor == "OTHER LIGHTS DEFECTIVE" or \
            factor == "TOW HITCH FAILURE":
        return 3

    elif factor == "ROADWAY SURFACE CONDITION" or \
            factor == "GLARE" or \
            factor == "HOLES/RUTS IN SURFACE" or \
            factor == "TRAFFIC CONTROL INOPERATIVE/MISSING/OBSC" or \
            factor == "ROAD UNDER CONSTRUCTION" or \
            factor == "SHOULDER DEFECTIVE" or \
            factor == "LANE MARKING OBSCURED" or \
            factor == "UTILITY WORK" or \
            factor == "SEVERE CROSSWINDS":
        return 4

    elif factor == "DRIVER DISTRACTED - EXPLAIN IN NARRATIVE" or \
            factor == "CELL PHONE USAGE" or \
            factor == "PASSENGER DISTRACTION":
        return 5

    elif factor == "ALCOHOLIC BEVERAGES" or \
            factor == "PRESCRIPTION DRUGS" or \
            factor == "ILLEGAL DRUGS" or \
            factor == "DRIVER ASLEEP OR FATIGUED" or \
            factor == "DRIVER ILLNESS":
        return 6
    elif factor == "ANIMAL/OBJECT IN ROADWAY" or \
            factor == "SEVERE CROSSWINDS" or \
            factor == "INSECURE/LEAKY LOAD" or \
            factor == "OVERSIZE/OVERWEIGHT LOAD":
        return 7 
    else:
        return 0
df['Primary Factor Num'] = df['Primary Factor'].apply(primary_factor_to_num)

#Limpeza
print(df.shape)
df = df.dropna()


#Definindo features 
features = ['Injury Type Num', 'Weekend Num', 'Primary Factor Num', 'Year', 'Month', 'Day', 'Hour', 'Latitude']
#coluna que vamos prever com a árvore de decisão
target = 'Collision Type Num' 

x = df[features]
y = df[target]

#Dividindo em treino e teste 80% treino 20% teste
x_train, x_test, y_train, y_test = train_test_split(
    x, y, test_size=0.2, random_state=42, stratify=y
)

print(f"<br>Treino: {x_train.shape[0]} amostras")
print(f"<br>Teste: {x_test.shape[0]} amostras")
print(f"<br>Proporção: {x_train.shape[0]/x.shape[0]*100:.1f}% treino, {x_test.shape[0]/x.shape[0]*100:.1f}% teste\n")

print("Distribuição das classes - Treino:\n")
print(y_train.value_counts().to_markdown(), "\n")

print("Distribuição das classes - Teste:\n")
print(y_test.value_counts().to_markdown(), "\n")

Etapa 4

Treinamento do modelo

O modelo de Árvore de Decisão foi treinado utilizando as variáveis mais relevantes do dataset:

Features utilizadas: Injury type (Tipo de lesão), dia da semana, Primary factor (fator primário), ano, mês, dia, hora e latitude. Target:Collision Type (Tipo de colisão).

(53943, 15)
Importância das Features:

Feature Importância
2 Primary Factor Num 0.237794
3 Year 0.218050
4 Month 0.198402
0 Injury Type Num 0.137037
7 Latitude 0.133980
6 Hour 0.068335
5 Day 0.006403
1 Weekend Num 0.000000


import matplotlib.pyplot as plt
import pandas as pd
from io import StringIO
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import confusion_matrix
df = pd.read_excel("docs/arvore-decisao/crashcar.xlsx")
#Transformando Collision Type em numérico
def collision_to_num(collision):
    if collision == '1-Car' or collision == '2-Car' or collision == '3+ Cars':
        return 1
    elif collision == 'Moped/Motorcycle':
        return 2
    elif collision == 'Bus':
        return 3
    elif collision == 'Pedestrian':
        return 4
    elif collision == 'Cyclist':
        return 5
    else:
        return 0
df['Collision Type Num'] = df['Collision Type'].apply(collision_to_num)
#Transformando Injury Type em numérico
def injury_to_num(injury):
    if injury == 'Fatal':
        return 1
    else:
        return 0
df['Injury Type Num'] = df['Injury Type'].apply(injury_to_num)
#Transformando Weekend? em numérico 
def weekend_to_num(value):
    if str(value).lower() == 'weekend':
        return 1
    elif str(value).lower() == 'weekday':
        return 2
    else:
        return 0
df['Weekend Num'] = df['Weekend?'].apply(weekend_to_num)
def primary_factor_to_num(factor):
    if factor == "FAILURE TO YIELD RIGHT OF WAY" or \
        factor == "FOLLOWING TOO CLOSELY" or \
        factor == "IMPROPER TURNING" or \
        factor == "UNSAFE BACKING" or \
        factor == "RAN OFF ROAD RIGHT" or \
        factor == "DISREGARD SIGNAL/REG SIGN" or \
        factor == "LEFT OF CENTER" or \
        factor == "IMPROPER LANE USAGE" or \
        factor == "UNSAFE LANE MOVEMENT" or \
        factor == "OVERCORRECTING/OVERSTEERING" or \
        factor == "IMPROPER PASSING" or \
        factor == "WRONG WAY ON ONE WAY" or \
        factor == "VIOLATION OF LICENSE RESTRICTION":
        return 1

    elif factor == 'SPEED TOO FAST FOR WEATHER CONDITIONS' or \
            factor == 'UNSAFE SPEED':
        return 2

    elif factor == "BRAKE FAILURE OR DEFECTIVE" or \
            factor == "TIRE FAILURE OR DEFECTIVE" or \
            factor == "ACCELERATOR FAILURE OR DEFECTIVE" or \
            factor == "STEERING FAILURE" or \
            factor == "ENGINE FAILURE OR DEFECTIVE" or \
            factor == "HEADLIGHT DEFECTIVE OR NOT ON" or \
            factor == "OTHER LIGHTS DEFECTIVE" or \
            factor == "TOW HITCH FAILURE":
        return 3

    elif factor == "ROADWAY SURFACE CONDITION" or \
            factor == "GLARE" or \
            factor == "HOLES/RUTS IN SURFACE" or \
            factor == "TRAFFIC CONTROL INOPERATIVE/MISSING/OBSC" or \
            factor == "ROAD UNDER CONSTRUCTION" or \
            factor == "SHOULDER DEFECTIVE" or \
            factor == "LANE MARKING OBSCURED" or \
            factor == "UTILITY WORK" or \
            factor == "SEVERE CROSSWINDS":
        return 4

    elif factor == "DRIVER DISTRACTED - EXPLAIN IN NARRATIVE" or \
            factor == "CELL PHONE USAGE" or \
            factor == "PASSENGER DISTRACTION":
        return 5

    elif factor == "ALCOHOLIC BEVERAGES" or \
            factor == "PRESCRIPTION DRUGS" or \
            factor == "ILLEGAL DRUGS" or \
            factor == "DRIVER ASLEEP OR FATIGUED" or \
            factor == "DRIVER ILLNESS":
        return 6
    elif factor == "ANIMAL/OBJECT IN ROADWAY" or \
            factor == "SEVERE CROSSWINDS" or \
            factor == "INSECURE/LEAKY LOAD" or \
            factor == "OVERSIZE/OVERWEIGHT LOAD":
        return 7 
    else:
        return 0
df['Primary Factor Num'] = df['Primary Factor'].apply(primary_factor_to_num)
print(df.shape)
df = df.dropna()
features = ['Injury Type Num', 'Weekend Num', 'Primary Factor Num', 'Year', 'Month', 'Day', 'Hour', 'Latitude']
target = 'Collision Type Num' 
x = df[features]
y = df[target]
x_train, x_test, y_train, y_test = train_test_split(
    x, y, test_size=0.2, random_state=42, stratify=y
)

###############################
###############################
#Treinamento do Decision Tree
###############################
###############################
clf = DecisionTreeClassifier(random_state=42, max_depth=5)
clf.fit(x_train, y_train)

#acurácia
y_pred = clf.predict(x_test)

#Importância das features
feature_importance = pd.DataFrame({
    "Feature": x_train.columns,
    "Importância": clf.feature_importances_
}).sort_values(by="Importância", ascending=False)
print("<br>Importância das Features:")
print(feature_importance.to_html() + "<br>")

Arvore de decisão

(53943, 15) 2025-09-26T12:34:56.673551 image/svg+xml Matplotlib v3.10.6, https://matplotlib.org/
Acurácia do modelo: 0.9446

# ==========================
#Visualização da árvore em SVG
# ==========================
plt.figure(figsize=(18,10), dpi=150)
tree.plot_tree(
    clf,
    feature_names=features,
    class_names=[str(c) for c in clf.classes_],
    filled=True,
    rounded=True,
    fontsize=10
)
buffer = BytesIO()
plt.savefig(buffer, format="svg", transparent=False)
svg_data = buffer.getvalue().decode("utf-8")
print(svg_data)
accuracy = accuracy_score(y_test, y_pred)
print(f"<br>Acurácia do modelo: {accuracy:.4f}")

O projeto cumpriu todas as etapas propostas: exploração, pré-processamento, divisão, treinamento, avaliação e documentação. O modelo de Árvore de Decisão mostrou-se eficiente para a tarefa de classificação do tipo de colisão