<jupyter_start><jupyter_text>importazione librerie<jupyter_code>import pandas as pd
#import geopandas as gpd
import numpy as np
import matplotlib.pyplot as plt 
import folium 
import math
import numpy as np
from sklearn.metrics.pairwise import euclidean_distances,haversine_distances
from typing import List
from collections import defaultdict 
from tqdm.notebook import tqdm
import networkx as nx
from math import radians
import json<jupyter_output><empty_output><jupyter_text>importazione dataset e caratterizzazione dati<jupyter_code>#Read the dataset 

fields = ["city","lat","lng","country","population"]
df = pd.read_excel('worldcities.xlsx', usecols=fields,engine="openpyxl")
airports_df= pd.read_csv('airports.csv')
Paths_df= pd.read_csv('routes.csv')
number_city = len(df)
#sostituisco il nome della prima colonna del data frame airport_df per comodità (tolgo lo spazio)
aux_list = list(airports_df.columns)
aux_list[0] = 'Airport_ID'
airports_df.columns = aux_list
#elimino gli spazi dai nomi delle colonne di Paths_df
Paths_df.columns =[column.replace(" ", "_") for column in Paths_df.columns]
#caratterizzo i dati 
#elimino da paths_df tutte le colonne che hanno dati rilevanti mancanti
Paths_df = Paths_df[(Paths_df['Destination_airport_ID'] != '\\N') & (Paths_df['Source_airport_ID'] != '\\N')]
#converto i dati nelle colonne "Source airport ID", "Destination airport ID" in interi
Paths_df[["Source_airport_ID", "Destination_airport_ID"]] = Paths_df[["Source_airport_ID", "Destination_airport_ID"]].apply(pd.to_numeric)
#creo le colonne 'Latitude_rad' 'Longitude_rad' applicando la funzione radians(x) alle colonne "Latitude" "Longitude"
#questo per evitare in futuro di doverle calcolare più volte inutilmente
airports_df['Latitude_rad'] = airports_df["Latitude"].apply(lambda x: radians(x))
airports_df['Longitude_rad'] = airports_df["Longitude"].apply(lambda x: radians(x))
df['lat_rad'] = df['lat'].apply(lambda x: radians(x))
df['lng_rad'] = df['lng'].apply(lambda x: radians(x))<jupyter_output><empty_output><jupyter_text>creazioni funzioni richiamabili ausiliarie<jupyter_code>def int_to_list (a):
    #converte un intero in una lista con quell'intero come unico elemento
    if type (a) == int:
        return([a])
    else:
        return(a)
def List_comparison (a,b):
    #prende in input due liste, se hanno almeno un elemento in comune restituisce True, altrimenti False
    result = False
    for e in a:
        if e in b:
            result = True
    return(result)<jupyter_output><empty_output><jupyter_text>passo 1 associazione per ogni città degli aereoporti corrispondenti o limitrofi<jupyter_code>#Passo 1.1a versione "bradipo"
#trovo il codice dell'aeroporto della corrispettiva città, se sono presenti più aeroporti per una città vengono 
#elencati in una lista
#l'associazione è fatta per le coppie (city,country) , non posso associare solamente usando city come discriminante
#in quanto, per esempio, possono esserci due città con lo stesso nome (Valencia esiste sia in Spagna sia in Venezuela)
#è possibile che vi siano città senza alcun aereoporto associato dopo questa fase per 2 motivi:
# 1: non esiste effettivamente alcun aeroporto
# 2: i dataframe df e airports_df non hanno rappresentazioni univoce per la coppia city-country in quanto certe 
#rappresentazioni di country non sono coerenti (es: South Korea in df e Korea,South in airports_df)
lista = [[]] *(len(df.index))
for e in tqdm(df.index):
        lista[e] = list(airports_df[(airports_df.City == df.loc[e].city) & (airports_df.Country == df.loc[e].country)].Airport_ID)
