Source code for pypielm.data.adapters.csv_adapter

"""CSV file adapter."""

from __future__ import annotations

from pathlib import Path
from typing import Any

import torch

from pypielm.data.dataset import PIELMDataset


[docs] class CSVAdapter: """Load a :class:`~pypielm.data.dataset.PIELMDataset` from a CSV file. By default (when *column_map* is ``None``) the adapter treats all columns except the last as ``X_colloc`` and the last column as ``y_data``. Args: path: Path to the CSV file. column_map: Optional dict mapping field names (``'X_colloc'``, ``'X_bc'``, ``'y_bc'``, ``'X_ic'``, ``'y_ic'``, ``'X_data'``, ``'y_data'``) to lists of column names or column indices (0-based integers). When ``None`` the default heuristic applies. delimiter: Field delimiter (default ``','``). dtype: Target tensor dtype. device: Target device. """ def __init__( self, path: str | Path, column_map: dict[str, list[str | int]] | None = None, delimiter: str = ",", dtype: torch.dtype = torch.float64, device: str | torch.device = "cpu", ) -> None: self.path = Path(path) self.column_map = column_map self.delimiter = delimiter self.dtype = dtype self.device = device # ------------------------------------------------------------------
[docs] def load(self) -> PIELMDataset: """Read the CSV and return a :class:`~pypielm.data.dataset.PIELMDataset`.""" import numpy as np # Read header to support column-name addressing with open(self.path, encoding="utf-8") as fh: first_line = fh.readline().rstrip("\n") # Detect whether first row is a header (non-numeric) sample_values = first_line.split(self.delimiter) is_header = False try: float(sample_values[0].strip()) except ValueError: is_header = True if is_header: col_names = [c.strip() for c in sample_values] arr = np.genfromtxt( self.path, delimiter=self.delimiter, skip_header=1, dtype=float, ) else: col_names = [str(i) for i in range(len(sample_values))] arr = np.genfromtxt( self.path, delimiter=self.delimiter, dtype=float, ) if arr.ndim == 1: arr = arr.reshape(1, -1) name_to_idx: dict[str, int] = {name: i for i, name in enumerate(col_names)} def _cols(spec: list[str | int]) -> np.ndarray: indices = [] for s in spec: if isinstance(s, int): indices.append(s) else: if s not in name_to_idx: raise KeyError(f"Column '{s}' not found in CSV header.") indices.append(name_to_idx[s]) sub = arr[:, indices] return sub def _t(data: np.ndarray) -> torch.Tensor: t = torch.tensor(data, dtype=self.dtype, device=self.device) if t.ndim == 1: t = t.unsqueeze(1) return t if self.column_map is not None: kwargs: dict[str, Any] = {} for role, spec in self.column_map.items(): kwargs[role] = _t(_cols(spec)) if "X_colloc" not in kwargs: raise ValueError("column_map must include an 'X_colloc' entry.") return PIELMDataset(**kwargs) # Default: last column is y_data, remainder is X_colloc X_colloc = _t(arr[:, :-1]) y_data = _t(arr[:, -1:]) return PIELMDataset(X_colloc=X_colloc, y_data=y_data)