Repository: iossifovlab/gpf

Issue:
Make VCF genome position table score configs "merge" with autodetected configs
So that we can specify a number of scores from the INFO field we wish to use, but also make use of the information that is provided by the VCF header such as type, number, etc.

Discussion:

Patch:
diff --git a/dae/dae/genomic_resources/genomic_position_table/__init__.py b/dae/dae/genomic_resources/genomic_position_table/__init__.py
index 6c468e50a..856c10ce1 100644
--- a/dae/dae/genomic_resources/genomic_position_table/__init__.py
+++ b/dae/dae/genomic_resources/genomic_position_table/__init__.py
@@ -1,4 +1,4 @@
-from .line import Line, LineBuffer
+from .line import Line, VCFLine, LineBuffer
 from .table_tabix import TabixGenomicPositionTable
 from .table_vcf import VCFGenomicPositionTable
 from .utils import build_genomic_position_table
@@ -6,6 +6,7 @@ from .utils import build_genomic_position_table
 
 __all__ = [
     "Line",
+    "VCFLine",
     "LineBuffer",
     "TabixGenomicPositionTable",
     "VCFGenomicPositionTable",
diff --git a/dae/dae/genomic_resources/genomic_position_table/line.py b/dae/dae/genomic_resources/genomic_position_table/line.py
index 902cb23f1..d2ccea5cd 100644
--- a/dae/dae/genomic_resources/genomic_position_table/line.py
+++ b/dae/dae/genomic_resources/genomic_position_table/line.py
@@ -1,112 +1,68 @@
 import collections
-from typing import Optional, Tuple, Any, Deque, Union, Dict, Generator
+from typing import Optional, Deque, Union, Generator
 # pylint: disable=no-member
 import pysam  # type: ignore
 
 
+Key = Union[str, int]
+
+
 class Line:
     """Represents a line read from a genomic position table.
 
-    Provides uniform access to a number of important columns - chromosome,
+    Provides attribute access to a number of important columns - chromosome,
     start position, end position, reference allele and alternative allele.
     """
 
     def __init__(
         self,
-        chrom: str,
-        pos_begin: Union[int, str],
-        pos_end: Union[int, str],
-        attributes: Dict[str, Any],
-        ref: Optional[str] = None,
-        alt: Optional[str] = None,
+        raw_line: tuple,
+        chrom_key: Key = 0,
+        pos_begin_key: Key = 1,
+        pos_end_key: Key = 2,
+        ref_key: Optional[Key] = None,
+        alt_key: Optional[Key] = None,
+        header: tuple[str] = tuple(),
     ):
-        self.chrom: str = chrom
-        self.pos_begin: int = int(pos_begin)
-        self.pos_end: int = int(pos_end)
-        self.attributes: Dict[str, Any] = attributes
-        self.ref: Optional[str] = ref
-        self.alt: Optional[str] = alt
-
-    def __eq__(self, other: object):
-        if isinstance(other, Line):
-            return self.chrom == other.chrom \
-                and self.pos_begin == other.pos_begin \
-                and self.pos_end == other.pos_end \
-                and self.attributes == other.attributes
-        if isinstance(other, tuple) and len(other) >= 3:
-            return tuple(self) == other
-        return False
-
-    def __iter__(self):
-        yield self.chrom
-        yield self.pos_begin
-        yield self.pos_end
-        for attr in self.attributes.values():
-            yield attr
-
-    def __getitem__(self, key: Union[int, slice]):
-        if not isinstance(key, (int, slice)):
-            raise TypeError(f"Key '{key}' must be of integer or slice type!")
-        if isinstance(key, slice):
-            return tuple(self)[key]
-        if key == 0:
-            return self.chrom
-        if key == 1:
-            return self.pos_begin
-        if key == 2:
-            return self.pos_end
-        return tuple(self.attributes.values())[key - 3]
-
-    def __repr__(self):
-        return str(tuple(self))
-
-    def _fetch_value(self, key):
-        return self.attributes[key]
-
-    def get(self, key: str):
-        """Universal getter function."""
-        if key == "chrom":
-            return self.chrom
-        if key == "pos_begin":
-            return self.pos_begin
-        if key == "pos_end":
-            return self.pos_end
-        return self._fetch_value(key)
-
-
-class VCFLine(Line):
+        self.data: tuple = raw_line
+        self.header: tuple[str] = header
+        self.chrom: str = self.get(chrom_key)
+        self.pos_begin: int = int(self.get(pos_begin_key))
+        self.pos_end: int = int(self.get(pos_end_key))
+        self.ref: Optional[str] = \
+            self.get(ref_key) if ref_key is not None else None
+        self.alt: Optional[str] = \
+            self.get(alt_key) if alt_key is not None else None
+
+    def get(self, key: Key):
+        idx = key if isinstance(key, int) else self.header.index(key)
+        return self.data[idx]
+
+
+class VCFLine:
     """Line adapter for lines derived from a VCF file.
 
     Implements functionality for handling multi-allelic variants
     and INFO fields.
     """
 
-    def __init__(
-        self,
-        chrom: str,
-        pos_begin: Union[int, str],
-        pos_end: Union[int, str],
-        ref: str,
-        alt: Optional[str],
-        allele_index: Optional[int],
-        info: pysam.VariantRecordInfo,
-        info_meta: pysam.VariantHeaderMetadata,
-    ):
-        super().__init__(
-            chrom, pos_begin, pos_end, {}, ref, alt
-        )
-        # Used for support of multiallelic variants in VCF files.
+    def __init__(self, raw_line, allele_index):
+        self.chrom: str = raw_line.contig
+        self.pos_begin: int = raw_line.pos
+        self.pos_end: int = raw_line.pos
+        self.ref: str = raw_line.ref
+        self.alt: Optional[str] = None
+        # Used to handle multiallelic variants in VCF files.
         # The allele index is None if the variant for this line
         # is missing its ALT, i.e. its value is '.'
         self.allele_index: Optional[int] = allele_index
-        # VCF INFO fields column
-        self.info: pysam.VariantRecordInfo = info
-        # VCF INFO fields metadata - holds metadata for info fields
-        # such as description, type, whether the value is a tuple
-        # of multiple values, etc.
-        self.info_meta: pysam.VariantHeaderMetadata = info_meta
-
-    def _fetch_value(self, key):
+        if self.allele_index is not None:
+            self.alt = raw_line.alts[allele_index]
+        self.info: pysam.VariantRecordInfo = raw_line.info
+        self.info_meta: pysam.VariantHeaderMetadata = raw_line.header.info
+
+    def get(self, key):
+        """Get a value from the INFO field of the VCF line."""
         value, meta = self.info.get(key), self.info_meta.get(key)
         if isinstance(value, tuple):
             if meta.number == "A" and self.allele_index is not None:
@@ -146,38 +102,34 @@ class LineBuffer:
     def peek_last(self) -> Line:
         return self.deque[-1]
 
-    def region(self) -> Tuple[Optional[str], Optional[int], Optional[int]]:
+    def region(self) -> tuple[Optional[str], Optional[int], Optional[int]]:
         """Return region stored in the buffer."""
         if len(self.deque) == 0:
             return None, None, None
 
-        first_chrom, first_begin, first_end, *_ = self.peek_first()
-        if len(self.deque) == 1:
-            return first_chrom, first_begin, first_end
+        first = self.peek_first()
+        last = self.peek_last()
 
-        last_chrom, _, last_end, *_ = self.peek_last()
-        if first_chrom != last_chrom:
-            self.clear()
-            return None, None, None
-        if first_end > last_end:
+        if first.chrom != last.chrom or first.pos_end > last.pos_end:
             self.clear()
             return None, None, None
-        return first_chrom, first_begin, last_end
+
+        return first.chrom, first.pos_begin, last.pos_end
 
     def prune(self, chrom: str, pos: int) -> None:
         """Prune the buffer if needed."""
         if len(self.deque) == 0:
             return
 
-        first_chrom, _first_beg, _, *_ = self.peek_first()
-        if chrom != first_chrom:
+        first = self.peek_first()
+
+        if chrom != first.chrom:
             self.clear()
             return
 
         while len(self.deque) > 0:
-            _, _first_beg, first_end, *_ = self.deque[0]
-
-            if pos <= first_end:
+            first = self.peek_first()
+            if pos <= first.pos_end:
                 break
             self.deque.popleft()
 
@@ -204,27 +156,27 @@ class LineBuffer:
             if last_index <= first_index:
                 break
 
-            _, mid_beg, mid_end, *_ = self.deque[mid_index]
-            if mid_end >= pos >= mid_beg:
+            mid = self.deque[mid_index]
+            if mid.pos_end >= pos >= mid.pos_begin:
                 break
 
-            if pos < mid_beg:
+            if pos < mid.pos_begin:
                 last_index = mid_index - 1
             else:
                 first_index = mid_index + 1
 
         while mid_index > 0:
-            _, prev_beg, _prev_end, *_ = self.deque[mid_index - 1]
-            if pos > prev_beg:
+            prev = self.deque[mid_index - 1]
+            if pos > prev.pos_begin:
                 break
             mid_index -= 1
 
         for index in range(mid_index, len(self.deque)):
-            _, t_beg, t_end, *_ = self.deque[index]
-            if t_end >= pos >= t_beg:
+            line = self.deque[index]
+            if line.pos_end >= pos >= line.pos_begin:
                 mid_index = index
                 break
-            if t_beg >= pos:
+            if line.pos_begin >= pos:
                 mid_index = index
                 break
 
@@ -238,9 +190,8 @@ class LineBuffer:
 
         for index in range(beg_index, len(self.deque)):
             row = self.deque[index]
-            _rchrom, rbeg, rend, *_rline = row
-            if rend < pos_begin:
+            if row.pos_end < pos_begin:
                 continue
-            if pos_end is not None and rbeg > pos_end:
+            if pos_end is not None and row.pos_begin > pos_end:
                 break
             yield row
diff --git a/dae/dae/genomic_resources/genomic_position_table/table.py b/dae/dae/genomic_resources/genomic_position_table/table.py
index 60bba49bc..e295d1d06 100644
--- a/dae/dae/genomic_resources/genomic_position_table/table.py
+++ b/dae/dae/genomic_resources/genomic_position_table/table.py
@@ -1,12 +1,11 @@
 from __future__ import annotations
 
 import abc
-from typing import Optional, Dict, List, Union, Tuple
-from functools import cached_property
+from typing import Optional, Dict, List
 from box import Box  # type: ignore
 
 from dae.genomic_resources.repository import GenomicResource
-
+from .line import Key
 
 class GenomicPositionTable(abc.ABC):
     """Abstraction over genomic scores table."""
@@ -14,6 +13,8 @@ class GenomicPositionTable(abc.ABC):
     CHROM = "chrom"
     POS_BEGIN = "pos_begin"
     POS_END = "pos_end"
+    REF = "reference"
+    ALT = "alternative"
 
     def __init__(self, genomic_resource: GenomicResource, table_definition):
         self.genomic_resource = genomic_resource
@@ -23,11 +24,12 @@ class GenomicPositionTable(abc.ABC):
         self.chrom_order: Optional[List[str]] = None
         self.rev_chrom_map: Optional[Dict[str, str]] = None
 
-        self.chrom_column_i = None
-        self.pos_begin_column_i = None
-        self.pos_end_column_i = None
+        self.chrom_key = None
+        self.pos_begin_key = None
+        self.pos_end_key = None
+        self.ref_key = None
+        self.alt_key = None
 
-        # handling the header property
         self.header: Optional[tuple] = None
 
         self.header_mode = self.definition.get("header_mode", "file")
@@ -38,16 +40,15 @@ class GenomicPositionTable(abc.ABC):
                     raise ValueError(
                         f"The {hindex}-th header {hcolumn} in the table "
                         f"definition is not a string.")
+        elif self.header_mode in {"file", "none"}:
+            self.header = None
         else:
-            if self.header_mode in {"file", "none"}:
-                self.header = None
-            else:
-                raise ValueError(
-                    f"The 'header' property in a table definition "
-                    f"must be 'file' [by default], 'none', or a "
-                    f"list of strings. The current value "
-                    f"{self.header_mode} does not meet these "
-                    f"requirements.")
+            raise ValueError(
+                f"The 'header' property in a table definition "
+                f"must be 'file' [by default], 'none', or a "
+                f"list of strings. The current value "
+                f"{self.header_mode} does not meet these "
+                f"requirements.")
 
     def _build_chrom_mapping(self):
         self.chrom_map = None
@@ -95,89 +96,33 @@ class GenomicPositionTable(abc.ABC):
             self.rev_chrom_map = {
                 fch: ch for ch, fch in self.chrom_map.items()}
 
-    def _set_special_column_indexes(self):
-        self.chrom_column_i = self.get_special_column_index(self.CHROM)
-        self.pos_begin_column_i = self.get_special_column_index(self.POS_BEGIN)
-        self.pos_end_column_i = self.pos_begin_column_i
-        try:
-            self.pos_end_column_i = self.get_special_column_index(self.POS_END)
-        except (ValueError, KeyError):
-            definition = self.definition.to_dict()
-            definition[self.POS_END] = {"index": self.pos_end_column_i}
-            self.definition = Box(definition)
-
-    def get_column_names(self):
-        return self.header
-
-    def _get_index_prop_for_special_column(self, key):
-        index_prop = key + ".index"
-
-        if key not in self.definition:
-            raise KeyError(f"The table definition has no index "
-                           f"({index_prop} property) for the special "
-                           f"column {key}.")
-        if "index" not in self.definition[key]:
-            raise KeyError(f"The table definition has no index "
-                           f"({index_prop} property) for the special "
-                           f"column {key}.")
-
-        try:
-            return int(self.definition[key].index)
-        except ValueError as ex:
-            raise ValueError(f"The {index_prop} property in the table "
-                             f"definition should be an integer.") from ex
-
-    def get_special_column_index(self, key):
-        """Get special columns index."""
-        if self.header_mode == "none" or not self.header:
-            return self._get_index_prop_for_special_column(key)
-        try:
-            return self._get_index_prop_for_special_column(key)
-        except KeyError:
-            column_name = self.get_special_column_name(key)
-            try:
-                return self.header.index(column_name)
-            except ValueError as ex:
-                raise ValueError(f"The column {column_name} for the "
-                                 f"special column {key} is not in the "
-                                 f"header {self.header}.") from ex
-
-    def get_special_column_name(self, key):
-        if self.header_mode == "none":
-            raise AttributeError("The table has no header.")
-        if key in self.definition and "name" in self.definition[key]:
-            return self.definition[key].name
-        return key
-
-    def _get_other_columns(self) -> Union[Tuple[None, None], zip]:
-        if self.header is not None:
-            return zip(*(
-                (i, x) for i, x in enumerate(self.header)
-                if i not in (self.chrom_column_i,
-                             self.pos_begin_column_i,
-                             self.pos_end_column_i)
-            ))
-        return None, None
-
-    @cached_property
-    def ref_key(self):
-        """Get the index or name of the column for the reference base."""
-        ref_key = None
-        if "reference" in self.definition:
-            ref_key = self.get_special_column_index("reference") \
-                if self.header_mode == "none" \
-                else self.get_special_column_name("reference")
-        return ref_key
-
-    @cached_property
-    def alt_key(self):
-        """Get the index or name of the column for the alternative base."""
-        alt_key = None
-        if "alternative" in self.definition:
-            alt_key = self.get_special_column_index("alternative") \
-                if self.header_mode == "none" \
-                else self.get_special_column_name("alternative")
-        return alt_key
+    def _get_column_key(self, col):
+        if col not in self.definition:
+            return None
+        if "name" in self.definition[col]:
+            return self.definition[col].name
+        if "index" in self.definition[col]:
+            return self.definition[col].index
+        return None
+
+    def _set_core_column_keys(self):
+        self.chrom_key = self._get_column_key(self.CHROM)
+        if self.chrom_key is None:
+            self.chrom_key = self.CHROM
+
+        self.pos_begin_key = self._get_column_key(self.POS_BEGIN)
+        if self.pos_begin_key is None:
+            self.pos_begin_key = self.POS_BEGIN
+
+        self.pos_end_key = self._get_column_key(self.POS_END)
+        if self.pos_end_key is None:
+            if self.header and self.POS_END in self.header:
+                self.pos_end_key = self.POS_END
+            else:
+                self.pos_end_key = self.pos_begin_key
+
+        self.ref_key = self._get_column_key(self.REF)
+        self.alt_key = self._get_column_key(self.ALT)
 
     def __enter__(self):
         self.open()
@@ -200,8 +145,11 @@ class GenomicPositionTable(abc.ABC):
 
     @abc.abstractmethod
     def get_records_in_region(
-            self, chrom: str,
-            pos_begin: Optional[int] = None, pos_end: Optional[int] = None):
+        self,
+        chrom: str,
+        pos_begin: Optional[int] = None,
+        pos_end: Optional[int] = None
+    ):
         """Return an iterable over the records in the specified range.
 
         The interval is closed on both sides and 1-based.
diff --git a/dae/dae/genomic_resources/genomic_position_table/table_inmemory.py b/dae/dae/genomic_resources/genomic_position_table/table_inmemory.py
index 9ef5c91db..4403d8d34 100644
--- a/dae/dae/genomic_resources/genomic_position_table/table_inmemory.py
+++ b/dae/dae/genomic_resources/genomic_position_table/table_inmemory.py
@@ -1,6 +1,5 @@
 import collections
 from typing import Optional, List, Any, TextIO
-from copy import copy
 
 from dae.genomic_resources.repository import GenomicResource
 from .table import GenomicPositionTable
@@ -25,6 +24,15 @@ class InmemoryGenomicPositionTable(GenomicPositionTable):
         self.records_by_chr: dict[str, Any] = {}
         super().__init__(genomic_resource, table_definition)
 
+    def _make_line(self, data):
+        return Line(
+            data,
+            self.chrom_key,
+            self.pos_begin_key, self.pos_end_key,
+            self.ref_key, self.alt_key,
+            self.header
+        )
+
     def open(self):
         compression = None
         if self.definition.filename.endswith(".gz"):
@@ -48,15 +56,10 @@ class InmemoryGenomicPositionTable(GenomicPositionTable):
             self.header = tuple(hcs)
         col_number = len(self.header) if self.header else None
 
-        self._set_special_column_indexes()
-
-        other_indices, other_columns = self._get_other_columns()
-
-        assert self.chrom_column_i is not None \
-            and self.pos_begin_column_i is not None \
-            and self.pos_end_column_i is not None
+        self._set_core_column_keys()
 
         records_by_chr = collections.defaultdict(list)
+
         for line in self.str_stream:
             line = line.strip(strip_chars)
             if not line:
@@ -68,27 +71,13 @@ class InmemoryGenomicPositionTable(GenomicPositionTable):
             col_number = len(columns)
             if space_replacement:
                 columns = tuple("" if v == "EMPTY" else v for v in columns)
-            chrom = columns[self.chrom_column_i]
-            ps_begin = int(columns[self.pos_begin_column_i])
-            ps_end = int(columns[self.pos_end_column_i])
-
-            if other_indices and other_columns:
-                attributes = dict(
-                    zip(other_columns, (columns[i] for i in other_indices))
-                )
-            else:
-                attributes = {str(idx): val for idx, val in enumerate(columns)
-                              if idx not in (self.chrom_column_i,
-                                             self.pos_begin_column_i,
-                                             self.pos_end_column_i)}
-            ref = attributes.get(self.ref_key)
-            alt = attributes.get(self.alt_key)
-            records_by_chr[chrom].append(
-                Line(chrom, ps_begin, ps_end,
-                     attributes, ref=ref, alt=alt)
-            )
+
+            line = self._make_line(columns)
+            records_by_chr[line.chrom].append(line)
+
         self.records_by_chr = {
-            c: sorted(pss, key=lambda l: (l[:3], l.ref, l.alt))
+            c: sorted(pss, key=lambda l: (l.chrom, l.pos_begin, l.pos_end,
+                                          l.ref, l.alt))
             for c, pss in records_by_chr.items()
         }
         self._build_chrom_mapping()
@@ -97,6 +86,15 @@ class InmemoryGenomicPositionTable(GenomicPositionTable):
     def get_file_chromosomes(self) -> List[str]:
         return sorted(self.records_by_chr.keys())
 
+    def _transform_result(self, line: Line, chrom) -> Line:
+        new_data = list(line.data)
+        if isinstance(self.chrom_key, int):
+            chrom_idx = self.chrom_key
+        else:
+            chrom_idx = self.header.index(self.chrom_key)
+        new_data[chrom_idx] = chrom
+        return self._make_line(tuple(new_data))
+
     def get_all_records(self):
         for chrom in self.get_chromosomes():
             if self.chrom_map:
@@ -104,9 +102,7 @@ class InmemoryGenomicPositionTable(GenomicPositionTable):
                     continue
                 fchrom = self.chrom_map[chrom]
                 for line in self.records_by_chr[fchrom]:
-                    transformed_line = copy(line)
-                    transformed_line.chrom = chrom
-                    yield transformed_line
+                    yield self._transform_result(line, chrom)
             else:
                 for line in self.records_by_chr[chrom]:
                     yield line
@@ -126,9 +122,7 @@ class InmemoryGenomicPositionTable(GenomicPositionTable):
             if pos_end and pos_end < line.pos_begin:
                 continue
             if self.chrom_map:
-                transformed_line = copy(line)
-                transformed_line.chrom = chrom
-                yield transformed_line
+                yield self._transform_result(line, chrom)
             else:
                 yield line
 
diff --git a/dae/dae/genomic_resources/genomic_position_table/table_tabix.py b/dae/dae/genomic_resources/genomic_position_table/table_tabix.py
index 1e06cedc4..189c423b1 100644
--- a/dae/dae/genomic_resources/genomic_position_table/table_tabix.py
+++ b/dae/dae/genomic_resources/genomic_position_table/table_tabix.py
@@ -1,6 +1,5 @@
 import logging
 from typing import Optional, Tuple, List, Union
-from copy import copy
 from collections import Counter
 # pylint: disable=no-member
 import pysam  # type: ignore
@@ -56,7 +55,7 @@ class TabixGenomicPositionTable(GenomicPositionTable):
             self.definition.filename)
         if self.header_mode == "file":
             self.header = self._load_header()
-        self._set_special_column_indexes()
+        self._set_core_column_keys()
         self._build_chrom_mapping()
         return self
 
@@ -87,13 +86,26 @@ class TabixGenomicPositionTable(GenomicPositionTable):
             return self.rev_chrom_map[chrom]
         return chrom
 
+    def _make_line(self, data):
+        return Line(
+            data,
+            self.chrom_key,
+            self.pos_begin_key, self.pos_end_key,
+            self.ref_key, self.alt_key,
+            self.header
+        )
+
     def _transform_result(self, line: Line) -> Line:
         rchrom = self._map_result_chrom(line.chrom)
         if rchrom is None:
             return None
-        line_copy = copy(line)
-        line_copy.chrom = rchrom
-        return line_copy
+        new_data = list(line.data)
+        if isinstance(self.chrom_key, int):
+            chrom_idx = self.chrom_key
+        else:
+            chrom_idx = self.header.index(self.chrom_key)
+        new_data[chrom_idx] = rchrom
+        return self._make_line(tuple(new_data))
 
     def get_all_records(self):
         # pylint: disable=no-member
@@ -121,13 +133,13 @@ class TabixGenomicPositionTable(GenomicPositionTable):
         if len(self.buffer) == 0:
             return False
 
-        last_chrom, _last_begin, last_end, *_ = self.buffer.peek_last()
-        if chrom != last_chrom:
+        last = self.buffer.peek_last()
+        if chrom != last.chrom:
             return False
-        if pos < last_end:
+        if pos < last.pos_end:
             return False
 
-        if pos - last_end >= self.jump_threshold:
+        if pos - last.pos_end >= self.jump_threshold:
             return False
         return True
 
@@ -136,15 +148,15 @@ class TabixGenomicPositionTable(GenomicPositionTable):
         assert len(self.buffer) > 0
         assert self.jump_threshold > 0
 
-        last_chrom, last_begin, last_end, *_ = self.buffer.peek_last()
-        assert chrom == last_chrom
-        assert pos >= last_begin
+        last = self.buffer.peek_last()
+        assert chrom == last.chrom
+        assert pos >= last.pos_begin
 
         self.stats["sequential seek forward"] += 1
 
         for row in self._gen_from_tabix(chrom, pos, buffering=True):
-            last_chrom, last_begin, last_end, *_ = row
-        return bool(pos >= last_end)
+            last = row
+        return bool(pos >= last.pos_end)
 
     def _gen_from_tabix(self, chrom, pos, buffering=True):
         try:
@@ -172,8 +184,8 @@ class TabixGenomicPositionTable(GenomicPositionTable):
             result = self._transform_result(line)
             if result:
                 yield result
-        _, _last_beg, last_end, *_ = self.buffer.peek_last()
-        if end < last_end:
+        last = self.buffer.peek_last()
+        if end < last.pos_end:
             return
 
         yield from self._gen_from_tabix(chrom, end, buffering=True)
@@ -208,11 +220,13 @@ class TabixGenomicPositionTable(GenomicPositionTable):
 
         if buffering and len(self.buffer) > 0 and prev_call_chrom == fchrom:
 
-            first_chrom, first_beg, _first_end, *_ = self.buffer.peek_first()
-            if first_chrom == fchrom and prev_call_end is not None \
-                    and pos_begin > prev_call_end and pos_end < first_beg:
+            first = self.buffer.peek_first()
+            if first.chrom == fchrom \
+               and prev_call_end is not None \
+               and pos_begin > prev_call_end \
+               and pos_end < first.pos_begin:
 
-                assert first_chrom == prev_call_chrom
+                assert first.chrom == prev_call_chrom
                 self.stats["not found"] += 1
                 return
 
@@ -250,27 +264,8 @@ class TabixGenomicPositionTable(GenomicPositionTable):
         self.stats["tabix fetch"] += 1
         self.buffer.clear()
 
-        other_indices, other_columns = self._get_other_columns()
         # Yes, the argument for the chromosome/contig is called "reference".
         for raw in self.pysam_file.fetch(
             reference=chrom, start=pos_begin, parser=pysam.asTuple()
         ):
-            if other_indices and other_columns:
-                attributes = dict(
-                    zip(other_columns, (raw[i] for i in other_indices))
-                )
-            else:
-                attributes = {
-                    idx: value for idx, value in enumerate(raw)
-                    if idx not in (self.chrom_column_i,
-                                   self.pos_begin_column_i,
-                                   self.pos_end_column_i)
-                }
-            ref = attributes.get(self.ref_key)
-            alt = attributes.get(self.alt_key)
-            yield Line(
-                raw[self.chrom_column_i],
-                raw[self.pos_begin_column_i],
-                raw[self.pos_end_column_i],
-                attributes, ref=ref, alt=alt,
-            )
+            yield self._make_line(raw)
diff --git a/dae/dae/genomic_resources/genomic_position_table/table_vcf.py b/dae/dae/genomic_resources/genomic_position_table/table_vcf.py
index 939e60b90..f713f4ba7 100644
--- a/dae/dae/genomic_resources/genomic_position_table/table_vcf.py
+++ b/dae/dae/genomic_resources/genomic_position_table/table_vcf.py
@@ -1,5 +1,6 @@
 from functools import cache
 from typing import List
+from copy import copy
 # pylint: disable=no-member
 import pysam  # type: ignore
 
@@ -18,9 +19,6 @@ class VCFGenomicPositionTable(TabixGenomicPositionTable):
         super().__init__(genomic_resource, table_definition)
         self.header = self._load_header()
 
-    def _get_index_prop_for_special_column(self, key):
-        return None
-
     def _load_header(self):
         assert self.definition.get("header_mode", "file") == "file"
         filename = self.definition.filename
@@ -30,10 +28,18 @@ class VCFGenomicPositionTable(TabixGenomicPositionTable):
             "VCF tables must have an accompanying *.header.vcf.gz file!"
         return self.genomic_resource.open_vcf_file(header_filename).header.info
 
+    def _transform_result(self, line):
+        rchrom = self._map_result_chrom(line.chrom)
+        if rchrom is None:
+            return None
+        new_line = copy(line)
+        new_line.chrom = rchrom
+        return new_line
+
     def open(self):
         self.pysam_file = self.genomic_resource.open_vcf_file(
             self.definition.filename)
-        self._set_special_column_indexes()
+        self._set_core_column_keys()
         self._build_chrom_mapping()
         return self
 
@@ -49,11 +55,5 @@ class VCFGenomicPositionTable(TabixGenomicPositionTable):
         for raw_line in self.pysam_file.fetch(*args):
             for allele_index, alt in enumerate(raw_line.alts or [None]):
                 assert raw_line.ref is not None
-                yield VCFLine(
-                    raw_line.contig, raw_line.pos, raw_line.pos,
-                    allele_index=allele_index if alt is not None else None,
-                    ref=raw_line.ref,
-                    alt=alt,
-                    info=raw_line.info,
-                    info_meta=raw_line.header.info,
-                )
+                allele_index = allele_index if alt is not None else None
+                yield VCFLine(raw_line, allele_index)
diff --git a/dae/dae/genomic_resources/genomic_position_table/utils.py b/dae/dae/genomic_resources/genomic_position_table/utils.py
index ba4d44160..66ec161cb 100644
--- a/dae/dae/genomic_resources/genomic_position_table/utils.py
+++ b/dae/dae/genomic_resources/genomic_position_table/utils.py
@@ -40,47 +40,6 @@ def build_genomic_position_table(
     raise ValueError(f"unknown table format {table_fmt}")
 
 
-# def build_genome_position_table(
-#         resource: GenomicResource, table_definition: dict):
-#     """Open a genome position table from genomic resource."""
-#     filename = table_definition["filename"]
-
-#     compression = None
-#     if filename.endswith(".bgz"):
-#         default_format = "tabix"
-#     elif filename.endswith(".txt") or filename.endswith(".tsv"):
-#         default_format = "tsv"
-#     elif filename.endswith(".txt.gz") or filename.endswith(".tsv.gz"):
-#         default_format = "tsv"
-#         compression = "gzip"
-#     elif filename.endswith(".csv"):
-#         default_format = "csv"
-#     elif filename.endswith(".csv.gz"):
-#         default_format = "csv"
-#         compression = "gzip"
-#     else:
-#         default_format = "mem"
-
-#     table_format = table_definition.get("format", default_format)
-
-#     table: GenomicPositionTable
-
-#     if table_format in ["mem", "csv", "tsv"]:
-#         with resource.open_raw_file(
-#                 filename, mode="rt", compression=compression) as infile:
-#             table = FlatGenomicPositionTable(
-#                 resource, table_definition, infile, table_format)
-#             table.load()
-#         return table
-#     if table_format == "tabix":
-#         table = TabixGenomicPositionTable(
-#             resource, table_definition, resource.open_tabix_file(filename))
-#         table.load()
-#         return table
-
-#     raise ValueError("unknown table format")
-
-
 def save_as_tabix_table(
         table: GenomicPositionTable,
         full_file_path: str):
@@ -88,13 +47,13 @@ def save_as_tabix_table(
     tmp_file = full_file_path + ".tmp"
     with open(tmp_file, "wt", encoding="utf8") as text_file:
         if table.header_mode != "none":
-            print("#" + "\t".join(table.get_column_names()), file=text_file)
+            print("#" + "\t".join(table.header), file=text_file)
         for rec in table.get_all_records():
             print(*rec, sep="\t", file=text_file)
     pysam.tabix_compress(tmp_file, full_file_path, force=True)
     os.remove(tmp_file)
 
     pysam.tabix_index(full_file_path, force=True,
-                      seq_col=table.chrom_column_i,
-                      start_col=table.pos_begin_column_i,
-                      end_col=table.pos_end_column_i)
+                      seq_col=table.chrom_key,
+                      start_col=table.pos_begin_key,
+                      end_col=table.pos_end_key)
diff --git a/dae/dae/genomic_resources/genomic_scores.py b/dae/dae/genomic_resources/genomic_scores.py
index 6e916568a..3e422d206 100644
--- a/dae/dae/genomic_resources/genomic_scores.py
+++ b/dae/dae/genomic_resources/genomic_scores.py
@@ -26,7 +26,7 @@ from .resource_implementation import GenomicResourceImplementation, \
     InfoImplementationMixin, \
     ResourceConfigValidationMixin
 from .genomic_position_table import build_genomic_position_table, Line, \
-    TabixGenomicPositionTable, VCFGenomicPositionTable
+    TabixGenomicPositionTable, VCFGenomicPositionTable, VCFLine
 from .histogram import Histogram
 
 from .aggregators import build_aggregator, AGGREGATOR_SCHEMA
@@ -43,6 +43,12 @@ VCF_TYPE_CONVERSION_MAP = {
     "Flag": "bool",
 }
 
+SCORE_TYPE_PARSERS = {
+    "str": str,
+    "float": float,
+    "int": int
+}
+
 
 @dataclass
 class ScoreDef:
@@ -61,11 +67,10 @@ class ScoreDef:
 class ScoreLine:
     """Abstraction for a genomic score line. Wraps the line adapter."""
 
-    def __init__(self, line: Line, score_defs: dict, off_by_one=False):
-        assert isinstance(line, Line)
+    def __init__(self, line: Line, score_defs: dict):
+        assert isinstance(line, (Line, VCFLine))
         self.line: Line = line
         self.score_defs = score_defs
-        self.off_by_one = off_by_one
 
     @property
     def chrom(self):
@@ -90,12 +95,7 @@ class ScoreLine:
     def get_score(self, score_id):
         """Get and parse configured score from line."""
         key = self.score_defs[score_id].col_key
-        if not isinstance(key, int):
-            value = self.line.get(key)
-        else:
-            if self.off_by_one:
-                key = key + 1
-            value = self.line[key]
+        value = self.line.get(key)
         if score_id in self.score_defs:
             col_def = self.score_defs[score_id]
             if value in col_def.na_values:
@@ -143,11 +143,6 @@ class GenomicScore(
     def _parse_scoredef_config(config):
         """Parse ScoreDef configuration."""
         scores = {}
-        type_parsers = {
-            "str": str,
-            "float": float,
-            "int": int
-        }
         default_na_values = {
             "str": {},
             "float": {"", "nan", ".", "NA"},
@@ -173,7 +168,7 @@ class GenomicScore(
                 col_key,
                 score_conf.get("desc", ""),
                 col_type,
-                type_parsers[col_type],
+                SCORE_TYPE_PARSERS[col_type],
                 score_conf.get(
                     "na_values",
                     config.get(
@@ -229,10 +224,22 @@ class GenomicScore(
                                          " be configured for scores!")
 
     def _generate_scoredefs(self):
+        if isinstance(self.table, VCFGenomicPositionTable):
+            vcf_scoredefs = GenomicScore._get_vcf_scoredefs(self.table.header)
+            scoredefs = {}
+            if "scores" in self.config:
+                # allow overriding of vcf-generated scoredefs
+                for over in self.config["scores"]:
+                    score = vcf_scoredefs[over["id"]]
+                    score.desc = over.get("desc", score.desc)
+                    score.type = over.get("type", score.type)
+                    score.value_parser = SCORE_TYPE_PARSERS[score.type]
+                    score.na_values = over.get("na_values", score.na_values)
+                    scoredefs[over["id"]] = score
+                return scoredefs
+            return vcf_scoredefs
         if "scores" in self.config:
             return GenomicScore._parse_scoredef_config(self.config)
-        if isinstance(self.table, VCFGenomicPositionTable):
-            return GenomicScore._get_vcf_scoredefs(self.table.header)
         raise ValueError("No scores configured and not using a VCF")
 
     def get_config(self):
@@ -296,7 +303,7 @@ class GenomicScore(
 
     def _get_header(self):
         assert self.table is not None
-        return self.table.get_column_names()
+        return self.table.header
 
     def get_resource_id(self):
         return self.config["id"]
@@ -307,10 +314,7 @@ class GenomicScore(
         for line in self.table.get_records_in_region(
             chrom, pos_begin, pos_end
         ):
-            if self.table.pos_begin_column_i == self.table.pos_end_column_i:
-                yield ScoreLine(line, self.score_definitions, True)
-            else:
-                yield ScoreLine(line, self.score_definitions)
+            yield ScoreLine(line, self.score_definitions)
 
     def get_all_chromosomes(self):
         if not self.is_open():
@@ -1003,14 +1007,6 @@ class AlleleScore(GenomicScore):
     def open(self) -> AlleleScore:
         return cast(AlleleScore, super().open())
 
-    @classmethod
-    def required_columns(cls):
-        return ("chrom", "pos_begin", "pos_end", "variant")
-
-    @staticmethod
-    def get_extra_special_columns():
-        return {"reference": str, "alternative": str}
-
     def fetch_scores(
             self, chrom: str, position: int, reference: str, alternative: str,
             scores: Optional[list[str]] = None):


Test patch:
diff --git a/dae/dae/genomic_resources/tests/test_genomic_position_table.py b/dae/dae/genomic_resources/tests/test_genomic_position_table.py
index f6ed95766..022f81638 100644
--- a/dae/dae/genomic_resources/tests/test_genomic_position_table.py
+++ b/dae/dae/genomic_resources/tests/test_genomic_position_table.py
@@ -6,6 +6,7 @@ import pytest
 import pysam
 
 from dae.genomic_resources.genomic_position_table import \
+    Line, \
     TabixGenomicPositionTable, \
     VCFGenomicPositionTable, \
     build_genomic_position_table
@@ -15,6 +16,17 @@ from dae.genomic_resources.testing import \
     setup_directories, convert_to_tab_separated, setup_tabix, setup_vcf
 
 
+def compare(
+    res: list[Line],
+    expected: list[tuple]
+) -> bool:
+    for idx, line in enumerate(res):
+        print(tuple(line.data), expected[idx])
+        if tuple(line.data) != expected[idx]:
+            return False
+    return True
+
+
 @pytest.fixture
 def vcf_res(tmp_path):
     setup_directories(
@@ -91,29 +103,6 @@ chr1   30   .  A   T,G,C   .    .     A=3;B=31;C=c31,c32,c33,c34;D=d31,d32,d33
     return build_filesystem_test_resource(tmp_path)
 
 
-def test_default_setup():
-    res = build_inmemory_test_resource({
-        "genomic_resource.yaml": """
-            table:
-                filename: data.mem
-            scores:
-            - id: c2
-              name: c2
-              type: float""",
-        "data.mem": convert_to_tab_separated("""
-            chrom pos_begin pos2  c2
-            1     10        12    3.14
-            1     11        11    4.14
-            1     12        10    5.14""")})
-    with build_genomic_position_table(res, res.config["table"]) as tab:
-        assert tab.chrom_column_i == 0
-        assert tab.pos_begin_column_i == 1
-        assert tab.pos_end_column_i == 1
-        assert tab.get_special_column_index("chrom") == 0
-        assert tab.get_special_column_index("pos_begin") == 1
-        assert tab.get_special_column_index("pos_end") == 1
-
-
 def test_regions():
     res = build_inmemory_test_resource({
         "genomic_resource.yaml": """
