{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":97984,"databundleVersionId":14096757,"isSourceIdPinned":false,"sourceType":"competition"}],"dockerImageVersionId":31153,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nfrom scipy.signal import butter, filtfilt\nfrom concurrent.futures import ThreadPoolExecutor, as_completed\n\n# Define constants\nTRAIN_DIRECTORY = '/kaggle/input/physionet-ecg-image-digitization/train'\nECG_LEADS = ['I', 'II', 'III', 'aVR', 'aVL', 'aVF', 'V1', 'V2', 'V3', 'V4', 'V5', 'V6']\nTEMPLATE_LENGTH = 500\nMIN_SIGNAL_VALUE = 0.0\nMAX_SIGNAL_VALUE = 0.09\n\n# Function to process a single recording and extract signals for all leads\ndef extract_signals_from_file(record_id):\n    file_path = os.path.join(TRAIN_DIRECTORY, str(record_id), f\"{record_id}.csv\")\n    if not os.path.exists(file_path):\n        return None\n    try:\n        data_frame = pd.read_csv(file_path)\n        lead_signals = {}\n        for lead_name in ECG_LEADS:\n            if lead_name in data_frame.columns:\n                raw_signal = data_frame[lead_name].dropna().values.astype(np.float32)\n                if len(raw_signal) >= 50:\n                    normalized_signal = (raw_signal - raw_signal.mean()) / (raw_signal.std() + 1e-8)\n                    resampled_signal = np.interp(\n                        np.linspace(0, 1, TEMPLATE_LENGTH),\n                        np.linspace(0, 1, len(normalized_signal)),\n                        normalized_signal\n                    )\n                    lead_signals[lead_name] = resampled_signal\n        return lead_signals if lead_signals else None\n    except Exception:\n        return None\n\n# Load training metadata\ntrain_metadata = pd.read_csv('/kaggle/input/physionet-ecg-image-digitization/train.csv')\n\n# Collect signals for each lead using parallel processing\nlead_signal_collections = {lead: [] for lead in ECG_LEADS}\n\nwith ThreadPoolExecutor(max_workers=os.cpu_count()) as executor:\n    future_to_id = {executor.submit(extract_signals_from_file, row['id']): row['id'] for _, row in train_metadata.iterrows()}\n    for future in as_completed(future_to_id):\n        result = future.result()\n        if result:\n            for lead_name, signal_data in result.items():\n                lead_signal_collections[lead_name].append(signal_data)\n\n# Compute average templates for each lead\naverage_templates = {}\nfor lead_name in ECG_LEADS:\n    collected_signals = lead_signal_collections[lead_name]\n    if collected_signals:\n        average_templates[lead_name] = np.mean(collected_signals, axis=0)\n    else:\n        time_points = np.linspace(0, 1, TEMPLATE_LENGTH)\n        average_templates[lead_name] = np.sin(2 * np.pi * time_points)\n\n# Load test metadata and sample submission\ntest_metadata = pd.read_csv('/kaggle/input/physionet-ecg-image-digitization/test.csv')\n\n# Generate predictions for test data\ntest_predictions = {}\nfor _, test_row in test_metadata.iterrows():\n    recording_id = test_row['id']\n    lead_name = test_row['lead']\n    row_count = test_row['number_of_rows']\n    sampling_rate = test_row.get('fs', 500)\n    \n    base_template = average_templates.get(lead_name, average_templates['II']).copy()\n    \n    if len(base_template) != row_count:\n        generated_signal = np.interp(\n            np.linspace(0, 1, row_count),\n            np.linspace(0, 1, len(base_template)),\n            base_template\n        )\n    else:\n        generated_signal = base_template.copy()\n    \n    if len(generated_signal) > 10:\n        nyquist_freq = 0.5 * sampling_rate\n        cutoff_freq = min(15.0 / nyquist_freq, 0.99)\n        filter_b, filter_a = butter(2, cutoff_freq, btype='low')\n        generated_signal = filtfilt(filter_b, filter_a, generated_signal)\n    \n    sig_min, sig_max = generated_signal.min(), generated_signal.max()\n    \n    if sig_max - sig_min < 1e-8:\n        generated_signal = np.full(row_count, (MIN_SIGNAL_VALUE + MAX_SIGNAL_VALUE) / 2)\n    else:\n        generated_signal = (generated_signal - sig_min) / (sig_max - sig_min)\n        generated_signal = MIN_SIGNAL_VALUE + generated_signal * (MAX_SIGNAL_VALUE - MIN_SIGNAL_VALUE)\n    \n    test_predictions[(recording_id, lead_name)] = generated_signal.astype(np.float32)\n\n# Prepare submission data\nsubmission_entries = []\nfor _, test_row in test_metadata.iterrows():\n    recording_id = test_row['id']\n    lead_name = test_row['lead']\n    row_count = test_row['number_of_rows']\n    signal_data = test_predictions[(recording_id, lead_name)]\n    \n    for index in range(row_count):\n        submission_entries.append({\n            'id': f\"{recording_id}_{index}_{lead_name}\",\n            'value': float(signal_data[index])\n        })\n\n# Create and save submission DataFrame\nsubmission_df = pd.DataFrame(submission_entries)\nsubmission_df.to_csv('submission.csv', index=False)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-25T15:41:12.991402Z","iopub.execute_input":"2025-10-25T15:41:12.991698Z","iopub.status.idle":"2025-10-25T15:41:31.755652Z","shell.execute_reply.started":"2025-10-25T15:41:12.991676Z","shell.execute_reply":"2025-10-25T15:41:31.754610Z"}},"outputs":[],"execution_count":null}]}