microsoft/qlib · error · NotImplementedError

please implement _align_indices func

Error message

please implement _align_indices func

What it means

IndexData is a base class; binary operations (+, -, *, / via BinaryOps) call _align_indices to bring the other operand's index to match self. The base implementation is deliberately a NotImplementedError stub — only SingleData and MultiData provide real alignment logic. Hitting it means an IndexData subclass (or the base class itself) was used in arithmetic without an alignment strategy.

Source

Thrown at qlib/utils/index_data.py:435

        # NOTE: this tries to behave like a numpy array to be compatible with numpy aggregating function like nansum and nanmean
        return self.iloc[args]

    def _align_indices(self, other: "IndexData") -> "IndexData":
        """
        Align all indices of `other` to `self` before performing the arithmetic operations.
        This function will return a new IndexData rather than changing data in `other` inplace

        Parameters
        ----------
        other : "IndexData"
            the index in `other` is to be changed

        Returns
        -------
        IndexData:
            the data in `other` with index aligned to `self`
        """
        raise NotImplementedError(f"please implement _align_indices func")

    def sort_index(self, axis=0, inplace=True):
        assert inplace, "Only support sorting inplace now"
        self.indices[axis], sorted_idx = self.indices[axis].sort()
        self.data = np.take(self.data, sorted_idx, axis=axis)

    # The code below could be simpler like methods in __getattribute__
    def __invert__(self):
        return self.__class__(~self.data.astype(bool), *self.indices)

    def abs(self):
        """get the abs of data except np.nan."""
        tmp_data = np.absolute(self.data)
        return self.__class__(tmp_data, *self.indices)

    def replace(self, to_replace: Dict[np.number, np.number]):
        assert isinstance(to_replace, dict)
        tmp_data = self.data.copy()

View on GitHub (pinned to 79633dd950)

Solutions

  1. Use SingleData (1-D) or MultiData (2-D) instead of the raw IndexData base class.
  2. If you subclass IndexData, implement _align_indices(self, other) -> IndexData that reindexes `other` onto self.indices and returns it.
  3. Alternatively pre-align manually with reindex()/reindexall() before doing arithmetic, which bypasses _align_indices.

Example fix

// before
c = IndexData(a.data, *a.indices) + b  # NotImplementedError

// after
c = SingleData(a.data, a.index) + b.reindex(a.index)  # concrete classes align fine
Defensive patterns

Strategy: type-guard

Validate before calling

from qlib.utils.index_data import SingleData, MultiData, IndexData
assert type(a) is not IndexData and type(b) is not IndexData, 'use SingleData/MultiData for arithmetic'

Type guard

def supports_alignment(obj) -> bool:
    return type(obj).__name__ in ('SingleData', 'MultiData') or type(obj)._align_indices is not IndexData._align_indices

Try / catch

try:
    c = a + b
except NotImplementedError:
    b = b.reindex(a.index)
    c = SingleData(a.data, a.index) + b

Prevention

When it happens

Trigger: Instantiating IndexData directly or subclassing it (e.g. for a custom 1-D/2-D variant) and then applying +, -, *, / between two instances; also any code path that calls obj._align_indices(other) on a bare IndexData.

Common situations: Extending qlib's index-data containers for a new data shape; importing IndexData instead of SingleData/MultiData by mistake; upgrading qlib versions where the alignment contract moved into subclass methods.

Related errors


AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15). Data as JSON: /api/errors/6ea6d5715cb462e5. Report an issue: GitHub.