@@ -130,25 +119,22 @@ def test_regions():
             1     21        30       5.14""")})
 
     with build_genomic_position_table(res, res.config["table"]) as tab:
-        assert tab.chrom_column_i == 0
-        assert tab.pos_begin_column_i == 1
-        assert tab.pos_end_column_i == 2
-        assert list(tab.get_all_records()) == [
-            ("1", 10, 12, "3.14"),
-            ("1", 15, 20, "4.14"),
-            ("1", 21, 30, "5.14")
-        ]
+        assert compare(tab.get_all_records(), [
+            ("1", "10", "12", "3.14"),
+            ("1", "15", "20", "4.14"),
+            ("1", "21", "30", "5.14")
+        ])
 
-        assert list(tab.get_records_in_region("1", 11, 11)) == [
-            ("1", 10, 12, "3.14")
-        ]
+        assert compare(tab.get_records_in_region("1", 11, 11), [
+            ("1", "10", "12", "3.14")
+        ])
 
         assert not list(tab.get_records_in_region("1", 13, 14))
 
-        assert list(tab.get_records_in_region("1", 18, 21)) == [
-            ("1", 15, 20, "4.14"),
-            ("1", 21, 30, "5.14")
-        ]
+        assert compare(tab.get_records_in_region("1", 18, 21), [
+            ("1", "15", "20", "4.14"),
+            ("1", "21", "30", "5.14")
+        ])
 
 
 @pytest.mark.parametrize("jump_threshold", [
@@ -182,22 +168,19 @@ def test_regions_in_tabix(tmp_path, jump_threshold):
     with build_genomic_position_table(res, res.config["table"]) as tab:
         assert tab
         tab.jump_threshold = jump_threshold
-        assert tab.chrom_column_i == 0
-        assert tab.pos_begin_column_i == 1
-        assert tab.pos_end_column_i == 2
-        assert list(tab.get_all_records()) == [
-            ("1", 10, 12, "3.14"),
-            ("1", 15, 20, "4.14"),
-            ("1", 21, 30, "5.14")
-        ]
-        assert list(tab.get_records_in_region("1", 11, 11)) == [
-            ("1", 10, 12, "3.14")
-        ]
+        assert compare(tab.get_all_records(), [
+            ("1", "10", "12", "3.14"),
+            ("1", "15", "20", "4.14"),
+            ("1", "21", "30", "5.14")
+        ])
+        assert compare(tab.get_records_in_region("1", 11, 11), [
+            ("1", "10", "12", "3.14")
+        ])
         assert not list(tab.get_records_in_region("1", 13, 14))
-        assert list(tab.get_records_in_region("1", 18, 21)) == [
-            ("1", 15, 20, "4.14"),
-            ("1", 21, 30, "5.14")
-        ]
+        assert compare(tab.get_records_in_region("1", 18, 21), [
+            ("1", "15", "20", "4.14"),
+            ("1", "21", "30", "5.14")
+        ])
 
 
 def test_last_call_is_updated(tmp_path):
@@ -224,16 +207,16 @@ def test_last_call_is_updated(tmp_path):
 
     with build_genomic_position_table(res, res.config["table"]) as tab:
         assert tab._last_call == ("", -1, -1)
-        assert list(tab.get_records_in_region("1", 11, 11)) == [
-            ("1", 10, 12, "3.14")
-        ]
+        assert compare(tab.get_records_in_region("1", 11, 11), [
+            ("1", "10", "12", "3.14")
+        ])
         assert tab._last_call == ("1", 11, 11)
         assert not list(tab.get_records_in_region("1", 13, 14))
         assert tab._last_call == ("1", 13, 14)
-        assert list(tab.get_records_in_region("1", 18, 21)) == [
-            ("1", 15, 20, "4.14"),
-            ("1", 21, 30, "5.14")
-        ]
+        assert compare(tab.get_records_in_region("1", 18, 21), [
+            ("1", "15", "20", "4.14"),
+            ("1", "21", "30", "5.14")
+        ])
         assert tab._last_call == ("1", 18, 21)
 
 
@@ -304,13 +287,13 @@ def test_chrom_mapping_file():
         """)})
     with build_genomic_position_table(res, res.config["table"]) as tab:
         assert tab.get_chromosomes() == ["gosho", "pesho"]