df['airport_id'] = lista
#Passo 1.2a versione "bradipo"
#per tutte le città senza aeroporto calcolo l'areoporto più vicino nello stesso paese
#se non esiste un aeroporto in quel paese cerco l'aeroporto più vicino
df['own_airport'] = np.ones(len(df.index))
for e in tqdm(df.index):
    if df.loc[e,'airport_id'] ==[]:
        country = df.loc[e].country
        #aux_airports contiene tutti gli aeroporti presenti nel paese della città che sto analizzando
        aux_airports = airports_df[airports_df.Country == country]
        #per ogni aeroporto calcolo la distanza dalla città che sto analizzando
        aux_airports['distance'] = [haversine_distances([[df.loc[e,'lat_rad'],df.loc[e,'lng_rad']],
                                                     [aux_airports.loc[x,'Latitude_rad'],
                                                      aux_airports.loc[x,'Longitude_rad']]])[0][1]
                                for x in aux_airports.index]
        #i dataframe airports_df e df presentano alcune incongruenze sulla stringa che definisce la nazione,
        #è quindi possibile che aux_airports non contenga alcun record proprio per questa incongruenza
        #per ovviare al problema è stato realizzata una gestione dell'eccezione che va a cercare
        #l'aereoporto più vicino in termini assoluti
    
        try:
            df.loc[e,'airport_id'] = int(aux_airports[aux_airports.distance ==  aux_airports['distance'].min()].Airport_ID)
            if df.loc[e,'city'] != aux_airports[aux_airports.distance ==  aux_airports['distance'].min()].City.item():
                df.loc[e,'own_airport'] = 0
        except TypeError:
            aux_airports = airports_df
            #per ogni aeroporto calcola la distanza da quella città e prelevo solo gli aereoporti di quella 
            #città oppure il più vicino
            aux_airports['distance'] = [haversine_distances([[df.loc[e,'lat_rad'],df.loc[e,'lng_rad']],
                                                         [aux_airports.loc[x,'Latitude_rad'],
                                                          aux_airports.loc[x,'Longitude_rad']]])[0][1]
                                    for x in aux_airports.index]
            #ordino gli aeroporti per distanza
            aux_airports = aux_airports.sort_values(by=['distance'], ascending = 0, inplace = False)
            #trovo il primo aeroporto che non appartiene a quella città
            count = 0
            find = 0
            #l'aeroporto più vicino è effettivamente associato alla città analizzata se ne esiste uno oppure 
            #potrebbe essere associato ad un altra città limitrofa
            while find == 0:
                if aux_airports.iloc[count]['City'] != df.loc[e,'city']:
                    find = 1
                else:
                    count += 1
            #se count >0 ho trovato aereoporti effettivamente associati alla città
            #se count è zero ho trovato l'aereoporto più vicino che tuttavia è in una città limitrofa e devo considerare
            #che devo fare un extra tragitto epr raggiungere l'aereoporto
            if count > 0:
                aux_airports = aux_airports.iloc[:count]
                df.loc[e,'airport_id'] = [a for a in aux_airports['Airport_ID']]
            if count == 0:
                df.loc[e,'own_airport'] = 0
                df.loc[e,'airport_id'] = aux_airports.iloc[0]['Airport_ID']

df['airport_id'] = df['airport_id'].apply(lambda x: int_to_list(x))             
#salvo il dataframe ricalcolato per evitare ulteriori inizializzazioni
df.to_csv (r'export_dataframe_1.csv', index = False, header=True)
#riprendo il dataframe già calcolato
df = pd.read_csv('export_dataframe_1.csv')
df['airport_id'] = df['airport_id'].apply(lambda x: json.loads(x))
df['airport_id'] = df['airport_id'].apply(lambda x: int_to_list(x))<jupyter_output><empty_output><jupyter_text>passo 2 associazione dei tragitti per ogni città e creazione del grafo<jupyter_code>#passo 2.1a versione "bradipo"
#calcolo la distanza di ogni tragitto
Paths_df['path_lenght'] = np.zeros(len(Paths_df.index))
for e in tqdm(Paths_df.index):
    start = Paths_df.loc[e,'Source_airport_ID']
    end = Paths_df.loc[e,'Destination_airport_ID']
    try:
        Paths_df.loc[e,'path_lenght'] = haversine_distances([[airports_df[airports_df.Airport_ID == start].Latitude_rad.item()
                                                        ,airports_df[airports_df.Airport_ID == start].Longitude_rad.item()]
                                                        ,[airports_df[airports_df.Airport_ID == end].Latitude_rad.item()
                                                          ,airports_df[airports_df.Airport_ID == end].Longitude_rad.item()]])[0][1]

    #nel caso non ci sia alcun aeroporto correlato alla tratta,sia di partenzia che di arrivo, elimino quella tratta
    except ValueError:
        Paths_df = Paths_df.loc[Paths_df.index != e]
    
