pandas-dev/pandas · error · NotImplementedError

cannot use an invert condition when passing to numexpr

Error message

cannot use an invert condition when passing to numexpr

What it means

Raised by ConditionBinOp.invert in pandas.core.computation.pytables. Conditions (ConditionBinOp) are translated into a numexpr string passed to PyTables; numexpr/PyTables cannot represent an arbitrary negation of a compiled condition, so calling .invert() on a ConditionBinOp raises NotImplementedError. It is triggered when a '~' (or 'not') wraps a condition that is destined for the numexpr path rather than the filter path.

Source

Thrown at pandas/core/computation/pytables.py:383

class JointFilterBinOp(FilterBinOp):
    def format(self):
        raise NotImplementedError("unable to collapse Joint Filters")

    # error: Signature of "evaluate" incompatible with supertype "BinOp"
    def evaluate(self) -> Self:  # type: ignore[override]
        return self


class ConditionBinOp(BinOp):
    def __repr__(self) -> str:
        return pprint_thing(f"[Condition : [{self.condition}]]")

    def invert(self):
        """invert the condition"""
        # if self.condition is not None:
        #    self.condition = "~(%s)" % self.condition
        # return self
        raise NotImplementedError(
            "cannot use an invert condition when passing to numexpr"
        )

    def format(self):
        """return the actual ne format"""
        return self.condition

    # error: Signature of "evaluate" incompatible with supertype "BinOp"
    def evaluate(self) -> Self | None:  # type: ignore[override]
        if not self.is_valid:
            raise ValueError(f"query term is not valid [{self}]")

        # convert values if we are in the table
        if not self.is_in_table:
            return None

        rhs = self.conform(self.rhs)
        values = [self.convert_value(v) for v in rhs]

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Rewrite the negation as a positive condition: '~(price > 100)' -> 'price <= 100'; '~(a == 5)' -> 'a != 5'.
  2. Read the data and invert in pandas: df = store.get('df'); df[~(df['price'] > 100)].
  3. If using list membership, prefer the '!=' filter path which does support inversion internally.

Example fix

# before
store.select('df', where='~(price > 100)')  # NotImplementedError: cannot use an invert condition when passing to numexpr

# after (rewrite positively)
store.select('df', where='price <= 100')
# or invert in pandas
df = store.get('df')
df[~(df['price'] > 100)]
Defensive patterns

Strategy: validation

Validate before calling

import re

def rewrite_inverted_condition(where: str) -> str:
    # ~(a > X) -> a <= X ; ~(a < X) -> a >= X ; etc.
    m = re.match(r'~\s*\(\s*(\w+)\s*(>=|<=|>|<|==|!=)\s*([^)]+)\s*\)\s*$', where.strip())
    if not m:
        return where
    col, op, val = m.groups()
    invert = {'>': '<=', '<': '>=', '>=': '<', '<=': '>', '==': '!=', '!=': '=='}
    return f'{col} {invert[op]} {val}'

where = rewrite_inverted_condition(where)

Try / catch

try:
    store.select('df', where=where)
except NotImplementedError as e:
    if 'cannot use an invert condition' in str(e):
        df = store.get('df')
        result = df[~df.eval(where.replace('~', ''))]
    else:
        raise

Prevention

When it happens

Trigger: store.select('df', where='~(price > 100)') where 'price > 100' compiles to a numexpr condition; store.select('df', where='not (a == 5)') against a condition column. The invert is fine for FilterBinOps but not for conditions.

Common situations: Wanting the complement of a range/equality query; migrating SQL NOT semantics to pytables where; combining '~' with comparison operators.

Related errors


AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07). Data as JSON: /api/errors/298a4db0d687916a. Report an issue: GitHub.