-        assert list(tab.get_all_records()) == [
-            ("gosho", 10, 12, "3.14"),
-            ("pesho", 11, 11, "4.14")
-        ]
-        assert list(tab.get_records_in_region("pesho")) == [
-            ("pesho", 11, 11, "4.14"),
-        ]
+        assert compare(tab.get_all_records(), [
+            ("gosho", "10", "12", "3.14"),
+            ("pesho", "11", "11", "4.14")
+        ])
+        assert compare(tab.get_records_in_region("pesho"), [
+            ("pesho", "11", "11", "4.14"),
+        ])
 
 
 def test_chrom_mapping_file_with_tabix(tmp_path):
@@ -347,13 +330,13 @@ def test_chrom_mapping_file_with_tabix(tmp_path):
 
     with build_genomic_position_table(res, res.config["table"]) as tab:
         assert tab.get_chromosomes() == ["gosho", "pesho"]
-        assert list(tab.get_all_records()) == [
-            ("gosho", 10, 12, "3.14"),
-            ("pesho", 11, 11, "4.14")
-        ]
-        assert list(tab.get_records_in_region("pesho")) == [
-            ("pesho", 11, 11, "4.14"),
-        ]
+        assert compare(tab.get_all_records(), [
+            ("gosho", "10", "12", "3.14"),
+            ("pesho", "11", "11", "4.14")
+        ])
+        assert compare(tab.get_records_in_region("pesho"), [
+            ("pesho", "11", "11", "4.14"),
+        ])
 
 
 def test_invalid_chrom_mapping_file_with_tabix(tmp_path):