#passo 2.2a versione "bradipo"
dictionary = {}
earth_diameter =12742 #[km]
Plane_Speed  = 900 #[km/h]
#per ogni città cerco al più 30 percorsi disponibili che hanno come partenza un aereoporto di quella città ordinati per distanza
for e in tqdm(df.index):
        airports_list = df.loc[e,'airport_id']
        Paths_Available = Paths_df.query('Source_airport_ID in @airports_list').sort_values(by=['path_lenght'], 
                                                                                          ascending = 1, inplace = False).iloc[0:30]
        #ci sono dei path che presentano stessa partenza e stesso arrivo ma con modelli di aereo differenti, 
        #elimino quindi i duplicati che si possono creare per questo effetto
        #trasformando i risultati in un insieme e poi di nuovo in una lista
        Paths_Available_List = list(set([h for h in Paths_Available['Destination_airport_ID']]))
        #tra tutte le città disponibili, quelle raggiungibili sono quelle che hanno come aereoporto associato un aereoporto
        #di destinazione contenuto in Paths_Avaible_List
        City_reacheable = df
        City_reacheable['Linked'] = City_reacheable['airport_id'].apply(lambda x: List_comparison(Paths_Available_List,x))
        City_reacheable = City_reacheable[City_reacheable['Linked'] == True]
        links_list = []
        start_latutude = df.loc[e,'lat']
        start_longitude = df.loc[e,'lng']
        for t in City_reacheable.index:
            #2 ore aggiuntive se la città non ha l'aeroporto
            distance = haversine_distances([[start_latutude,start_longitude]
                                            ,[City_reacheable.loc[t,'lat']
                                              ,City_reacheable.loc[t,'lng']]])[0][1]
            time = distance * (earth_diameter/2) / Plane_Speed
            time_path = 2 * (City_reacheable.loc[t,'own_airport']+df.loc[e,'own_airport']) + time 
            links_list.append((t,time_path))
        dictionary[e] = links_list    
            
#salvo il dizionario per evitare ulteriori inizializzazioni
with open("dictionary.json", "w") as outfile:
    json.dump(dictionary, outfile)
#riprendo il dizionario già calcolato
with open("dictionary.json", "r") as outfile:
    data = json.load(outfile)
dictionary = {}
for e in data.keys():
    dictionary[int(e)] = data[e]
    
# creation of the graph with Networkx library from the dictionary C 
G = nx.DiGraph()
for city in dictionary:
    G.add_node(city)

for city in dictionary:
    for near_city in dictionary[city]:
        G.add_edge(city,near_city[0],weight = near_city[1])
nx.has_path(G,536,873)
my_path=nx.shortest_path(G,536,873, weight="weight")
#ore = nx.path_weight(G,my_path, "weight")
print(my_path)
#for e in my_path:
    #print(dictionary[e],'\n')
df.loc[my_path]<jupyter_output>[536, 17805, 5325, 3659, 14372, 4403, 14239, 5750, 198, 18281, 3357, 7098, 5267, 4862, 2020, 5619, 1369, 1411, 873]
{'fraction_non_alphanumeric': 0.09881284916201118, 'fraction_numerical': 0.012569832402234637, 'mean_word_length': 3.1920965971459934, 'pattern_counts': {'":': 0, '<': 20, '<?xml version=': 0, '>': 22, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 26, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '97980', 'license': 'Unlicense', 'n_tokens_mistral': 3861, 'n_tokens_neox': 3825, 'n_words': 991, 'path': 'Progetto Cropelli_Non_Ottimizzato.ipynb', 'repo_name': 'reallybanana/Data', 'size': '11456'}