import json import pickle import pandas as pd import panel as pn import geopandas as gpd from shapely import Point import folium from branca import colormap as cm import model2vec from model2vec import StaticModel from vicinity import Vicinity, Backend, Metric from sklearn.decomposition import TruncatedSVD proximax_emploi=0.65 proximax_formation=0.68 gcantons=gpd.read_file("cantons-normandie.geojson").rename(columns={"nom": "canton"}) si=gcantons.sindex def assigne_canton(row): lon, lat=row.longitude, row.latitude if pd.isna(lon) or pd.isna(lat): return None proche=si.nearest(Point(lon, lat))[1][0] return gcantons.iloc[proche]['canton'] dfcomp=pd.read_csv("offres_emploi_competences_cantons.csv")\ .rename(columns={"competencelibelle": "Compétence"})[["canton", "Compétence"]] dfform=pd.read_csv("formations_en_normandie.csv", index_col=0)\ .rename(columns={"y_latitude": "latitude", "x_longitude": "longitude"}) certcol="intitule_certification" items_form= list(set(dfform[certcol])) dfdist=pd.read_csv("cantons_dist.csv", index_col=0).astype(int) potion128="minishlab/potion-multilingual-128M" model = StaticModel.from_pretrained(potion128) with open("sklearn_svd.pkl", "rb") as f: svd=pickle.load(f) def encode_and_project(query: str): if isinstance(query, str): query=[query] q = model.encode(query) q_reduced = svd.transform(q) return q_reduced certcol="intitule_certification" items_form= list(set(dfform[certcol])) items_emploi=dfcomp.Compétence.drop_duplicates().to_list() vice = Vicinity.from_vectors_and_items( vectors=model.encode(items_emploi), items=items_emploi, backend_type=Backend.USEARCH, metric=Metric.COSINE ) vicf = Vicinity.from_vectors_and_items( vectors=model.encode(items_form), items=items_form, backend_type=Backend.USEARCH, metric=Metric.COSINE ) query=pn.widgets.TextInput(name="Décrire la compétence recherchée et presser 'Entrée'") score=pn.indicators.Number(name="Score d'adéquation", value=2, visible=False, format="{value}/10", title_size='10pt', font_size='30pt', colors=[(3, 'red'), (7, 'orange'), (10, 'green')]) def calcul_score(dft): dft['canton']=dft.apply(assigne_canton, axis=1) dist=dfdist[:][dft.canton].min(axis=1).sum() return min(10, max(0, 10-dist//2000)) def carte(col): req=query.value if req=='': m = folium.Map(location=[49.124854, -0.0730575], zoom_start=8, tiles="CartoDB positron") score.visible=False else: test_emb=model.encode(req) selcol = [nom for (nom, dist) in vice.query(test_emb, k=200)[0] if dist"))).reset_index() gdet=gpd.GeoDataFrame(dfa.merge(gcantons, how='right')).rename(columns={"total": "total offres"}) m=gdet.explore(column="total offres", tooltip=["canton", "compétence", "total offres"], cmap="viridis", vmax=10, tiles="CartoDB positron") res_form = [nom for (nom, dist) in vicf.query(test_emb, k=50)[0] if dist{row['intitule_certification']}

{row['intitule_formation']}", icon=folium.Icon(color="red", prefix="fa", icon="fa-university") ).add_to(m) score.value=calcul_score(dft) score.visible=True return pn.pane.plot.Folium(m, height=650, width=1024) lien=pn.bind(carte, col=query) modal_text="""# Compétences à la carte. Veuillez rechercher une compétence dans le champ associé. La carte montrera pour la région Normandie les offres d'emploi de sens associé, ainsi que les offres de formation correspondantes. """ app = pn.template.MaterialTemplate( title='Compétences à la carte', header='par Solo³', main=pn.FlexBox(pn.Row(query, score), lien, height=800), modal=modal_text, ) app.open_modal() app.servable();