@@ -410,10 +393,8 @@ def test_column_with_name():
             """)
     })
     with build_genomic_position_table(res, res.config["table"]) as tab:
-        assert tab.chrom_column_i == 0
-        assert tab.pos_begin_column_i == 2
-        assert list(tab.get_records_in_region("1", 12, 12)) == [
-            ("1", 12, 12, "10", "3.14")]
+        assert compare(tab.get_records_in_region("1", 12, 12), [
+            ("1", "10", "12", "3.14")])
 
 
 def test_column_with_index():
@@ -434,10 +415,8 @@ def test_column_with_index():
             1     12  14    5.14""")
     })
     with build_genomic_position_table(res, res.config["table"]) as tab:
-        assert tab.chrom_column_i == 0
-        assert tab.pos_begin_column_i == 2
-        assert list(tab.get_records_in_region("1", 12, 12)) == [
-            ("1", 12, 12, "10", "3.14")]
+        assert compare(tab.get_records_in_region("1", 12, 12), [
+            ("1", "10", "12", "3.14")])
 
 
 def test_no_header():
@@ -461,10 +440,8 @@ def test_no_header():
             """)
     })
     with build_genomic_position_table(res, res.config["table"]) as tab:
-        assert tab.chrom_column_i == 0
-        assert tab.pos_begin_column_i == 2
-        assert list(tab.get_records_in_region("1", 12, 12)) == [
-            ("1", 12, 12, "10", "3.14")]
+        assert compare(tab.get_records_in_region("1", 12, 12), [
+            ("1", "10", "12", "3.14")])
 
 
 def test_header_in_config():
@@ -485,10 +462,8 @@ def test_header_in_config():
             1   11  11  4.14
             1   12  10  5.14""")})
     with build_genomic_position_table(res, res.config["table"]) as tab:
