| <html> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>Modélisation Markovienne - Formule Article</title> | |
| <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script> | |
| <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script> | |
| <style> | |
| body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; max-width: 900px; margin: 0 auto; padding: 20px; line-height: 1.6; } | |
| h1 { border-bottom: 3px solid #2c3e50; padding-bottom: 10px; color: #2c3e50; } | |
| h2 { color: #34495e; border-bottom: 1px solid #ddd; padding-bottom: 5px; margin-top: 30px; } | |
| h3 { color: #7f8c8d; } | |
| code { background: #f4f4f4; padding: 2px 6px; border-radius: 3px; font-family: 'Consolas', monospace; font-size: 0.95em; } | |
| pre { background: #2d2d2d; color: #f8f8f2; padding: 15px; border-radius: 5px; overflow-x: auto; } | |
| pre code { background: none; padding: 0; color: inherit; } | |
| .formula { background: #ecf0f1; padding: 15px; border-radius: 8px; margin: 15px 0; border-left: 4px solid #3498db; } | |
| .note { background: #fff3cd; padding: 15px; border-radius: 5px; border-left: 4px solid #ffc107; margin: 15px 0; } | |
| .warning { background: #f8d7da; padding: 15px; border-radius: 5px; border-left: 4px solid #dc3545; margin: 15px 0; } | |
| table { border-collapse: collapse; width: 100%; margin: 15px 0; } | |
| th, td { border: 1px solid #ddd; padding: 10px; text-align: left; } | |
| th { background: #34495e; color: white; } | |
| tr:nth-child(even) { background: #f9f9f9; } | |
| .param { background: #d5f4e6; padding: 3px 8px; border-radius: 3px; font-weight: bold; } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>Modélisation Markovienne - Formule de l'Article</h1> | |
| <p>Cette version implémente <strong>exactement</strong> la formule de l'article pour calculer la matrice de transition de Markov.</p> | |
| <hr> | |
| <h2>1. Fondements Mathématiques</h2> | |
| <h3>1.1 Formule de la Matrice de Transition (Équation 8)</h3> | |
| <p>Selon l'article, la matrice de transition est calculée par:</p> | |
| <div class="formula"> | |
| \[ | |
| P_{ij} = \frac{1}{N_{LT}} \sum_{n=1}^{N_{LT}} \frac{T_{ij}(n)}{\phi(i, t_{n-1})} | |
| \] | |
| </div> | |
| <p>où:</p> | |
| <ul> | |
| <li>\(N_{LT}\) = nombre de pas de temps pour l'apprentissage (<strong>Learning Time</strong>)</li> | |
| <li>\(T_{ij}(n)\) = nombre de transitions observées de \(i \to j\) au pas de temps \(n\)</li> | |
| <li>\(\phi(i, t_{n-1})\) = nombre de particules dans l'état \(i\) au temps \(t_{n-1}\)</li> | |
| </ul> | |
| <h3>1.2 Interprétation</h3> | |
| <p>Cette formule calcule <strong>la moyenne arithmétique</strong> de matrices de transition instantanées:</p> | |
| <div class="formula"> | |
| \[ | |
| P^{(n)}_{ij} = \frac{T_{ij}(n)}{\phi(i, t_{n-1})} | |
| \] | |
| </div> | |
| <p>pour chaque pas de temps \(n\), puis:</p> | |
| <div class="formula"> | |
| \[ | |
| P_{ij} = \frac{1}{N_{LT}} \sum_{n=1}^{N_{LT}} P^{(n)}_{ij} | |
| \] | |
| </div> | |
| <h3>1.3 Différence avec l'approche précédente</h3> | |
| <div class="note"> | |
| <strong>Approche précédente:</strong> Compter TOUTES les transitions, puis diviser par le total.<br><br> | |
| <strong>Approche article:</strong> Calculer une matrice normalisée à chaque pas de temps, puis moyenner ces matrices. | |
| </div> | |
| <hr> | |
| <h2>2. Code Complet</h2> | |
| <pre><code>import polars as pl | |
| from huggingface_hub import HfFileSystem | |
| import numpy as np | |
| from tqdm import tqdm | |
| fs = HfFileSystem() | |
| folder_path = "hf://buckets/ktongue/DEM_MCM/Output Telem" | |
| files = sorted(fs.glob(f"{folder_path}/*.csv")) | |
| # ============================================ | |
| # PARAMÈTRES UTILISATEUR | |
| # ============================================ | |
| <strong>N_LT = 100</strong> # Nombre de pas de temps pour l'apprentissage | |
| nx, ny, nz = 5, 5, 5 # Discrétisation spatiale | |
| # ============================================ | |
| print(f"📁 {len(files)} fichiers disponibles") | |
| print(f"⚙️ Utilisation de N_LT = {N_LT} pas de temps pour l'apprentissage") | |
| # 1. Calcul des limites (échantillonnage rapide) | |
| print("🔍 Calcul des limites spatiales...") | |
| sample_files = files[::50] | |
| x_vals, y_vals, z_vals = [], [], [] | |
| for f in sample_files: | |
| with fs.open(f, 'rb') as file: | |
| df = pl.read_csv(file) | |
| x_vals.extend(df['x'].to_list()) | |
| y_vals.extend(df['y'].to_list()) | |
| z_vals.extend(df['z'].to_list()) | |
| xmin, xmax = min(x_vals) - 0.001, max(x_vals) + 0.001 | |
| ymin, ymax = min(y_vals) - 0.001, max(y_vals) + 0.001 | |
| zmin, zmax = min(z_vals) - 0.001, max(z_vals) + 0.001 | |
| n_states = nx * ny * nz | |
| dx = (xmax - xmin) / nx | |
| dy = (ymax - ymin) / ny | |
| dz = (zmax - zmin) / nz | |
| def get_state(x, y, z): | |
| ix = min(max(int((x - xmin) / dx), 0), nx - 1) | |
| iy = min(max(int((y - ymin) / dy), 0), ny - 1) | |
| iz = min(max(int((z - zmin) / dz), 0), nz - 1) | |
| return ix + iy * nx + iz * nx * ny | |
| # 2. Calcul de la matrice selon la formule de l'article | |
| print(f"📊 Calcul de la matrice sur {N_LT} pas de temps...") | |
| # Accumulateur pour la somme des matrices normalisées | |
| P_sum = np.zeros((n_states, n_states)) | |
| # On prend les N_LT premiers pas de temps | |
| files_to_process = files[:N_LT + 1] | |
| for i in tqdm(range(1, len(files_to_process)), desc="Learning"): | |
| # Lecture de deux fichiers consécutifs | |
| with fs.open(files_to_process[i-1], 'rb') as f: | |
| df_prev = pl.read_csv(f) | |
| with fs.open(files_to_process[i], 'rb') as f: | |
| df_curr = pl.read_csv(f) | |
| # Calculer les états pour chaque particule | |
| df_prev = df_prev.with_columns([ | |
| pl.struct(['x', 'y', 'z']).map_elements( | |
| lambda s: get_state(s['x'], s['y'], s['z']), return_dtype=pl.Int64 | |
| ).alias('state_prev') | |
| ]) | |
| df_curr = df_curr.with_columns([ | |
| pl.struct(['x', 'y', 'z']).map_elements( | |
| lambda s: get_state(s['x'], s['y'], s['z']), return_dtype=pl.Int64 | |
| ).alias('state_curr') | |
| ]) | |
| # Jointure sur les IDs pour tracker les particules | |
| merged = df_prev.select(['ID', 'state_prev']).join( | |
| df_curr.select(['ID', 'state_curr']), on='ID' | |
| ) | |
| # Comptage pour ce pas de temps n | |
| phi_counts = merged.group_by('state_prev').len().rename({'len': 'total_source'}) | |
| transitions = merged.group_by(['state_prev', 'state_curr']).len() | |
| # Calculer P_n et l'ajouter à la somme | |
| trans_with_total = transitions.join(phi_counts, on='state_prev') | |
| for row in trans_with_total.iter_rows(): | |
| state_from, state_to, count, total = row | |
| if total > 0: | |
| P_sum[state_from, state_to] += count / total | |
| # 3. Moyenne finale | |
| P = P_sum / N_LT | |
| print(f"\n✅ Matrice calculée avec N_LT = {N_LT}") | |
| print(f" Shape: {P.shape}") | |
| print(f" Vérification (somme lignes): {P.sum(axis=1)[:5]}...") | |
| # Sauvegarde | |
| np.save(f'/kaggle/working/transition_matrix_NLT_{N_LT}.npy', P) | |
| print(f"💾 Sauvegardé: transition_matrix_NLT_{N_LT}.npy")</code></pre> | |
| <hr> | |
| <h2>3. Explication Détaillée du Code</h2> | |
| <h3>3.1 Paramètres</h3> | |
| <table> | |
| <tr><th>Paramètre</th><th>Valeur</th><th>Description</th></tr> | |
| <tr><td class="param">N_LT</td><td>100</td><td>Nombre de pas de temps pour l'apprentissage</td></tr> | |
| <tr><td class="param">nx, ny, nz</td><td>5, 5, 5</td><td>Discrétisation spatiale (125 états)</td></tr> | |
| </table> | |
| <h3>3.2 Étape 1: Calcul des Limites Spatiales</h3> | |
| <pre><code>sample_files = files[::50] # Échantillonnage: 1 fichier sur 50 | |
| x_vals, y_vals, z_vals = [], [], [] | |
| for f in sample_files: | |
| with fs.open(f, 'rb') as file: | |
| df = pl.read_csv(file) | |
| x_vals.extend(df['x'].to_list()) | |
| y_vals.extend(df['y'].to_list()) | |
| z_vals.extend(df['z'].to_list())</code></pre> | |
| <p>Même principe que précédemment, mais avec un échantillonnage de 1/50 pour plus de précision.</p> | |
| <h3>3.3 Étape 2: Fonction de Discrétisation</h3> | |
| <pre><code>def get_state(x, y, z): | |
| ix = min(max(int((x - xmin) / dx), 0), nx - 1) | |
| iy = min(max(int((y - ymin) / dy), 0), ny - 1) | |
| iz = min(max(int((z - zmin) / dz), 0), nz - 1) | |
| return ix + iy * nx + iz * nx * ny</code></pre> | |
| <div class="formula"> | |
| \[ | |
| state = i_x + i_y \cdot n_x + i_z \cdot n_x \cdot n_y | |
| \] | |
| </div> | |
| <p>où:</p> | |
| <ul> | |
| <li>\(i_x = \text{clamp}\left(\left\lfloor \frac{x - x_{min}}{\Delta x} \right\rfloor, 0, n_x-1\right)\)</li> | |
| <li>Même formule pour \(i_y\) et \(i_z\)</li> | |
| </ul> | |
| <h3>3.4 Étape 3: Calcul Itératif de la Matrice</h3> | |
| <p>C'est <strong>l核心</strong> de l'implémentation qui suit la formule de l'article.</p> | |
| <h4>3.4.1 Lecture de deux fichiers consécutifs</h4> | |
| <pre><code>with fs.open(files_to_process[i-1], 'rb') as f: | |
| df_prev = pl.read_csv(f) | |
| with fs.open(files_to_process[i], 'rb') as f: | |
| df_curr = pl.read_csv(f)</code></pre> | |
| <p>À chaque itération \(n\), on lit le fichier à \(t_{n-1}\) et à \(t_n\).</p> | |
| <h4>3.4.2 Calcul des états</h4> | |
| <pre><code>df_prev = df_prev.with_columns([ | |
| pl.struct(['x', 'y', 'z']).map_elements( | |
| lambda s: get_state(s['x'], s['y'], s['z']), return_dtype=pl.Int64 | |
| ).alias('state_prev') | |
| ])</code></pre> | |
| <p>Cette opération Polars applique <code>get_state()</code> à chaque ligne du DataFrame pour créer la colonne <code>state_prev</code>.</p> | |
| <h4>3.4.3 Jointure pour tracker les particules</h4> | |
| <pre><code>merged = df_prev.select(['ID', 'state_prev']).join( | |
| df_curr.select(['ID', 'state_curr']), on='ID' | |
| )</code></pre> | |
| <div class="formula"> | |
| \[ | |
| \text{merged} = \{ (ID, state_{n-1}, state_n) \mid ID \in \text{IDs communes} \} | |
| \] | |
| </div> | |
| <h4>3.4.4 Comptage des transitions</h4> | |
| <pre><code>phi_counts = merged.group_by('state_prev').len().rename({'len': 'total_source'}) | |
| transitions = merged.group_by(['state_prev', 'state_curr']).len()</code></pre> | |
| <ul> | |
| <li><code>phi_counts</code>: Pour chaque état source \(i\), compte \(\phi(i, t_{n-1})\)</li> | |
| <li><code>transitions</code>: Pour chaque paire \((i, j)\), compte \(T_{ij}(n)\)</li> | |
| </ul> | |
| <h4>3.4.5 Accumulation</h4> | |
| <pre><code>trans_with_total = transitions.join(phi_counts, on='state_prev') | |
| for row in trans_with_total.iter_rows(): | |
| state_from, state_to, count, total = row | |
| if total > 0: | |
| P_sum[state_from, state_to] += count / total</code></pre> | |
| <div class="formula"> | |
| \[ | |
| P_{ij}^{(n)} = \frac{T_{ij}(n)}{\phi(i, t_{n-1})} | |
| \] | |
| </div> | |
| <p>Pour chaque pas de temps \(n\), on calcule la matrice instantanée et on l'ajoute à <code>P_sum</code>.</p> | |
| <h3>3.5 Étape 4: Moyenne Finale</h3> | |
| <pre><code>P = P_sum / N_LT</code></pre> | |
| <div class="formula"> | |
| \[ | |
| P_{ij} = \frac{1}{N_{LT}} \sum_{n=1}^{N_{LT}} P_{ij}^{(n)} = \frac{1}{N_{LT}} \sum_{n=1}^{N_{LT}} \frac{T_{ij}(n)}{\phi(i, t_{n-1})} | |
| \] | |
| </div> | |
| <hr> | |
| <h2>4. Choix de \(N_{LT}\)</h2> | |
| <h3>4.1 Influence du paramètre</h3> | |
| <table> | |
| <tr><th>\(N_{LT}\)</th><th>Comportement</th><th>Avantage</th><th>Inconvénient</th></tr> | |
| <tr><td>Petit (10-50)</td><td>Captures rapides, bruité</td><td>Dynamiques locales</td><td>Variance élevée</td></tr> | |
| <tr><td>Grand (100-500)</td><td>Lissé, stable</td><td>Estimation robuste</td><td>Moyenne de régimes différents</td></tr> | |
| </table> | |
| <h3>4.2 Régime Permanent</h3> | |
| <div class="note"> | |
| <strong>Recommandation de l'article:</strong> Commencer l'apprentissage une fois le <strong>régime permanent</strong> atteint (après que le système ait atteint un état stationnaire). | |
| </div> | |
| <pre><code># Pour commencer après le régime transitoire: | |
| start_index = 500 # Par exemple, après 500 pas de temps | |
| files_to_process = files[start_index : start_index + N_LT + 1]</code></pre> | |
| <hr> | |
| <h2>5. Résumé des Formules</h2> | |
| <table> | |
| <tr><th>Concept</th><th>Formule</th></tr> | |
| <tr><td>Matrice instantanée</td><td>\(P^{(n)}_{ij} = \frac{T_{ij}(n)}{\phi(i, t_{n-1})}\)</td></tr> | |
| <tr><td>Matrice finale</td><td>\(P_{ij} = \frac{1}{N_{LT}} \sum_{n=1}^{N_{LT}} P^{(n)}_{ij}\)</td></tr> | |
| <tr><td>Index d'état</td><td>\(state = i_x + i_y \cdot n_x + i_z \cdot n_x \cdot n_y\)</td></tr> | |
| <tr><td>Coordonnées</td><td>\(i_x = \left\lfloor \frac{x - x_{min}}{\Delta x} \right\rfloor\)</td></tr> | |
| </table> | |
| <hr> | |
| <h2>6. Notes d'Implémentation</h2> | |
| <ul> | |
| <li><strong>Choix de Polars</strong>: Utilisé pour les jointures et group_by efficaces sur les DataFrames</li> | |
| <li><strong>Pas de GPU</strong>: Cette version CPU est plus lente mais plus simple à comprendre</li> | |
| <li><strong>Jointure interne</strong>: Seules les particules présentes aux deux temps sont considérées</li> | |
| <li><strong>Normalisation par pas</strong>: Chaque matrice \(P^{(n)}\) est stochastique par ligne</li> | |
| </ul> | |
| </body> | |
| </html> | |
Xet Storage Details
- Size:
- 12.4 kB
- Xet hash:
- 65f72d2dfbeb9b859c26244c1bbfde4f3f3873473f5e5d44000aa3fc650394cf
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.