feat: adiciona analise e Modelos 4, 5 e 6 exclusivos para a frota de aeronaves C-99A
This commit is contained in:
290
create_nb.py
290
create_nb.py
@@ -568,6 +568,296 @@ print("== QUADRO DE HORÁRIOS DIÁRIOS (COM 40 MIN TAT) ==")
|
|||||||
print(tabulate(tabela_horarios3, headers=["Aeronave", "Origem", "Destino", "Tipo", "Partida", "Chegada", "Duração Voo"], tablefmt="github"))
|
print(tabulate(tabela_horarios3, headers=["Aeronave", "Origem", "Destino", "Tipo", "Partida", "Chegada", "Duração Voo"], tablefmt="github"))
|
||||||
"""))
|
"""))
|
||||||
|
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
# SEÇÃO C-99
|
||||||
|
# ==========================================
|
||||||
|
cells.append(nbf.v4.new_markdown_cell("""# Alocação de Frotas - Aeronaves C-99A
|
||||||
|
Agora replicaremos a mesma lógica e os mesmos modelos (1, 2 e 3) para a aeronave C-99A (que será nomeada como Modelos 4, 5 e 6).
|
||||||
|
"""))
|
||||||
|
|
||||||
|
cells.append(nbf.v4.new_code_cell("""# 1. Filtro e Limpeza C-99A
|
||||||
|
df_c99 = df[df['Modelo'] == 'C-99A'].copy()
|
||||||
|
df_c99 = df_c99[df_c99['Localidade Decolagem'] != 0]
|
||||||
|
df_c99 = df_c99[df_c99['Localidade Pouso'] != 0]
|
||||||
|
df_c99['Trecho'] = df_c99['Localidade Decolagem'] + '-' + df_c99['Localidade Pouso']
|
||||||
|
df_c99['PAX'] = pd.to_numeric(df_c99['PAX']).fillna(0)
|
||||||
|
df_c99['Dep TimeStamp'] = pd.to_datetime(df_c99['Data de Decolagem'] + ' ' + df_c99['Hora de Decolagem'], format='mixed', dayfirst=True)
|
||||||
|
df_c99['Data Apenas'] = df_c99['Dep TimeStamp'].dt.date
|
||||||
|
|
||||||
|
CAPACIDADE_C99 = 50
|
||||||
|
matriculas_unicas_c99 = df_c99['Matrícula'].unique()
|
||||||
|
total_aeronaves_c99 = len(matriculas_unicas_c99)
|
||||||
|
|
||||||
|
print(f"Total de matrículas únicas C-99A (aeronaves disponíveis): {total_aeronaves_c99}")
|
||||||
|
|
||||||
|
# Demanda diária
|
||||||
|
demanda_diaria_c99 = df_c99.groupby(['Trecho', 'Data Apenas'])['PAX'].sum().reset_index()
|
||||||
|
|
||||||
|
import itertools
|
||||||
|
todos_trechos_c99 = demanda_diaria_c99['Trecho'].unique()
|
||||||
|
idx_c99 = pd.MultiIndex.from_product([todos_trechos_c99, datas_2025], names=['Trecho', 'Data Apenas'])
|
||||||
|
demanda_diaria_completa_c99 = pd.DataFrame(index=idx_c99).reset_index()
|
||||||
|
demanda_diaria_completa_c99 = pd.merge(demanda_diaria_completa_c99, demanda_diaria_c99, on=['Trecho', 'Data Apenas'], how='left').fillna({'PAX': 0})
|
||||||
|
|
||||||
|
estatisticas_trechos_c99 = demanda_diaria_completa_c99.groupby('Trecho').agg(
|
||||||
|
Media_PAX_Diario=('PAX', 'mean'),
|
||||||
|
Desvio_Padrao_PAX=('PAX', 'std'),
|
||||||
|
Total_PAX=('PAX', 'sum'),
|
||||||
|
Dias_Operados=('PAX', lambda x: (x > 0).sum())
|
||||||
|
).reset_index()
|
||||||
|
|
||||||
|
estatisticas_trechos_c99['Media_PAX_Dias_Operados'] = estatisticas_trechos_c99.apply(
|
||||||
|
lambda row: row['Total_PAX'] / row['Dias_Operados'] if row['Dias_Operados'] > 0 else 0, axis=1
|
||||||
|
)
|
||||||
|
estatisticas_trechos_c99['Score_Relevancia'] = estatisticas_trechos_c99['Total_PAX'] * estatisticas_trechos_c99['Dias_Operados']
|
||||||
|
estatisticas_trechos_c99 = estatisticas_trechos_c99.sort_values(by='Score_Relevancia', ascending=False)
|
||||||
|
|
||||||
|
NUM_TRECHOS_C99 = 10
|
||||||
|
top_trechos_c99 = estatisticas_trechos_c99.head(NUM_TRECHOS_C99)
|
||||||
|
|
||||||
|
display(top_trechos_c99)
|
||||||
|
|
||||||
|
plt.figure(figsize=(10, 6))
|
||||||
|
sns.barplot(data=top_trechos_c99, x='Trecho', y='Score_Relevancia', hue='Trecho', palette='magma', legend=False)
|
||||||
|
plt.title(f'Top {NUM_TRECHOS_C99} Trechos C-99A (PAX * Dias Operados)')
|
||||||
|
plt.xticks(rotation=45)
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
# Rede C-99
|
||||||
|
G_c99 = nx.DiGraph()
|
||||||
|
for _, row in top_trechos_c99.iterrows():
|
||||||
|
origem, destino = row['Trecho'].split('-')
|
||||||
|
G_c99.add_edge(origem, destino, weight=row['Score_Relevancia'])
|
||||||
|
pos_c99 = nx.spring_layout(G_c99, k=2.5, iterations=100, seed=42)
|
||||||
|
plt.figure(figsize=(12, 8))
|
||||||
|
nx.draw_networkx_nodes(G_c99, pos_c99, node_size=1200, node_color='lightcoral', edgecolors='black')
|
||||||
|
nx.draw_networkx_edges(G_c99, pos_c99, edgelist=G_c99.edges(), arrows=True, arrowstyle='-|>', arrowsize=25, edge_color='gray', width=2, node_size=1200, connectionstyle='arc3,rad=0.15')
|
||||||
|
nx.draw_networkx_labels(G_c99, pos_c99, font_size=12, font_weight='bold')
|
||||||
|
plt.title('Diagrama de Rede - C-99A', fontsize=14)
|
||||||
|
plt.axis('off')
|
||||||
|
plt.show()
|
||||||
|
"""))
|
||||||
|
|
||||||
|
cells.append(nbf.v4.new_markdown_cell("""## MODELO 4: Minimização de Distância (Custo)
|
||||||
|
Equivalente ao Modelo 1, mas para C-99A.
|
||||||
|
"""))
|
||||||
|
|
||||||
|
cells.append(nbf.v4.new_code_cell("""# Preparação Modelo 4
|
||||||
|
rotas_lista_c99 = top_trechos_c99['Trecho'].tolist()
|
||||||
|
|
||||||
|
distancias_voo_c99 = {}
|
||||||
|
for r in rotas_lista_c99:
|
||||||
|
origem, destino = r.split('-')
|
||||||
|
distancias_voo_c99[r] = calc_distancia(origem, destino)
|
||||||
|
|
||||||
|
voos_requeridos_c99 = {}
|
||||||
|
for _, row in top_trechos_c99.iterrows():
|
||||||
|
voos = math.ceil(row['Media_PAX_Dias_Operados'] / CAPACIDADE_C99)
|
||||||
|
voos_requeridos_c99[row['Trecho']] = max(1, voos)
|
||||||
|
|
||||||
|
prob4 = pulp.LpProblem("Fleet_Assignment_C99_Brasil", pulp.LpMinimize)
|
||||||
|
X_c99 = pulp.LpVariable.dicts("X_c99", rotas_lista_c99, lowBound=0, cat='Integer')
|
||||||
|
|
||||||
|
prob4 += pulp.lpSum([distancias_voo_c99[r] * X_c99[r] for r in rotas_lista_c99])
|
||||||
|
|
||||||
|
for r in rotas_lista_c99:
|
||||||
|
prob4 += X_c99[r] >= voos_requeridos_c99[r]
|
||||||
|
|
||||||
|
prob4 += pulp.lpSum([X_c99[r] for r in rotas_lista_c99]) <= total_aeronaves_c99
|
||||||
|
|
||||||
|
prob4.solve(pulp.PULP_CBC_CMD(msg=False))
|
||||||
|
|
||||||
|
print("== MODELO 4 (C-99A) ==")
|
||||||
|
print(f"Status: {pulp.LpStatus[prob4.status]}")
|
||||||
|
print(f"Distância Total: {pulp.value(prob4.objective):.2f} km\\n")
|
||||||
|
for r in rotas_lista_c99:
|
||||||
|
print(f"Trecho: {r:10} | Alocados: {int(X_c99[r].varValue)} voo(s)")
|
||||||
|
"""))
|
||||||
|
|
||||||
|
cells.append(nbf.v4.new_markdown_cell("""## MODELO 5: Minimização de Frota C-99A (S/ TAT)
|
||||||
|
Equivalente ao Modelo 2.
|
||||||
|
"""))
|
||||||
|
|
||||||
|
cells.append(nbf.v4.new_code_cell("""# Velocidade Média C-99A
|
||||||
|
df_c99_spd = df_c99.copy()
|
||||||
|
df_c99_spd['Tempo_Horas'] = pd.to_timedelta(df_c99_spd['Tempo de Voo']).dt.total_seconds() / 3600
|
||||||
|
df_c99_spd['Dist_km'] = df_c99_spd.apply(get_dist_row, axis=1)
|
||||||
|
df_valid_c99 = df_c99_spd[(df_c99_spd['Tempo_Horas'] > 0) & (df_c99_spd['Dist_km'] > 0)].copy()
|
||||||
|
df_valid_c99['Velocidade'] = df_valid_c99['Dist_km'] / df_valid_c99['Tempo_Horas']
|
||||||
|
vel_media_c99 = df_valid_c99['Velocidade'].mean()
|
||||||
|
print(f"Velocidade média C-99A: {vel_media_c99:.2f} km/h")
|
||||||
|
|
||||||
|
aeroportos_malha_c99 = set()
|
||||||
|
for r in rotas_lista_c99:
|
||||||
|
o, d = r.split('-')
|
||||||
|
aeroportos_malha_c99.add(o)
|
||||||
|
aeroportos_malha_c99.add(d)
|
||||||
|
aeroportos_malha_c99 = list(aeroportos_malha_c99)
|
||||||
|
|
||||||
|
tempos_voo_min_c99 = {}
|
||||||
|
for o in aeroportos_malha_c99:
|
||||||
|
for d in aeroportos_malha_c99:
|
||||||
|
if o == d:
|
||||||
|
tempos_voo_min_c99[(o, d)] = 0
|
||||||
|
else:
|
||||||
|
dist = calc_distancia(o, d)
|
||||||
|
tempos_voo_min_c99[(o, d)] = int(round((dist / vel_media_c99) * 60))
|
||||||
|
|
||||||
|
D_c99 = {(o, d): 0 for o in aeroportos_malha_c99 for d in aeroportos_malha_c99}
|
||||||
|
for r in rotas_lista_c99:
|
||||||
|
o, d = r.split('-')
|
||||||
|
D_c99[(o, d)] = voos_requeridos_c99[r]
|
||||||
|
|
||||||
|
prob5 = pulp.LpProblem("Minimizar_Aeronaves_C99", pulp.LpMinimize)
|
||||||
|
Y5 = pulp.LpVariable.dicts("Y5", [(o, d) for o in aeroportos_malha_c99 for d in aeroportos_malha_c99], lowBound=0, cat='Integer')
|
||||||
|
N5 = pulp.LpVariable("N5", lowBound=0, cat='Integer')
|
||||||
|
|
||||||
|
prob5 += N5 + 0.0001 * pulp.lpSum([Y5[(i, j)] * tempos_voo_min_c99[(i, j)] for i in aeroportos_malha_c99 for j in aeroportos_malha_c99])
|
||||||
|
|
||||||
|
for no in aeroportos_malha_c99:
|
||||||
|
chegadas = pulp.lpSum([D_c99[(i, no)] + Y5[(i, no)] for i in aeroportos_malha_c99])
|
||||||
|
partidas = pulp.lpSum([D_c99[(no, j)] + Y5[(no, j)] for j in aeroportos_malha_c99])
|
||||||
|
prob5 += chegadas == partidas
|
||||||
|
|
||||||
|
tempo_total_voo_c99 = pulp.lpSum([(D_c99[(i, j)] + Y5[(i, j)]) * tempos_voo_min_c99[(i, j)] for i in aeroportos_malha_c99 for j in aeroportos_malha_c99])
|
||||||
|
prob5 += tempo_total_voo_c99 <= N5 * 1440
|
||||||
|
|
||||||
|
prob5.solve(pulp.PULP_CBC_CMD(msg=False))
|
||||||
|
|
||||||
|
print("\\n== RESULTADO DO MODELO 5 (C-99A) ==")
|
||||||
|
print(f"Status: {pulp.LpStatus[prob5.status]}")
|
||||||
|
print(f"Aeronaves mínimas: {int(N5.varValue)}")
|
||||||
|
|
||||||
|
tempo_gasto_c99 = sum([(D_c99[(i, j)] + int(Y5[(i, j)].varValue)) * tempos_voo_min_c99[(i, j)] for i in aeroportos_malha_c99 for j in aeroportos_malha_c99])
|
||||||
|
print(f"Tempo total de voo: {int(tempo_gasto_c99)} min ({tempo_gasto_c99/60:.2f} h)\\n")
|
||||||
|
|
||||||
|
voos_para_fazer_c99 = []
|
||||||
|
D_copy_c99 = {k: v for k, v in D_c99.items()}
|
||||||
|
for i in aeroportos_malha_c99:
|
||||||
|
for j in aeroportos_malha_c99:
|
||||||
|
total_voos = D_c99[(i, j)] + int(Y5[(i, j)].varValue)
|
||||||
|
for _ in range(total_voos):
|
||||||
|
if D_copy_c99[(i, j)] > 0:
|
||||||
|
tipo = 'Passageiro'
|
||||||
|
D_copy_c99[(i, j)] -= 1
|
||||||
|
else:
|
||||||
|
tipo = 'Vazio'
|
||||||
|
voos_para_fazer_c99.append({'origem': i, 'destino': j, 'tipo': tipo, 'tempo': tempos_voo_min_c99[(i, j)]})
|
||||||
|
|
||||||
|
tabela_horarios_c99 = []
|
||||||
|
aeronave_id = 1
|
||||||
|
|
||||||
|
while voos_para_fazer_c99:
|
||||||
|
voo_atual = voos_para_fazer_c99.pop(0)
|
||||||
|
hora_atual = datetime.datetime(2025, 1, 1, 0, 0, 0)
|
||||||
|
tempo_delta = datetime.timedelta(minutes=int(voo_atual['tempo']))
|
||||||
|
hora_chegada = hora_atual + tempo_delta
|
||||||
|
|
||||||
|
tabela_horarios_c99.append([f"Aeronave {aeronave_id}", voo_atual['origem'], voo_atual['destino'], voo_atual['tipo'], hora_atual.strftime('%H:%M'), hora_chegada.strftime('%H:%M'), f"{voo_atual['tempo']} min"])
|
||||||
|
hora_atual = hora_chegada
|
||||||
|
local_atual = voo_atual['destino']
|
||||||
|
|
||||||
|
while True:
|
||||||
|
prox_voo = None
|
||||||
|
for idx, v in enumerate(voos_para_fazer_c99):
|
||||||
|
if v['origem'] == local_atual:
|
||||||
|
if (hora_atual - datetime.datetime(2025, 1, 1, 0, 0, 0) + datetime.timedelta(minutes=int(v['tempo']))).total_seconds() <= 1440 * 60:
|
||||||
|
prox_voo = voos_para_fazer_c99.pop(idx)
|
||||||
|
break
|
||||||
|
if prox_voo:
|
||||||
|
tempo_delta = datetime.timedelta(minutes=int(prox_voo['tempo']))
|
||||||
|
hora_chegada = hora_atual + tempo_delta
|
||||||
|
tabela_horarios_c99.append([f"Aeronave {aeronave_id}", prox_voo['origem'], prox_voo['destino'], prox_voo['tipo'], hora_atual.strftime('%H:%M'), hora_chegada.strftime('%H:%M'), f"{prox_voo['tempo']} min"])
|
||||||
|
hora_atual = hora_chegada
|
||||||
|
local_atual = prox_voo['destino']
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
aeronave_id += 1
|
||||||
|
|
||||||
|
print(tabulate(tabela_horarios_c99, headers=["Aeronave", "Origem", "Destino", "Tipo", "Partida", "Chegada", "Duração"], tablefmt="github"))
|
||||||
|
"""))
|
||||||
|
|
||||||
|
cells.append(nbf.v4.new_markdown_cell("""## MODELO 6: Minimização de Frota C-99A (C/ 40 min TAT)
|
||||||
|
Equivalente ao Modelo 3.
|
||||||
|
"""))
|
||||||
|
|
||||||
|
cells.append(nbf.v4.new_code_cell("""# Modelo 6
|
||||||
|
prob6 = pulp.LpProblem("Minimizar_Aeronaves_C99_TAT", pulp.LpMinimize)
|
||||||
|
Y6 = pulp.LpVariable.dicts("Y6", [(o, d) for o in aeroportos_malha_c99 for d in aeroportos_malha_c99], lowBound=0, cat='Integer')
|
||||||
|
N6 = pulp.LpVariable("N6", lowBound=0, cat='Integer')
|
||||||
|
|
||||||
|
TAT_min = 40
|
||||||
|
|
||||||
|
prob6 += N6 + 0.0001 * pulp.lpSum([Y6[(i, j)] * tempos_voo_min_c99[(i, j)] for i in aeroportos_malha_c99 for j in aeroportos_malha_c99])
|
||||||
|
|
||||||
|
for no in aeroportos_malha_c99:
|
||||||
|
chegadas = pulp.lpSum([D_c99[(i, no)] + Y6[(i, no)] for i in aeroportos_malha_c99])
|
||||||
|
partidas = pulp.lpSum([D_c99[(no, j)] + Y6[(no, j)] for j in aeroportos_malha_c99])
|
||||||
|
prob6 += chegadas == partidas
|
||||||
|
|
||||||
|
tempo_total_op_c99 = pulp.lpSum([(D_c99[(i, j)] + Y6[(i, j)]) * (tempos_voo_min_c99[(i, j)] + TAT_min) for i in aeroportos_malha_c99 for j in aeroportos_malha_c99])
|
||||||
|
prob6 += tempo_total_op_c99 <= N6 * 1440
|
||||||
|
|
||||||
|
prob6.solve(pulp.PULP_CBC_CMD(msg=False))
|
||||||
|
|
||||||
|
print("\\n== RESULTADO DO MODELO 6 (C-99A C/ TAT 40 MIN) ==")
|
||||||
|
print(f"Status: {pulp.LpStatus[prob6.status]}")
|
||||||
|
print(f"Aeronaves mínimas: {int(N6.varValue)}")
|
||||||
|
|
||||||
|
tempo_voo_c99_6 = sum([(D_c99[(i, j)] + int(Y6[(i, j)].varValue)) * tempos_voo_min_c99[(i, j)] for i in aeroportos_malha_c99 for j in aeroportos_malha_c99])
|
||||||
|
tot_voos_6 = sum([(D_c99[(i, j)] + int(Y6[(i, j)].varValue)) for i in aeroportos_malha_c99 for j in aeroportos_malha_c99])
|
||||||
|
solo_tot_6 = tot_voos_6 * TAT_min
|
||||||
|
|
||||||
|
print(f"Tempo total operacional comprometido: {tempo_voo_c99_6 + solo_tot_6} min ({(tempo_voo_c99_6 + solo_tot_6)/60:.2f} h)\\n")
|
||||||
|
|
||||||
|
voos_para_fazer_c99_6 = []
|
||||||
|
D_copy_c99_6 = {k: v for k, v in D_c99.items()}
|
||||||
|
for i in aeroportos_malha_c99:
|
||||||
|
for j in aeroportos_malha_c99:
|
||||||
|
tot = D_c99[(i, j)] + int(Y6[(i, j)].varValue)
|
||||||
|
for _ in range(tot):
|
||||||
|
if D_copy_c99_6[(i, j)] > 0:
|
||||||
|
tipo = 'Passageiro'
|
||||||
|
D_copy_c99_6[(i, j)] -= 1
|
||||||
|
else:
|
||||||
|
tipo = 'Vazio'
|
||||||
|
voos_para_fazer_c99_6.append({'origem': i, 'destino': j, 'tipo': tipo, 'tempo': tempos_voo_min_c99[(i, j)]})
|
||||||
|
|
||||||
|
tabela_horarios_c99_6 = []
|
||||||
|
aeronave_id = 1
|
||||||
|
|
||||||
|
while voos_para_fazer_c99_6:
|
||||||
|
voo_atual = voos_para_fazer_c99_6.pop(0)
|
||||||
|
hora_atual = datetime.datetime(2025, 1, 1, 0, 0, 0)
|
||||||
|
tempo_delta = datetime.timedelta(minutes=int(voo_atual['tempo']))
|
||||||
|
hora_chegada = hora_atual + tempo_delta
|
||||||
|
|
||||||
|
tabela_horarios_c99_6.append([f"Aeronave {aeronave_id}", voo_atual['origem'], voo_atual['destino'], voo_atual['tipo'], hora_atual.strftime('%H:%M'), hora_chegada.strftime('%H:%M'), f"{voo_atual['tempo']} min"])
|
||||||
|
hora_atual = hora_chegada + datetime.timedelta(minutes=40)
|
||||||
|
local_atual = voo_atual['destino']
|
||||||
|
|
||||||
|
while True:
|
||||||
|
prox_voo = None
|
||||||
|
for idx, v in enumerate(voos_para_fazer_c99_6):
|
||||||
|
if v['origem'] == local_atual:
|
||||||
|
if (hora_atual - datetime.datetime(2025, 1, 1, 0, 0, 0) + datetime.timedelta(minutes=int(v['tempo']))).total_seconds() <= 1440 * 60:
|
||||||
|
prox_voo = voos_para_fazer_c99_6.pop(idx)
|
||||||
|
break
|
||||||
|
if prox_voo:
|
||||||
|
tempo_delta = datetime.timedelta(minutes=int(prox_voo['tempo']))
|
||||||
|
hora_chegada = hora_atual + tempo_delta
|
||||||
|
tabela_horarios_c99_6.append([f"Aeronave {aeronave_id}", prox_voo['origem'], prox_voo['destino'], prox_voo['tipo'], hora_atual.strftime('%H:%M'), hora_chegada.strftime('%H:%M'), f"{prox_voo['tempo']} min"])
|
||||||
|
hora_atual = hora_chegada + datetime.timedelta(minutes=40)
|
||||||
|
local_atual = prox_voo['destino']
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
aeronave_id += 1
|
||||||
|
|
||||||
|
print(tabulate(tabela_horarios_c99_6, headers=["Aeronave", "Origem", "Destino", "Tipo", "Partida", "Chegada", "Duração"], tablefmt="github"))
|
||||||
|
"""))
|
||||||
|
|
||||||
nb.cells = cells
|
nb.cells = cells
|
||||||
|
|
||||||
with open('modelos.ipynb', 'w', encoding='utf-8') as f:
|
with open('modelos.ipynb', 'w', encoding='utf-8') as f:
|
||||||
|
|||||||
770
modelos.ipynb
770
modelos.ipynb
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user