test_query_eval.py 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040
  1. # -*- coding: utf-8 -*-
  2. from __future__ import print_function
  3. import operator
  4. import numpy as np
  5. import pytest
  6. from pandas.compat import StringIO, lrange, range, zip
  7. import pandas.util._test_decorators as td
  8. import pandas as pd
  9. from pandas import DataFrame, Index, MultiIndex, Series, date_range
  10. from pandas.core.computation.check import _NUMEXPR_INSTALLED
  11. from pandas.tests.frame.common import TestData
  12. import pandas.util.testing as tm
  13. from pandas.util.testing import (
  14. assert_frame_equal, assert_series_equal, makeCustomDataframe as mkdf)
  15. PARSERS = 'python', 'pandas'
  16. ENGINES = 'python', pytest.param('numexpr', marks=td.skip_if_no_ne)
  17. @pytest.fixture(params=PARSERS, ids=lambda x: x)
  18. def parser(request):
  19. return request.param
  20. @pytest.fixture(params=ENGINES, ids=lambda x: x)
  21. def engine(request):
  22. return request.param
  23. def skip_if_no_pandas_parser(parser):
  24. if parser != 'pandas':
  25. pytest.skip("cannot evaluate with parser {0!r}".format(parser))
  26. class TestCompat(object):
  27. def setup_method(self, method):
  28. self.df = DataFrame({'A': [1, 2, 3]})
  29. self.expected1 = self.df[self.df.A > 0]
  30. self.expected2 = self.df.A + 1
  31. def test_query_default(self):
  32. # GH 12749
  33. # this should always work, whether _NUMEXPR_INSTALLED or not
  34. df = self.df
  35. result = df.query('A>0')
  36. assert_frame_equal(result, self.expected1)
  37. result = df.eval('A+1')
  38. assert_series_equal(result, self.expected2, check_names=False)
  39. def test_query_None(self):
  40. df = self.df
  41. result = df.query('A>0', engine=None)
  42. assert_frame_equal(result, self.expected1)
  43. result = df.eval('A+1', engine=None)
  44. assert_series_equal(result, self.expected2, check_names=False)
  45. def test_query_python(self):
  46. df = self.df
  47. result = df.query('A>0', engine='python')
  48. assert_frame_equal(result, self.expected1)
  49. result = df.eval('A+1', engine='python')
  50. assert_series_equal(result, self.expected2, check_names=False)
  51. def test_query_numexpr(self):
  52. df = self.df
  53. if _NUMEXPR_INSTALLED:
  54. result = df.query('A>0', engine='numexpr')
  55. assert_frame_equal(result, self.expected1)
  56. result = df.eval('A+1', engine='numexpr')
  57. assert_series_equal(result, self.expected2, check_names=False)
  58. else:
  59. pytest.raises(ImportError,
  60. lambda: df.query('A>0', engine='numexpr'))
  61. pytest.raises(ImportError,
  62. lambda: df.eval('A+1', engine='numexpr'))
  63. class TestDataFrameEval(TestData):
  64. def test_ops(self):
  65. # tst ops and reversed ops in evaluation
  66. # GH7198
  67. # smaller hits python, larger hits numexpr
  68. for n in [4, 4000]:
  69. df = DataFrame(1, index=range(n), columns=list('abcd'))
  70. df.iloc[0] = 2
  71. m = df.mean()
  72. for op_str, op, rop in [('+', '__add__', '__radd__'),
  73. ('-', '__sub__', '__rsub__'),
  74. ('*', '__mul__', '__rmul__'),
  75. ('/', '__truediv__', '__rtruediv__')]:
  76. base = (DataFrame(np.tile(m.values, n) # noqa
  77. .reshape(n, -1),
  78. columns=list('abcd')))
  79. expected = eval("base{op}df".format(op=op_str))
  80. # ops as strings
  81. result = eval("m{op}df".format(op=op_str))
  82. assert_frame_equal(result, expected)
  83. # these are commutative
  84. if op in ['+', '*']:
  85. result = getattr(df, op)(m)
  86. assert_frame_equal(result, expected)
  87. # these are not
  88. elif op in ['-', '/']:
  89. result = getattr(df, rop)(m)
  90. assert_frame_equal(result, expected)
  91. # GH7192
  92. df = DataFrame(dict(A=np.random.randn(25000)))
  93. df.iloc[0:5] = np.nan
  94. expected = (1 - np.isnan(df.iloc[0:25]))
  95. result = (1 - np.isnan(df)).iloc[0:25]
  96. assert_frame_equal(result, expected)
  97. def test_query_non_str(self):
  98. # GH 11485
  99. df = pd.DataFrame({'A': [1, 2, 3], 'B': ['a', 'b', 'b']})
  100. msg = "expr must be a string to be evaluated"
  101. with pytest.raises(ValueError, match=msg):
  102. df.query(lambda x: x.B == "b")
  103. with pytest.raises(ValueError, match=msg):
  104. df.query(111)
  105. def test_query_empty_string(self):
  106. # GH 13139
  107. df = pd.DataFrame({'A': [1, 2, 3]})
  108. msg = "expr cannot be an empty string"
  109. with pytest.raises(ValueError, match=msg):
  110. df.query('')
  111. def test_eval_resolvers_as_list(self):
  112. # GH 14095
  113. df = DataFrame(np.random.randn(10, 2), columns=list('ab'))
  114. dict1 = {'a': 1}
  115. dict2 = {'b': 2}
  116. assert (df.eval('a + b', resolvers=[dict1, dict2]) ==
  117. dict1['a'] + dict2['b'])
  118. assert (pd.eval('a + b', resolvers=[dict1, dict2]) ==
  119. dict1['a'] + dict2['b'])
  120. class TestDataFrameQueryWithMultiIndex(object):
  121. def test_query_with_named_multiindex(self, parser, engine):
  122. skip_if_no_pandas_parser(parser)
  123. a = np.random.choice(['red', 'green'], size=10)
  124. b = np.random.choice(['eggs', 'ham'], size=10)
  125. index = MultiIndex.from_arrays([a, b], names=['color', 'food'])
  126. df = DataFrame(np.random.randn(10, 2), index=index)
  127. ind = Series(df.index.get_level_values('color').values, index=index,
  128. name='color')
  129. # equality
  130. res1 = df.query('color == "red"', parser=parser, engine=engine)
  131. res2 = df.query('"red" == color', parser=parser, engine=engine)
  132. exp = df[ind == 'red']
  133. assert_frame_equal(res1, exp)
  134. assert_frame_equal(res2, exp)
  135. # inequality
  136. res1 = df.query('color != "red"', parser=parser, engine=engine)
  137. res2 = df.query('"red" != color', parser=parser, engine=engine)
  138. exp = df[ind != 'red']
  139. assert_frame_equal(res1, exp)
  140. assert_frame_equal(res2, exp)
  141. # list equality (really just set membership)
  142. res1 = df.query('color == ["red"]', parser=parser, engine=engine)
  143. res2 = df.query('["red"] == color', parser=parser, engine=engine)
  144. exp = df[ind.isin(['red'])]
  145. assert_frame_equal(res1, exp)
  146. assert_frame_equal(res2, exp)
  147. res1 = df.query('color != ["red"]', parser=parser, engine=engine)
  148. res2 = df.query('["red"] != color', parser=parser, engine=engine)
  149. exp = df[~ind.isin(['red'])]
  150. assert_frame_equal(res1, exp)
  151. assert_frame_equal(res2, exp)
  152. # in/not in ops
  153. res1 = df.query('["red"] in color', parser=parser, engine=engine)
  154. res2 = df.query('"red" in color', parser=parser, engine=engine)
  155. exp = df[ind.isin(['red'])]
  156. assert_frame_equal(res1, exp)
  157. assert_frame_equal(res2, exp)
  158. res1 = df.query('["red"] not in color', parser=parser, engine=engine)
  159. res2 = df.query('"red" not in color', parser=parser, engine=engine)
  160. exp = df[~ind.isin(['red'])]
  161. assert_frame_equal(res1, exp)
  162. assert_frame_equal(res2, exp)
  163. def test_query_with_unnamed_multiindex(self, parser, engine):
  164. skip_if_no_pandas_parser(parser)
  165. a = np.random.choice(['red', 'green'], size=10)
  166. b = np.random.choice(['eggs', 'ham'], size=10)
  167. index = MultiIndex.from_arrays([a, b])
  168. df = DataFrame(np.random.randn(10, 2), index=index)
  169. ind = Series(df.index.get_level_values(0).values, index=index)
  170. res1 = df.query('ilevel_0 == "red"', parser=parser, engine=engine)
  171. res2 = df.query('"red" == ilevel_0', parser=parser, engine=engine)
  172. exp = df[ind == 'red']
  173. assert_frame_equal(res1, exp)
  174. assert_frame_equal(res2, exp)
  175. # inequality
  176. res1 = df.query('ilevel_0 != "red"', parser=parser, engine=engine)
  177. res2 = df.query('"red" != ilevel_0', parser=parser, engine=engine)
  178. exp = df[ind != 'red']
  179. assert_frame_equal(res1, exp)
  180. assert_frame_equal(res2, exp)
  181. # list equality (really just set membership)
  182. res1 = df.query('ilevel_0 == ["red"]', parser=parser, engine=engine)
  183. res2 = df.query('["red"] == ilevel_0', parser=parser, engine=engine)
  184. exp = df[ind.isin(['red'])]
  185. assert_frame_equal(res1, exp)
  186. assert_frame_equal(res2, exp)
  187. res1 = df.query('ilevel_0 != ["red"]', parser=parser, engine=engine)
  188. res2 = df.query('["red"] != ilevel_0', parser=parser, engine=engine)
  189. exp = df[~ind.isin(['red'])]
  190. assert_frame_equal(res1, exp)
  191. assert_frame_equal(res2, exp)
  192. # in/not in ops
  193. res1 = df.query('["red"] in ilevel_0', parser=parser, engine=engine)
  194. res2 = df.query('"red" in ilevel_0', parser=parser, engine=engine)
  195. exp = df[ind.isin(['red'])]
  196. assert_frame_equal(res1, exp)
  197. assert_frame_equal(res2, exp)
  198. res1 = df.query('["red"] not in ilevel_0', parser=parser,
  199. engine=engine)
  200. res2 = df.query('"red" not in ilevel_0', parser=parser, engine=engine)
  201. exp = df[~ind.isin(['red'])]
  202. assert_frame_equal(res1, exp)
  203. assert_frame_equal(res2, exp)
  204. # ## LEVEL 1
  205. ind = Series(df.index.get_level_values(1).values, index=index)
  206. res1 = df.query('ilevel_1 == "eggs"', parser=parser, engine=engine)
  207. res2 = df.query('"eggs" == ilevel_1', parser=parser, engine=engine)
  208. exp = df[ind == 'eggs']
  209. assert_frame_equal(res1, exp)
  210. assert_frame_equal(res2, exp)
  211. # inequality
  212. res1 = df.query('ilevel_1 != "eggs"', parser=parser, engine=engine)
  213. res2 = df.query('"eggs" != ilevel_1', parser=parser, engine=engine)
  214. exp = df[ind != 'eggs']
  215. assert_frame_equal(res1, exp)
  216. assert_frame_equal(res2, exp)
  217. # list equality (really just set membership)
  218. res1 = df.query('ilevel_1 == ["eggs"]', parser=parser, engine=engine)
  219. res2 = df.query('["eggs"] == ilevel_1', parser=parser, engine=engine)
  220. exp = df[ind.isin(['eggs'])]
  221. assert_frame_equal(res1, exp)
  222. assert_frame_equal(res2, exp)
  223. res1 = df.query('ilevel_1 != ["eggs"]', parser=parser, engine=engine)
  224. res2 = df.query('["eggs"] != ilevel_1', parser=parser, engine=engine)
  225. exp = df[~ind.isin(['eggs'])]
  226. assert_frame_equal(res1, exp)
  227. assert_frame_equal(res2, exp)
  228. # in/not in ops
  229. res1 = df.query('["eggs"] in ilevel_1', parser=parser, engine=engine)
  230. res2 = df.query('"eggs" in ilevel_1', parser=parser, engine=engine)
  231. exp = df[ind.isin(['eggs'])]
  232. assert_frame_equal(res1, exp)
  233. assert_frame_equal(res2, exp)
  234. res1 = df.query('["eggs"] not in ilevel_1', parser=parser,
  235. engine=engine)
  236. res2 = df.query('"eggs" not in ilevel_1', parser=parser, engine=engine)
  237. exp = df[~ind.isin(['eggs'])]
  238. assert_frame_equal(res1, exp)
  239. assert_frame_equal(res2, exp)
  240. def test_query_with_partially_named_multiindex(self, parser, engine):
  241. skip_if_no_pandas_parser(parser)
  242. a = np.random.choice(['red', 'green'], size=10)
  243. b = np.arange(10)
  244. index = MultiIndex.from_arrays([a, b])
  245. index.names = [None, 'rating']
  246. df = DataFrame(np.random.randn(10, 2), index=index)
  247. res = df.query('rating == 1', parser=parser, engine=engine)
  248. ind = Series(df.index.get_level_values('rating').values, index=index,
  249. name='rating')
  250. exp = df[ind == 1]
  251. assert_frame_equal(res, exp)
  252. res = df.query('rating != 1', parser=parser, engine=engine)
  253. ind = Series(df.index.get_level_values('rating').values, index=index,
  254. name='rating')
  255. exp = df[ind != 1]
  256. assert_frame_equal(res, exp)
  257. res = df.query('ilevel_0 == "red"', parser=parser, engine=engine)
  258. ind = Series(df.index.get_level_values(0).values, index=index)
  259. exp = df[ind == "red"]
  260. assert_frame_equal(res, exp)
  261. res = df.query('ilevel_0 != "red"', parser=parser, engine=engine)
  262. ind = Series(df.index.get_level_values(0).values, index=index)
  263. exp = df[ind != "red"]
  264. assert_frame_equal(res, exp)
  265. def test_query_multiindex_get_index_resolvers(self):
  266. df = mkdf(10, 3, r_idx_nlevels=2, r_idx_names=['spam', 'eggs'])
  267. resolvers = df._get_index_resolvers()
  268. def to_series(mi, level):
  269. level_values = mi.get_level_values(level)
  270. s = level_values.to_series()
  271. s.index = mi
  272. return s
  273. col_series = df.columns.to_series()
  274. expected = {'index': df.index,
  275. 'columns': col_series,
  276. 'spam': to_series(df.index, 'spam'),
  277. 'eggs': to_series(df.index, 'eggs'),
  278. 'C0': col_series}
  279. for k, v in resolvers.items():
  280. if isinstance(v, Index):
  281. assert v.is_(expected[k])
  282. elif isinstance(v, Series):
  283. assert_series_equal(v, expected[k])
  284. else:
  285. raise AssertionError("object must be a Series or Index")
  286. @pytest.mark.filterwarnings("ignore::FutureWarning")
  287. def test_raise_on_panel_with_multiindex(self, parser, engine):
  288. p = tm.makePanel(7)
  289. p.items = tm.makeCustomIndex(len(p.items), nlevels=2)
  290. with pytest.raises(NotImplementedError):
  291. pd.eval('p + 1', parser=parser, engine=engine)
  292. @td.skip_if_no_ne
  293. class TestDataFrameQueryNumExprPandas(object):
  294. @classmethod
  295. def setup_class(cls):
  296. cls.engine = 'numexpr'
  297. cls.parser = 'pandas'
  298. @classmethod
  299. def teardown_class(cls):
  300. del cls.engine, cls.parser
  301. def test_date_query_with_attribute_access(self):
  302. engine, parser = self.engine, self.parser
  303. skip_if_no_pandas_parser(parser)
  304. df = DataFrame(np.random.randn(5, 3))
  305. df['dates1'] = date_range('1/1/2012', periods=5)
  306. df['dates2'] = date_range('1/1/2013', periods=5)
  307. df['dates3'] = date_range('1/1/2014', periods=5)
  308. res = df.query('@df.dates1 < 20130101 < @df.dates3', engine=engine,
  309. parser=parser)
  310. expec = df[(df.dates1 < '20130101') & ('20130101' < df.dates3)]
  311. assert_frame_equal(res, expec)
  312. def test_date_query_no_attribute_access(self):
  313. engine, parser = self.engine, self.parser
  314. df = DataFrame(np.random.randn(5, 3))
  315. df['dates1'] = date_range('1/1/2012', periods=5)
  316. df['dates2'] = date_range('1/1/2013', periods=5)
  317. df['dates3'] = date_range('1/1/2014', periods=5)
  318. res = df.query('dates1 < 20130101 < dates3', engine=engine,
  319. parser=parser)
  320. expec = df[(df.dates1 < '20130101') & ('20130101' < df.dates3)]
  321. assert_frame_equal(res, expec)
  322. def test_date_query_with_NaT(self):
  323. engine, parser = self.engine, self.parser
  324. n = 10
  325. df = DataFrame(np.random.randn(n, 3))
  326. df['dates1'] = date_range('1/1/2012', periods=n)
  327. df['dates2'] = date_range('1/1/2013', periods=n)
  328. df['dates3'] = date_range('1/1/2014', periods=n)
  329. df.loc[np.random.rand(n) > 0.5, 'dates1'] = pd.NaT
  330. df.loc[np.random.rand(n) > 0.5, 'dates3'] = pd.NaT
  331. res = df.query('dates1 < 20130101 < dates3', engine=engine,
  332. parser=parser)
  333. expec = df[(df.dates1 < '20130101') & ('20130101' < df.dates3)]
  334. assert_frame_equal(res, expec)
  335. def test_date_index_query(self):
  336. engine, parser = self.engine, self.parser
  337. n = 10
  338. df = DataFrame(np.random.randn(n, 3))
  339. df['dates1'] = date_range('1/1/2012', periods=n)
  340. df['dates3'] = date_range('1/1/2014', periods=n)
  341. df.set_index('dates1', inplace=True, drop=True)
  342. res = df.query('index < 20130101 < dates3', engine=engine,
  343. parser=parser)
  344. expec = df[(df.index < '20130101') & ('20130101' < df.dates3)]
  345. assert_frame_equal(res, expec)
  346. def test_date_index_query_with_NaT(self):
  347. engine, parser = self.engine, self.parser
  348. n = 10
  349. df = DataFrame(np.random.randn(n, 3))
  350. df['dates1'] = date_range('1/1/2012', periods=n)
  351. df['dates3'] = date_range('1/1/2014', periods=n)
  352. df.iloc[0, 0] = pd.NaT
  353. df.set_index('dates1', inplace=True, drop=True)
  354. res = df.query('index < 20130101 < dates3', engine=engine,
  355. parser=parser)
  356. expec = df[(df.index < '20130101') & ('20130101' < df.dates3)]
  357. assert_frame_equal(res, expec)
  358. def test_date_index_query_with_NaT_duplicates(self):
  359. engine, parser = self.engine, self.parser
  360. n = 10
  361. d = {}
  362. d['dates1'] = date_range('1/1/2012', periods=n)
  363. d['dates3'] = date_range('1/1/2014', periods=n)
  364. df = DataFrame(d)
  365. df.loc[np.random.rand(n) > 0.5, 'dates1'] = pd.NaT
  366. df.set_index('dates1', inplace=True, drop=True)
  367. res = df.query('dates1 < 20130101 < dates3', engine=engine,
  368. parser=parser)
  369. expec = df[(df.index.to_series() < '20130101') &
  370. ('20130101' < df.dates3)]
  371. assert_frame_equal(res, expec)
  372. def test_date_query_with_non_date(self):
  373. engine, parser = self.engine, self.parser
  374. n = 10
  375. df = DataFrame({'dates': date_range('1/1/2012', periods=n),
  376. 'nondate': np.arange(n)})
  377. result = df.query('dates == nondate', parser=parser, engine=engine)
  378. assert len(result) == 0
  379. result = df.query('dates != nondate', parser=parser, engine=engine)
  380. assert_frame_equal(result, df)
  381. for op in ['<', '>', '<=', '>=']:
  382. with pytest.raises(TypeError):
  383. df.query('dates %s nondate' % op, parser=parser, engine=engine)
  384. def test_query_syntax_error(self):
  385. engine, parser = self.engine, self.parser
  386. df = DataFrame({"i": lrange(10), "+": lrange(3, 13),
  387. "r": lrange(4, 14)})
  388. with pytest.raises(SyntaxError):
  389. df.query('i - +', engine=engine, parser=parser)
  390. def test_query_scope(self):
  391. from pandas.core.computation.ops import UndefinedVariableError
  392. engine, parser = self.engine, self.parser
  393. skip_if_no_pandas_parser(parser)
  394. df = DataFrame(np.random.randn(20, 2), columns=list('ab'))
  395. a, b = 1, 2 # noqa
  396. res = df.query('a > b', engine=engine, parser=parser)
  397. expected = df[df.a > df.b]
  398. assert_frame_equal(res, expected)
  399. res = df.query('@a > b', engine=engine, parser=parser)
  400. expected = df[a > df.b]
  401. assert_frame_equal(res, expected)
  402. # no local variable c
  403. with pytest.raises(UndefinedVariableError):
  404. df.query('@a > b > @c', engine=engine, parser=parser)
  405. # no column named 'c'
  406. with pytest.raises(UndefinedVariableError):
  407. df.query('@a > b > c', engine=engine, parser=parser)
  408. def test_query_doesnt_pickup_local(self):
  409. from pandas.core.computation.ops import UndefinedVariableError
  410. engine, parser = self.engine, self.parser
  411. n = m = 10
  412. df = DataFrame(np.random.randint(m, size=(n, 3)), columns=list('abc'))
  413. # we don't pick up the local 'sin'
  414. with pytest.raises(UndefinedVariableError):
  415. df.query('sin > 5', engine=engine, parser=parser)
  416. def test_query_builtin(self):
  417. from pandas.core.computation.engines import NumExprClobberingError
  418. engine, parser = self.engine, self.parser
  419. n = m = 10
  420. df = DataFrame(np.random.randint(m, size=(n, 3)), columns=list('abc'))
  421. df.index.name = 'sin'
  422. msg = 'Variables in expression.+'
  423. with pytest.raises(NumExprClobberingError, match=msg):
  424. df.query('sin > 5', engine=engine, parser=parser)
  425. def test_query(self):
  426. engine, parser = self.engine, self.parser
  427. df = DataFrame(np.random.randn(10, 3), columns=['a', 'b', 'c'])
  428. assert_frame_equal(df.query('a < b', engine=engine, parser=parser),
  429. df[df.a < df.b])
  430. assert_frame_equal(df.query('a + b > b * c', engine=engine,
  431. parser=parser),
  432. df[df.a + df.b > df.b * df.c])
  433. def test_query_index_with_name(self):
  434. engine, parser = self.engine, self.parser
  435. df = DataFrame(np.random.randint(10, size=(10, 3)),
  436. index=Index(range(10), name='blob'),
  437. columns=['a', 'b', 'c'])
  438. res = df.query('(blob < 5) & (a < b)', engine=engine, parser=parser)
  439. expec = df[(df.index < 5) & (df.a < df.b)]
  440. assert_frame_equal(res, expec)
  441. res = df.query('blob < b', engine=engine, parser=parser)
  442. expec = df[df.index < df.b]
  443. assert_frame_equal(res, expec)
  444. def test_query_index_without_name(self):
  445. engine, parser = self.engine, self.parser
  446. df = DataFrame(np.random.randint(10, size=(10, 3)),
  447. index=range(10), columns=['a', 'b', 'c'])
  448. # "index" should refer to the index
  449. res = df.query('index < b', engine=engine, parser=parser)
  450. expec = df[df.index < df.b]
  451. assert_frame_equal(res, expec)
  452. # test against a scalar
  453. res = df.query('index < 5', engine=engine, parser=parser)
  454. expec = df[df.index < 5]
  455. assert_frame_equal(res, expec)
  456. def test_nested_scope(self):
  457. engine = self.engine
  458. parser = self.parser
  459. skip_if_no_pandas_parser(parser)
  460. df = DataFrame(np.random.randn(5, 3))
  461. df2 = DataFrame(np.random.randn(5, 3))
  462. expected = df[(df > 0) & (df2 > 0)]
  463. result = df.query('(@df > 0) & (@df2 > 0)', engine=engine,
  464. parser=parser)
  465. assert_frame_equal(result, expected)
  466. result = pd.eval('df[df > 0 and df2 > 0]', engine=engine,
  467. parser=parser)
  468. assert_frame_equal(result, expected)
  469. result = pd.eval('df[df > 0 and df2 > 0 and df[df > 0] > 0]',
  470. engine=engine, parser=parser)
  471. expected = df[(df > 0) & (df2 > 0) & (df[df > 0] > 0)]
  472. assert_frame_equal(result, expected)
  473. result = pd.eval('df[(df>0) & (df2>0)]', engine=engine, parser=parser)
  474. expected = df.query('(@df>0) & (@df2>0)', engine=engine, parser=parser)
  475. assert_frame_equal(result, expected)
  476. def test_nested_raises_on_local_self_reference(self):
  477. from pandas.core.computation.ops import UndefinedVariableError
  478. df = DataFrame(np.random.randn(5, 3))
  479. # can't reference ourself b/c we're a local so @ is necessary
  480. with pytest.raises(UndefinedVariableError):
  481. df.query('df > 0', engine=self.engine, parser=self.parser)
  482. def test_local_syntax(self):
  483. skip_if_no_pandas_parser(self.parser)
  484. engine, parser = self.engine, self.parser
  485. df = DataFrame(np.random.randn(100, 10), columns=list('abcdefghij'))
  486. b = 1
  487. expect = df[df.a < b]
  488. result = df.query('a < @b', engine=engine, parser=parser)
  489. assert_frame_equal(result, expect)
  490. expect = df[df.a < df.b]
  491. result = df.query('a < b', engine=engine, parser=parser)
  492. assert_frame_equal(result, expect)
  493. def test_chained_cmp_and_in(self):
  494. skip_if_no_pandas_parser(self.parser)
  495. engine, parser = self.engine, self.parser
  496. cols = list('abc')
  497. df = DataFrame(np.random.randn(100, len(cols)), columns=cols)
  498. res = df.query('a < b < c and a not in b not in c', engine=engine,
  499. parser=parser)
  500. ind = (df.a < df.b) & (df.b < df.c) & ~df.b.isin(df.a) & ~df.c.isin(df.b) # noqa
  501. expec = df[ind]
  502. assert_frame_equal(res, expec)
  503. def test_local_variable_with_in(self):
  504. engine, parser = self.engine, self.parser
  505. skip_if_no_pandas_parser(parser)
  506. a = Series(np.random.randint(3, size=15), name='a')
  507. b = Series(np.random.randint(10, size=15), name='b')
  508. df = DataFrame({'a': a, 'b': b})
  509. expected = df.loc[(df.b - 1).isin(a)]
  510. result = df.query('b - 1 in a', engine=engine, parser=parser)
  511. assert_frame_equal(expected, result)
  512. b = Series(np.random.randint(10, size=15), name='b')
  513. expected = df.loc[(b - 1).isin(a)]
  514. result = df.query('@b - 1 in a', engine=engine, parser=parser)
  515. assert_frame_equal(expected, result)
  516. def test_at_inside_string(self):
  517. engine, parser = self.engine, self.parser
  518. skip_if_no_pandas_parser(parser)
  519. c = 1 # noqa
  520. df = DataFrame({'a': ['a', 'a', 'b', 'b', '@c', '@c']})
  521. result = df.query('a == "@c"', engine=engine, parser=parser)
  522. expected = df[df.a == "@c"]
  523. assert_frame_equal(result, expected)
  524. def test_query_undefined_local(self):
  525. from pandas.core.computation.ops import UndefinedVariableError
  526. engine, parser = self.engine, self.parser
  527. skip_if_no_pandas_parser(parser)
  528. df = DataFrame(np.random.rand(10, 2), columns=list('ab'))
  529. msg = "local variable 'c' is not defined"
  530. with pytest.raises(UndefinedVariableError, match=msg):
  531. df.query('a == @c', engine=engine, parser=parser)
  532. def test_index_resolvers_come_after_columns_with_the_same_name(self):
  533. n = 1 # noqa
  534. a = np.r_[20:101:20]
  535. df = DataFrame({'index': a, 'b': np.random.randn(a.size)})
  536. df.index.name = 'index'
  537. result = df.query('index > 5', engine=self.engine, parser=self.parser)
  538. expected = df[df['index'] > 5]
  539. assert_frame_equal(result, expected)
  540. df = DataFrame({'index': a,
  541. 'b': np.random.randn(a.size)})
  542. result = df.query('ilevel_0 > 5', engine=self.engine,
  543. parser=self.parser)
  544. expected = df.loc[df.index[df.index > 5]]
  545. assert_frame_equal(result, expected)
  546. df = DataFrame({'a': a, 'b': np.random.randn(a.size)})
  547. df.index.name = 'a'
  548. result = df.query('a > 5', engine=self.engine, parser=self.parser)
  549. expected = df[df.a > 5]
  550. assert_frame_equal(result, expected)
  551. result = df.query('index > 5', engine=self.engine, parser=self.parser)
  552. expected = df.loc[df.index[df.index > 5]]
  553. assert_frame_equal(result, expected)
  554. def test_inf(self):
  555. n = 10
  556. df = DataFrame({'a': np.random.rand(n), 'b': np.random.rand(n)})
  557. df.loc[::2, 0] = np.inf
  558. ops = '==', '!='
  559. d = dict(zip(ops, (operator.eq, operator.ne)))
  560. for op, f in d.items():
  561. q = 'a %s inf' % op
  562. expected = df[f(df.a, np.inf)]
  563. result = df.query(q, engine=self.engine, parser=self.parser)
  564. assert_frame_equal(result, expected)
  565. @td.skip_if_no_ne
  566. class TestDataFrameQueryNumExprPython(TestDataFrameQueryNumExprPandas):
  567. @classmethod
  568. def setup_class(cls):
  569. super(TestDataFrameQueryNumExprPython, cls).setup_class()
  570. cls.engine = 'numexpr'
  571. cls.parser = 'python'
  572. cls.frame = TestData().frame
  573. def test_date_query_no_attribute_access(self):
  574. engine, parser = self.engine, self.parser
  575. df = DataFrame(np.random.randn(5, 3))
  576. df['dates1'] = date_range('1/1/2012', periods=5)
  577. df['dates2'] = date_range('1/1/2013', periods=5)
  578. df['dates3'] = date_range('1/1/2014', periods=5)
  579. res = df.query('(dates1 < 20130101) & (20130101 < dates3)',
  580. engine=engine, parser=parser)
  581. expec = df[(df.dates1 < '20130101') & ('20130101' < df.dates3)]
  582. assert_frame_equal(res, expec)
  583. def test_date_query_with_NaT(self):
  584. engine, parser = self.engine, self.parser
  585. n = 10
  586. df = DataFrame(np.random.randn(n, 3))
  587. df['dates1'] = date_range('1/1/2012', periods=n)
  588. df['dates2'] = date_range('1/1/2013', periods=n)
  589. df['dates3'] = date_range('1/1/2014', periods=n)
  590. df.loc[np.random.rand(n) > 0.5, 'dates1'] = pd.NaT
  591. df.loc[np.random.rand(n) > 0.5, 'dates3'] = pd.NaT
  592. res = df.query('(dates1 < 20130101) & (20130101 < dates3)',
  593. engine=engine, parser=parser)
  594. expec = df[(df.dates1 < '20130101') & ('20130101' < df.dates3)]
  595. assert_frame_equal(res, expec)
  596. def test_date_index_query(self):
  597. engine, parser = self.engine, self.parser
  598. n = 10
  599. df = DataFrame(np.random.randn(n, 3))
  600. df['dates1'] = date_range('1/1/2012', periods=n)
  601. df['dates3'] = date_range('1/1/2014', periods=n)
  602. df.set_index('dates1', inplace=True, drop=True)
  603. res = df.query('(index < 20130101) & (20130101 < dates3)',
  604. engine=engine, parser=parser)
  605. expec = df[(df.index < '20130101') & ('20130101' < df.dates3)]
  606. assert_frame_equal(res, expec)
  607. def test_date_index_query_with_NaT(self):
  608. engine, parser = self.engine, self.parser
  609. n = 10
  610. df = DataFrame(np.random.randn(n, 3))
  611. df['dates1'] = date_range('1/1/2012', periods=n)
  612. df['dates3'] = date_range('1/1/2014', periods=n)
  613. df.iloc[0, 0] = pd.NaT
  614. df.set_index('dates1', inplace=True, drop=True)
  615. res = df.query('(index < 20130101) & (20130101 < dates3)',
  616. engine=engine, parser=parser)
  617. expec = df[(df.index < '20130101') & ('20130101' < df.dates3)]
  618. assert_frame_equal(res, expec)
  619. def test_date_index_query_with_NaT_duplicates(self):
  620. engine, parser = self.engine, self.parser
  621. n = 10
  622. df = DataFrame(np.random.randn(n, 3))
  623. df['dates1'] = date_range('1/1/2012', periods=n)
  624. df['dates3'] = date_range('1/1/2014', periods=n)
  625. df.loc[np.random.rand(n) > 0.5, 'dates1'] = pd.NaT
  626. df.set_index('dates1', inplace=True, drop=True)
  627. with pytest.raises(NotImplementedError):
  628. df.query('index < 20130101 < dates3', engine=engine, parser=parser)
  629. def test_nested_scope(self):
  630. from pandas.core.computation.ops import UndefinedVariableError
  631. engine = self.engine
  632. parser = self.parser
  633. # smoke test
  634. x = 1 # noqa
  635. result = pd.eval('x + 1', engine=engine, parser=parser)
  636. assert result == 2
  637. df = DataFrame(np.random.randn(5, 3))
  638. df2 = DataFrame(np.random.randn(5, 3))
  639. # don't have the pandas parser
  640. with pytest.raises(SyntaxError):
  641. df.query('(@df>0) & (@df2>0)', engine=engine, parser=parser)
  642. with pytest.raises(UndefinedVariableError):
  643. df.query('(df>0) & (df2>0)', engine=engine, parser=parser)
  644. expected = df[(df > 0) & (df2 > 0)]
  645. result = pd.eval('df[(df > 0) & (df2 > 0)]', engine=engine,
  646. parser=parser)
  647. assert_frame_equal(expected, result)
  648. expected = df[(df > 0) & (df2 > 0) & (df[df > 0] > 0)]
  649. result = pd.eval('df[(df > 0) & (df2 > 0) & (df[df > 0] > 0)]',
  650. engine=engine, parser=parser)
  651. assert_frame_equal(expected, result)
  652. class TestDataFrameQueryPythonPandas(TestDataFrameQueryNumExprPandas):
  653. @classmethod
  654. def setup_class(cls):
  655. super(TestDataFrameQueryPythonPandas, cls).setup_class()
  656. cls.engine = 'python'
  657. cls.parser = 'pandas'
  658. cls.frame = TestData().frame
  659. def test_query_builtin(self):
  660. engine, parser = self.engine, self.parser
  661. n = m = 10
  662. df = DataFrame(np.random.randint(m, size=(n, 3)), columns=list('abc'))
  663. df.index.name = 'sin'
  664. expected = df[df.index > 5]
  665. result = df.query('sin > 5', engine=engine, parser=parser)
  666. assert_frame_equal(expected, result)
  667. class TestDataFrameQueryPythonPython(TestDataFrameQueryNumExprPython):
  668. @classmethod
  669. def setup_class(cls):
  670. super(TestDataFrameQueryPythonPython, cls).setup_class()
  671. cls.engine = cls.parser = 'python'
  672. cls.frame = TestData().frame
  673. def test_query_builtin(self):
  674. engine, parser = self.engine, self.parser
  675. n = m = 10
  676. df = DataFrame(np.random.randint(m, size=(n, 3)), columns=list('abc'))
  677. df.index.name = 'sin'
  678. expected = df[df.index > 5]
  679. result = df.query('sin > 5', engine=engine, parser=parser)
  680. assert_frame_equal(expected, result)
  681. class TestDataFrameQueryStrings(object):
  682. def test_str_query_method(self, parser, engine):
  683. df = DataFrame(np.random.randn(10, 1), columns=['b'])
  684. df['strings'] = Series(list('aabbccddee'))
  685. expect = df[df.strings == 'a']
  686. if parser != 'pandas':
  687. col = 'strings'
  688. lst = '"a"'
  689. lhs = [col] * 2 + [lst] * 2
  690. rhs = lhs[::-1]
  691. eq, ne = '==', '!='
  692. ops = 2 * ([eq] + [ne])
  693. for lhs, op, rhs in zip(lhs, ops, rhs):
  694. ex = '{lhs} {op} {rhs}'.format(lhs=lhs, op=op, rhs=rhs)
  695. pytest.raises(NotImplementedError, df.query, ex,
  696. engine=engine, parser=parser,
  697. local_dict={'strings': df.strings})
  698. else:
  699. res = df.query('"a" == strings', engine=engine, parser=parser)
  700. assert_frame_equal(res, expect)
  701. res = df.query('strings == "a"', engine=engine, parser=parser)
  702. assert_frame_equal(res, expect)
  703. assert_frame_equal(res, df[df.strings.isin(['a'])])
  704. expect = df[df.strings != 'a']
  705. res = df.query('strings != "a"', engine=engine, parser=parser)
  706. assert_frame_equal(res, expect)
  707. res = df.query('"a" != strings', engine=engine, parser=parser)
  708. assert_frame_equal(res, expect)
  709. assert_frame_equal(res, df[~df.strings.isin(['a'])])
  710. def test_str_list_query_method(self, parser, engine):
  711. df = DataFrame(np.random.randn(10, 1), columns=['b'])
  712. df['strings'] = Series(list('aabbccddee'))
  713. expect = df[df.strings.isin(['a', 'b'])]
  714. if parser != 'pandas':
  715. col = 'strings'
  716. lst = '["a", "b"]'
  717. lhs = [col] * 2 + [lst] * 2
  718. rhs = lhs[::-1]
  719. eq, ne = '==', '!='
  720. ops = 2 * ([eq] + [ne])
  721. for lhs, op, rhs in zip(lhs, ops, rhs):
  722. ex = '{lhs} {op} {rhs}'.format(lhs=lhs, op=op, rhs=rhs)
  723. with pytest.raises(NotImplementedError):
  724. df.query(ex, engine=engine, parser=parser)
  725. else:
  726. res = df.query('strings == ["a", "b"]', engine=engine,
  727. parser=parser)
  728. assert_frame_equal(res, expect)
  729. res = df.query('["a", "b"] == strings', engine=engine,
  730. parser=parser)
  731. assert_frame_equal(res, expect)
  732. expect = df[~df.strings.isin(['a', 'b'])]
  733. res = df.query('strings != ["a", "b"]', engine=engine,
  734. parser=parser)
  735. assert_frame_equal(res, expect)
  736. res = df.query('["a", "b"] != strings', engine=engine,
  737. parser=parser)
  738. assert_frame_equal(res, expect)
  739. def test_query_with_string_columns(self, parser, engine):
  740. df = DataFrame({'a': list('aaaabbbbcccc'),
  741. 'b': list('aabbccddeeff'),
  742. 'c': np.random.randint(5, size=12),
  743. 'd': np.random.randint(9, size=12)})
  744. if parser == 'pandas':
  745. res = df.query('a in b', parser=parser, engine=engine)
  746. expec = df[df.a.isin(df.b)]
  747. assert_frame_equal(res, expec)
  748. res = df.query('a in b and c < d', parser=parser, engine=engine)
  749. expec = df[df.a.isin(df.b) & (df.c < df.d)]
  750. assert_frame_equal(res, expec)
  751. else:
  752. with pytest.raises(NotImplementedError):
  753. df.query('a in b', parser=parser, engine=engine)
  754. with pytest.raises(NotImplementedError):
  755. df.query('a in b and c < d', parser=parser, engine=engine)
  756. def test_object_array_eq_ne(self, parser, engine):
  757. df = DataFrame({'a': list('aaaabbbbcccc'),
  758. 'b': list('aabbccddeeff'),
  759. 'c': np.random.randint(5, size=12),
  760. 'd': np.random.randint(9, size=12)})
  761. res = df.query('a == b', parser=parser, engine=engine)
  762. exp = df[df.a == df.b]
  763. assert_frame_equal(res, exp)
  764. res = df.query('a != b', parser=parser, engine=engine)
  765. exp = df[df.a != df.b]
  766. assert_frame_equal(res, exp)
  767. def test_query_with_nested_strings(self, parser, engine):
  768. skip_if_no_pandas_parser(parser)
  769. raw = """id event timestamp
  770. 1 "page 1 load" 1/1/2014 0:00:01
  771. 1 "page 1 exit" 1/1/2014 0:00:31
  772. 2 "page 2 load" 1/1/2014 0:01:01
  773. 2 "page 2 exit" 1/1/2014 0:01:31
  774. 3 "page 3 load" 1/1/2014 0:02:01
  775. 3 "page 3 exit" 1/1/2014 0:02:31
  776. 4 "page 1 load" 2/1/2014 1:00:01
  777. 4 "page 1 exit" 2/1/2014 1:00:31
  778. 5 "page 2 load" 2/1/2014 1:01:01
  779. 5 "page 2 exit" 2/1/2014 1:01:31
  780. 6 "page 3 load" 2/1/2014 1:02:01
  781. 6 "page 3 exit" 2/1/2014 1:02:31
  782. """
  783. df = pd.read_csv(StringIO(raw), sep=r'\s{2,}', engine='python',
  784. parse_dates=['timestamp'])
  785. expected = df[df.event == '"page 1 load"']
  786. res = df.query("""'"page 1 load"' in event""", parser=parser,
  787. engine=engine)
  788. assert_frame_equal(expected, res)
  789. def test_query_with_nested_special_character(self, parser, engine):
  790. skip_if_no_pandas_parser(parser)
  791. df = DataFrame({'a': ['a', 'b', 'test & test'],
  792. 'b': [1, 2, 3]})
  793. res = df.query('a == "test & test"', parser=parser, engine=engine)
  794. expec = df[df.a == 'test & test']
  795. assert_frame_equal(res, expec)
  796. def test_query_lex_compare_strings(self, parser, engine):
  797. import operator as opr
  798. a = Series(np.random.choice(list('abcde'), 20))
  799. b = Series(np.arange(a.size))
  800. df = DataFrame({'X': a, 'Y': b})
  801. ops = {'<': opr.lt, '>': opr.gt, '<=': opr.le, '>=': opr.ge}
  802. for op, func in ops.items():
  803. res = df.query('X %s "d"' % op, engine=engine, parser=parser)
  804. expected = df[func(df.X, 'd')]
  805. assert_frame_equal(res, expected)
  806. def test_query_single_element_booleans(self, parser, engine):
  807. columns = 'bid', 'bidsize', 'ask', 'asksize'
  808. data = np.random.randint(2, size=(1, len(columns))).astype(bool)
  809. df = DataFrame(data, columns=columns)
  810. res = df.query('bid & ask', engine=engine, parser=parser)
  811. expected = df[df.bid & df.ask]
  812. assert_frame_equal(res, expected)
  813. def test_query_string_scalar_variable(self, parser, engine):
  814. skip_if_no_pandas_parser(parser)
  815. df = pd.DataFrame({'Symbol': ['BUD US', 'BUD US', 'IBM US', 'IBM US'],
  816. 'Price': [109.70, 109.72, 183.30, 183.35]})
  817. e = df[df.Symbol == 'BUD US']
  818. symb = 'BUD US' # noqa
  819. r = df.query('Symbol == @symb', parser=parser, engine=engine)
  820. assert_frame_equal(e, r)
  821. class TestDataFrameEvalWithFrame(object):
  822. def setup_method(self, method):
  823. self.frame = DataFrame(np.random.randn(10, 3), columns=list('abc'))
  824. def teardown_method(self, method):
  825. del self.frame
  826. def test_simple_expr(self, parser, engine):
  827. res = self.frame.eval('a + b', engine=engine, parser=parser)
  828. expect = self.frame.a + self.frame.b
  829. assert_series_equal(res, expect)
  830. def test_bool_arith_expr(self, parser, engine):
  831. res = self.frame.eval('a[a < 1] + b', engine=engine, parser=parser)
  832. expect = self.frame.a[self.frame.a < 1] + self.frame.b
  833. assert_series_equal(res, expect)
  834. @pytest.mark.parametrize('op', ['+', '-', '*', '/'])
  835. def test_invalid_type_for_operator_raises(self, parser, engine, op):
  836. df = DataFrame({'a': [1, 2], 'b': ['c', 'd']})
  837. msg = r"unsupported operand type\(s\) for .+: '.+' and '.+'"
  838. with pytest.raises(TypeError, match=msg):
  839. df.eval('a {0} b'.format(op), engine=engine, parser=parser)