-        assert tab.chrom_column_i == 0
-        assert tab.pos_begin_column_i == 2
-        assert list(tab.get_records_in_region("1", 12, 12)) == [
-            ("1", 12, 12, "10", "3.14")]
+        assert compare(tab.get_records_in_region("1", 12, 12), [
+            ("1", "10", "12", "3.14")])
 
 
 def test_space_in_mem_table():
@@ -506,10 +481,8 @@ def test_space_in_mem_table():
             1     11        EMPTY  4.14
             1     12        10     5.14""")})
     with build_genomic_position_table(res, res.config["table"]) as tab:
-        assert tab.chrom_column_i == 0
-        assert tab.pos_begin_column_i == 1
-        assert list(tab.get_records_in_region("1", 11, 11)) == [
-            ("1", 11, 11, "", "4.14")]
+        assert compare(tab.get_records_in_region("1", 11, 11), [
+            ("1", "11", "", "4.14")])
 
 
 def test_text_table():
@@ -534,35 +507,34 @@ def test_text_table():
         })
 
     with build_genomic_position_table(res, res.config["table"]) as table:
-        assert table.get_column_names() == ("chrom", "pos_begin", "c1", "c2")
-        assert list(table.get_all_records()) == [
-            ("1", 3, 3, "3.14", "aa"),
-            ("1", 4, 4, "4.14", "bb"),
-            ("1", 4, 4, "5.14", "cc"),
-            ("1", 5, 5, "6.14", "dd"),
-            ("1", 8, 8, "7.14", "ee"),
-            ("2", 3, 3, "8.14", "ff")
-        ]
-        assert list(table.get_records_in_region("1", 4, 5)) == [
-            ("1", 4, 4, "4.14", "bb"),
-            ("1", 4, 4, "5.14", "cc"),
-            ("1", 5, 5, "6.14", "dd")
-        ]
-        assert list(table.get_records_in_region("1", 4, None)) == [
-            ("1", 4, 4, "4.14", "bb"),
-            ("1", 4, 4, "5.14", "cc"),
-            ("1", 5, 5, "6.14", "dd"),
-            ("1", 8, 8, "7.14", "ee")
-        ]
-        assert list(table.get_records_in_region("1", None, 4)) == [
-            ("1", 3, 3, "3.14", "aa"),
-            ("1", 4, 4, "4.14", "bb"),
-            ("1", 4, 4, "5.14", "cc")
-        ]
+        assert compare(table.get_all_records(), [
+            ("1", "3", "3.14", "aa"),
+            ("1", "4", "4.14", "bb"),
+            ("1", "4", "5.14", "cc"),
+            ("1", "5", "6.14", "dd"),
+            ("1", "8", "7.14", "ee"),
+            ("2", "3", "8.14", "ff")
+        ])
+        assert compare(table.get_records_in_region("1", 4, 5), [
+            ("1", "4", "4.14", "bb"),
+            ("1", "4", "5.14", "cc"),
+            ("1", "5", "6.14", "dd")
+        ])
+        assert compare(table.get_records_in_region("1", 4, None), [
+            ("1", "4", "4.14", "bb"),
+            ("1", "4", "5.14", "cc"),
+            ("1", "5", "6.14", "dd"),
+            ("1", "8", "7.14", "ee")
+        ])
+        assert compare(table.get_records_in_region("1", None, 4), [
+            ("1", "3", "3.14", "aa"),
+            ("1", "4", "4.14", "bb"),
+            ("1", "4", "5.14", "cc")
+        ])
         assert not list(table.get_records_in_region("1", 20, 25))
-        assert list(table.get_records_in_region("2", None, None)) == [
-            ("2", 3, 3, "8.14", "ff")
-        ]
+        assert compare(table.get_records_in_region("2", None, None), [
+            ("2", "3", "8.14", "ff")
+        ])
         with pytest.raises(Exception):
             list(table.get_records_in_region("3"))
 
@@ -603,36 +575,35 @@ def test_tabix_table(tmp_path, jump_threshold):
     res = build_filesystem_test_resource(tmp_path)
 
     with build_genomic_position_table(res, res.config["table"]) as table:
-        assert table.get_column_names() == ("chrom", "pos_begin", "c1", "c2")
         table.jump_threshold = jump_threshold
-        assert list(table.get_all_records()) == [
-            ("1", 3, 3, "3.14", "aa"),
-            ("1", 4, 4, "4.14", "bb"),
-            ("1", 4, 4, "5.14", "cc"),
-            ("1", 5, 5, "6.14", "dd"),
-            ("1", 8, 8, "7.14", "ee"),
-            ("2", 3, 3, "8.14", "ff")
-        ]
-        assert list(table.get_records_in_region("1", 4, 5)) == [
-            ("1", 4, 4, "4.14", "bb"),
-            ("1", 4, 4, "5.14", "cc"),
-            ("1", 5, 5, "6.14", "dd")
-        ]
-        assert list(table.get_records_in_region("1", 4, None)) == [
-            ("1", 4, 4, "4.14", "bb"),
-            ("1", 4, 4, "5.14", "cc"),
-            ("1", 5, 5, "6.14", "dd"),
-            ("1", 8, 8, "7.14", "ee")
-        ]
-        assert list(table.get_records_in_region("1", None, 4)) == [
-            ("1", 3, 3, "3.14", "aa"),
-            ("1", 4, 4, "4.14", "bb"),
-            ("1", 4, 4, "5.14", "cc")
-        ]
+        assert compare(table.get_all_records(), [
+            ("1", "3", "3.14", "aa"),
+            ("1", "4", "4.14", "bb"),
+            ("1", "4", "5.14", "cc"),
+            ("1", "5", "6.14", "dd"),
+            ("1", "8", "7.14", "ee"),
+            ("2", "3", "8.14", "ff")
+        ])
+        assert compare(table.get_records_in_region("1", 4, 5), [
+            ("1", "4", "4.14", "bb"),
+            ("1", "4", "5.14", "cc"),
+            ("1", "5", "6.14", "dd")
+        ])
+        assert compare(table.get_records_in_region("1", 4, None), [
+            ("1", "4", "4.14", "bb"),
+            ("1", "4", "5.14", "cc"),
+            ("1", "5", "6.14", "dd"),
+            ("1", "8", "7.14", "ee")
+        ])
+        assert compare(table.get_records_in_region("1", None, 4), [
+            ("1", "3", "3.14", "aa"),
+            ("1", "4", "4.14", "bb"),
+            ("1", "4", "5.14", "cc")
+        ])
         assert not list(table.get_records_in_region("1", 20, 25))
-        assert list(table.get_records_in_region("2", None, None)) == [
-            ("2", 3, 3, "8.14", "ff")
-        ]
+        assert compare(table.get_records_in_region("2", None, None), [
+            ("2", "3", "8.14", "ff")
+        ])
         with pytest.raises(Exception):
             list(table.get_records_in_region("3"))
 
@@ -704,16 +675,11 @@ def regions_tabix_table(tmp_path):
 
     table = build_genomic_position_table(res, res.config["tabix_table"])
     table.open()
-    assert table.chrom_column_i == 0
-    assert table.pos_begin_column_i == 1
-    assert table.pos_end_column_i == 2
-
     return table
 
 
 def test_tabix_table_should_use_sequential_seek_forward(tabix_table):
     table = tabix_table
-    assert table.get_column_names() == ("chrom", "pos_begin", "c1")
 
     assert not table._should_use_sequential_seek_forward("1", 1)
     for row in table.get_records_in_region("1", 1, 1):
@@ -732,7 +698,6 @@ def test_regions_tabix_table_should_use_sequential_seek_forward(
     regions_tabix_table
 ):
     table = regions_tabix_table
-    assert table.get_column_names() == ("chrom", "pos_begin", "pos_end", "c1")
 
     assert not table._should_use_sequential_seek_forward("1", 1)
     for row in table.get_records_in_region("1", 2, 2):
@@ -751,17 +716,15 @@ def test_regions_tabix_table_should_use_sequential_seek_forward(
 
 def test_tabix_table_jumper_current_position(tabix_table):
     table = tabix_table
-    assert table.get_column_names() == ("chrom", "pos_begin", "c1")
 
     for rec in table.get_records_in_region("1", 1):
-        print(rec)
-        assert rec[0] == "1"
-        assert int(rec[1]) == 1
+        assert rec.chrom == "1"
+        assert rec.pos_begin == 1
         break
 
     for rec in table.get_records_in_region("1", 6):
-        assert rec[0] == "1", rec
-        assert int(rec[1]) == 6, rec
+        assert rec.chrom == "1", rec
+        assert rec.pos_begin == 6, rec
         break
 
 
@@ -798,34 +761,26 @@ def tabix_table_multiline(tmp_path):
 
 
 @pytest.mark.parametrize("pos_beg,pos_end,expected", [
-    (1, 1, [("1", 1, 1, "1")]),
-    (2, 2, [("1", 2, 2, "2"), ("1", 2, 2, "3")]),
-    (3, 3, [("1", 3, 3, "4"), ("1", 3, 3, "5")]),
-    (4, 4, [("1", 4, 4, "6"), ("1", 4, 4, "7")]),
-    (3, 4, [
-        ("1", 3, 3, "4"), ("1", 3, 3, "5"),
-        ("1", 4, 4, "6"), ("1", 4, 4, "7")
-    ]),
+    (1, 1, [("1", "1", "1")]),
+    (2, 2, [("1", "2", "2"), ("1", "2", "3")]),
+    (3, 3, [("1", "3", "4"), ("1", "3", "5")]),
+    (4, 4, [("1", "4", "6"), ("1", "4", "7")]),
+    (3, 4, [("1", "3", "4"), ("1", "3", "5"),
+            ("1", "4", "6"), ("1", "4", "7")]),
 ])
 def test_tabix_table_multi_get_regions(
         tabix_table_multiline, pos_beg, pos_end, expected):
     table = tabix_table_multiline
-    assert table.get_column_names() == ("chrom", "pos_begin", "c1")
-
     assert not table._should_use_sequential_seek_forward("1", 1)
     for row in table.get_records_in_region("1", 1, 1):
-        print(row)
-
-    lines = table.get_records_in_region("1", pos_beg, pos_end)
-    lines = list(lines)
-    print(lines)
-    assert lines == expected
+        print(row.data)
+    assert compare(
+        table.get_records_in_region("1", pos_beg, pos_end), expected
+    )
 
 
 def test_tabix_table_multi_get_regions_partial(tabix_table_multiline):
-
     table = tabix_table_multiline
-    assert table.get_column_names() == ("chrom", "pos_begin", "c1")
 
     assert not table._should_use_sequential_seek_forward("1", 1)
     for row in table.get_records_in_region("1", 1, 1):
@@ -835,11 +790,10 @@ def test_tabix_table_multi_get_regions_partial(tabix_table_multiline):
         print(row)
         if index == 1:
             break
-
-    lines = table.get_records_in_region("1", 3, 3)
-    lines = list(lines)
-    print(lines)
-    assert lines == [("1", 3, 3, "4"), ("1", 3, 3, "5")]
+    assert compare(
+        table.get_records_in_region("1", 3, 3),
+        [("1", "3", "4"), ("1", "3", "5")]
+    )
 
 
 def test_tabix_middle_optimization(tmp_path):
@@ -873,19 +827,14 @@ def test_tabix_middle_optimization(tmp_path):
     with build_genomic_position_table(res, res.config["tabix_table"]) as table:
         row = None
         for row in table.get_records_in_region("1", 1, 1):
-            assert row == ("1", 1, 1, "1")
+            assert row.data == ("1", "1", "1")
             break
-        assert row == ("1", 1, 1, "1")
+        assert row.data == ("1", "1", "1")
 
         row = None
         for row in table.get_records_in_region("1", 1, 1):
-            assert row == ("1", 1, 1, "1")
-        assert row == ("1", 1, 1, "1")
-
-        row = None
-        for row in table.get_records_in_region("1", 2, 2):
-            print(row)
-        assert row is None
+            assert row.data == ("1", "1", "1")
+        assert row.data == ("1", "1", "1")
 
 
 def test_tabix_middle_optimization_regions(tmp_path):
@@ -914,29 +863,27 @@ def test_tabix_middle_optimization_regions(tmp_path):
     with build_genomic_position_table(res, res.config["tabix_table"]) as table:
         row = None
         for row in table.get_records_in_region("1", 1, 1):
-            assert row == ("1", 1, 1, "1")
+            assert row.data == ("1", "1", "1", "1")
             break
 
         row = None
         for row in table.get_records_in_region("1", 1, 1):
-            assert row == ("1", 1, 1, "1")
+            assert row.data == ("1", "1", "1", "1")
 
         row = None
         for row in table.get_records_in_region("1", 4, 4):
-            print(row)
-        assert row == ("1", 4, 8, "2")
+            pass
+        assert row.data == ("1", "4", "8", "2")
 
         row = None
         for row in table.get_records_in_region("1", 4, 4):
-            print(row)
             break
-        assert row == ("1", 4, 8, "2")
+        assert row.data == ("1", "4", "8", "2")
 
         row = None
         for row in table.get_records_in_region("1", 5, 5):
-            print(row)
             break
-        assert row == ("1", 4, 8, "2")
+        assert row.data == ("1", "4", "8", "2")
 
 
 def test_tabix_middle_optimization_regions_buggy_1(tmp_path):
@@ -976,23 +923,20 @@ def test_tabix_middle_optimization_regions_buggy_1(tmp_path):
     res = build_filesystem_test_resource(tmp_path)
 
     with build_genomic_position_table(res, res.config["tabix_table"]) as table:
-        rows = None
-        rows = list(table.get_records_in_region("chr1", 505637, 505637))
-        assert rows == [("chr1", 505637, 505637, "0.009")]
-
-        rows = None
-        rows = list(table.get_records_in_region("chr1", 505643, 505646))
-        assert rows == [
-            ("chr1", 505643, 505643, "0.012"),
-            ("chr1", 505644, 505645, "0.006"),
-            ("chr1", 505646, 505646, "0.005"),
-        ]
+        assert compare(
+            table.get_records_in_region("chr1", 505637, 505637),
+            [("chr1", "505637", "505637", "0.009")]
+        )
 
-        rows = None
-        rows = list(table.get_records_in_region("chr1", 505762, 505762))
-        assert rows == [
-            ("chr1", 505762, 505764, "0.002"),
-        ]
+        assert compare(table.get_records_in_region("chr1", 505643, 505646), [
+            ("chr1", "505643", "505643", "0.012"),
+            ("chr1", "505644", "505645", "0.006"),
+            ("chr1", "505646", "505646", "0.005"),
+        ])
+
+        assert compare(table.get_records_in_region("chr1", 505762, 505762), [
+            ("chr1", "505762", "505764", "0.002"),
+        ])
 
 
 def test_buggy_fitcons_e67(tmp_path):
@@ -1019,17 +963,13 @@ def test_buggy_fitcons_e67(tmp_path):
     res = build_filesystem_test_resource(tmp_path)
 
     with build_genomic_position_table(res, res.config["tabix_table"]) as table:
-        rows = None
-        rows = list(table.get_records_in_region("5", 180740299, 180740300))
-        assert rows == [
-            ("5", 180739426, 180742735, "0.065122"),
-        ]
+        assert compare(table.get_records_in_region("5", 180740299, 180740300), [
+            ("5", "180739426", "180742735", "0.065122"),
+        ])
 
-        rows = None
-        rows = list(table.get_records_in_region("5", 180740301, 180740301))
-        assert rows == [
-            ("5", 180739426, 180742735, "0.065122"),
-        ]
+        assert compare(table.get_records_in_region("5", 180740301, 180740301), [
+            ("5", "180739426", "180742735", "0.065122"),
+        ])
 
 
 @pytest.mark.parametrize("jump_threshold,expected", [
@@ -1063,17 +1003,12 @@ def test_tabix_jump_config(tmp_path, jump_threshold, expected):
 
     with build_genomic_position_table(res, res.config["tabix_table"]) as table:
         assert table.jump_threshold == expected
-        rows = None
-        rows = list(table.get_records_in_region("5", 180740299, 180740300))
-        assert rows == [
-            ("5", 180739426, 180742735, "0.065122"),
-        ]
-
-        rows = None
-        rows = list(table.get_records_in_region("5", 180740301, 180740301))
-        assert rows == [
-            ("5", 180739426, 180742735, "0.065122"),
-        ]
+        assert compare(table.get_records_in_region("5", 180740299, 180740300), [
+            ("5", "180739426", "180742735", "0.065122"),
+        ])
+        assert compare(table.get_records_in_region("5", 180740301, 180740301), [
+            ("5", "180739426", "180742735", "0.065122"),
+        ])
 
 
 @pytest.mark.parametrize("buffer_maxsize,jump_threshold", [
@@ -1114,24 +1049,15 @@ def test_tabix_max_buffer(
     with build_genomic_position_table(res, res.config["tabix_table"]) as table:
         assert table.BUFFER_MAXSIZE == buffer_maxsize
         assert table.jump_threshold == jump_threshold
-
-        rows = None
-        rows = list(table.get_records_in_region("5", 180740299, 180740300))
-        assert rows == [
-            ("5", 180739426, 180742735, "0.065122"),
-        ]
-
-        rows = None
-        rows = list(table.get_records_in_region("5", 180740301, 180740301))
-        assert rows == [
-            ("5", 180739426, 180742735, "0.065122"),
-        ]
-
-        rows = None
-        rows = list(table.get_records_in_region("5", 180740301, 180742735))
-        assert rows == [
-            ("5", 180739426, 180742735, "0.065122"),
-        ]
+        assert compare(table.get_records_in_region("5", 180740299, 180740300), [
+            ("5", "180739426", "180742735", "0.065122"),
+        ])
+        assert compare(table.get_records_in_region("5", 180740301, 180740301), [
+            ("5", "180739426", "180742735", "0.065122"),
+        ])
+        assert compare(table.get_records_in_region("5", 180740301, 180742735), [
+            ("5", "180739426", "180742735", "0.065122"),
+        ])
 
 
 def test_contig_length():
@@ -1176,9 +1102,18 @@ def test_vcf_get_all_records(vcf_res):
 
         results = tuple(tab.get_all_records())
         assert len(results) == 3
-        assert results[0][:3] == ("chr1", 5, 5)
-        assert results[1][:3] == ("chr1", 15, 15)
-        assert results[2][:3] == ("chr1", 30, 30)
+
+        assert results[0].chrom == "chr1"
+        assert results[0].pos_begin == 5
+        assert results[0].pos_end == 5
+
+        assert results[1].chrom == "chr1"
+        assert results[1].pos_begin == 15
+        assert results[1].pos_end == 15
+
+        assert results[2].chrom == "chr1"
+        assert results[2].pos_begin == 30
+        assert results[2].pos_end == 30
 
         assert results[0].ref == "A"
         assert results[0].alt == "T"
@@ -1195,16 +1130,28 @@ def test_vcf_get_records_in_region(vcf_res):
 
         results = tuple(tab.get_records_in_region("chr1", 1, 6))
         assert len(results) == 1
-        assert results[0][:3] == ("chr1", 5, 5)
+        assert results[0].chrom == "chr1"
+        assert results[0].pos_begin == 5
+        assert results[0].pos_end == 5
         results = tuple(tab.get_records_in_region("chr1", 14, 31))
         assert len(results) == 2
-        assert results[0][:3] == ("chr1", 15, 15)
-        assert results[1][:3] == ("chr1", 30, 30)
+        assert results[0].chrom == "chr1"
+        assert results[0].pos_begin == 15
+        assert results[0].pos_end == 15
+        assert results[1].chrom == "chr1"
+        assert results[1].pos_begin == 30
+        assert results[1].pos_end == 30
         results = tuple(tab.get_records_in_region("chr1", 4, 30))
         assert len(results) == 3
-        assert results[0][:3] == ("chr1", 5, 5)
-        assert results[1][:3] == ("chr1", 15, 15)
-        assert results[2][:3] == ("chr1", 30, 30)
+        assert results[0].chrom == "chr1"
+        assert results[0].pos_begin == 5
+        assert results[0].pos_end == 5
+        assert results[1].chrom == "chr1"
+        assert results[1].pos_begin == 15
+        assert results[1].pos_end == 15
+        assert results[2].chrom == "chr1"
+        assert results[2].pos_begin == 30
+        assert results[2].pos_end == 30
 
 
 def test_vcf_get_info_fields(vcf_res):
@@ -1213,9 +1160,6 @@ def test_vcf_get_info_fields(vcf_res):
     ) as tab:
         results = tuple(tab.get_all_records())
         assert len(results) == 3
-        assert results[0][:3] == ("chr1", 5, 5)
-        assert results[1][:3] == ("chr1", 15, 15)
-        assert results[2][:3] == ("chr1", 30, 30)
 
         expected_all = [
             {"A": 1, "B": None, "C": ("c11", "c12"), "D": ("d11",)},
@@ -1298,7 +1242,7 @@ def test_vcf_multiallelic(vcf_res_multiallelic):
         assert isinstance(tab, VCFGenomicPositionTable)
 
         results = tuple(map(
-            lambda r: (*r[:3], r.allele_index),
+            lambda r: (r.chrom, r.pos_begin, r.pos_end, r.allele_index),
             tab.get_all_records()
         ))
         assert results == (
@@ -1321,7 +1265,7 @@ def test_vcf_multiallelic_region(vcf_res_multiallelic):
         assert isinstance(tab, VCFGenomicPositionTable)
 
         results = tuple(map(
-            lambda r: (*r[:3], r.allele_index),
+            lambda r: (r.chrom, r.pos_begin, r.pos_end, r.allele_index),
             tab.get_records_in_region("chr1", 14, 15))
         )
         assert results == (
@@ -1341,10 +1285,12 @@ def test_vcf_multiallelic_info_fields(vcf_res_multiallelic):
     ) as tab:
         assert isinstance(tab, VCFGenomicPositionTable)
 
-        results = list()
+        results = []
         for line in tab.get_all_records():
             results.append(
-                (*line[:3],
+                (line.chrom,
+                 line.pos_begin,
+                 line.pos_end,
                  line.allele_index,
                  line.get("A"),
                  line.get("B"),
@@ -1520,3 +1466,49 @@ def test_vcf_get_missing_alt(vcf_res_multiallelic):
         no_alt_line = next(tab.get_all_records())
         assert no_alt_line.ref == "A"
         assert no_alt_line.alt is None
+
+
+def test_overlapping_nonattribute_columns_config(tmp_path):
+    setup_directories(
+        tmp_path, {
+            "genomic_resource.yaml": """
+                table:
+                  filename: data.txt.gz
+                  format: tabix
+                  header_mode: none
+                  chrom:
+                    index: 0
+                  pos_begin:
+                    index: 1
+                  pos_end:
+                    index: 1
+                  reference:
+                    index: 2
+                  alternative:
+                    index: 3
+                scores:
+                  - id: raw
+                    index: 4
+                    type: float
+                    desc: "raw"
+                  - id: phred
+                    index: 5
+                    type: float
+                    desc: "phred"
+            """,
+        })
+    setup_tabix(
+        tmp_path / "data.txt.gz",
+        """
+        1       101   A       C       0.123        1
+        1       102   A       C       0.456        2
+        1       103   A       C       0.789        3
+        """, seq_col=0, start_col=1, end_col=1)
+    res = build_filesystem_test_resource(tmp_path)
+    with build_genomic_position_table(res, res.config["table"]) as tab:
+        results = tuple(map(lambda l: (l.get(4), l.get(5)), tab.get_all_records()))
+        assert results == (
+            ("0.123", "1"),
+            ("0.456", "2"),
+            ("0.789", "3"),
+        )
diff --git a/dae/dae/genomic_resources/tests/test_genomic_scores.py b/dae/dae/genomic_resources/tests/test_genomic_scores.py
index a7457bcb7..516601540 100644
--- a/dae/dae/genomic_resources/tests/test_genomic_scores.py
+++ b/dae/dae/genomic_resources/tests/test_genomic_scores.py
@@ -380,3 +380,41 @@ def test_vcf_tuple_scores_autoconcat_to_string(vcf_score):
         ("chr1", 30, 30, "31"),
         ("chr1", 30, 30, "31"),
     )
+
+
+def test_vcf_tables_can_select_subset_of_autogenerated_scoredefs(tmp_path_factory):
+    root_path = tmp_path_factory.mktemp("vcf")
+    setup_directories(
+        root_path / "grr",
+        {
+            "tmp": {
+                "genomic_resource.yaml": textwrap.dedent("""
+                    type: allele_score
+                    table:
+                        filename: data.vcf.gz
+                    scores:
+                    - id: A
+                    - id: C
+                """)
+            }
+        }
+    )
+    setup_vcf(
+        root_path / "grr" / "tmp" / "data.vcf.gz",
+        textwrap.dedent("""
+##fileformat=VCFv4.1
+##INFO=<ID=A,Number=1,Type=Integer,Description="Score A">
+##INFO=<ID=B,Number=1,Type=Integer,Description="Score B">
+##INFO=<ID=C,Number=.,Type=String,Description="Score C">
+##INFO=<ID=D,Number=.,Type=String,Description="Score D">
+#CHROM POS ID REF ALT QUAL FILTER  INFO
+chr1   5   .  A   T   .    .       A=1;C=c11,c12;D=d11
+    """))
+    proto = build_fsspec_protocol("testing", str(root_path / "grr"))
+    score = build_score_from_resource(proto.get_resource("tmp"))
+    assert isinstance(score.table, VCFGenomicPositionTable)
+    assert set(score.score_definitions.keys()) == {"A", "C"}
+    assert score.score_definitions["A"].desc == "Score A"
+    assert score.score_definitions["A"].type == "int"
+    assert score.score_definitions["C"].desc == "Score C"
+    assert score.score_definitions["C"].type == "str"
diff --git a/dae/dae/genomic_resources/tests/test_line_buffer.py b/dae/dae/genomic_resources/tests/test_line_buffer.py
index 3af14e970..05721a093 100644
--- a/dae/dae/genomic_resources/tests/test_line_buffer.py
+++ b/dae/dae/genomic_resources/tests/test_line_buffer.py
@@ -7,22 +7,24 @@ from dae.genomic_resources.genomic_position_table import Line, LineBuffer
 
 def test_line_buffer_simple():
     buffer = LineBuffer()
-    buffer.append(Line("1", 1435348, 1435664, {}))
-    buffer.append(Line("1", 1435665, 1435739, {}))
+    buffer.append(Line(("1", 1435348, 1435664, {})))
+    buffer.append(Line(("1", 1435665, 1435739, {})))
 
     assert buffer.region() == ("1", 1435348, 1435739)
 
     for row in buffer.fetch("1", 1435400, 1435400):
         print(row)
-        assert row == ("1", 1435348, 1435664)
+        assert row.chrom == "1"
+        assert row.pos_begin == 1435348
+        assert row.pos_end == 1435664
 
 
 def test_line_buffer_simple_2():
     buffer = LineBuffer()
-    buffer.append(Line("1", 4, 4, {}))
-    buffer.append(Line("1", 4, 4, {}))
-    buffer.append(Line("1", 5, 5, {}))
-    buffer.append(Line("1", 8, 8, {}))
+    buffer.append(Line(("1", 4, 4, {})))
+    buffer.append(Line(("1", 4, 4, {})))
+    buffer.append(Line(("1", 5, 5, {})))
+    buffer.append(Line(("1", 8, 8, {})))
 
     res = list(buffer.fetch("1", 4, 8))
     assert len(res) == 4
@@ -38,11 +40,11 @@ def test_line_buffer_simple_2():
 def test_line_buffer_prune(pos, expected):
     buffer = LineBuffer()
 
-    buffer.append(Line("1", 2, 2, {}))
-    buffer.append(Line("1", 4, 4, {}))
-    buffer.append(Line("1", 4, 4, {}))
-    buffer.append(Line("1", 5, 5, {}))
-    buffer.append(Line("1", 8, 8, {}))
+    buffer.append(Line(("1", 2, 2, {})))
+    buffer.append(Line(("1", 4, 4, {})))
+    buffer.append(Line(("1", 4, 4, {})))
+    buffer.append(Line(("1", 5, 5, {})))
+    buffer.append(Line(("1", 8, 8, {})))
 
     buffer.prune("1", pos)
     assert len(buffer) == expected
@@ -59,30 +61,32 @@ def test_line_buffer_prune(pos, expected):
 ])
 def test_line_buffer_find_index(pos, expected):
     buffer = LineBuffer()
-    buffer.append(Line("1", 4, 4, {}))  # 0
-    buffer.append(Line("1", 4, 4, {}))  # 1
-    buffer.append(Line("1", 5, 5, {}))  # 2
-    buffer.append(Line("1", 8, 8, {}))  # 3
-    buffer.append(Line("1", 9, 10, {}))  # 4
-    buffer.append(Line("1", 12, 14, {}))  # 5
+    buffer.append(Line(("1", 4, 4, {})))  # 0
+    buffer.append(Line(("1", 4, 4, {})))  # 1
+    buffer.append(Line(("1", 5, 5, {})))  # 2
+    buffer.append(Line(("1", 8, 8, {})))  # 3
+    buffer.append(Line(("1", 9, 10, {})))  # 4
+    buffer.append(Line(("1", 12, 14, {})))  # 5
 
     assert buffer.find_index("1", pos) == expected
 
 
 def test_line_buffer_simple_3():
     buffer = LineBuffer()
-    buffer.append(Line("1", 1, 10, {}))
-    buffer.append(Line("1", 11, 20, {}))
-    buffer.append(Line("1", 21, 30, {}))
-    buffer.append(Line("1", 31, 40, {}))
-    buffer.append(Line("1", 41, 50, {}))
-    buffer.append(Line("1", 61, 70, {}))
+    buffer.append(Line(("1", 1, 10, {})))
+    buffer.append(Line(("1", 11, 20, {})))
+    buffer.append(Line(("1", 21, 30, {})))
+    buffer.append(Line(("1", 31, 40, {})))
+    buffer.append(Line(("1", 41, 50, {})))
+    buffer.append(Line(("1", 61, 70, {})))
 
     assert buffer.contains("1", 1)
 
     res = list(buffer.fetch("1", 1, 1))
     assert len(res) == 1
-    assert res[0][:3] == ("1", 1, 10,)
+    assert res[0].chrom == "1"
+    assert res[0].pos_begin == 1
+    assert res[0].pos_end == 10
 
 
 @pytest.mark.parametrize("pos,index", [
@@ -95,31 +99,31 @@ def test_line_buffer_simple_3():
 ])
 def test_find_index_buggy(pos, index):
     buffer = LineBuffer()
-    buffer.append(Line("1", 1847880, 1847880, {}))  # 0
-    buffer.append(Line("1", 1847880, 1847880, {}))  # 1
-    buffer.append(Line("1", 1847880, 1847880, {}))  # 2
-    buffer.append(Line("1", 1847881, 1847881, {}))  # 3
-    buffer.append(Line("1", 1847881, 1847881, {}))  # 4
-    buffer.append(Line("1", 1847881, 1847881, {}))  # 5
-    buffer.append(Line("1", 1847883, 1847883, {}))  # 6
-    buffer.append(Line("1", 1847883, 1847883, {}))  # 7
-    buffer.append(Line("1", 1847884, 1847884, {}))  # 8
-    buffer.append(Line("1", 1847884, 1847884, {}))  # 9
-    buffer.append(Line("1", 1847884, 1847884, {}))  # 10
-    buffer.append(Line("1", 1847885, 1847885, {}))  # 11
+    buffer.append(Line(("1", 1847880, 1847880, {})))  # 0
+    buffer.append(Line(("1", 1847880, 1847880, {})))  # 1
+    buffer.append(Line(("1", 1847880, 1847880, {})))  # 2
+    buffer.append(Line(("1", 1847881, 1847881, {})))  # 3
+    buffer.append(Line(("1", 1847881, 1847881, {})))  # 4
+    buffer.append(Line(("1", 1847881, 1847881, {})))  # 5
+    buffer.append(Line(("1", 1847883, 1847883, {})))  # 6
+    buffer.append(Line(("1", 1847883, 1847883, {})))  # 7
+    buffer.append(Line(("1", 1847884, 1847884, {})))  # 8
+    buffer.append(Line(("1", 1847884, 1847884, {})))  # 9
+    buffer.append(Line(("1", 1847884, 1847884, {})))  # 10
+    buffer.append(Line(("1", 1847885, 1847885, {})))  # 11
 
     assert buffer.find_index("1", pos) == index
 
 
 def test_find_index_buggy_2():
     buffer = LineBuffer()
-    buffer.append(Line("1", 503142, 503143, {}))
-    buffer.append(Line("1", 503144, 503144, {}))
-    buffer.append(Line("1", 503145, 503145, {}))
-    buffer.append(Line("1", 503146, 503146, {}))
-    buffer.append(Line("1", 503147, 503148, {"key": "found"}))
-    buffer.append(Line("1", 503149, 503158, {}))
-    buffer.append(Line("1", 503159, 503159, {}))
+    buffer.append(Line(("1", 503142, 503143, {})))
+    buffer.append(Line(("1", 503144, 503144, {})))
+    buffer.append(Line(("1", 503145, 503145, {})))
+    buffer.append(Line(("1", 503146, 503146, {})))
+    buffer.append(Line(("1", 503147, 503148, "found")))
+    buffer.append(Line(("1", 503149, 503158, {})))
+    buffer.append(Line(("1", 503159, 503159, {})))
 
     index = buffer.find_index("1", 503148)
     assert index != -1
@@ -132,4 +136,4 @@ def test_find_index_buggy_2():
     assert line.chrom == "1"
     assert line.pos_begin == 503147
     assert line.pos_end == 503148
-    assert line.get("key") == "found"
+    assert line.get(3) == "found"
diff --git a/dae/dae/genomic_resources/tests/test_np_score.py b/dae/dae/genomic_resources/tests/test_np_score.py
index 1b5eb0993..2b00b6694 100644
--- a/dae/dae/genomic_resources/tests/test_np_score.py
+++ b/dae/dae/genomic_resources/tests/test_np_score.py
@@ -84,9 +84,9 @@ def test_np_score_aggregation():
     score = build_np_score_from_resource(res)
     score.open()
 
-    assert score.table.chrom_column_i == 0
-    assert score.table.pos_begin_column_i == 1
-    assert score.table.pos_end_column_i == 2
+    assert score.table.chrom_key == "chrom"
+    assert score.table.pos_begin_key == "pos_begin"
+    assert score.table.pos_end_key == "pos_end"
 
     assert score.fetch_scores_agg("1", 14, 18, ["cadd_raw"]) == \
         {"cadd_raw": (2 * 0.04 + 2 * 0.05) / 4.}
diff --git a/dae/dae/genomic_resources/tests/test_position_score.py b/dae/dae/genomic_resources/tests/test_position_score.py
index 8c895e7fe..08daa64ff 100644
--- a/dae/dae/genomic_resources/tests/test_position_score.py
+++ b/dae/dae/genomic_resources/tests/test_position_score.py
@@ -78,9 +78,9 @@ def test_region_score():
     score.open()
 
     assert score.table is not None
-    assert score.table.chrom_column_i == 0
-    assert score.table.pos_begin_column_i == 1
-    assert score.table.pos_end_column_i == 2
+    assert score.table.chrom_key == "chrom"
+    assert score.table.pos_begin_key == "pos_begin"
+    assert score.table.pos_end_key == "pos_end"
 
     assert score.fetch_scores("1", 12) == {
         "phastCons100way": 0.02, "phastCons5way": None}