Pandas 1.4 eval cache keyed to expression string only
0 reputation · 06 Aug 2020, 20:30 UTC
0 reputation · 06 Aug 2020, 20:30 UTC
Since pandas 1.4, the pd.eval function caches the parsed expression tree to accelerate repeated evaluations of identical strings. The cache key is derived exclusively from the expression string and does not track the current values of variables referenced within it. This design choice can produce stale results when a variable's value changes after the first pd.eval call, because subsequent evaluations return outcomes based on the original variable state.
The pandas documentation acknowledges this as a performance optimization but does not resolve whether the cache should be invalidated upon variable mutation, leaving the behavior as an unresolved design decision. Determining an appropriate invalidation strategy requires balancing the performance benefits of caching against the correctness guarantees required by users working with mutable state in pd.eval expressions.
Should pd.eval automatically invalidate its cache when any referenced variable is mutated? Would a version-specific option to disable caching be preferable to the current always-on behavior? Is there a recommended pattern for users to avoid stale results without manual cache intervention?
In pandas 1.4+, pd.eval() caches the compiled expression tree using the expression string as the key. To avoid stale results when referenced variables are mutated, you must ensure the execution phase receives the updated environment or bypass the cache entirely.
It is important to distinguish between the compiled expression tree and the evaluated result. The cache stores the parsed logic (the "how"), not the final output (the "what"). Under normal operation, pd.eval() should use the current values of the variables provided in the local or global namespace during the execution phase.
If you are observing stale results despite variable mutation, it is likely due to one of the following:
local_dict or global_dict during the evaluation.numexpr engine (the default for many numeric operations) may handle memory alignment or caching differently than the python engine.Since there is currently no global toggle to disable the expression tree cache in pandas 1.4, use these strategies to ensure correctness:
Avoid relying on implicit global lookups. Pass a dictionary to the local_dict parameter to ensure the evaluator is looking at the most recent version of your data.
import pandas as pd
df = pd.DataFrame({'a': [1, 2]})
# First call
res1 = pd.eval("df.a + 1", local_dict={'df': df})
# Mutate data
df['a'] = [10, 20]
# Second call with explicit namespace
res2 = pd.eval("df.a + 1", local_dict={'df': df})
If numexpr is producing unexpected results during mutation, switch to the python engine. This bypasses the specialized numeric optimizations of numexpr and uses standard Python evaluation logic.
pd.eval("df.a + 1", engine='python')
If you must completely bypass the cache, appending a unique identifier (like a timestamp or counter) to a comment within the string will force a re-parse, as the cache key is the string itself.
# Forces a cache miss by changing the string key
pd.eval(f"df.a + 1 # {timestamp}")
To determine if your specific environment is suffering from a cache-related bug or a namespace issue, run the following test:
pd.eval("df.a + 1").df in place.pd.eval("df.a + 1", engine='python').If the python engine returns the correct value while the default engine does not, the issue is isolated to the numexpr backend's interaction with the cached tree.
Diagnostic Detail Needed: Are you using the default numexpr engine or have you explicitly set engine='python'?
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.