FScanpy-package/FScanpy_Demo.ipynb

980 lines
84 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# FScanpy \n",
"\n",
"This notebook demonstrates how to use FScanpy with real test data for complete PRF site prediction analysis, including:\n",
"\n",
"## 🎯 Complete Workflow\n",
"1. **Load Test Data** - Use built-in real test data\n",
"2. **FScanR Analysis** - Identify potential PRF sites from BLASTX results\n",
"3. **Sequence Extraction** - Extract sequences around PRF sites\n",
"4. **FScanpy Prediction** - Use machine learning models to predict probabilities\n",
"5. **Results Visualization** - Generate prediction result plots using built-in plotting functions\n",
"6. **Sequence-level Prediction Demo** - Sliding window analysis of complete sequences\n",
"\n",
"## 📊 Data Description\n",
"- **blastx_example.xlsx**: Real BLASTX alignment results\n",
"- **mrna_example.fasta**: Real mRNA sequence data\n",
"- **region_example.csv**: Sample for individual site prediction"
]
},
{
"cell_type": "markdown",
"metadata": {
"vscode": {
"languageId": "raw"
}
},
"source": [
"## 📚 FScanpy Function Usage Guide\n",
"\n",
"### Core Functions Overview\n",
"\n",
"FScanpy provides several main functions for PRF prediction:\n",
"\n",
"#### 1. `predict_prf()` - Universal Prediction Function\n",
"```python\n",
"# Single sequence prediction\n",
"results = predict_prf(sequence=\"ATGCGT...\", window_size=3, ensemble_weight=0.4)\n",
"\n",
"# Multiple sequences prediction \n",
"results = predict_prf(sequence=[\"seq1\", \"seq2\"], window_size=3)\n",
"\n",
"# DataFrame region prediction\n",
"results = predict_prf(data=df_with_399bp_column, ensemble_weight=0.4)\n",
"```\n",
"\n",
"#### 2. `plot_prf_prediction()` - Prediction with Visualization\n",
"```python\n",
"# Basic plotting\n",
"results, fig = plot_prf_prediction(sequence=\"ATGCGT...\")\n",
"\n",
"# Custom parameters\n",
"results, fig = plot_prf_prediction(\n",
" sequence=\"ATGCGT...\",\n",
" window_size=1,\n",
" short_threshold=0.65,\n",
" long_threshold=0.8,\n",
" ensemble_weight=0.4,\n",
" save_path=\"plot.png\"\n",
")\n",
"```\n",
"\n",
"#### 3. `PRFPredictor` Class Methods\n",
"```python\n",
"predictor = PRFPredictor()\n",
"\n",
"# Sliding window prediction\n",
"results = predictor.predict_sequence(sequence, window_size=3, ensemble_weight=0.4)\n",
"\n",
"# Region prediction\n",
"results = predictor.predict_regions(sequences_399bp, ensemble_weight=0.4)\n",
"\n",
"# Single position prediction\n",
"result = predictor.predict_single_position(fs_period_33bp, full_seq_399bp)\n",
"\n",
"# Plot prediction\n",
"results, fig = predictor.plot_sequence_prediction(sequence)\n",
"```\n",
"\n",
"#### 4. Utility Functions\n",
"```python\n",
"from FScanpy.utils import fscanr, extract_prf_regions\n",
"\n",
"# Detect PRF sites from BLASTX\n",
"prf_sites = fscanr(blastx_df, mismatch_cutoff=10, evalue_cutoff=1e-5)\n",
"\n",
"# Extract sequences around PRF sites\n",
"prf_sequences = extract_prf_regions(mrna_file, prf_sites)\n",
"```\n",
"\n",
"### Parameter Guidelines\n",
"\n",
"- **ensemble_weight**: 0.4 (default, balanced), 0.2-0.3 (conservative), 0.7-0.8 (sensitive)\n",
"- **window_size**: 1 (detailed), 3 (standard), 6-9 (fast)\n",
"- **short_threshold**: 0.1 (default), 0.2-0.3 (stricter filtering)\n",
"- **Display thresholds**: 0.3-0.8 for visualization filtering\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 📦 Environment Setup and Data Loading"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2025-08-14 15:54:26.764777: I external/local_xla/xla/tsl/cuda/cudart_stub.cc:31] Could not find cuda drivers on your machine, GPU will not be used.\n",
"2025-08-14 15:54:26.765259: I tensorflow/core/util/port.cc:153] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`.\n",
"2025-08-14 15:54:26.818561: I tensorflow/core/platform/cpu_feature_guard.cc:210] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.\n",
"To enable the following instructions: AVX2 AVX512F AVX512_VNNI FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/google/protobuf/runtime_version.py:98: UserWarning: Protobuf gencode version 5.28.3 is exactly one major version older than the runtime version 6.31.1 at tensorflow/core/framework/attr_value.proto. Please update the gencode to avoid compatibility violations in the next runtime release.\n",
" warnings.warn(\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/google/protobuf/runtime_version.py:98: UserWarning: Protobuf gencode version 5.28.3 is exactly one major version older than the runtime version 6.31.1 at tensorflow/core/framework/tensor.proto. Please update the gencode to avoid compatibility violations in the next runtime release.\n",
" warnings.warn(\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/google/protobuf/runtime_version.py:98: UserWarning: Protobuf gencode version 5.28.3 is exactly one major version older than the runtime version 6.31.1 at tensorflow/core/framework/resource_handle.proto. Please update the gencode to avoid compatibility violations in the next runtime release.\n",
" warnings.warn(\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/google/protobuf/runtime_version.py:98: UserWarning: Protobuf gencode version 5.28.3 is exactly one major version older than the runtime version 6.31.1 at tensorflow/core/framework/tensor_shape.proto. Please update the gencode to avoid compatibility violations in the next runtime release.\n",
" warnings.warn(\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/google/protobuf/runtime_version.py:98: UserWarning: Protobuf gencode version 5.28.3 is exactly one major version older than the runtime version 6.31.1 at tensorflow/core/framework/types.proto. Please update the gencode to avoid compatibility violations in the next runtime release.\n",
" warnings.warn(\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/google/protobuf/runtime_version.py:98: UserWarning: Protobuf gencode version 5.28.3 is exactly one major version older than the runtime version 6.31.1 at tensorflow/core/framework/full_type.proto. Please update the gencode to avoid compatibility violations in the next runtime release.\n",
" warnings.warn(\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/google/protobuf/runtime_version.py:98: UserWarning: Protobuf gencode version 5.28.3 is exactly one major version older than the runtime version 6.31.1 at tensorflow/core/framework/function.proto. Please update the gencode to avoid compatibility violations in the next runtime release.\n",
" warnings.warn(\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/google/protobuf/runtime_version.py:98: UserWarning: Protobuf gencode version 5.28.3 is exactly one major version older than the runtime version 6.31.1 at tensorflow/core/framework/node_def.proto. Please update the gencode to avoid compatibility violations in the next runtime release.\n",
" warnings.warn(\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/google/protobuf/runtime_version.py:98: UserWarning: Protobuf gencode version 5.28.3 is exactly one major version older than the runtime version 6.31.1 at tensorflow/core/framework/op_def.proto. Please update the gencode to avoid compatibility violations in the next runtime release.\n",
" warnings.warn(\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/google/protobuf/runtime_version.py:98: UserWarning: Protobuf gencode version 5.28.3 is exactly one major version older than the runtime version 6.31.1 at tensorflow/core/framework/graph.proto. Please update the gencode to avoid compatibility violations in the next runtime release.\n",
" warnings.warn(\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/google/protobuf/runtime_version.py:98: UserWarning: Protobuf gencode version 5.28.3 is exactly one major version older than the runtime version 6.31.1 at tensorflow/core/framework/graph_debug_info.proto. Please update the gencode to avoid compatibility violations in the next runtime release.\n",
" warnings.warn(\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/google/protobuf/runtime_version.py:98: UserWarning: Protobuf gencode version 5.28.3 is exactly one major version older than the runtime version 6.31.1 at tensorflow/core/framework/versions.proto. Please update the gencode to avoid compatibility violations in the next runtime release.\n",
" warnings.warn(\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/google/protobuf/runtime_version.py:98: UserWarning: Protobuf gencode version 5.28.3 is exactly one major version older than the runtime version 6.31.1 at tensorflow/core/protobuf/config.proto. Please update the gencode to avoid compatibility violations in the next runtime release.\n",
" warnings.warn(\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/google/protobuf/runtime_version.py:98: UserWarning: Protobuf gencode version 5.28.3 is exactly one major version older than the runtime version 6.31.1 at xla/tsl/protobuf/coordination_config.proto. Please update the gencode to avoid compatibility violations in the next runtime release.\n",
" warnings.warn(\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/google/protobuf/runtime_version.py:98: UserWarning: Protobuf gencode version 5.28.3 is exactly one major version older than the runtime version 6.31.1 at tensorflow/core/framework/cost_graph.proto. Please update the gencode to avoid compatibility violations in the next runtime release.\n",
" warnings.warn(\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/google/protobuf/runtime_version.py:98: UserWarning: Protobuf gencode version 5.28.3 is exactly one major version older than the runtime version 6.31.1 at tensorflow/core/framework/step_stats.proto. Please update the gencode to avoid compatibility violations in the next runtime release.\n",
" warnings.warn(\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/google/protobuf/runtime_version.py:98: UserWarning: Protobuf gencode version 5.28.3 is exactly one major version older than the runtime version 6.31.1 at tensorflow/core/framework/allocation_description.proto. Please update the gencode to avoid compatibility violations in the next runtime release.\n",
" warnings.warn(\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/google/protobuf/runtime_version.py:98: UserWarning: Protobuf gencode version 5.28.3 is exactly one major version older than the runtime version 6.31.1 at tensorflow/core/framework/tensor_description.proto. Please update the gencode to avoid compatibility violations in the next runtime release.\n",
" warnings.warn(\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/google/protobuf/runtime_version.py:98: UserWarning: Protobuf gencode version 5.28.3 is exactly one major version older than the runtime version 6.31.1 at tensorflow/core/protobuf/cluster.proto. Please update the gencode to avoid compatibility violations in the next runtime release.\n",
" warnings.warn(\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/google/protobuf/runtime_version.py:98: UserWarning: Protobuf gencode version 5.28.3 is exactly one major version older than the runtime version 6.31.1 at tensorflow/core/protobuf/debug.proto. Please update the gencode to avoid compatibility violations in the next runtime release.\n",
" warnings.warn(\n",
"2025-08-14 15:54:28.305921: I tensorflow/core/util/port.cc:153] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`.\n",
"2025-08-14 15:54:28.307332: I external/local_xla/xla/tsl/cuda/cudart_stub.cc:31] Could not find cuda drivers on your machine, GPU will not be used.\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"✅ Environment setup complete!\n",
"📋 Available test data:\n"
]
},
{
"data": {
"text/plain": [
"['blastx_example.xlsx',\n",
" 'full_seq.xlsx',\n",
" 'mrna_example.fasta',\n",
" 'region_example.csv']"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Import necessary libraries\n",
"import pandas as pd\n",
"import matplotlib.pyplot as plt\n",
"\n",
"# Import FScanpy related modules\n",
"from FScanpy import PRFPredictor, predict_prf, plot_prf_prediction\n",
"from FScanpy.data import get_test_data_path, list_test_data\n",
"from FScanpy.utils import fscanr, extract_prf_regions\n",
"\n",
"print(\"✅ Environment setup complete!\")\n",
"print(\"📋 Available test data:\")\n",
"list_test_data()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 1. Load and Explore Test Data\n",
"\n",
"First, load the real test data provided by FScanpy to understand the data structure."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"📁 Data file paths:\n",
" BLASTX data: /mnt/lmpbe/guest01/FScanpy-package-main/FScanpy/data/test_data/blastx_example.xlsx\n",
" mRNA sequences: /mnt/lmpbe/guest01/FScanpy-package-main/FScanpy/data/test_data/mrna_example.fasta\n",
" Validation regions: /mnt/lmpbe/guest01/FScanpy-package-main/FScanpy/data/test_data/region_example.csv\n",
"\n",
"🧬 BLASTX data overview:\n",
" Data shape: (1000, 14)\n",
" Column names: ['DNA_seqid', 'Pep_seqid', 'pident', 'length', 'mismatch', 'gapopen', 'qstart', 'qend', 'sstart', 'send', 'evalue', 'bitscore', 'qframe', 'sframe']\n",
" Unique sequences: 704\n",
"\n",
"📊 BLASTX data examples:\n",
" DNA_seqid Pep_seqid pident length evalue qframe\n",
"0 MSTRG.9998.1 CAMPEP_0196994412 68.27 104 1.000000e-33 2\n",
"1 MSTRG.9996.1 CAMPEP_0197017426 49.16 297 3.000000e-79 2\n",
"2 MSTRG.9994.1 CAMPEP_0197009206 98.31 354 0.000000e+00 2\n",
"3 MSTRG.9993.1 CAMPEP_0168331218 51.67 60 2.000000e-37 2\n",
"4 MSTRG.9993.1 CAMPEP_0168331218 45.45 88 2.000000e-37 3\n"
]
}
],
"source": [
"# Get test data paths\n",
"blastx_file = get_test_data_path('blastx_example.xlsx')\n",
"mrna_file = get_test_data_path('mrna_example.fasta')\n",
"region_file = get_test_data_path('region_example.csv')\n",
"\n",
"print(f\"📁 Data file paths:\")\n",
"print(f\" BLASTX data: {blastx_file}\")\n",
"print(f\" mRNA sequences: {mrna_file}\")\n",
"print(f\" Validation regions: {region_file}\")\n",
"\n",
"# Load BLASTX data\n",
"blastx_data = pd.read_excel(blastx_file)\n",
"print(f\"\\n🧬 BLASTX data overview:\")\n",
"print(f\" Data shape: {blastx_data.shape}\")\n",
"print(f\" Column names: {list(blastx_data.columns)}\")\n",
"print(f\" Unique sequences: {blastx_data['DNA_seqid'].nunique()}\")\n",
"\n",
"# Display first few rows\n",
"print(\"\\n📊 BLASTX data examples:\")\n",
"display_cols = ['DNA_seqid', 'Pep_seqid', 'pident', 'length', 'evalue', 'qframe']\n",
"print(blastx_data[display_cols].head())"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"🎯 Validation region data overview:\n",
" Data shape: (3, 8)\n",
" Column names: ['FS_period', '399bp', 'fs_position', 'DNA_seqid', 'label', 'source', 'FS_type', 'dataset']\n",
" Data sources: {'EUPLOTES': 3}\n",
"\n",
"📋 Validation region data examples:\n",
" fs_position DNA_seqid label source FS_type\n",
"0 16.0 MSTRG.18491.1 0 EUPLOTES negative\n",
"1 16.0 MSTRG.4662.1 0 EUPLOTES negative\n",
"2 16.0 MSTRG.14742.1 0 EUPLOTES negative\n",
"\n",
"📈 Label distribution:\n",
"label\n",
"0 3\n",
"Name: count, dtype: int64\n",
"\n",
"🔬 FS type distribution:\n",
"FS_type\n",
"negative 3\n",
"Name: count, dtype: int64\n"
]
}
],
"source": [
"# Load validation region data\n",
"region_data = pd.read_csv(region_file)\n",
"print(f\"🎯 Validation region data overview:\")\n",
"print(f\" Data shape: {region_data.shape}\")\n",
"print(f\" Column names: {list(region_data.columns)}\")\n",
"print(f\" Data sources: {region_data['source'].value_counts().to_dict()}\")\n",
"\n",
"print(\"\\n📋 Validation region data examples:\")\n",
"display_cols = ['fs_position', 'DNA_seqid', 'label', 'source', 'FS_type']\n",
"print(region_data[display_cols].head())\n",
"\n",
"# Statistical analysis\n",
"print(f\"\\n📈 Label distribution:\")\n",
"print(region_data['label'].value_counts())\n",
"print(f\"\\n🔬 FS type distribution:\")\n",
"print(region_data['FS_type'].value_counts())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2. FScanR Analysis - Identify Potential PRF Sites from BLASTX\n",
"\n",
"Use the FScanR algorithm to analyze BLASTX results and identify potential programmed ribosomal frameshift sites."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"🔍 Running FScanR analysis...\n",
"Parameter settings: mismatch_cutoff=10, evalue_cutoff=1e-5, frameDist_cutoff=10\n",
"\n",
"✅ FScanR analysis complete!\n",
"Number of potential PRF sites detected: 16\n",
"\n",
"📊 FScanR results overview:\n",
" Column names: ['DNA_seqid', 'FS_start', 'FS_end', 'Pep_seqid', 'Pep_FS_start', 'Pep_FS_end', 'FS_type', 'Strand']\n",
" Number of sequences involved: 12\n",
" Strand orientation distribution: {'+': 11, '-': 5}\n",
" FS type distribution: {1: 9, -1: 7}\n",
"\n",
"🎯 FScanR results examples:\n",
" DNA_seqid FS_start FS_end Pep_seqid Pep_FS_start \\\n",
"0 MSTRG.9380.1 3797 3802 CAMPEP_0197017206 1137 \n",
"1 MSTRG.9582.1 302 304 CAMPEP_0197003180 214 \n",
"2 MSTRG.961.1 1536 1533 CAMPEP_0197017908 590 \n",
"3 MSTRG.9622.1 555 560 CAMPEP_0197016962 182 \n",
"4 MSTRG.9648.1 801 803 CAMPEP_0197001104 257 \n",
"\n",
" Pep_FS_end FS_type Strand \n",
"0 1138 1 + \n",
"1 214 1 + \n",
"2 19 -1 - \n",
"3 183 1 + \n",
"4 257 1 + \n"
]
}
],
"source": [
"# Run FScanR analysis\n",
"print(\"🔍 Running FScanR analysis...\")\n",
"print(\"Parameter settings: mismatch_cutoff=10, evalue_cutoff=1e-5, frameDist_cutoff=10\")\n",
"\n",
"fscanr_results = fscanr(\n",
" blastx_data,\n",
" mismatch_cutoff=10,\n",
" evalue_cutoff=1e-5,\n",
" frameDist_cutoff=10\n",
")\n",
"\n",
"print(f\"\\n✅ FScanR analysis complete!\")\n",
"print(f\"Number of potential PRF sites detected: {len(fscanr_results)}\")\n",
"\n",
"if len(fscanr_results) > 0:\n",
" print(f\"\\n📊 FScanR results overview:\")\n",
" print(f\" Column names: {list(fscanr_results.columns)}\")\n",
" print(f\" Number of sequences involved: {fscanr_results['DNA_seqid'].nunique()}\")\n",
" print(f\" Strand orientation distribution: {fscanr_results['Strand'].value_counts().to_dict()}\")\n",
" print(f\" FS type distribution: {fscanr_results['FS_type'].value_counts().to_dict()}\")\n",
" \n",
" print(\"\\n🎯 FScanR results examples:\")\n",
" print(fscanr_results.head())\n",
"else:\n",
" print(\"⚠️ No PRF sites detected, may need to adjust parameters\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 3. Sequence Extraction - Extract Sequences Around PRF Sites\n",
"\n",
"Extract sequence fragments around PRF sites identified by FScanR from mRNA sequences."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"📝 Extracting sequences around PRF sites from mRNA sequences...\n",
"\n",
"✅ Sequence extraction complete!\n",
"Number of successfully extracted sequences: 16\n",
"\n",
"📏 Sequence length validation:\n",
" 399bp sequence length distribution: {399: 16}\n",
" Average length: 399.0\n",
"\n",
"🧬 Extracted sequence examples:\n",
"Sequence 1: MSTRG.9380.1\n",
" FS position: 3797-3802\n",
" Strand orientation: +\n",
" FS type: 1\n",
" Sequence fragment: AAGGAGTTTGAAGAAGAACAGGAAAAACAAGAGAAAGAGAGAAAGGAGAA...NNNNNNNNNNNNNNNNNNNN\n",
"\n",
"Sequence 2: MSTRG.9582.1\n",
" FS position: 302-304\n",
" Strand orientation: +\n",
" FS type: 1\n",
" Sequence fragment: ATCAAGCTGATTAGAGATGGAGGGGGAGGTGTGTTCAATAATATATCTAC...AGTCAACTTCCAGTCCAACA\n",
"\n",
"Sequence 3: MSTRG.961.1\n",
" FS position: 1536-1533\n",
" Strand orientation: -\n",
" FS type: -1\n",
" Sequence fragment: ATGCTACTTTGGGAGAGAAAATTAACTGGGGAGAACTTGCATATGATTCT...ACAAATATTTCTCTAATTCA\n",
"\n"
]
}
],
"source": [
"# Extract sequences around PRF sites\n",
"if len(fscanr_results) > 0:\n",
" print(\"📝 Extracting sequences around PRF sites from mRNA sequences...\")\n",
" \n",
" prf_sequences = extract_prf_regions(\n",
" mrna_file=mrna_file,\n",
" prf_data=fscanr_results\n",
" )\n",
" \n",
" print(f\"\\n✅ Sequence extraction complete!\")\n",
" print(f\"Number of successfully extracted sequences: {len(prf_sequences)}\")\n",
" \n",
" if len(prf_sequences) > 0:\n",
" print(f\"\\n📏 Sequence length validation:\")\n",
" seq_lengths = prf_sequences['399bp'].str.len()\n",
" print(f\" 399bp sequence length distribution: {seq_lengths.value_counts().to_dict()}\")\n",
" print(f\" Average length: {seq_lengths.mean():.1f}\")\n",
" \n",
" print(\"\\n🧬 Extracted sequence examples:\")\n",
" for i, row in prf_sequences.head(3).iterrows():\n",
" print(f\"Sequence {i+1}: {row['DNA_seqid']}\")\n",
" print(f\" FS position: {row['FS_start']}-{row['FS_end']}\")\n",
" print(f\" Strand orientation: {row['Strand']}\")\n",
" print(f\" FS type: {row['FS_type']}\")\n",
" print(f\" Sequence fragment: {row['399bp'][:50]}...{row['399bp'][-20:]}\")\n",
" print()\n",
" else:\n",
" print(\"❌ Sequence extraction failed\")\n",
"else:\n",
" print(\"⚠️ Skipping sequence extraction - no FScanR results\")\n",
" prf_sequences = pd.DataFrame()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 4. FScanpy Prediction - Machine Learning Model Analysis\n",
"\n",
"Use FScanpy's machine learning models to predict PRF probabilities for the extracted sequences."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/mnt/lmpbe/guest01/FScanpy-package-main/FScanpy/predictor.py:23: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.\n",
" from pkg_resources import resource_filename\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/sklearn/base.py:380: InconsistentVersionWarning: Trying to unpickle estimator _BinMapper from version 1.6.0 when using version 1.6.1. This might lead to breaking code or invalid results. Use at your own risk. For more info please refer to:\n",
"https://scikit-learn.org/stable/model_persistence.html#security-maintainability-limitations\n",
" warnings.warn(\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/sklearn/base.py:380: InconsistentVersionWarning: Trying to unpickle estimator HistGradientBoostingClassifier from version 1.6.0 when using version 1.6.1. This might lead to breaking code or invalid results. Use at your own risk. For more info please refer to:\n",
"https://scikit-learn.org/stable/model_persistence.html#security-maintainability-limitations\n",
" warnings.warn(\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"🤖 FScanpy predictor initialization complete\n",
"\n",
"🎯 Predicting 16 sequences identified by FScanR...\n",
"\n",
"📊 FScanR+FScanpy prediction results:\n",
" DNA_seqid FS_start FS_type Short_Probability Long_Probability \\\n",
"0 MSTRG.9380.1 3797 1 0.239192 0.087024 \n",
"1 MSTRG.9582.1 302 1 0.272451 0.223354 \n",
"2 MSTRG.961.1 1536 -1 0.263269 0.046773 \n",
"3 MSTRG.9622.1 555 1 0.652591 0.408316 \n",
"4 MSTRG.9648.1 801 1 0.287211 0.308532 \n",
"\n",
" Ensemble_Probability \n",
"0 0.147891 \n",
"1 0.242993 \n",
"2 0.133372 \n",
"3 0.506026 \n",
"4 0.300004 \n"
]
}
],
"source": [
"# Initialize predictor\n",
"predictor = PRFPredictor()\n",
"print(\"🤖 FScanpy predictor initialization complete\")\n",
"\n",
"# Predict FScanR identified sequences\n",
"if len(prf_sequences) > 0:\n",
" print(f\"\\n🎯 Predicting {len(prf_sequences)} sequences identified by FScanR...\")\n",
" \n",
" fscanr_predictions = predictor.predict_regions(\n",
" sequences=prf_sequences['399bp'],\n",
" ensemble_weight=0.4 # Balanced configuration\n",
" )\n",
" \n",
" # Merge results\n",
" fscanr_predictions = pd.concat([\n",
" prf_sequences.reset_index(drop=True),\n",
" fscanr_predictions.reset_index(drop=True)\n",
" ], axis=1)\n",
" \n",
" print(\"\\n📊 FScanR+FScanpy prediction results:\")\n",
" result_cols = ['DNA_seqid', 'FS_start', 'FS_type', 'Short_Probability', 'Long_Probability', 'Ensemble_Probability']\n",
" print(fscanr_predictions[result_cols].head())"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"🧪 Predicting 3 validation regions...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/sklearn/base.py:380: InconsistentVersionWarning: Trying to unpickle estimator _BinMapper from version 1.6.0 when using version 1.6.1. This might lead to breaking code or invalid results. Use at your own risk. For more info please refer to:\n",
"https://scikit-learn.org/stable/model_persistence.html#security-maintainability-limitations\n",
" warnings.warn(\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/sklearn/base.py:380: InconsistentVersionWarning: Trying to unpickle estimator HistGradientBoostingClassifier from version 1.6.0 when using version 1.6.1. This might lead to breaking code or invalid results. Use at your own risk. For more info please refer to:\n",
"https://scikit-learn.org/stable/model_persistence.html#security-maintainability-limitations\n",
" warnings.warn(\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"📊 Validation region prediction results:\n",
" DNA_seqid label source Short_Probability Long_Probability \\\n",
"0 MSTRG.18491.1 0 EUPLOTES 0.368610 0.144442 \n",
"1 MSTRG.4662.1 0 EUPLOTES 0.229811 0.053352 \n",
"2 MSTRG.14742.1 0 EUPLOTES 0.454152 0.345118 \n",
"\n",
" Ensemble_Probability \n",
"0 0.234109 \n",
"1 0.123936 \n",
"2 0.388732 \n"
]
}
],
"source": [
"# Predict validation region data\n",
"print(f\"\\n🧪 Predicting {len(region_data)} validation regions...\")\n",
"\n",
"validation_predictions = predict_prf(\n",
" data=region_data.rename(columns={'399bp': 'Long_Sequence'}),\n",
" ensemble_weight=0.4\n",
")\n",
"\n",
"print(\"\\n📊 Validation region prediction results:\")\n",
"result_cols = ['DNA_seqid', 'label', 'source', 'Short_Probability', 'Long_Probability', 'Ensemble_Probability']\n",
"print(validation_predictions[result_cols].head())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 5. Sequence-level Prediction and Visualization\n",
"\n",
"Select a specific mRNA sequence and use the built-in plot_prf_prediction function for complete sliding window prediction and visualization."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"🧬 Selected demonstration sequence: MSTRG.9127.1\n",
"Sequence length: 256 bp\n",
"First 100bp of sequence: TGGCCTTCTTACTTGGAAGTCCCCAAGGATCATCTTGGCCATCCTTGCTTTCTTCATGGCTAGATTCTACCTCCTCCCATAATTGTGTGAAACAAGTAAC...\n",
"\n",
"🎯 Using plot_prf_prediction for sequence prediction and visualization...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/sklearn/base.py:380: InconsistentVersionWarning: Trying to unpickle estimator _BinMapper from version 1.6.0 when using version 1.6.1. This might lead to breaking code or invalid results. Use at your own risk. For more info please refer to:\n",
"https://scikit-learn.org/stable/model_persistence.html#security-maintainability-limitations\n",
" warnings.warn(\n",
"/home/guest01/.conda/envs/fs/lib/python3.9/site-packages/sklearn/base.py:380: InconsistentVersionWarning: Trying to unpickle estimator HistGradientBoostingClassifier from version 1.6.0 when using version 1.6.1. This might lead to breaking code or invalid results. Use at your own risk. For more info please refer to:\n",
"https://scikit-learn.org/stable/model_persistence.html#security-maintainability-limitations\n",
" warnings.warn(\n",
"/mnt/lmpbe/guest01/FScanpy-package-main/FScanpy/predictor.py:347: UserWarning: This figure includes Axes that are not compatible with tight_layout, so results might be incorrect.\n",
" plt.tight_layout()\n"
]
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABR8AAALtCAYAAAChPBNAAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8ekN5oAAAACXBIWXMAAA9hAAAPYQGoP6dpAABxwElEQVR4nOzdd5gV5dk/8HsX2AWWqjRBZK0YlG5UMHYi1ogmhthQYgy2iGKDBEUkSuxoNMES2xsVxJrEFkUsifhqxLUCYsVIF5GiUnbn94c/zuuyCywLw9kDn8917cWe50y5Z3aeOct3n5nJS5IkCQAAAACADSw/2wUAAAAAAJsm4SMAAAAAkArhIwAAAACQCuEjAAAAAJAK4SMAAAAAkArhIwAAAACQCuEjAAAAAJAK4SMAAAAAkArhIwAAAACQCuEjAKzB888/H3l5ebFgwYKIiLjrrruiSZMm67XMDbGMXHHppZdGly5dUlluy5YtIy8vLx599NENvnzYlOyzzz5x3333ZbuMtSouLo5Ro0ZluwzWw5577hkPPfRQtssAoIYRPgJQbSeffHLk5eVFXl5eFBQUxA477BCXXXZZrFixIiL+L7hb+dW8efM49NBD4+23317tcr7/9cEHH1S63lWX27Jly/jpT38aH330Uerb3Ldv33j//ferPH1l/5le12VU13777ZfZR3Xr1o2ddtopRo4cGUmSpL7u1Tn55JOjT58+67WMyZMnx/Dhw+OWW26JmTNnxiGHHLJhivv/3nzzzfjJT34SLVq0iLp160ZxcXH07ds35syZs0HXk6tW9r+mTZvGt99+W+691157LXPMfd9tt90WnTt3jgYNGkSTJk2ia9euMXLkyIj4ro9U1v9Xfp188skREeXaGjVqFD/84Q/jscceq1DfsmXL4uqrr45u3bpFUVFRNG7cODp37hxDhw6NGTNmrHHbHnjggejSpUvUr18/2rVrF1dffXW592fOnBnHHXdc7LTTTpGfnx/nnHNOhWXcdtttsffee0fTpk2jadOm0atXr3j11VfLTbO6bV11fd/34osvxhFHHBGtW7dep9D9b3/7W8yePTt+8YtfZNq+v89r1aoVrVu3jlNOOSW+/PLLKi2zOhYuXBi/+93vYuedd466detGq1atolevXvHwww9vlHPSfvvtV+nPa2OtJ40/OtXEP2QNHTo0Bg8eHGVlZdkuBYAaRPgIwHo5+OCDY+bMmTFt2rQ477zz4tJLL63wH+ipU6fGzJkz4+mnn46lS5fGYYcdFsuWLat0Od//2nbbbde47qlTp8aMGTNi3Lhx8e6778YRRxwRpaWlFaZLkiQTiK6vevXqRYsWLbK+jKo69dRTY+bMmTF16tQYMmRIXHLJJTF69OiNsu60fPjhhxERceSRR0arVq2isLCwWstZvnx5hba5c+fGgQceGFtssUU8/fTTMXny5LjzzjujdevWsWTJkvWqe1PTsGHDeOSRR8q1/eUvf4ltttmmXNsdd9wR55xzTpx99tlRUlIS//73v+PCCy+MxYsXR8R3geXKPr9yxNTKc8bMmTPjhhtuyCzrzjvvjJkzZ8Z//vOf2GuvveJnP/tZuT9mLF26NH784x/HFVdcESeffHK8+OKL8fbbb8eNN94Y8+bNiz/+8Y+r3Z4nn3wyjj/++DjttNPinXfeiT/96U9x/fXXx0033VRu+c2bN4+hQ4dG586dK13O888/H8cee2xMmDAhJk6cGG3bto2DDjooPv/888w0q57r7rjjjsjLy4uf/vSnq61vyZIl0blz57j55ptXO01lbrzxxujfv3/k55f/tf+yyy6LmTNnxvTp0+Pee++NF198Mc4+++x1WvaqVj2vr7RgwYLo2bNn3HPPPTFkyJCYNGlSvPjii9G3b9+48MIL46uvvlqv9VanpqoqLi6O559/fsMUsxk45JBDYtGiRfHkk09muxQAapIEAKrppJNOSo488shybT/+8Y+TPffcM0mSJJkwYUISEcmXX36Zef9vf/tbEhHJm2++ucblrElly7333nuTiEimTJmSef+JJ55IunXrltSpUyeZMGFCUlpamlxxxRVJcXFxUrdu3aRTp07JuHHjyi378ccfT3bcccekbt26yX777Zfceeed5dZ15513Jo0bNy43z9/+9rdkt912SwoLC5Mtt9wy6dOnT5IkSbLvvvsmEVHua3XL+NOf/pRst912SZ06dZKddtopueeee8q9HxHJbbfdlvTp0yepV69essMOOySPPfbYGvfTvvvumwwcOLBcW7du3ZKjjjoq8/rbb79NzjvvvKR169ZJ/fr1k9133z2ZMGFC5v1PPvkkOfzww5MmTZok9evXTzp06JA8/vjjq92ORx55JPn+rxfDhg1LOnfunPl+1f0xYcKEZOnSpcmZZ56ZtGrVKiksLEy22Wab5Iorrqh0mypbRpIkSWlpaTJ8+PCkTZs2SUFBQdK5c+fkySefzMz38ccfJxGRjBkzJtlnn32SwsLC5M4776yw/EceeSSpXbt2snz58jXu27fffjs5+OCDk6KioqRFixbJCSeckMydOzfz/uLFi5MTTzwxKSoqSlq1apVcc801FX4eEZE88sgj5ZbbuHHjcnVNnz49OeaYY5LGjRsnTZs2TX7yk58kH3/8ceb9lX3n6quvTlq1apVsscUWyRlnnJEsW7YsM823336bXHjhhcnWW2+dFBQUJNtvv31y++23V3lbVrWyfw0dOjTp1atXpv3rr79OGjdunFx88cXljoEjjzwyOfnkk9e4P1dd9vf79kqr7q+FCxcmEZHccMMNmbaRI0cm+fn5yaRJkypdfllZ2WrXfeyxxyY/+9nPyrXdeOONydZbb13pfJX1r8qsWLEiadiwYXL33XevdpojjzwyOeCAA9a6rJUqO3YqM2fOnCQvLy955513yrW3a9cuuf7668u1jRgxIunQoUPm9bx585Jf/OIXSevWrZN69eolu+66a3LfffeVm2ffffdNzjzzzGTgwIHJlltumey3336V1nH66acnRUVFyeeff17hvUWLFmX6W7t27ZLLL7886d+/f9KgQYOkbdu2yS233FJu+gsvvDDZcccdk3r16iXbbrttMnTo0HLH+8pzzm233ZYUFxcneXl5yUknnVThvPH9frQm7dq1K3dOXJvVHReVnS8fffTRpGvXrklhYWGy7bbbJpdeemm5c8+1116b7Lrrrkn9+vWTrbfeOjn99NOTRYsWJUnyf33l+1/Dhg3L1DxixIjMOWibbbZJHnvssWTOnDnJT37yk6SoqCjp2LFj8tprr2XWtS4/7zPPPDNp1KhRsuWWWyZDhw6t0D/69++fnHDCCVXeZwBs+ox8BGCDqlev3mpHmnz11VcxZsyYiIgoKCjY4OuNKD/KZfDgwfGHP/whJk+eHJ06dYqRI0fGPffcE6NHj4533303zj333DjhhBPihRdeiIiIzz77LI4++ug44ogjoqSkJH71q1/F4MGD17jexx9/PI466qg49NBD44033ojx48fH7rvvHhERDz/8cGy99daZEUYzZ86sdBmPPPJIDBw4MM4777x45513YsCAAdG/f/+YMGFCuemGDx8eP//5z+Ott96KQw89NI4//viYP39+lfZPkiTx0ksvxZQpU8rt+7POOismTpwYY8aMibfeeiuOOeaYOPjgg2PatGkREXHmmWfG0qVLMyPIrrzyymjQoEGV1rmq888/P37+85+XG+Xas2fPuPHGG+Nvf/tbPPDAAzF16tS49957o7i4eLXLuPPOOyMiyu3TG264Ia699tq45ppr4q233orevXvHT37yk8x2rDR48OAYOHBgTJ48OXr37l1h+a1atYoVK1bEI488stpLQRcsWBAHHHBAdO3aNf7zn//EU089FbNnz46f//znmWkuuOCCeOGFF+Kxxx6Lf/7zn/H888/HpEmT1ml/LV++PHr37h0NGzaMl156Kf79739HgwYN4uCDDy53nE+YMCE+/PDDmDBhQtx9991x1113xV133ZV5v1+/fnH//ffHjTfeGJMnT45bbrkl8zOsyraszoknnhgvvfRSTJ8+PSIiHnrooSguLo5u3bqVm65Vq1bxyiuvxKeffrpO278mK1asiL/85S8RUf5ccv/998ePf/zj6Nq1a6XzrXo5+PctXbo06tatW66tXr168d///ne9av/6669j+fLlscUWW1T6/uzZs+Pxxx+PU045pdrrWJ1//etfUb9+/fjBD36wxuk+//zz+Pvf/x577LFHpu3bb7+N7t27x+OPPx7vvPNO/PrXv44TTzyxwiXkd999dxQUFMS///3vSkdVl5WVxZgxY+L444+P1q1bV3i/QYMGUbt27czra6+9Nnbbbbd444034owzzojTTz89pk6dmnm/YcOGcdddd8V7770XN9xwQ9x2221x/fXXl1vmBx98EA899FA8/PDDUVJSEjfccEP06NEjMxJ85syZ0bZt2zXvvJS99NJL0a9fvxg4cGC89957ccstt8Rdd90Vl19+eWaa/Pz8uPHGG+Pdd9+Nu+++O5577rm48MILIyKiZ8+eMWrUqGjUqFFmm84///zMvNdff33stdde8cYbb8Rhhx0WJ554YvTr1y9OOOGEmDRpUmy//fbRr1+/zHluXX7etWvXjldffTVuuOGGuO666+L2228vN83uu+8eL730Ulq7DoBclOXwE4Ac9v0Ri2VlZckzzzyTFBYWJueff36SJP83MqOoqCgpKirKjM74yU9+UmE5tWrVykxXVFRUYQTS9606OmrGjBlJz549kzZt2iRLly7NvP/oo49m5vn222+T+vXrJy+//HK5ZZ1yyinJsccemyRJkgwZMqTcyJ8kSZKLLrpojSMfe/TokRx//PGrrbWyEUarLqNnz57JqaeeWm6aY445Jjn00EMzr+P/jzRbafHixUlElBvdt6p99903qVOnTlJUVJTUqVMniYikbt26yb///e8kSZLk008/TWrVqlVhNNKBBx6YDBkyJEmSJOnYsWNy6aWXVrr8dR35mCSVj3L9zW9+kxxwwAFrHJW2pnUkSZK0bt06ufzyy8u1/fCHP0zOOOOMJEn+b+TjqFGj1rr83/72t0nt2rWTLbbYIjn44IOTq666Kpk1a1bm/REjRiQHHXRQuXk+++yzJCKSqVOnJosWLUoKCgqSBx54IPP+F198kdSrV2+dRj7+z//8T9K+ffty+2Xp0qVJvXr1kqeffjpJku/2Z7t27ZIVK1ZkpjnmmGOSvn37JkmSJFOnTk0iInnmmWcq3da1bUtlvt//+vTpkwwfPjxJkiTZf//9kxtuuKHCz2fGjBnJnnvumUREstNOOyUnnXRSMnbs2KS0tHSNy17VyuO3qKgoyc/PTyIiKS4uTr744ovMNHXr1k3OPvvscvP16dMnc17p0aNHpduUJElyyy23JPXr10+effbZpLS0NJk6dWqy8847JxFR4byRJFUf+Xj66acn2223XfLNN99U+v6VV16ZNG3adLXvV6ayY6cy119/fbLddttVaG/Xrl1SUFCQFBUVJXXr1k0iItljjz0q3e/fd9hhhyXnnXde5vW+++6bdO3adY3zzJ49O4mI5Lrrrltrve3atSs3Yq6srCxp0aJF8uc//3m181x99dVJ9+7dM6+HDRuW1KlTJ5kzZ0656ar686qspnUd+bjyvPv9r8LCwnLnywMPPLDCCO//+Z//SbbaaqvVLnvcuHHJlltumXld2Tl4Zc3f348zZ85MIiK5+OKLM20TJ05MIiKZOXPmatdX2c/7Bz/4Qblz0kUXXZT84Ac/KDffY489luTn51faxwHYPBn5CMB6+cc//hENGjSIunXrxiGHHBJ9+/aNSy+9tNw0L730Urz++utx1113xU477VTp6Jj9998/SkpKMl833njjWte99dZbR1FRUeZ+fA899FC5UVC77bZb5vsPPvggvv766/jxj38cDRo0yHzdc889mXsITp48udzIn4iIHj16rLGGkpKSOPDAA9da65pMnjw59tprr3Jte+21V0yePLlcW6dOnTLfFxUVRaNGjdb6EJTjjz8+c5+9Qw45JH73u99Fz549IyLi7bffjtLS0thpp53K7ZMXXnghs0/OPvvs+P3vfx977bVXDBs2LN5666312tbKnHzyyVFSUhLt27ePs88+O/75z3+u0/wLFy6MGTNmVGkffv+YWJ3LL788Zs2aFaNHj45ddtklRo8eHTvvvHPm3oJvvvlmTJgwodw+23nnnSPiu/tRfvjhh7Fs2bJyx9IWW2wR7du3X6ftevPNN+ODDz6Ihg0bZtazxRZbxLfffpv5+URE7LLLLlGrVq3M66222ipzXJSUlEStWrVi3333Xe061rQta/PLX/4y7rrrrvjoo49i4sSJcfzxx1eYZquttoqJEyfG22+/HQMHDowVK1bESSedFAcffPA6P5Ti+uuvj5KSknjyySejQ4cOcfvtt692ROFKf/rTn6KkpCR++ctfxtdff73a6U499dQ466yz4vDDD4+CgoLYc889Mw9pWfV+iVX1hz/8IcaMGROPPPJIhVGVK91xxx1x/PHHr/b99fHNN9+sdrkXXHBBlJSUxFtvvRXjx4+PiIjDDjssc9/c0tLSGDFiRHTs2DG22GKLaNCgQTz99NOZka4rde/efY01JOv4MJnvn+fy8vKiVatW5c5zY8eOjb322itatWoVDRo0iKFDh1aoqV27dtG8efN1Wu9Kp512Wrn+MH369DjkkEPKta3NyvPu978uu+yyctO8+eabcdlll5Vb7sqRmSuP02effTYOPPDAaNOmTTRs2DBOPPHE+OKLL9Z4HK/0/f3YsmXLiIjo2LFjhbaV+7aqP+8999yz3AjiHj16xLRp08rdb7levXpRVlYWS5cuXWudAGweaq99EgBYvf333z/+/Oc/R0FBQbRu3brc5XMrbbvtttGkSZNo3759zJkzJ/r27RsvvvhiuWmKiopihx12WKd1v/TSS9GoUaNo0aJFNGzYsML7RUVFme9XPtzi8ccfjzZt2pSbrroPLIn4v8u9N4Y6deqUe52Xl7fW8KZx48aZ/frAAw/EDjvsEHvuuWf06tUrFi9eHLVq1YrXX3+9XHgVEZn/YP/qV7+K3r17x+OPPx7//Oc/Y+TIkXHttdfGb37zm8jPz68QLFT2EJe16datW3z88cfx5JNPxrPPPhs///nPo1evXvHggw+u87LW5vvHxJpsueWWccwxx8QxxxwTV1xxRXTt2jWuueaauPvuu2Px4sVxxBFHxJVXXllhvq222mq1T2lfVV5e3hr33+LFi6N79+5x7733Vpj3+8HKmo6LtR2fa9uWtTnkkEPi17/+dZxyyilxxBFHxJZbbrnaaXfdddfYdddd44wzzojTTjst9t5773jhhRdi//33X+t6VmrVqlXssMMOscMOO8Sdd94Zhx56aLz33nuZBzjtuOOO5S7R/f52rC2kzMvLiyuvvDKuuOKKmDVrVjRv3jwTym233XZVrnGla665Jv7whz/Es88+Wy4I+r6XXnoppk6dGmPHjl3n5VdFs2bNVvsE62bNmmXODTvuuGOMGjUqevToERMmTIhevXrF1VdfHTfccEOMGjUqOnbsGEVFRXHOOedUuK3G2vpU8+bNo0mTJjFlypQq1bym43llwD18+PDo3bt3NG7cOMaMGRPXXnvtOtW0Jpdddlm5y5f322+/uPLKKyv8YWpNvn/eXWnVh4wtXrw4hg8fHkcffXSF+evWrRuffPJJHH744XH66afH5ZdfHltssUX861//ilNOOSWWLVsW9evXX2MN39+PK8PCytpW7tuq/ryrYv78+VFUVLRRPx8BqNmMfARgvawMDbfZZptKg8dVnXnmmfHOO+9UeEpudWy77bax/fbbVxo8rqpDhw5RWFgY06dPz4QXK79W3vvrBz/4QYX7W73yyitrXG6nTp0yAUVlCgoKKn0C9/f94Ac/iH//+9/l2v79739Hhw4d1jjfumrQoEEMHDgwzj///EiSJLp27RqlpaUxZ86cCvukVatWmfnatm0bp512Wjz88MNx3nnnxW233RYR34UKixYtKvcU6JKSkjXWsLr90ahRo+jbt2/cdtttMXbs2HjooYeqfD/LRo0aRevWrVPbhwUFBbH99ttntrNbt27x7rvvRnFxcYX9VlRUFNtvv33UqVMn/vd//zezjC+//DLef//9cstt3rx5ufuATps2rdyIpm7dusW0adOiRYsWFdbTuHHjKtXesWPHKCsry9zXdFVr25a1qV27dvTr1y+ef/75+OUvf1mlmiIi83NZnyeI77777tG9e/dy98g79thj45lnnok33nij2sutVatWtGnTJgoKCuL++++PHj16rPMouquuuipGjBgRTz311BpH2/7lL3+J7t27r/bJ2eura9euMWvWrNUGkN+38g8Q33zzTUR813+OPPLIOOGEE6Jz586x3XbbVTiGqyI/Pz9+8YtfxL333hszZsyo8P7ixYtjxYoVVVrWyy+/HO3atYvf/e53sdtuu8WOO+5Y5ftxVuVcHBEV+lvt2rWjTZs25do2hG7dusXUqVMr9Lsddtgh8vPz4/XXX4+ysrK49tprY88994yddtqpwv6r6jZVRVV/3t8/r0V89xm54447lvsD1jvvvLPa+64CsHkSPgKwUdWvXz9OPfXUGDZs2Dpfjrc+GjZsGOeff36ce+65cffdd8eHH34YkyZNij/+8Y9x9913R8R3l9tNmzYtLrjggpg6dWrcd9995R7cUZlhw4bF/fffH8OGDYvJkydnHsqyUnFxcbz44ovx+eefx7x58ypdxgUXXBB33XVX/PnPf45p06bFddddFw8//HC50TcbyoABA+L999+Phx56KHbaaac4/vjjo1+/fvHwww/Hxx9/HK+++mqMHDkyHn/88YiIOOecc+Lpp5+Ojz/+OCZNmhQTJkzIPLxijz32iPr168dvf/vb+PDDD6u0v4qLi+Ott96KqVOnxrx582L58uVx3XXXxf333x9TpkyJ999/P8aNGxetWrWKJk2aVHm7Lrjggrjyyitj7NixMXXq1Bg8eHCUlJTEwIED12n//OMf/4gTTjgh/vGPf8T7778fU6dOjWuuuSaeeOKJOPLIIyPiuwB9/vz5ceyxx8Zrr70WH374YTz99NPRv3//KC0tjQYNGsQpp5wSF1xwQTz33HPxzjvvxMknn1zh0t0DDjggbrrppnjjjTfiP//5T5x22mnlRiYdf/zx0axZszjyyCPjpZdeio8//jief/75OPvss+O///1vlbanuLg4TjrppPjlL38Zjz76aGYZDzzwQJW2pSpGjBgRc+fOrfQBPhERp59+eowYMSL+/e9/x6effhqvvPJK9OvXL5o3b77W2xqszTnnnBO33HJLfP755xERce6550aPHj3iwAMPjBtuuCEmTZoUH3/8cTz99NPx5JNPlgtIbrrppnK3TJg3b16MHj06pkyZkjl2xo0bF6NGjSq3zpWX0S5evDjmzp0bJSUl8d5772Xev/LKK+Piiy+OO+64I4qLi2PWrFkxa9aszOjrlRYuXBjjxo2LX/3qV5Vu24EHHhg33XRT5vXixYsz646I+Pjjj6OkpKTCZbHf17Vr12jWrFmFYD4iYtGiRTFr1qyYOXNmvPrqq3HBBRdE8+bNM7dl2HHHHeOZZ56Jl19+OSZPnhwDBgyI2bNnr3Zda3L55ZdH27ZtY4899oh77rkn3nvvvZg2bVrccccd0bVr1wr7ZnV23HHHmD59eowZMyY+/PDDuPHGG6v8h6zi4uL43//93/jkk09i3rx563zJ/4Z2ySWXxD333BPDhw+Pd999NyZPnhxjxoyJoUOHRkTEDjvsEMuXL48//vGP8dFHH8X//M//VLhlSXFxcSxevDjGjx8f8+bNq9Ll2KtT1Z/39OnTY9CgQTF16tS4//77449//GOF8+xLL70UBx10ULVrAWATlNU7TgKQ0yp7eMj3re7hEdOnT09q166djB07tkrLqepy1/Z+WVlZMmrUqKR9+/ZJnTp1kubNmye9e/dOXnjhhcw0f//735MddtghKSwsTPbee+/kjjvuWOMDZ5IkSR566KGkS5cuSUFBQdKsWbPk6KOPzrw3ceLEpFOnTklhYWHmIRyVLeNPf/pTst122yV16tRJdtppp+See+4p936s5eEklVndAxYGDBiQ7LLLLklpaWmybNmy5JJLLkmKi4uTOnXqJFtttVVy1FFHJW+99VaSJEly1llnJdtvv31SWFiYNG/ePDnxxBOTefPmZZb1yCOPJDvssENSr1695PDDD09uvfXWNT5wZs6cOcmPf/zjpEGDBklEJBMmTEhuvfXWpEuXLklRUVHSqFGj5MADD0wmTZq02u2q7IEzpaWlyaWXXpq0adMmqVOnTtK5c+dyD+NZ+cCZN954Y7XLTZIk+fDDD5NTTz012WmnnZJ69eolTZo0SX74wx9W2M/vv/9+ctRRRyVNmjRJ6tWrl+y8887JOeeck3kQw6JFi5ITTjghqV+/ftKyZcvkqquuqvDz+Pzzz5ODDjooKSoqSnbcccfkiSeeqPAznTlzZtKvX7+kWbNmSWFhYbLddtslp556avLVV18lSVJ53xk4cGCy7777Zl5/8803ybnnnptstdVWSUFBQbLDDjskd9xxR5W3ZVVr63+r/nwefPDB5NBDD82sv3Xr1slPf/rTzDFW1WVX1gfKysqSnXfeOTn99NMzbd9++23yhz/8IencuXNSr169pLCwMNl5552Tc889N5k+fXpmumHDhiXt2rXLvJ47d26y5557JkVFRUn9+vWTAw88MHnllVcqrWPVr+8vp127dpVOM2zYsHLLueWWW5J69eolCxYsqHQ/tmvXrtw8K/fNql8nnXRSpfOvdOGFFya/+MUvKiz7+8to3rx5cuihh5brH1988UVy5JFHJg0aNEhatGiRDB06NOnXr1+5421dHuKyYMGCZPDgwcmOO+6YFBQUJC1btkx69eqVPPLII5ljrbIHdHXu3LncfrjggguSLbfcMmnQoEHSt2/f5Prrry93Pl31nLPS1KlTkz333DOpV69eEhHJxx9/XKW6q/PAmcr2SWXn/aeeeirp2bNnUq9evaRRo0bJ7rvvntx6662Z96+77rpkq622SurVq5f07t07ueeeeyr0j9NOOy3Zcsstyx1jle3HVfvPqufEqv68zzjjjOS0005LGjVqlDRt2jT57W9/W+5c8d///jepU6dO8tlnn1V5nwGw6ctLko047AQAYDO13377RZcuXSqMpIM0zZo1K3bZZZeYNGlStGvXLtvlkMOqcg676KKL4ssvv4xbb7114xUGQI3nsmsAANhEtWrVKv7yl7+s8fJs2FBatGgRI0aMyHYZANQwnnYNAACbsD59+mS7BDYT5513XrZLAKAGctk1AAAAAJAKl10DAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CALBBFRcXR/v27aNLly7RpUuX+NWvfhURES+88EL06NEjunTpEh06dIi99torZs+eXeXljh49Oq6++uqIiCgpKYkxY8akUj8AABtOXpIkSbaLAABg01FcXByPPvpodOnSJdO2YsWKaNGiRTz77LPRrVu3iIiYOnVqtGnTJho0aLDO67jrrrvi0UcfjUcffXQDVQ0AQBqMfAQAIHWLFi2KhQsXRqtWrTJt7du3rzR4nDZtWuy1117RuXPn6NixYwwdOjQiIi699NI455xzYs6cOXHJJZfEhAkTokuXLnHaaadFRMRrr70WBxxwQOy2227RtWvXGDdu3MbZOAAAVqt2tgsAAGDT07dv36hXr15ERAwbNiyOOuqoOOuss6J9+/ax9957R48ePaJv376x0047VZj3pptuisMPPzyGDBkSERHz588v936LFi3isssuKzfyccGCBfHrX/86nnjiidhqq61i3rx50a1bt+jZs2e0adMm3Y0FAGC1hI8AAGxwY8eOLXfZdUTEqFGj4txzz40JEybE+PHjo2vXrvH000/Hj370o3LT7bPPPnHBBRfE4sWLY999941evXqtdX0vv/xyfPTRR3HIIYeUa195aTcAANkhfAQAYKNp165dnHzyyXHyySdHUVFRPPDAAxXCx5/+9KfRs2fPeOaZZ+Kmm26KUaNGxRNPPLHG5SZJErvssku8/PLLaZYPAMA6cs9HAABSt3jx4njyySdj5bMOv/nmm5g8eXJsv/32FaadNm1atGzZMvr16xdXXXVVvPLKKxWmadSoUXz11VeZ1z179oyPP/44nn322UxbSUlJLFu2LIWtAQCgqoSPAACkLkmSGD16dLRv3z46d+4c3bt3j+7du8eZZ55ZYdoHH3wwOnbsGF27do2+ffvG6NGjK0xz4IEHxtKlS6NTp05x2mmnRdOmTePxxx+PK664Ijp37hwdOnSIwYMHR1lZ2cbYPAAAViMvWfnnZwAAAACADWizu+djWVlZzJgxIxo2bBh5eXnZLgcAAAAAckqSJLFo0aJo3bp15Oev+cLqzS58nDFjRrRt2zbbZQAAAABATvvss89i6623XuM0m1342LBhw4j4buc0atQo4uuv1jIHAAAAALDSwhV50bZt20zOtiabXfi48lLrRo0afRc+1nbLSwAAAACoshXf5WtVuaWhp10DAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CAAAAAKkQPgIAAAAAqRA+AgAAAACpED4CAAAAAKkQPgIAAAAAqaid7QI2tiRJIiJi4cKF3zV8vTCL1QAAAABAblm4Ii8i/i9nW5PNLnxctGhRRES0bds2y5UAAAAAQO5atGhRNG7ceI3T5CVViSg3IWVlZTFjxoxo2LBh5OXlZbscAAAAAMgpSZLEokWLonXr1pGfv+a7Om524SMAAAAAsHF44AwAAAAAkArhIwAAAACQCuEjAAAAAJAK4SMAAAAAkArhIwAAAACQCuEjAAAAAJAK4SMAAAAAkArhIwAAAACQCuEjAAAAAJAK4SMAAAAAkArhIwAAAACQCuEjAAAAAJAK4SMAAAAAkArhIwAAAACQCuEjAAAAAJAK4SMAAAAAkArhIwAAAACQCuEjAAAAAJAK4SMAAAAAkArhIwAAAACQCuEjAAAAAJAK4SMAAAAAkArhIwAAAACQCuEjAAAAAJAK4SMAAFVWXFwc7du3jy5dukSHDh3i5ptvXq/lvfPOO1FcXBwRETNmzIi99957rfOMGjUqZs2alXk9evTouPrqq9erDgAA0pGXJEmS7SIAAMgNxcXF8eijj0aXLl3i008/jU6dOsVLL70UnTp1ioiIsrKyiIjIz6/a37jfeeedOPzww+OTTz6pVg0AANRstbNdAAAAualdu3bRvn37OO6446J9+/axePHi+Oyzz+KZZ56Jd955J0aMGBHffPNN1KpVK6688srYf//9IyLi0ksvjXvvvTcaNWoUhxxySGZ5n3zySXTp0iUWLFgQERETJ06MCy64IBYtWhRJksSIESPizTffjBkzZkTfvn2jXr16cdddd8Wjjz4aCxYsiFGjRkVpaWkMHjw4nnzyyYiI2H///ePaa6+NgoKCOPnkk6OwsDA++OCD+Oyzz2LXXXeNMWPGREFBwUbfdwAAmwuXXQMAUC1vv/12TJkyJTp37hwTJ06Me+65J957771YunRpXHrppfHEE0/E66+/Hvfdd18cd9xxsXTp0nj88cdj3Lhx8frrr8d//vOf1Y54nD9/fvTp0ydGjhwZb775ZpSUlMTee+8dl1xySbRu3TrGjh0bJSUlFUY/3nrrrfHaa6/F66+/HiUlJfHhhx/G9ddfn3m/pKQk/v73v8fkyZNj9uzZ8dBDD6W4hwAAMPIRAIB1snLUYf369eOOO+6Id955J+rVqxctW7aMiIinnnoqPvjgg9hnn30y8+Tn58f06dNj/Pjx8fOf/zwaNWoUEREDBgyIf/3rXxXWMXHixGjfvn3mHpD5+fmxxRZbrLW2Z599NjPCMSLi1FNPjZtvvjkuuuiiiIg46qijon79+hERsfvuu8eHH364HnsCAIC1ET4CALBOxo4dW27E4TvvvBMNGjTIvE6SJH784x/Hfffdt9Zl5eXlpVHiapdft27dzPe1atWKFStWpLp+AIDNncuuAQDYoHr37h3PPvtsvPXWW5m2V199NSIievXqFePGjcvcx/HWW2+tdBk9e/aMadOmxUsvvRQR3z3IZv78+RER0ahRo/jqq68qna9Xr15xzz33xLJly2LFihVx++23x0EHHbQhNw8AgHVg5CMAABvUDjvsEPfdd18MGDAgvv7661i2bFl07do17rvvvjj00EPj1VdfjW7dulV44Mz3NW3aNB555JE477zzYtGiRZGfnx8jRoyII444Is4+++w49dRTo379+nHXXXeVm+/Xv/51fPjhh9GtW7eIiNhvv/3inHPOSXmLAQBYnbwkSZJsFwEAAAAAbHo2u5GPZWVlMWPGjGjYsGHq9xgCAAAAgE1NkiSxaNGiaN26deTnr/mujptd+Dhjxoxo27ZttssAAAAAgJz22WefxdZbb73GaTa78LFhw4YR8d3OadSoUZROGFNhmlp7VH7voc3RihuHVGirffbIqs37x99WnPc3V6x3TWko/edfK7TVOuiELFQCNVvpxH9UaKvV4/B01/n3v1Rc5xGnpLrOXFP6yJ/Lva511OlVm2/cTRXaah1z1lrnW/H7Myu01R56c5XWueLPl1Sc9/TL1jpf6ct/q9BWq+dPqrbOm4dWXOeZv1/7Ou+/vuI6jz23Suus7udK6RN3VZzv0JOrts4JYyvOu3/ftc634p4/VGir3W9w1db57P0V19nr2LXPN+nZivN161W1dT5Xye9uB/witfkiIkpfeLDivPv+bO3zPVPxad+1fnxcldZZXdk4T+eaVY+/Kh97b4yv0Far64EbpKY0rHrcVuWYjYgoff2fFdpqdU/3oU3VPZdky6rn6iqfp/9+e4W2Wkf8agNUVLOUvv1ihbZaHffJQiWQvvX5HbX0P09XnHe33muf783nK87Xeb8qrXNDWrgiL9q2bZvJ2dZkswsfV15q3ahRo+/Cx6J6Faap1ajRxi6rxlpRt6BCW+0q7p/1mXdjK61ft0Kb4wAqysY5U/9cu9J65fdRVfdPab3CCm1VmXdFYZ0KbWl/NqzPsVftdVZz/0RU/7hdn+O9tH719tGKuhW3s6o/z2pvZ1H9as333Tqrt53VnW/91rnxz19+t127VY+/Kh8H63HcZsOqx21N3s5c+6xftd6qf+7m1nZWV671FVgf6/O5W92+UmP62Irv8rWq3NJwzRdlAwAAAABUk/ARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEhF7WwXsLElSRIREQsXLoyIiNIl31SYptb/f4+IFd8uq9BWu4r7Z33m3dhKv/62QpvjACrKxjlT/1y70m/K76Oq7p/Sb5ZWaKvKvCuWLq/QlvZnw/oce9VeZzX3T0T1j9v1Od5Lv67ePlrxbcXtrOrPs9rbueTras333Tqrt53VnW/91rnxz19+t127VY+/Kh8H63HcZsOqx21N3s5c+6xftd6qf+7m1nZWV671FVgf6/O5W92+UlP62MIVeRHxfznbmuQlVZlqE/Lf//432rZtm+0yAAAAACCnffbZZ7H11luvcZrNLnwsKyuLGTNmRMOGDSMvLy/b5QAAAABATkmSJBYtWhStW7eO/Pw139VxswsfAQAAAICNwwNnAAAAAIBUCB8BAAAAgFQIHwEAAACAVAgfAQAAAIBUCB8BAAAAgFQIHwEAAACAVAgfAQAAAIBUCB8BAAAAgFQIHwEAAACAVAgfAQAAAIBUCB8BAAAAgFQIHwEAAACAVAgfAQAAAIBUCB8BAAAAgFQIHwEAAACAVAgfAQAAAIBUCB8BAAAAgFQIHwEAAACAVAgfAQAAAIBUCB8BAAAAgFQIHwEAAACAVAgfAQAAAIBUCB8BAAAAgFQIHwEAAACAVAgfAQAAAIBUCB8BAAAAgFQIHwEAAACAVAgfAQAAAIBUCB8BAAAAgFQIHwEAAACAVGQ1fHzxxRfjiCOOiNatW0deXl48+uija53n+eefj27dukVhYWHssMMOcdddd6VeJwAAAACw7rIaPi5ZsiQ6d+4cN998c5Wm//jjj+Owww6L/fffP0pKSuKcc86JX/3qV/H000+nXCkAAAAAsK7ykiRJsl1EREReXl488sgj0adPn9VOc9FFF8Xjjz8e77zzTqbtF7/4RSxYsCCeeuqpSudZunRpLF26NPO6rKws5s+fH1tuuWXk5eVtsPoBAAAAYHOQJEksWrQoWrduHfn5ax7bWHsj1bRBTJw4MXr16lWurXfv3nHOOeesdp6RI0fG8OHDU64MAAAAADYvn332WWy99dZrnCanwsdZs2ZFy5Yty7W1bNkyFi5cGN98803Uq1evwjxDhgyJQYMGZV5/9dVXsc0228Snn34ajRo1iojvRkPOmzcvmjVrtta0Fqh59GHIffox5DZ9GHKbPgy5LRt9eOHChdGuXbto2LDhWqfNqfCxOgoLC6OwsLBCe5MmTcqFj8uWLYsmTZo40UIO0och9+nHkNv0Ycht+jDktmz04ZXrqcotDXPqrNKqVauYPXt2ubbZs2dHo0aNKh31CAAAAABkT06Fjz169Ijx48eXa3vmmWeiR48eWaoIAAAAAFidrIaPixcvjpKSkigpKYmIiI8//jhKSkpi+vTpEfHd/Rr79euXmf60006Ljz76KC688MKYMmVK/OlPf4oHHnggzj333GyUDwAAAACsQVbDx//85z/RtWvX6Nq1a0REDBo0KLp27RqXXHJJRETMnDkzE0RGRGy77bbx+OOPxzPPPBOdO3eOa6+9Nm6//fbo3bt3VuoHAAAAAFYvqw+c2W+//SJJktW+f9ddd1U6zxtvvJFiVQAAAADAhpBT93wEAAAAAHKH8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEiF8BEAAAAASIXwEQAAAABIhfARAAAAAEhF1sPHm2++OYqLi6Nu3bqxxx57xKuvvrrG6UeNGhXt27ePevXqRdu2bePcc8+Nb7/9diNVCwAAAABUVVbDx7Fjx8agQYNi2LBhMWnSpOjcuXP07t075syZU+n09913XwwePDiGDRsWkydPjr/85S8xduzY+O1vf7uRKwcAAAAA1iar4eN1110Xp556avTv3z86dOgQo0ePjvr168cdd9xR6fQvv/xy7LXXXnHcccdFcXFxHHTQQXHssceudbQkAAAAALDx1c7WipctWxavv/56DBkyJNOWn58fvXr1iokTJ1Y6T8+ePeOvf/1rvPrqq7H77rvHRx99FE88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9/j888/j6quvXm34WFhYGIWFhRXa8/Pzy/1A8vLyKrQBuUMfhtynH0Nu04cht+nDkNs2dh9el/VkLXxs1qxZ1KpVK2bPnl2uffbs2dGqVatK59lqq62iTp065S6x/sEPfhCzZs2KZcuWRUFBQao1AwAAAABVl7U/aRQUFET37t1j/PjxmbaysrIYP3589OjRo9J59tprr/jggw/K3dTy/fffj6222krwCAAAAAA1TFbHUw8aNChuu+22uPvuu2Py5Mlx+umnx5IlSzJPv+7Xr1+5B9KcfvrpMX/+/Bg4cGC8//778fjjj8cVV1wRZ555ZrY2AQAAAABYjaze87Fv374xd+7cuOSSS2LWrFnRpUuXeOqppzIPoZk+fXq5a8jbtm0bTz/9dJx77rnRqVOnaNOmTQwcODAuuuiibG0CAAAAALAaWX/gzFlnnRVnnXVWpe89//zzFdp69OgRr7zySspVAQAAAADry2OsAAAAAIBUCB8BAAAAgFQIHwEAAACAVAgfAQAAAIBUVCt8nDBhwoauAwAAAADYxFQrfDz44INj++23j9///vfx2WefbeiaAAAAAIBNQLXCx88//zzOOuusePDBB2O77baL3r17xwMPPBDLli3b0PUBAAAAADmqWuFjs2bN4txzz42SkpL43//939hpp53ijDPOiNatW8fZZ58db7755oauEwAAAADIMev9wJlu3brFkCFD4qyzzorFixfHHXfcEd27d4+999473n333Q1RIwAAAACQg6odPi5fvjwefPDBOPTQQ6Ndu3bx9NNPx0033RSzZ8+ODz74INq1axfHHHPMhqwVAAAAAMghtasz029+85u4//77I0mSOPHEE+Oqq66KXXfdNfN+UVFRXHPNNdG6desNVigAAAAAkFuqFT6+99578cc//jGOPvroKCwsrHSaZs2axYQJE9arOAAAAAAgd1Xrsuthw4bFMcccUyF4XLFiRbz44osREVG7du3Yd999179CAAAAACAnVSt83H///WP+/PkV2r/66qvYf//917soAAAAACD3VSt8TJIk8vLyKrR/8cUXUVRUtN5FAQAAAAC5b53u+Xj00UdHREReXl6cfPLJ5S67Li0tjbfeeit69uy5YSsEAAAAAHLSOoWPjRs3jojvRj42bNgw6tWrl3mvoKAg9txzzzj11FM3bIUAAAAAQE5ap/DxzjvvjIiI4uLiOP/8811iDQAAAACs1jqFjysNGzZsQ9cBAAAAAGxiqhw+duvWLcaPHx9NmzaNrl27VvrAmZUmTZq0QYoDAAAAAHJXlcPHI488MvOAmT59+qRVDwAAAACwiahy+Pj9S61ddg0AAAAArE1+tgsAAAAAADZNVR752LRp0zXe5/H75s+fX+2CAAAAAIBNQ5XDx1GjRqVYBgAAAACwqaly+HjSSSelWQcAAAAAsImpcvi4cOHCaNSoUeb7NVk5HQAAAACw+Vqnez7OnDkzWrRoEU2aNKn0/o9JkkReXl6UlpZu0CIBAAAAgNxT5fDxueeeiy222CIiIiZMmJBaQQAAAADApqHK4eO+++5b6fcAAAAAAJWpcvi4qi+//DL+8pe/xOTJkyMiokOHDtG/f//M6EgAAAAAYPOWX52ZXnzxxSguLo4bb7wxvvzyy/jyyy/jxhtvjG233TZefPHFDV0jAAAAAJCDqjXy8cwzz4y+ffvGn//856hVq1ZERJSWlsYZZ5wRZ555Zrz99tsbtEgAAAAAIPdUa+TjBx98EOedd14meIyIqFWrVgwaNCg++OCDDVYcAAAAAJC7qhU+duvWLXOvx++bPHlydO7ceb2LAgAAAAByX5Uvu37rrbcy35999tkxcODA+OCDD2LPPfeMiIhXXnklbr755vjDH/6w4asEAAAAAHJOlcPHLl26RF5eXiRJkmm78MILK0x33HHHRd++fTdMdQAAAABAzqpy+Pjxxx+nWQcAAAAAsImpcvjYrl27NOsAAAAAADYxVQ4fK/Pee+/F9OnTY9myZeXaf/KTn6xXUQAAAABA7qtW+PjRRx/FUUcdFW+//Xa5+0Dm5eVFRERpaemGqxAAAAAAyEn51Zlp4MCBse2228acOXOifv368e6778aLL74Yu+22Wzz//PMbuEQAAAAAIBdVa+TjxIkT47nnnotmzZpFfn5+5Ofnx49+9KMYOXJknH322fHGG29s6DoBAAAAgBxTrZGPpaWl0bBhw4iIaNasWcyYMSMivnsozdSpUzdcdQAAAABAzqrWyMddd9013nzzzdh2221jjz32iKuuuioKCgri1ltvje22225D1wgAAAAA5KBqhY9Dhw6NJUuWRETEZZddFocffnjsvffeseWWW8bYsWM3aIEAAAAAQG6qVvjYu3fvzPc77LBDTJkyJebPnx9NmzbNPPEaAAAAANi8VSt8/L7PPvssIiLatm273sUAAAAAAJuOaj1wZsWKFXHxxRdH48aNo7i4OIqLi6Nx48YxdOjQWL58+YauEQAAAADIQdUa+fib3/wmHn744bjqqquiR48eERExceLEuPTSS+OLL76IP//5zxu0SACg5hgwYECFtltuuSULlQAAADVdtcLH++67L8aMGROHHHJIpq1Tp07Rtm3bOPbYY4WPAAAAAED1LrsuLCyM4uLiCu3bbrttFBQUrG9NAAAAAMAmoFrh41lnnRUjRoyIpUuXZtqWLl0al19+eZx11lkbrDgAAAAAIHdV+bLro48+utzrZ599Nrbeeuvo3LlzRES8+eabsWzZsjjwwAM3bIUAAAAAQE6qcvjYuHHjcq9/+tOflnvdtm3bDVMRAAAAALBJqHL4eOedd6ZZBwAAAACwianW065Xmjt3bkydOjUiItq3bx/NmzffIEUBAAAAALmvWg+cWbJkSfzyl7+MrbbaKvbZZ5/YZ599onXr1nHKKafE119/vaFrBAAAAAByULXCx0GDBsULL7wQf//732PBggWxYMGCeOyxx+KFF16I8847b0PXCAAAAADkoGpddv3QQw/Fgw8+GPvtt1+m7dBDD4169erFz3/+8/jzn/+8oeoDAAAAAHJUtUY+fv3119GyZcsK7S1atHDZNQAAAAAQEdUMH3v06BHDhg2Lb7/9NtP2zTffxPDhw6NHjx4brDgAAAAAIHdV67LrUaNGxcEHHxxbb711dO7cOSIi3nzzzahbt248/fTTG7RAAAAAACA3VSt87NixY0ybNi3uvffemDJlSkREHHvssXH88cdHvXr1NmiBAAAAAEBuWufwcfny5bHzzjvHP/7xjzj11FPTqAkAAAAA2ASs8z0f69SpU+5ejwAAAAAAlanWA2fOPPPMuPLKK2PFihUbuh4AAAAAYBNRrXs+vvbaazF+/Pj45z//GR07doyioqJy7z/88MMbpDgAAAAAIHdVK3xs0qRJ/PSnP93QtQAAAAAAm5B1Ch/Lysri6quvjvfffz+WLVsWBxxwQFx66aWecA0AAAAAVLBO4ePll18el156afTq1Svq1asXN954Y8ydOzfuuOOOtOojywYMGFCh7ZZbbslCJQAAAADkmnUKH++5557405/+lAmknn322TjssMPi9ttvj/z8aj27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0Ich9+nHkNv0Ycht+jDktpreh7P6tOuVbrrpprj66qtj1qxZ0aVLl7jxxhtjjz32iIiI/fbbL4qLi+Ouu+7KTD9u3LgYOnRofPLJJ7HjjjvGVVddFYceemiWqgcAAAAAKlMjwkcAAAAAYNOT1Xs+AgAAAACbLuEjAAAAAJAK4SMAAAAAkArhIwAAAACQCuEjAAAAAJCKzTp8LCsri9LS0myXAQBUIkmSSJIk22UA1aQPAwAREbWzXUC2vPfee3HFFVfErFmzYscdd4wTTzwxevbsme2ygA0kSZLIy8vLdhlANSxdujQKCwtjxYoVUadOnWyXA6wjfRhy2yeffBLPPPNM5OfnR9u2beOggw7KdknAOqiJfTgv2Qz/HDl16tTYY4894pBDDoni4uJ48skno06dOnHiiSfG2Wefne3ygHXwwQcfxIMPPhhfffVVdOrUKY444oho0KBBRAggIRe9++67cfHFF8eiRYuiVq1a8dvf/jb23HPPKCgoyHZpQBXow5Db3n777dh///1jxx13jLlz58bs2bPjF7/4RVx22WWx1VZbZbs8YC1qah/e7C67TpIk7rnnnujdu3fcf//9MXLkyHjppZeiT58+ceedd8ZVV12V7RKBKnr33Xfjhz/8YTz11FPx8ssvR79+/eLkk0+Op59+OiIi8vLyXO4FOWTatGnRs2fPaN68eXTt2jUaNmwY++23X1xxxRUxffr0bJcHrIU+DLlt8eLFMWDAgDjuuONi4sSJ8a9//SvGjRsXDz/8cPzyl7+MDz/8MNslAmtQk/vwZnfZdV5eXsyYMSNmzZqVaWvYsGGcffbZUbdu3RgzZky0adMmjj/++CxWCazNN998E4MHD47jjz8+brrppoiImDRpUgwYMCCuueaa+Prrr+Ooo44y8hFyyD333BN77rln3HLLLZm2P/7xjzF8+PD49ttv49xzz42WLVtmsUJgTfRhyG21a9eOpUuXxl577RUREa1atYqDDz44Jk6cGHvttVecf/758eCDD0atWrWyXClQmZrchzerkY8rR0B169YtSktLY+rUqZn3GjZsGL/85S+ja9eu8ac//Sm+/vrrbJUJVEG9evVi/vz50axZs4j47gFS3bp1i//5n/+JFStWxK233hpvvvlmlqsE1sU333yT+X7FihUREfGb3/wmLr/88rjpppvikUceiYjv+jtQ8+jDkNtKS0tj9uzZ5f6fvHz58thpp51i/Pjx8cwzz8TIkSOzWCGwOqWlpTW6D29W4ePKEVCHHnpoTJ06Na666qpYvHhxRHwXTDZt2jQuvvjimDhxYrz44ovZLBVYjZX/YVm0aFEUFhbGnDlzIuK7PrxixYrYeeed4+abb4533nkn7rzzzmyWCqyjbbbZJiZOnBgzZsyI2rVrx7JlyyIiYsCAAXHhhRfGBRdcEJ999lnk529Wv75AzmjXrp0+DDmsqKgoBg0aFLfddlv84x//iIiIOnXqxPLly6NTp04xZMiQ+Mc//hHz5893ayOoIRYsWBAREbVq1YqioqI477zzamQf3iw/+bfffvt44IEH4t57743BgwfHvHnzMsFknTp1olOnTtG4ceMsVwmsqqSkJI488shYsmRJNGzYMM4444wYPXp0PPzww1GrVq3Iz8+P5cuXR4cOHeKqq66Ke+65xz2mIIecdtpp0bVr1/jpT38aX3zxRRQUFMS3334bERG//vWvo2nTpvGf//wny1UCK33wwQfx2muvZV7/6le/iu7du+vDkCNmzpwZr776ajz99NNRWloaERFHH3109OjRI6666qr45z//GRGReWp9s2bNYuHChVG3bl23NoIaoKSkJI444oh46623Mm2HHnpo7LXXXjWuD2+W4WNExP777x/jxo2L22+/PQYMGBBjx46NyZMnxw033BBz5syJtm3bZrtE4HvefPPN6NmzZ+yyyy5RVFQUERF9+vSJM888M4477rj4+9//Hvn5+ZkTa5MmTaJVq1aZaYGa5f3334+LLroo+vfvHzfccENMmzYtCgoKYtiwYVFWVhZ9+/aN+fPnR926dSMiorCwMIqKijJ9HMiukpKS6N69e5SUlGTa6tWrF+eff37k5eXpw1DDvfXWW9GjR4848cQTo2/fvrHLLrtknn9w4YUXRuPGjWPo0KExZsyYiPju0s2PPvooWrRokQkqgex58803Y/fdd48ePXpEp06dMu3t27ePU045JZo2bVqj+nBespmPl540aVIMGjQoPvnkk6hdu3bUqlUrxowZE127ds12acD/99Zbb0XPnj3jjDPOKPdE+tLS0liwYEEMGzYsbr311rjxxhujT58+0aRJkxg+fHg8+eSTMWHChGjatGkWqwdW9d5770XPnj2jR48eUVRUFM8++2x07949Tj755DjxxBPjH//4R4wYMSLmzp0bo0ePjjp16sRzzz0Xt99+e/zv//5vbLPNNtneBNisrfyD4GmnnRbXXnttufdKS0vjkUceiWuuuSbmzJmjD0MNNHfu3Nhnn33i6KOPjlNOOSXq1q0bgwYNijfeeCOOP/74uOiii2LKlCkxevTouP3222OXXXaJevXqxdSpU+O5556LLl26ZHsTYLP27rvvxg9/+MO44IILYvjw4ZEkSXz55Zfx5Zdfxvbbbx8REa+88krcfffdNaYPb/bhY0TEwoULY/78+bFo0aLYaqutMg+wALJv1qxZ0bVr1+jcuXM89dRTUVpaGueff35MnTo1Pv300zj99NNj1113jbfffjvOP//8aNOmTTRs2DBmzpwZTz/9tD8kQA2zbNmyOOWUU6JevXpx6623RsR3l24OHTo0Pvroo/jVr34Vv/71r2Py5MkxYsSIePbZZ6Np06ZRp06duOeee6Jbt25Z3gLYvE2bNi06duwY559/fvz+97+P5cuXx1NPPRWzZs2KZs2axRFHHBG1a9eOd999Ny6//HJ9GGqg9957Lw477LB48MEHo3v37pn2wYMHxz/+8Y/o379/DBo0KL7++ut4++2349lnn43mzZvHgQceGDvssEMWKwe++OKL2HPPPaNhw4YxadKkiIj45S9/GW+99VbMmDEjtt9++7jpppuic+fOsXjx4njnnXdqRB8WPgI12qxZs+KMM86Izz77LIYOHRqjR4+O5cuXR5cuXeKbb76Jp59+Ovbff/8YNWpUfPjhhzFlypRIkiT23HPPaNeuXbbLBypx0EEHxbbbbhu33HJLJEkSeXl5MX369Bg2bFhMmzYtfve738UhhxwSERFTpkyJRo0aRUFBgT8OQpatWLEizjvvvLj33ntj9OjR8bOf/SwOO+yw+O9//xsLFy6M6dOnR58+feLSSy+Njh07RoQ+DDXRm2++GYcffnjcd999sffee8c333wT9erVi4iIgQMHxmOPPRZ/+9vfyl3KCdQcv/nNb6KkpCQOPPDAeOKJJ2LLLbeMo48+Opo3bx5XXXVV/Pe//43nnnuuRv2xQPgI1HgzZ86MwYMHx7hx4+JHP/pR3H///bHllltGRMS9994bZ555Zvz1r3+Nww8/PMuVAmtSWloaZWVlMWDAgFi0aFH89a9/jYKCgkiSJPLz8+Ojjz6KE044Idq2bRtjx46NiMiEk0DNMG3atLjmmmvirbfeis8//zw6duwY1157bbRr1y7ee++9OPLII+OAAw6Ie+65JyL0Yaipdt9992jQoEE899xzERGxdOnSKCwsjIiIH/7wh7HDDjvE/fffn80SgVWUlZVFfv53j25Z+cfA3XbbLf7yl79Ey5YtM9Ptuuuusdtuu8Vdd92VpUorEj4COWHGjBlx0003Ra9eveKAAw4o95+ZHXfcMfr06RNXX311lqsEKlNaWhq1atXKvH7hhRfiwAMPjOuuuy7OPvvsctO88MILccABB8Rbb70Vu+yyS7ZKBr5n1T784YcfxvDhw2P+/Plx7bXXRvv27TPv/f3vf48jjzwypkyZEjvttFM2ygVWsWTJkigrK4skSaJRo0YREfHGG2/EwQcfHAceeGDcd999EfHd6ObatWvHeeedF9OmTYu//e1v2Swb+P8q68MREddee21su+22cdRRR0VeXl7m8/pnP/tZ5OXlxbhx47JYdXmb7dOugdzSunXrGDx4cPzoRz+KiIi8vLxIkiS++OKLaN68uXs7Qg31/vvvx6hRo2LmzJmZtn333TeuvPLKOPfcc+P222+PiMgEGw0bNoz27dt7Uj3UEJX14e233z5+//vfx1lnnRXbbbddRHw3wjHiu/u6tm/fPlq0aJGVeoHy3nvvvTj66KNj3333jR/84Adx7733RkTED37wg7jhhhvimWeeiWOOOSaWL1+eGVE1Z86cKCoqihUrVoSxSpBdlfXhlU+rPu+88+Lwww/PDMqpVatWZpBOhw4dIiJqTB+une0CAKrq+3/lifgugLzxxhtj3rx5sddee2WpKmB1Pvjgg+jRo0d8+eWX8cUXX8SgQYMy93w7/fTTY8mSJfHrX/86Pv300zj66KOjXbt2MW7cuFi+fLnwEWqANfXhbbbZJtq2bZv5D8/Kf1955ZVo165dJsQAsue9996LffbZJ/r16xe77bZbvP7669G/f//o0KFDdO3aNX7yk59EUVFRnHHGGdGpU6fYeeedo6CgIB5//PF45ZVXonZtcQFk0+r68C677JJ5YnVBQUFm+hUrVsTw4cPj3//+d4wcOTIiosbc+sRl10BOGjNmTEyYMCHGjRsX48ePN/IRapglS5bE2WefHWVlZfHDH/4wzjrrrDj//PPjggsuiObNm0fEd/et+etf/xoXXXRR1KpVKxo2bBgLFy6Mv//9756IC1m2uj584YUXZgLI798C5d133437778//vjHP8a//vWvzANngOyYP39+HHvssbHzzjvHDTfckGnff//9o2PHjnHjjTdm2hYtWhS///3vY/78+VG3bt04/fTTM6OmgOyoSh/+/ufwM888E3/84x/jtddeiyeeeKLG/f/YnzKAnNShQ4f461//Gi+99JL7wkENlJ+fH927d48tt9wy+vbtG82aNYtf/OIXERGZADI/Pz/69esX++yzT0yfPj2+/vrr6NixY7Rp0ybL1QNr6sMrA8iV/+H55JNP4vzzz4/3338/XnjhBcEj1ADLly+PBQsWxM9+9rOI+L8HVWy77bYxf/78iPjuDwhJkkTDhg3jyiuvLDcdkF1V6cMrP4eTJIltt902OnToEFdddVXsvPPOWat7dYx8BHLWsmXLyg0zB2qWJUuWlLt8euzYsXHsscfGeeedFxdddFE0a9YsVqxYETNmzIhtttkmi5UClVlTHx48eHBsueWWUVpaGvPnz48lS5ZEfn6+vgw1yLRp02LHHXeMiO+CjDp16sTFF18cn376aeaJ9BERCxcuzNzeyBPqoeaoah/++uuvo379+hUeEFeTGPkI5CzBI9RsK0OL0tLSyM/Pj759+0aSJHHcccdFXl5enHPOOXHNNddkfoGqX7++//BADVLVPvzxxx/H/fffH3Xr1s1yxcD3rQwtysrKok6dOhHxXbg4Z86czDQjR46MwsLCOPvss6N27do+h6EGqWofLigoiIEDB9bo+7TW3MoAgE3CyifvlZWVxS9+8YvIy8uLE088Mf72t7/Fhx9+GK+99poHzEANtrY+/OqrrwoeoQbLz88vN6Jx5WXVl1xySfz+97+PN954o0aHFrC52xT6sJs5AACpy8vLi7y8vEiSJPr27Rt77713zJ07NyZNmpR5Wh9Qc62pD9e0m9oDFa2821rt2rWjbdu2cc0118RVV10V//nPf6Jz585Zrg5Ym1zvwzU7GgUANhl5eXlRWloaF1xwQUyYMCFKSko8mAJyiD4MuWvlSKk6derEbbfdFo0aNYp//etf0a1btyxXBlRFrvdhIx8BgI1ql112iUmTJkWnTp2yXQpQDfow5K7evXtHRMTLL78cu+22W5arAdZVrvZhT7sGADYqT9KE3KYPQ25b9Un2QG7JxT4sfAQAAAAAUuGyawAAAAAgFcJHAAAAACAVwkcAAAAAIBXCRwAAAAAgFcJHAAD4f+3cT0hU/xrH8fcBnf5o6hCCEVMiVowiBBnRBEXgQqOYRZRBKQYVhhBBbVolUVCthKBNC61o0SZMZEBiSKOIMqQSkpCUdCFFf4yisNS5i7gDYdQ1m7r3/t6v3ZzznO/znLP88P2OJEmSMsLwUZIkSZIkSVJGGD5KkiTpv0J3dzdBEDA+Pv7DuuLiYlpaWv7ITJIkSZobw0dJkiTNSkNDA0EQEAQBoVCI0tJSTpw4weTk5JzWjcVijI2NkZ+fD0BbWxsFBQUz6np7ezlw4MCcekmSJOnPyPrbA0iSJOl/T3V1Na2trUxMTJBIJGhqaiI7O5tjx4798pqhUIiioqKf1hUWFv5yD0mSJP1Z7nyUJEnSrM2bN4+ioiKWL1/OwYMHqaqqoqOjg7dv31JfX084HGbhwoXU1NQwODiYfu758+ds27aNcDhMTk4O5eXlJBIJ4Ntj193d3ezdu5d3796ld1k2NzcDM49dj4yMEI/Hyc3NJS8vj507d/LixYv0/ebmZlavXs3ly5cpLi4mPz+fXbt28f79+z/yrSRJkv7JDB8lSZI0ZwsWLODz5880NDTw4MEDOjo6uHv3LqlUii1btvDlyxcAmpqamJiY4NatW/T393PmzBlyc3NnrBeLxWhpaSEvL4+xsTHGxsY4evTojLrp6Wni8Thv3ryhp6eHGzduMDQ0RG1t7Td1z549o729nc7OTjo7O+np6eH06dOZ+RiSJElK89i1JEmSflkqlSKZTNLV1UVNTQ3t7e3cuXOHWCwGwJUrV4hEIrS3t7Njxw5GRkbYvn07FRUVAJSUlHx33VAoRH5+PkEQ/PAodjKZpL+/n+HhYSKRCACXLl2ivLyc3t5e1q5dC3wNKdva2li0aBEAdXV1JJNJTp069du+hSRJkmZy56MkSZJmrbOzk9zcXObPn09NTQ21tbU0NDSQlZXFunXr0nWLFy9m1apVDAwMAHDo0CFOnjzJhg0bOH78OI8fP57THAMDA0QikXTwCFBWVkZBQUG6J3w9qv3v4BFgyZIlvHz5ck69JUmS9HOGj5IkSZq1zZs38/DhQwYHB/n06RMXL14kCIKfPrdv3z6Ghoaoq6ujv7+fyspKzp07l/F5s7Ozv/kdBAHT09MZ7ytJkvRPZ/goSZKkWcvJyaG0tJRly5aRlfX1n3yi0SiTk5Pcu3cvXff69WuePn1KWVlZ+lokEqGxsZFr165x5MgRLly48N0eoVCIqampH84RjUYZHR1ldHQ0fe3JkyeMj49/01OSJEl/h+GjJEmSfosVK1YQj8fZv38/t2/f5tGjR+zZs4elS5cSj8cBOHz4MF1dXQwPD9PX18fNmzeJRqPfXa+4uJgPHz6QTCZ59eoVHz9+nFFTVVVFRUUFu3fvpq+vj/v371NfX8+mTZuorKzM6PtKkiTp5wwfJUmS9Nu0trayZs0atm7dyvr160mlUiQSifSx56mpKZqamohGo1RXV7Ny5UrOnz//3bVisRiNjY3U1tZSWFjI2bNnZ9QEQcD169cJh8Ns3LiRqqoqSkpKuHr1akbfU5IkSf+ZIJVKpf72EJIkSZIkSZL+/7jzUZIkSZIkSVJGGD5KkiRJkiRJygjDR0mSJEmSJEkZYfgoSZIkSZIkKSMMHyVJkiRJkiRlhOGjJEmSJEmSpIwwfJQkSZIkSZKUEYaPkiRJkiRJkjLC8FGSJEmSJElSRhg+SpIkSZIkScoIw0dJkiRJkiRJGfEvYriHkIM4qV8AAAAASUVORK5CYII=",
"text/plain": [
"<Figure size 1600x800 with 3 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"📊 Sequence prediction result statistics:\n",
" Total predicted sites: 85\n",
" High probability sites (>0.8): 0\n",
" Medium probability sites (0.4-0.8): 6\n",
" Highest prediction probability: 0.475\n"
]
}
],
"source": [
"# Select a sequence for demonstration\n",
"from Bio import SeqIO\n",
"\n",
"# Read the first mRNA sequence for demonstration\n",
"mrna_sequences = list(SeqIO.parse(mrna_file, \"fasta\"))\n",
"demo_seq = mrna_sequences[0] # Select the first sequence\n",
"\n",
"print(f\"🧬 Selected demonstration sequence: {demo_seq.id}\")\n",
"print(f\"Sequence length: {len(demo_seq.seq)} bp\")\n",
"print(f\"First 100bp of sequence: {str(demo_seq.seq)[:100]}...\")\n",
"\n",
"# Use built-in plot_prf_prediction function for prediction and visualization\n",
"print(f\"\\n🎯 Using plot_prf_prediction for sequence prediction and visualization...\")\n",
"\n",
"sequence_results, fig = plot_prf_prediction(\n",
" sequence=str(demo_seq.seq),\n",
" window_size=3,\n",
" short_threshold=0.2,\n",
" long_threshold=0.2,\n",
" ensemble_weight=0.6,\n",
" title=f\"PRF Prediction Results for Sequence {demo_seq.id} (Bar Chart + Heatmap)\",\n",
" figsize=(16, 8),\n",
" dpi=150\n",
")\n",
"\n",
"plt.show()\n",
"\n",
"print(f\"\\n📊 Sequence prediction result statistics:\")\n",
"print(f\" Total predicted sites: {len(sequence_results)}\")\n",
"print(f\" High probability sites (>0.8): {(sequence_results['Ensemble_Probability'] > 0.8).sum()}\")\n",
"print(f\" Medium probability sites (0.4-0.8): {((sequence_results['Ensemble_Probability'] >= 0.4) & (sequence_results['Ensemble_Probability'] <= 0.8)).sum()}\")\n",
"print(f\" Highest prediction probability: {sequence_results['Ensemble_Probability'].max():.3f}\")"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"🔝 Top 5 predicted sites:\n",
" 1. Position 96: \n",
" - Short probability: 0.288\n",
" - Long probability: 0.755\n",
" - Ensemble probability: 0.475\n",
" - Codon: TAA\n",
" 2. Position 12: \n",
" - Short probability: 0.606\n",
" - Long probability: 0.177\n",
" - Ensemble probability: 0.434\n",
" - Codon: TTG\n",
" 3. Position 15: \n",
" - Short probability: 0.493\n",
" - Long probability: 0.329\n",
" - Ensemble probability: 0.428\n",
" - Codon: GAA\n",
" 4. Position 18: \n",
" - Short probability: 0.369\n",
" - Long probability: 0.510\n",
" - Ensemble probability: 0.426\n",
" - Codon: GTC\n",
" 5. Position 105: \n",
" - Short probability: 0.248\n",
" - Long probability: 0.671\n",
" - Ensemble probability: 0.418\n",
" - Codon: ACT\n",
"\n",
"📊 Visualization analysis complete!\n",
"The chart contains heatmaps and bar charts showing the PRF prediction probability distribution across the entire sequence.\n"
]
}
],
"source": [
"# Print top predicted site probabilities\n",
"if sequence_results['Ensemble_Probability'].max() > 0.3:\n",
" top_predictions = sequence_results.nlargest(5, 'Ensemble_Probability')\n",
" print(f\"\\n🔝 Top 5 predicted sites:\")\n",
" for i, (_, row) in enumerate(top_predictions.iterrows(), 1):\n",
" print(f\" {i}. Position {row['Position']}: \")\n",
" print(f\" - Short probability: {row['Short_Probability']:.3f}\")\n",
" print(f\" - Long probability: {row['Long_Probability']:.3f}\")\n",
" print(f\" - Ensemble probability: {row['Ensemble_Probability']:.3f}\")\n",
" print(f\" - Codon: {row['Codon']}\")\n",
"else:\n",
" print(\"\\n💡 No high-probability PRF sites detected in this sequence\")\n",
"\n",
"print(\"\\n📊 Visualization analysis complete!\")\n",
"print(\"The chart contains heatmaps and bar charts showing the PRF prediction probability distribution across the entire sequence.\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"vscode": {
"languageId": "raw"
}
},
"source": [
"## 📖 Complete Function Reference\n",
"\n",
"### All Available Functions and Methods\n",
"\n",
"#### Core Prediction Functions\n",
"\n",
"**1. `predict_prf(sequence=None, data=None, window_size=3, short_threshold=0.1, ensemble_weight=0.4, model_dir=None)`**\n",
"- **Purpose**: Universal prediction function for both sliding window and region-based analysis\n",
"- **Input modes**: \n",
" - Single/multiple sequences → sliding window prediction\n",
" - DataFrame with 'Long_Sequence'/'399bp' column → region prediction\n",
"- **Key parameters**:\n",
" - `ensemble_weight`: Short model weight (0.0-1.0, default: 0.4)\n",
" - `window_size`: Scanning step size (default: 3)\n",
" - `short_threshold`: Filtering threshold (default: 0.1)\n",
"\n",
"**2. `plot_prf_prediction(sequence, window_size=3, short_threshold=0.65, long_threshold=0.8, ensemble_weight=0.4, title=None, save_path=None, figsize=(12,8), dpi=300)`**\n",
"- **Purpose**: Prediction with built-in visualization (3-subplot layout: FS site heatmap, prediction heatmap, bar chart)\n",
"- **Returns**: (prediction_results_df, matplotlib_figure)\n",
"- **Visualization features**: \n",
" - Black bars with alpha=0.6\n",
" - 'Reds' colormap for heatmaps\n",
" - Height ratios [0.1, 0.1, 1] for subplots\n",
"\n",
"#### PRFPredictor Class Methods\n",
"\n",
"**3. Class initialization: `PRFPredictor(model_dir=None)`**\n",
"- Loads HistGradientBoosting (short, 33bp) and BiLSTM-CNN (long, 399bp) models\n",
"- Uses ensemble weighting for final predictions\n",
"\n",
"**4. `predictor.predict_sequence(sequence, window_size=3, short_threshold=0.1, ensemble_weight=0.4)`**\n",
"- **Purpose**: Sliding window analysis of complete sequences\n",
"- **Process**: Scans sequence with specified window size, applies both models\n",
"\n",
"**5. `predictor.predict_regions(sequences, short_threshold=0.1, ensemble_weight=0.4)`**\n",
"- **Purpose**: Batch prediction for pre-defined 399bp regions\n",
"- **Input**: List/Series of 399bp sequences\n",
"- **Efficient**: Direct region analysis without sliding window\n",
"\n",
"**6. `predictor.predict_single_position(fs_period, full_seq, short_threshold=0.1, ensemble_weight=0.4)`**\n",
"- **Purpose**: Single position analysis\n",
"- **Inputs**: 33bp sequence (fs_period) + 399bp sequence (full_seq)\n",
"- **Returns**: Dictionary with individual and ensemble probabilities\n",
"\n",
"**7. `predictor.plot_sequence_prediction(...)`** \n",
"- **Purpose**: Class method version of plot_prf_prediction()\n",
"- **Same parameters** as standalone function\n",
"\n",
"#### Utility Functions\n",
"\n",
"**8. `fscanr(blastx_output, mismatch_cutoff=10, evalue_cutoff=1e-5, frameDist_cutoff=10)`**\n",
"- **Purpose**: Detect PRF sites from BLASTX alignment results\n",
"- **Input**: DataFrame with BLASTX columns (qseqid, sseqid, pident, length, mismatch, gapopen, qstart, qend, sstart, send, evalue, bitscore, qframe, sframe)\n",
"- **Output**: PRF sites with FS_start, FS_end, FS_type, Strand information\n",
"\n",
"**9. `extract_prf_regions(mrna_file, prf_data)`**\n",
"- **Purpose**: Extract 399bp sequences around detected PRF sites\n",
"- **Inputs**: FASTA file path + FScanR results DataFrame\n",
"- **Handles**: Strand orientation (reverse complement for '-' strand)\n",
"\n",
"#### Data Access Functions\n",
"\n",
"**10. `get_test_data_path(filename)`**\n",
"- **Purpose**: Get path to built-in test data files\n",
"- **Available files**: 'blastx_example.xlsx', 'mrna_example.fasta', 'region_example.csv'\n",
"\n",
"**11. `list_test_data()`**\n",
"- **Purpose**: Display all available test data files\n",
"\n",
"### Usage Pattern Examples\n",
"\n",
"#### Pattern 1: Quick Single Sequence Analysis\n",
"```python\n",
"from FScanpy import predict_prf, plot_prf_prediction\n",
"\n",
"# Simple prediction\n",
"results = predict_prf(sequence=\"ATGCGT...\")\n",
"\n",
"# With visualization \n",
"results, fig = plot_prf_prediction(sequence=\"ATGCGT...\")\n",
"```\n",
"\n",
"#### Pattern 2: Batch Sequence Analysis\n",
"```python\n",
"sequences = [\"seq1\", \"seq2\", \"seq3\"]\n",
"results = predict_prf(sequence=sequences, ensemble_weight=0.5)\n",
"```\n",
"\n",
"#### Pattern 3: BLASTX Pipeline\n",
"```python\n",
"from FScanpy.utils import fscanr, extract_prf_regions\n",
"\n",
"# Step 1: Detect PRF sites\n",
"prf_sites = fscanr(blastx_df)\n",
"\n",
"# Step 2: Extract sequences\n",
"prf_sequences = extract_prf_regions(fasta_file, prf_sites)\n",
"\n",
"# Step 3: Predict probabilities\n",
"results = predict_prf(data=prf_sequences)\n",
"```\n",
"\n",
"#### Pattern 4: Custom Analysis with PRFPredictor\n",
"```python\n",
"from FScanpy import PRFPredictor\n",
"\n",
"predictor = PRFPredictor()\n",
"\n",
"# Method chaining for different analysis types\n",
"seq_results = predictor.predict_sequence(sequence)\n",
"region_results = predictor.predict_regions(sequences_399bp)\n",
"single_result = predictor.predict_single_position(seq_33bp, seq_399bp)\n",
"```\n",
"\n",
"### Parameter Optimization Guide\n",
"\n",
"**Ensemble Weight Selection:**\n",
"- `0.2-0.3`: Conservative (high specificity, favor long model)\n",
"- `0.4-0.6`: Balanced (recommended default)\n",
"- `0.7-0.8`: Sensitive (high sensitivity, favor short model)\n",
"\n",
"**Window Size Selection:**\n",
"- `1`: High resolution, every position (slow but detailed)\n",
"- `3`: Standard resolution (balanced speed/detail) \n",
"- `6-9`: Low resolution, faster analysis\n",
"\n",
"**Threshold Guidelines:**\n",
"- `short_threshold`: 0.1-0.3 (controls efficiency by filtering low-probability candidates)\n",
"- Display thresholds: 0.3-0.8 (controls visualization, higher = cleaner plots)\n",
"- Classification threshold: 0.5 (standard binary classification cutoff)\n",
"\n",
"### Output Interpretation\n",
"\n",
"**Main Result Columns:**\n",
"- `Short_Probability`: HistGradientBoosting model prediction (0-1)\n",
"- `Long_Probability`: BiLSTM-CNN model prediction (0-1)\n",
"- `Ensemble_Probability`: **Final prediction** (weighted combination)\n",
"- `Position`: Sequence position (sliding window mode)\n",
"- `Codon`: Codon at position (sliding window mode)\n",
"\n",
"**Ensemble Probability Interpretation:**\n",
"- `> 0.8`: High confidence PRF site\n",
"- `0.5-0.8`: Moderate confidence PRF site \n",
"- `0.3-0.5`: Low confidence, worth investigating\n",
"- `< 0.3`: Unlikely to be PRF site\n",
"\n",
"### Best Practices\n",
"\n",
"1. **For exploration**: Use `window_size=1, ensemble_weight=0.4`\n",
"2. **For screening**: Use `window_size=3, ensemble_weight=0.4, short_threshold=0.2`\n",
"3. **For validation**: Use region-based prediction with known sequences\n",
"4. **For visualization**: Adjust `short_threshold` and `long_threshold` in plotting functions to control display density\n",
"\n",
"This demo covers all major FScanpy functionalities. For detailed parameter descriptions and advanced usage, please refer to the complete tutorial documentation.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 📝 Analysis Summary\n",
"\n",
"### 🎯 Key Findings\n",
"1. **Data Quality**: Test dataset contains real BLASTX alignment results and validation regions\n",
"2. **FScanR Performance**: Successfully identified potential PRF sites from BLASTX results\n",
"3. **Model Performance**: Short and Long models each have advantages in different scenarios\n",
"4. **Prediction Results**: Ensemble model provides more stable prediction performance\n",
"5. **Visualization**: Built-in plotting functions generate clear heatmaps and bar charts\n",
"\n",
"### 🔧 Best Practices\n",
"- **Data Preprocessing**: Ensure BLASTX results are in correct format\n",
"- **Parameter Settings**: Use default ensemble weights (0.4:0.6) for balanced performance\n",
"- **Result Interpretation**: When using FScanpy for whole sequence prediction, don't use 0.5 as threshold, but compare relative probabilities across positions\n",
"- **Visualization**: Use plot_prf_prediction function to generate standardized plots\n",
"\n",
"### 📚 Usage Recommendations\n",
"1. **Threshold Selection**: Adjust probability thresholds based on application scenarios\n",
"2. **Result Validation**: Validate prediction results with biological knowledge\n",
"3. **Performance Optimization**: Use reasonable sliding window sizes for large-scale data\n",
"4. **Visualization Parameters**: Adjust figsize and dpi for optimal display"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "fs",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.23"
}
},
"nbformat": 4,
"nbformat_minor": 4
}