So there are 2 questions here.
This is a trick that makes the syntax a little nicer.
In [111]: idx = pd.IndexSlice
1) Your .query does not have the correct priority. The & operator has higher precedence than comparison operators, such as <= , and needs parentheses around its left and right operands.
In [102]: result3 = mdt.query("(@ test_A-@eps _A <= A <= @ test_A+@eps _A) & (@ test_B-@eps _B <= B <= @ test_B+@eps _B) & (@ test_C-@eps _C <= C <= @ test_C+@eps _C) & (@ test_D-@eps _D <= D <= @ test_D+@eps _D)").set_index(['A','B','C','D']).sortlevel()
This is your original request using the MultiIndex slicers.
In [103]: result1 = mdt2.loc[idx[test_A-eps_A:test_A+eps_A,test_B-eps_B:test_B+eps_B,test_C-eps_C:test_C+eps_C,test_D-eps_D:test_D+eps_D],:]
Here is a chained version of this request. IOW re-selects it in the result set.
In [104]: result2 = mdt2.loc[idx[test_A-eps_A:test_A+eps_A],:].loc[idx[:,test_B-eps_B:test_B+eps_B],:].loc[idx[:,:,test_C-eps_C:test_C+eps_C],:].loc[idx[:,:,:,test_D-eps_D:test_D+eps_D],:]
Always check the correctness before working on performance.
In [109]: (result1==result2).all().all() Out[109]: True In [110]: (result1==result3).all().all() Out[110]: True
Performance
.query IMHO will really scale very well and uses multi-core processors. For a large selection of choices, this will be the way to go.
In [107]: %timeit mdt.query("(@ test_A-@eps _A <= A <= @ test_A+@eps _A) & (@ test_B-@eps _B <= B <= @ test_B+@eps _B) & (@ test_C-@eps _C <= C <= @ test_C+@eps _C) & (@ test_D-@eps _D <= D <= @ test_D+@eps _D)").set_index(['A','B','C','D']).sortlevel() 10 loops, best of 3: 107 ms per loop
2) Original slicing with several indices. There are problems here, see below. I donβt know exactly why this does not work, and weβll explore this one here
In [106]: %timeit mdt2.loc[idx[test_A-eps_A:test_A+eps_A,test_B-eps_B:test_B+eps_B,test_C-eps_C:test_C+eps_C,test_D-eps_D:test_D+eps_D],:] 1 loops, best of 3: 4.34 s per loop
Repeated elections make this quite effective. Please note that I usually do not recommend doing this, since you cannot assign it, but thatβs normal for this.
In [105]: %timeit mdt2.loc[idx[test_A-eps_A:test_A+eps_A],:].loc[idx[:,test_B-eps_B:test_B+eps_B],:].loc[idx[:,:,test_C-eps_C:test_C+eps_C],:].loc[idx[:,:,:,test_D-eps_D:test_D+eps_D],:] 10 loops, best of 3: 140 ms per loop