Description
df[df['col'] == value] on a large (~7M row) DataFrame intermittently
and silently returns zero matching rows for values that are confirmed
to exist in the column. The set of affected values differs between
runs of the exact same script, even against the exact same in-memory
DataFrame within a single process, and across sessions with an
MD5-verified identical source file.
Setting pd.set_option('compute.use_numexpr', False) before running
the comparisons makes the problem disappear completely and reliably
(10/10 trials pass when disabled, vs. 10/10 trials fail when enabled,
on both test machines below).
Environment
- numpy: 2.3.5
- pandas: 2.3.3
- numexpr: 2.14.1
- BLAS: Intel MKL 2025 (mkl-sdl)
- OS: Windows 11
- Reproduced on two separate machines with different CPU architectures:
- Intel Core i7-9700, 32GB RAM (traditional architecture)
- Intel Core Ultra 7 256V, 16GB RAM (Lunar Lake hybrid architecture)
Minimal reproducible example
"""
Minimal reproducible example: numexpr / pandas large-array boolean comparison
produces intermittent, silently incorrect results under default (multithreaded)
settings, but is 100% stable when compute.use_numexpr is disabled.
Environment where this was observed:
numpy : 2.3.5
pandas : 2.3.3
numexpr : 2.14.1
BLAS : Intel MKL 2025 (mkl-sdl)
OS : Windows 11, reproduced on TWO different machines:
- Intel Core i7-9700, 32GB RAM
- Intel Core Ultra 7 256V, 16GB RAM
Summary of findings:
- Pure numpy array comparisons (no pandas) on a same-sized random int64
array are 100% stable across repeated trials, with or without MKL
multithreading enabled.
- A DataFrame with ~7,000,000 rows, when repeatedly filtered by
df[df['col'] == value] for every unique value in col, will
sometimes (not always, not deterministically) report zero matching
rows for a handful of values that are known to exist in the column
(confirmed via .isin(), value_counts(), and independent numpy-only
checks against the same underlying array).
- The set of "missing" values differs between runs, even when reading
the exact same on-disk file (verified via MD5 checksum) into a fresh
process each time.
- Setting
pd.set_option('compute.use_numexpr', False) before running
the filtering loop makes the problem disappear completely (tested
5+ consecutive runs, 100% consistent, zero false negatives).
- This strongly points to numexpr's multithreaded evaluation path
(which pandas invokes automatically for Series/DataFrame comparisons
once the array exceeds ~100,000 elements) as the source of the
incorrect results, rather than numpy or the underlying BLAS library.
This script generates a synthetic dataset that mimics the shape of the
data where the bug was originally observed (~7M rows, ~1000 distinct
group IDs) and runs the same filter-loop pattern many times to check
for inconsistent results.
NOTE: On smaller arrays / fewer trials this may not reproduce every time.
If you don't see any "MISMATCH" lines after running this, try increasing
N_ROWS, N_GROUPS, or N_TRIALS.
"""
import sys
import platform
import numpy as np
import pandas as pd
---- Config ----
N_ROWS = 7_000_000
N_GROUPS = 1000
N_TRIALS = 10
SEED = 12345
def build_dataframe():
"""Build a synthetic DataFrame with the same shape/dtype characteristics
as the dataset where the bug was originally observed: a large number of
rows, an int64 'group id' column with every id from 0..N_GROUPS-1
guaranteed present, plus a couple of extra float/int columns to make
it a realistic multi-column DataFrame (the bug was NOT reproducible with
a bare 1-D numpy array; it required a pandas DataFrame with Series-level
comparison)."""
rng = np.random.default_rng(SEED)
group_id = rng.integers(0, N_GROUPS, size=N_ROWS).astype(np.int64)
# guarantee every group id appears at least once
group_id[:N_GROUPS] = np.arange(N_GROUPS, dtype=np.int64)
rng.shuffle(group_id)
df = pd.DataFrame({
"group_id": group_id,
"value_a": rng.random(N_ROWS),
"value_b": rng.random(N_ROWS),
"label": rng.integers(0, 5, size=N_ROWS).astype(np.int64),
})
return df
def run_filter_loop(df, unique_ids):
"""Reproduces the real-world usage pattern: loop over every known
group id and filter the DataFrame with a boolean comparison."""
missing = []
for gid in unique_ids:
subset = df[df["group_id"] == gid]
if subset.empty:
missing.append(int(gid))
return missing
def main():
print("=" * 70)
print("Environment")
print("=" * 70)
print("Python :", sys.version.split()[0])
print("Platform:", platform.platform())
print("numpy :", np.version)
print("pandas :", pd.version)
try:
import numexpr
print("numexpr :", numexpr.version)
print("numexpr detected cores:", numexpr.detect_number_of_cores())
except ImportError:
print("numexpr : not installed")
df = build_dataframe()
unique_ids = df["group_id"].unique()
assert len(unique_ids) == N_GROUPS, "sanity check failed: not all group ids present"
print(f"\nDataFrame shape: {df.shape}, unique group_id count: {len(unique_ids)}")
# ---- Phase 1: default settings (numexpr enabled, as pandas ships by default) ----
print("\n" + "=" * 70)
print(f"Phase 1: default settings (compute.use_numexpr = "
f"{pd.get_option('compute.use_numexpr')}), {N_TRIALS} trials")
print("=" * 70)
any_mismatch_default = False
for trial in range(N_TRIALS):
missing = run_filter_loop(df, unique_ids)
if missing:
any_mismatch_default = True
print(f" Trial {trial}: MISMATCH - {len(missing)} group_id(s) "
f"incorrectly reported as missing: {missing}")
else:
print(f" Trial {trial}: OK")
# ---- Phase 2: numexpr disabled ----
pd.set_option("compute.use_numexpr", False)
print("\n" + "=" * 70)
print(f"Phase 2: compute.use_numexpr forced to "
f"{pd.get_option('compute.use_numexpr')}, {N_TRIALS} trials")
print("=" * 70)
any_mismatch_disabled = False
for trial in range(N_TRIALS):
missing = run_filter_loop(df, unique_ids)
if missing:
any_mismatch_disabled = True
print(f" Trial {trial}: MISMATCH - {len(missing)} group_id(s) "
f"incorrectly reported as missing: {missing}")
else:
print(f" Trial {trial}: OK")
# ---- Summary ----
print("\n" + "=" * 70)
print("Summary")
print("=" * 70)
print(f"Mismatches with numexpr ENABLED : {'YES' if any_mismatch_default else 'no'}")
print(f"Mismatches with numexpr DISABLED: {'YES' if any_mismatch_disabled else 'no'}")
if name == "main":
main()
Actual output — Machine 1 (Intel Core i7-9700, 32GB RAM)
======================================================================
Environment
Python : 3.13.9
Platform: Windows-11-10.0.26200-SP0
numpy : 2.3.5
pandas : 2.3.3
numexpr : 2.14.1
numexpr detected cores: 8
DataFrame shape: (7000000, 4), unique group_id count: 1000
Phase 1: default settings (compute.use_numexpr = True), 10 trials
Trial 0: MISMATCH - 8 group_id(s) incorrectly reported as missing: [140, 81, 389, 100, 596, 473, 693, 50]
Trial 1: MISMATCH - 9 group_id(s) incorrectly reported as missing: [493, 698, 761, 327, 93, 393, 477, 619, 960]
Trial 2: MISMATCH - 8 group_id(s) incorrectly reported as missing: [871, 642, 997, 772, 122, 571, 239, 685]
Trial 3: MISMATCH - 9 group_id(s) incorrectly reported as missing: [229, 708, 724, 874, 956, 909, 577, 896, 396]
Trial 4: MISMATCH - 7 group_id(s) incorrectly reported as missing: [460, 850, 665, 671, 758, 914, 340]
Trial 5: MISMATCH - 4 group_id(s) incorrectly reported as missing: [635, 180, 188, 103]
Trial 6: MISMATCH - 9 group_id(s) incorrectly reported as missing: [651, 181, 462, 97, 237, 503, 714, 497, 202]
Trial 7: MISMATCH - 5 group_id(s) incorrectly reported as missing: [879, 657, 68, 912, 197]
Trial 8: MISMATCH - 9 group_id(s) incorrectly reported as missing: [779, 460, 179, 636, 866, 739, 528, 469, 625]
Trial 9: MISMATCH - 3 group_id(s) incorrectly reported as missing: [203, 854, 950]
Phase 2: compute.use_numexpr forced to False, 10 trials
Trial 0: OK
Trial 1: OK
Trial 2: OK
Trial 3: OK
Trial 4: OK
Trial 5: OK
Trial 6: OK
Trial 7: OK
Trial 8: OK
Trial 9: OK
Summary
Mismatches with numexpr ENABLED : YES
Mismatches with numexpr DISABLED: no
Actual output — Machine 2 (Intel Core Ultra 7 256V, 16GB RAM)
======================================================================
Environment
Python : 3.13.9
Platform: Windows-11-10.0.26200-SP0
numpy : 2.3.5
pandas : 2.3.3
numexpr : 2.14.1
numexpr detected cores: 8
DataFrame shape: (7000000, 4), unique group_id count: 1000
Phase 1: default settings (compute.use_numexpr = True), 10 trials
Trial 0: MISMATCH - 7 group_id(s) incorrectly reported as missing: [152, 750, 578, 492, 898, 412, 341]
Trial 1: MISMATCH - 2 group_id(s) incorrectly reported as missing: [136, 580]
Trial 2: MISMATCH - 4 group_id(s) incorrectly reported as missing: [253, 630, 262, 360]
Trial 3: MISMATCH - 1 group_id(s) incorrectly reported as missing: [376]
Trial 4: MISMATCH - 2 group_id(s) incorrectly reported as missing: [469, 202]
Trial 5: MISMATCH - 3 group_id(s) incorrectly reported as missing: [574, 472, 877]
Trial 6: MISMATCH - 3 group_id(s) incorrectly reported as missing: [991, 519, 443]
Trial 7: MISMATCH - 3 group_id(s) incorrectly reported as missing: [969, 306, 775]
Trial 8: MISMATCH - 1 group_id(s) incorrectly reported as missing: [463]
Trial 9: MISMATCH - 2 group_id(s) incorrectly reported as missing: [493, 957]
Phase 2: compute.use_numexpr forced to False, 10 trials
Trial 0: OK
Trial 1: OK
Trial 2: OK
Trial 3: OK
Trial 4: OK
Trial 5: OK
Trial 6: OK
Trial 7: OK
Trial 8: OK
Trial 9: OK
Summary
Mismatches with numexpr ENABLED : YES
Mismatches with numexpr DISABLED: no
Additional notes
- A pure numpy 1-D array comparison (same size, same dtype, no pandas
involved) does NOT reproduce the issue across 10 trials on either
machine, with or without MKL multithreading forced to 1 thread.
- This suggests the issue is specific to the code path pandas uses
for large Series/DataFrame comparisons (i.e. numexpr's evaluate()),
rather than numpy or the BLAS backend itself.
- Forcing single-threaded execution via
MKL_NUM_THREADS=1 / OMP_NUM_THREADS=1 / NUMEXPR_NUM_THREADS=1
(set before importing numpy) also incidentally resolves the issue,
consistent with numexpr honoring the same thread-count environment
variables.
- The number and identity of affected group_ids differs between runs
and between machines, consistent with a race condition rather than
a deterministic logic error.
Description
df[df['col'] == value]on a large (~7M row) DataFrame intermittentlyand silently returns zero matching rows for values that are confirmed
to exist in the column. The set of affected values differs between
runs of the exact same script, even against the exact same in-memory
DataFrame within a single process, and across sessions with an
MD5-verified identical source file.
Setting
pd.set_option('compute.use_numexpr', False)before runningthe comparisons makes the problem disappear completely and reliably
(10/10 trials pass when disabled, vs. 10/10 trials fail when enabled,
on both test machines below).
Environment
Minimal reproducible example
"""
Minimal reproducible example: numexpr / pandas large-array boolean comparison
produces intermittent, silently incorrect results under default (multithreaded)
settings, but is 100% stable when
compute.use_numexpris disabled.Environment where this was observed:
numpy : 2.3.5
pandas : 2.3.3
numexpr : 2.14.1
BLAS : Intel MKL 2025 (mkl-sdl)
OS : Windows 11, reproduced on TWO different machines:
- Intel Core i7-9700, 32GB RAM
- Intel Core Ultra 7 256V, 16GB RAM
Summary of findings:
array are 100% stable across repeated trials, with or without MKL
multithreading enabled.
df[df['col'] == value]for every unique value incol, willsometimes (not always, not deterministically) report zero matching
rows for a handful of values that are known to exist in the column
(confirmed via .isin(), value_counts(), and independent numpy-only
checks against the same underlying array).
the exact same on-disk file (verified via MD5 checksum) into a fresh
process each time.
pd.set_option('compute.use_numexpr', False)before runningthe filtering loop makes the problem disappear completely (tested
5+ consecutive runs, 100% consistent, zero false negatives).
(which pandas invokes automatically for Series/DataFrame comparisons
once the array exceeds ~100,000 elements) as the source of the
incorrect results, rather than numpy or the underlying BLAS library.
This script generates a synthetic dataset that mimics the shape of the
data where the bug was originally observed (~7M rows, ~1000 distinct
group IDs) and runs the same filter-loop pattern many times to check
for inconsistent results.
NOTE: On smaller arrays / fewer trials this may not reproduce every time.
If you don't see any "MISMATCH" lines after running this, try increasing
N_ROWS, N_GROUPS, or N_TRIALS.
"""
import sys
import platform
import numpy as np
import pandas as pd
---- Config ----
N_ROWS = 7_000_000
N_GROUPS = 1000
N_TRIALS = 10
SEED = 12345
def build_dataframe():
"""Build a synthetic DataFrame with the same shape/dtype characteristics
as the dataset where the bug was originally observed: a large number of
rows, an int64 'group id' column with every id from 0..N_GROUPS-1
guaranteed present, plus a couple of extra float/int columns to make
it a realistic multi-column DataFrame (the bug was NOT reproducible with
a bare 1-D numpy array; it required a pandas DataFrame with Series-level
comparison)."""
rng = np.random.default_rng(SEED)
group_id = rng.integers(0, N_GROUPS, size=N_ROWS).astype(np.int64)
# guarantee every group id appears at least once
group_id[:N_GROUPS] = np.arange(N_GROUPS, dtype=np.int64)
rng.shuffle(group_id)
def run_filter_loop(df, unique_ids):
"""Reproduces the real-world usage pattern: loop over every known
group id and filter the DataFrame with a boolean comparison."""
missing = []
for gid in unique_ids:
subset = df[df["group_id"] == gid]
if subset.empty:
missing.append(int(gid))
return missing
def main():
print("=" * 70)
print("Environment")
print("=" * 70)
print("Python :", sys.version.split()[0])
print("Platform:", platform.platform())
print("numpy :", np.version)
print("pandas :", pd.version)
try:
import numexpr
print("numexpr :", numexpr.version)
print("numexpr detected cores:", numexpr.detect_number_of_cores())
except ImportError:
print("numexpr : not installed")
if name == "main":
main()
Actual output — Machine 1 (Intel Core i7-9700, 32GB RAM)
======================================================================
Environment
Python : 3.13.9
Platform: Windows-11-10.0.26200-SP0
numpy : 2.3.5
pandas : 2.3.3
numexpr : 2.14.1
numexpr detected cores: 8
DataFrame shape: (7000000, 4), unique group_id count: 1000
Phase 1: default settings (compute.use_numexpr = True), 10 trials
Trial 0: MISMATCH - 8 group_id(s) incorrectly reported as missing: [140, 81, 389, 100, 596, 473, 693, 50]
Trial 1: MISMATCH - 9 group_id(s) incorrectly reported as missing: [493, 698, 761, 327, 93, 393, 477, 619, 960]
Trial 2: MISMATCH - 8 group_id(s) incorrectly reported as missing: [871, 642, 997, 772, 122, 571, 239, 685]
Trial 3: MISMATCH - 9 group_id(s) incorrectly reported as missing: [229, 708, 724, 874, 956, 909, 577, 896, 396]
Trial 4: MISMATCH - 7 group_id(s) incorrectly reported as missing: [460, 850, 665, 671, 758, 914, 340]
Trial 5: MISMATCH - 4 group_id(s) incorrectly reported as missing: [635, 180, 188, 103]
Trial 6: MISMATCH - 9 group_id(s) incorrectly reported as missing: [651, 181, 462, 97, 237, 503, 714, 497, 202]
Trial 7: MISMATCH - 5 group_id(s) incorrectly reported as missing: [879, 657, 68, 912, 197]
Trial 8: MISMATCH - 9 group_id(s) incorrectly reported as missing: [779, 460, 179, 636, 866, 739, 528, 469, 625]
Trial 9: MISMATCH - 3 group_id(s) incorrectly reported as missing: [203, 854, 950]
Phase 2: compute.use_numexpr forced to False, 10 trials
Trial 0: OK
Trial 1: OK
Trial 2: OK
Trial 3: OK
Trial 4: OK
Trial 5: OK
Trial 6: OK
Trial 7: OK
Trial 8: OK
Trial 9: OK
Summary
Mismatches with numexpr ENABLED : YES
Mismatches with numexpr DISABLED: no
Actual output — Machine 2 (Intel Core Ultra 7 256V, 16GB RAM)
======================================================================
Environment
Python : 3.13.9
Platform: Windows-11-10.0.26200-SP0
numpy : 2.3.5
pandas : 2.3.3
numexpr : 2.14.1
numexpr detected cores: 8
DataFrame shape: (7000000, 4), unique group_id count: 1000
Phase 1: default settings (compute.use_numexpr = True), 10 trials
Trial 0: MISMATCH - 7 group_id(s) incorrectly reported as missing: [152, 750, 578, 492, 898, 412, 341]
Trial 1: MISMATCH - 2 group_id(s) incorrectly reported as missing: [136, 580]
Trial 2: MISMATCH - 4 group_id(s) incorrectly reported as missing: [253, 630, 262, 360]
Trial 3: MISMATCH - 1 group_id(s) incorrectly reported as missing: [376]
Trial 4: MISMATCH - 2 group_id(s) incorrectly reported as missing: [469, 202]
Trial 5: MISMATCH - 3 group_id(s) incorrectly reported as missing: [574, 472, 877]
Trial 6: MISMATCH - 3 group_id(s) incorrectly reported as missing: [991, 519, 443]
Trial 7: MISMATCH - 3 group_id(s) incorrectly reported as missing: [969, 306, 775]
Trial 8: MISMATCH - 1 group_id(s) incorrectly reported as missing: [463]
Trial 9: MISMATCH - 2 group_id(s) incorrectly reported as missing: [493, 957]
Phase 2: compute.use_numexpr forced to False, 10 trials
Trial 0: OK
Trial 1: OK
Trial 2: OK
Trial 3: OK
Trial 4: OK
Trial 5: OK
Trial 6: OK
Trial 7: OK
Trial 8: OK
Trial 9: OK
Summary
Mismatches with numexpr ENABLED : YES
Mismatches with numexpr DISABLED: no
Additional notes
involved) does NOT reproduce the issue across 10 trials on either
machine, with or without MKL multithreading forced to 1 thread.
for large Series/DataFrame comparisons (i.e. numexpr's evaluate()),
rather than numpy or the BLAS backend itself.
MKL_NUM_THREADS=1 / OMP_NUM_THREADS=1 / NUMEXPR_NUM_THREADS=1(set before importing numpy) also incidentally resolves the issue,
consistent with numexpr honoring the same thread-count environment
variables.
and between machines, consistent with a race condition rather than
a deterministic logic error.