ZhengyangZhang commited on
Commit
3145f8d
·
verified ·
1 Parent(s): 4a78ed9

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. lib/python3.12/site-packages/pandas/pyproject.toml +811 -0
  2. lib/python3.12/site-packages/pandas/testing.py +18 -0
  3. lib/python3.12/site-packages/pandas/tests/__init__.py +0 -0
  4. lib/python3.12/site-packages/pandas/tests/computation/__init__.py +0 -0
  5. lib/python3.12/site-packages/pandas/tests/computation/__pycache__/__init__.cpython-312.pyc +0 -0
  6. lib/python3.12/site-packages/pandas/tests/computation/__pycache__/test_compat.cpython-312.pyc +0 -0
  7. lib/python3.12/site-packages/pandas/tests/computation/test_compat.py +32 -0
  8. lib/python3.12/site-packages/pandas/tests/computation/test_eval.py +2001 -0
  9. lib/python3.12/site-packages/pandas/tests/indexes/__init__.py +0 -0
  10. lib/python3.12/site-packages/pandas/tests/indexes/categorical/__init__.py +0 -0
  11. lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/__init__.cpython-312.pyc +0 -0
  12. lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/test_append.cpython-312.pyc +0 -0
  13. lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/test_astype.cpython-312.pyc +0 -0
  14. lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/test_category.cpython-312.pyc +0 -0
  15. lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/test_constructors.cpython-312.pyc +0 -0
  16. lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/test_equals.cpython-312.pyc +0 -0
  17. lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/test_fillna.cpython-312.pyc +0 -0
  18. lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/test_formats.cpython-312.pyc +0 -0
  19. lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/test_indexing.cpython-312.pyc +0 -0
  20. lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/test_map.cpython-312.pyc +0 -0
  21. lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/test_reindex.cpython-312.pyc +0 -0
  22. lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/test_setops.cpython-312.pyc +0 -0
  23. lib/python3.12/site-packages/pandas/tests/indexes/categorical/test_append.py +62 -0
  24. lib/python3.12/site-packages/pandas/tests/indexes/categorical/test_astype.py +90 -0
  25. lib/python3.12/site-packages/pandas/tests/indexes/categorical/test_category.py +394 -0
  26. lib/python3.12/site-packages/pandas/tests/indexes/categorical/test_constructors.py +142 -0
  27. lib/python3.12/site-packages/pandas/tests/indexes/categorical/test_equals.py +96 -0
  28. lib/python3.12/site-packages/pandas/tests/indexes/categorical/test_fillna.py +54 -0
  29. lib/python3.12/site-packages/pandas/tests/indexes/categorical/test_formats.py +120 -0
  30. lib/python3.12/site-packages/pandas/tests/indexes/categorical/test_indexing.py +420 -0
  31. lib/python3.12/site-packages/pandas/tests/indexes/categorical/test_map.py +144 -0
  32. lib/python3.12/site-packages/pandas/tests/indexes/categorical/test_reindex.py +78 -0
  33. lib/python3.12/site-packages/pandas/tests/indexes/categorical/test_setops.py +18 -0
  34. lib/python3.12/site-packages/pandas/tests/indexes/conftest.py +41 -0
  35. lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__init__.py +0 -0
  36. lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/__init__.cpython-312.pyc +0 -0
  37. lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_arithmetic.cpython-312.pyc +0 -0
  38. lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_constructors.cpython-312.pyc +0 -0
  39. lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_date_range.cpython-312.pyc +0 -0
  40. lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_datetime.cpython-312.pyc +0 -0
  41. lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_formats.cpython-312.pyc +0 -0
  42. lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_freq_attr.cpython-312.pyc +0 -0
  43. lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_indexing.cpython-312.pyc +0 -0
  44. lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_iter.cpython-312.pyc +0 -0
  45. lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_join.cpython-312.pyc +0 -0
  46. lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_npfuncs.cpython-312.pyc +0 -0
  47. lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_ops.cpython-312.pyc +0 -0
  48. lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_partial_slicing.cpython-312.pyc +0 -0
  49. lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_pickle.cpython-312.pyc +0 -0
  50. lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_reindex.cpython-312.pyc +0 -0
lib/python3.12/site-packages/pandas/pyproject.toml ADDED
@@ -0,0 +1,811 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [build-system]
2
+ # Minimum requirements for the build system to execute.
3
+ # See https://github.com/scipy/scipy/pull/12940 for the AIX issue.
4
+ requires = [
5
+ "meson-python==0.13.1",
6
+ "meson==1.2.1",
7
+ "wheel",
8
+ "Cython~=3.0.5", # Note: sync with setup.py, environment.yml and asv.conf.json
9
+ # Force numpy higher than 2.0, so that built wheels are compatible
10
+ # with both numpy 1 and 2
11
+ "numpy>=2.0",
12
+ "versioneer[toml]"
13
+ ]
14
+
15
+ build-backend = "mesonpy"
16
+
17
+ [project]
18
+ name = 'pandas'
19
+ dynamic = [
20
+ 'version'
21
+ ]
22
+ description = 'Powerful data structures for data analysis, time series, and statistics'
23
+ readme = 'README.md'
24
+ authors = [
25
+ { name = 'The Pandas Development Team', email='pandas-dev@python.org' },
26
+ ]
27
+ license = {file = 'LICENSE'}
28
+ requires-python = '>=3.9'
29
+ dependencies = [
30
+ "numpy>=1.22.4; python_version<'3.11'",
31
+ "numpy>=1.23.2; python_version=='3.11'",
32
+ "numpy>=1.26.0; python_version>='3.12'",
33
+ "python-dateutil>=2.8.2",
34
+ "pytz>=2020.1",
35
+ "tzdata>=2022.7"
36
+ ]
37
+ classifiers = [
38
+ 'Development Status :: 5 - Production/Stable',
39
+ 'Environment :: Console',
40
+ 'Intended Audience :: Science/Research',
41
+ 'License :: OSI Approved :: BSD License',
42
+ 'Operating System :: OS Independent',
43
+ 'Programming Language :: Cython',
44
+ 'Programming Language :: Python',
45
+ 'Programming Language :: Python :: 3',
46
+ 'Programming Language :: Python :: 3 :: Only',
47
+ 'Programming Language :: Python :: 3.9',
48
+ 'Programming Language :: Python :: 3.10',
49
+ 'Programming Language :: Python :: 3.11',
50
+ 'Programming Language :: Python :: 3.12',
51
+ 'Topic :: Scientific/Engineering'
52
+ ]
53
+
54
+ [project.urls]
55
+ homepage = 'https://pandas.pydata.org'
56
+ documentation = 'https://pandas.pydata.org/docs/'
57
+ repository = 'https://github.com/pandas-dev/pandas'
58
+
59
+ [project.entry-points."pandas_plotting_backends"]
60
+ matplotlib = "pandas:plotting._matplotlib"
61
+
62
+ [project.optional-dependencies]
63
+ test = ['hypothesis>=6.46.1', 'pytest>=7.3.2', 'pytest-xdist>=2.2.0']
64
+ pyarrow = ['pyarrow>=10.0.1']
65
+ performance = ['bottleneck>=1.3.6', 'numba>=0.56.4', 'numexpr>=2.8.4']
66
+ computation = ['scipy>=1.10.0', 'xarray>=2022.12.0']
67
+ fss = ['fsspec>=2022.11.0']
68
+ aws = ['s3fs>=2022.11.0']
69
+ gcp = ['gcsfs>=2022.11.0', 'pandas-gbq>=0.19.0']
70
+ excel = ['odfpy>=1.4.1', 'openpyxl>=3.1.0', 'python-calamine>=0.1.7', 'pyxlsb>=1.0.10', 'xlrd>=2.0.1', 'xlsxwriter>=3.0.5']
71
+ parquet = ['pyarrow>=10.0.1']
72
+ feather = ['pyarrow>=10.0.1']
73
+ hdf5 = [# blosc only available on conda (https://github.com/Blosc/python-blosc/issues/297)
74
+ #'blosc>=1.20.1',
75
+ 'tables>=3.8.0']
76
+ spss = ['pyreadstat>=1.2.0']
77
+ postgresql = ['SQLAlchemy>=2.0.0', 'psycopg2>=2.9.6', 'adbc-driver-postgresql>=0.8.0']
78
+ mysql = ['SQLAlchemy>=2.0.0', 'pymysql>=1.0.2']
79
+ sql-other = ['SQLAlchemy>=2.0.0', 'adbc-driver-postgresql>=0.8.0', 'adbc-driver-sqlite>=0.8.0']
80
+ html = ['beautifulsoup4>=4.11.2', 'html5lib>=1.1', 'lxml>=4.9.2']
81
+ xml = ['lxml>=4.9.2']
82
+ plot = ['matplotlib>=3.6.3']
83
+ output-formatting = ['jinja2>=3.1.2', 'tabulate>=0.9.0']
84
+ clipboard = ['PyQt5>=5.15.9', 'qtpy>=2.3.0']
85
+ compression = ['zstandard>=0.19.0']
86
+ consortium-standard = ['dataframe-api-compat>=0.1.7']
87
+ all = ['adbc-driver-postgresql>=0.8.0',
88
+ 'adbc-driver-sqlite>=0.8.0',
89
+ 'beautifulsoup4>=4.11.2',
90
+ # blosc only available on conda (https://github.com/Blosc/python-blosc/issues/297)
91
+ #'blosc>=1.21.3',
92
+ 'bottleneck>=1.3.6',
93
+ 'dataframe-api-compat>=0.1.7',
94
+ 'fastparquet>=2022.12.0',
95
+ 'fsspec>=2022.11.0',
96
+ 'gcsfs>=2022.11.0',
97
+ 'html5lib>=1.1',
98
+ 'hypothesis>=6.46.1',
99
+ 'jinja2>=3.1.2',
100
+ 'lxml>=4.9.2',
101
+ 'matplotlib>=3.6.3',
102
+ 'numba>=0.56.4',
103
+ 'numexpr>=2.8.4',
104
+ 'odfpy>=1.4.1',
105
+ 'openpyxl>=3.1.0',
106
+ 'pandas-gbq>=0.19.0',
107
+ 'psycopg2>=2.9.6',
108
+ 'pyarrow>=10.0.1',
109
+ 'pymysql>=1.0.2',
110
+ 'PyQt5>=5.15.9',
111
+ 'pyreadstat>=1.2.0',
112
+ 'pytest>=7.3.2',
113
+ 'pytest-xdist>=2.2.0',
114
+ 'python-calamine>=0.1.7',
115
+ 'pyxlsb>=1.0.10',
116
+ 'qtpy>=2.3.0',
117
+ 'scipy>=1.10.0',
118
+ 's3fs>=2022.11.0',
119
+ 'SQLAlchemy>=2.0.0',
120
+ 'tables>=3.8.0',
121
+ 'tabulate>=0.9.0',
122
+ 'xarray>=2022.12.0',
123
+ 'xlrd>=2.0.1',
124
+ 'xlsxwriter>=3.0.5',
125
+ 'zstandard>=0.19.0']
126
+
127
+ # TODO: Remove after setuptools support is dropped.
128
+ [tool.setuptools]
129
+ include-package-data = true
130
+
131
+ [tool.setuptools.packages.find]
132
+ include = ["pandas", "pandas.*"]
133
+ namespaces = false
134
+
135
+ [tool.setuptools.exclude-package-data]
136
+ "*" = ["*.c", "*.h"]
137
+
138
+ # See the docstring in versioneer.py for instructions. Note that you must
139
+ # re-run 'versioneer.py setup' after changing this section, and commit the
140
+ # resulting files.
141
+ [tool.versioneer]
142
+ VCS = "git"
143
+ style = "pep440"
144
+ versionfile_source = "pandas/_version.py"
145
+ versionfile_build = "pandas/_version.py"
146
+ tag_prefix = "v"
147
+ parentdir_prefix = "pandas-"
148
+
149
+ [tool.meson-python.args]
150
+ setup = ['--vsenv'] # For Windows
151
+
152
+ [tool.cibuildwheel]
153
+ skip = "cp36-* cp37-* cp38-* pp* *_i686 *_ppc64le *_s390x"
154
+ build-verbosity = "3"
155
+ environment = {LDFLAGS="-Wl,--strip-all"}
156
+ # pytz 2024.2 causing some failures
157
+ test-requires = "hypothesis>=6.46.1 pytest>=7.3.2 pytest-xdist>=2.2.0 pytz<2024.2"
158
+ test-command = """
159
+ PANDAS_CI='1' python -c 'import pandas as pd; \
160
+ pd.test(extra_args=["-m not clipboard and not single_cpu and not slow and not network and not db", "-n 2", "--no-strict-data-files"]); \
161
+ pd.test(extra_args=["-m not clipboard and single_cpu and not slow and not network and not db", "--no-strict-data-files"]);' \
162
+ """
163
+ free-threaded-support = true
164
+ before-build = "PACKAGE_DIR={package} bash {package}/scripts/cibw_before_build.sh"
165
+
166
+ [tool.cibuildwheel.windows]
167
+ before-build = "pip install delvewheel && bash {package}/scripts/cibw_before_build.sh"
168
+ repair-wheel-command = "delvewheel repair -w {dest_dir} {wheel}"
169
+
170
+ [[tool.cibuildwheel.overrides]]
171
+ select = "*-manylinux_aarch64*"
172
+ test-command = """
173
+ PANDAS_CI='1' python -c 'import pandas as pd; \
174
+ pd.test(extra_args=["-m not clipboard and not single_cpu and not slow and not network and not db and not fails_arm_wheels", "-n 2", "--no-strict-data-files"]); \
175
+ pd.test(extra_args=["-m not clipboard and single_cpu and not slow and not network and not db", "--no-strict-data-files"]);' \
176
+ """
177
+
178
+ [[tool.cibuildwheel.overrides]]
179
+ select = "*-musllinux*"
180
+ before-test = "apk update && apk add musl-locales"
181
+
182
+ [[tool.cibuildwheel.overrides]]
183
+ select = "*-win*"
184
+ # We test separately for Windows, since we use
185
+ # the windowsservercore docker image to check if any dlls are
186
+ # missing from the wheel
187
+ test-command = ""
188
+
189
+ [[tool.cibuildwheel.overrides]]
190
+ # Don't strip wheels on macOS.
191
+ # macOS doesn't support stripping wheels with linker
192
+ # https://github.com/MacPython/numpy-wheels/pull/87#issuecomment-624878264
193
+ select = "*-macosx*"
194
+ environment = {CFLAGS="-g0"}
195
+
196
+ [tool.black]
197
+ target-version = ['py39', 'py310']
198
+ required-version = '23.11.0'
199
+ exclude = '''
200
+ (
201
+ asv_bench/env
202
+ | \.egg
203
+ | \.git
204
+ | \.hg
205
+ | \.mypy_cache
206
+ | \.nox
207
+ | \.tox
208
+ | \.venv
209
+ | _build
210
+ | buck-out
211
+ | build
212
+ | dist
213
+ | setup.py
214
+ )
215
+ '''
216
+
217
+ [tool.ruff]
218
+ line-length = 88
219
+ target-version = "py310"
220
+ fix = true
221
+ unfixable = []
222
+ typing-modules = ["pandas._typing"]
223
+
224
+ select = [
225
+ # pyflakes
226
+ "F",
227
+ # pycodestyle
228
+ "E", "W",
229
+ # flake8-2020
230
+ "YTT",
231
+ # flake8-bugbear
232
+ "B",
233
+ # flake8-quotes
234
+ "Q",
235
+ # flake8-debugger
236
+ "T10",
237
+ # flake8-gettext
238
+ "INT",
239
+ # pylint
240
+ "PL",
241
+ # misc lints
242
+ "PIE",
243
+ # flake8-pyi
244
+ "PYI",
245
+ # tidy imports
246
+ "TID",
247
+ # implicit string concatenation
248
+ "ISC",
249
+ # type-checking imports
250
+ "TCH",
251
+ # comprehensions
252
+ "C4",
253
+ # pygrep-hooks
254
+ "PGH",
255
+ # Ruff-specific rules
256
+ "RUF",
257
+ # flake8-bandit: exec-builtin
258
+ "S102",
259
+ # numpy-legacy-random
260
+ "NPY002",
261
+ # Perflint
262
+ "PERF",
263
+ # flynt
264
+ "FLY",
265
+ # flake8-logging-format
266
+ "G",
267
+ # flake8-future-annotations
268
+ "FA",
269
+ ]
270
+
271
+ ignore = [
272
+ ### Intentionally disabled
273
+ # space before : (needed for how black formats slicing)
274
+ "E203",
275
+ # module level import not at top of file
276
+ "E402",
277
+ # do not assign a lambda expression, use a def
278
+ "E731",
279
+ # line break before binary operator
280
+ # "W503", # not yet implemented
281
+ # line break after binary operator
282
+ # "W504", # not yet implemented
283
+ # controversial
284
+ "B006",
285
+ # controversial
286
+ "B007",
287
+ # controversial
288
+ "B008",
289
+ # setattr is used to side-step mypy
290
+ "B009",
291
+ # getattr is used to side-step mypy
292
+ "B010",
293
+ # tests use assert False
294
+ "B011",
295
+ # tests use comparisons but not their returned value
296
+ "B015",
297
+ # false positives
298
+ "B019",
299
+ # Loop control variable overrides iterable it iterates
300
+ "B020",
301
+ # Function definition does not bind loop variable
302
+ "B023",
303
+ # Functions defined inside a loop must not use variables redefined in the loop
304
+ # "B301", # not yet implemented
305
+ # Only works with python >=3.10
306
+ "B905",
307
+ # Too many arguments to function call
308
+ "PLR0913",
309
+ # Too many returns
310
+ "PLR0911",
311
+ # Too many branches
312
+ "PLR0912",
313
+ # Too many statements
314
+ "PLR0915",
315
+ # Redefined loop name
316
+ "PLW2901",
317
+ # Global statements are discouraged
318
+ "PLW0603",
319
+ # Docstrings should not be included in stubs
320
+ "PYI021",
321
+ # Use `typing.NamedTuple` instead of `collections.namedtuple`
322
+ "PYI024",
323
+ # No builtin `eval()` allowed
324
+ "PGH001",
325
+ # compare-to-empty-string
326
+ "PLC1901",
327
+ # while int | float can be shortened to float, the former is more explicit
328
+ "PYI041",
329
+ # incorrect-dict-iterator, flags valid Series.items usage
330
+ "PERF102",
331
+ # try-except-in-loop, becomes useless in Python 3.11
332
+ "PERF203",
333
+
334
+
335
+ ### TODO: Enable gradually
336
+ # Useless statement
337
+ "B018",
338
+ # Within an except clause, raise exceptions with ...
339
+ "B904",
340
+ # Magic number
341
+ "PLR2004",
342
+ # comparison-with-itself
343
+ "PLR0124",
344
+ # Consider `elif` instead of `else` then `if` to remove indentation level
345
+ "PLR5501",
346
+ # collection-literal-concatenation
347
+ "RUF005",
348
+ # pairwise-over-zipped (>=PY310 only)
349
+ "RUF007",
350
+ # explicit-f-string-type-conversion
351
+ "RUF010",
352
+ # mutable-class-default
353
+ "RUF012"
354
+ ]
355
+
356
+ exclude = [
357
+ "doc/sphinxext/*.py",
358
+ "doc/build/*.py",
359
+ "doc/temp/*.py",
360
+ ".eggs/*.py",
361
+ # vendored files
362
+ "pandas/util/version/*",
363
+ "pandas/io/clipboard/__init__.py",
364
+ # exclude asv benchmark environments from linting
365
+ "env",
366
+ ]
367
+
368
+ [tool.ruff.per-file-ignores]
369
+ # relative imports allowed for asv_bench
370
+ "asv_bench/*" = ["TID", "NPY002"]
371
+ # to be enabled gradually
372
+ "pandas/core/*" = ["PLR5501"]
373
+ "pandas/tests/*" = ["B028", "FLY"]
374
+ "scripts/*" = ["B028"]
375
+ # Keep this one enabled
376
+ "pandas/_typing.py" = ["TCH"]
377
+
378
+ [tool.pylint.messages_control]
379
+ max-line-length = 88
380
+ disable = [
381
+ # intentionally turned off
382
+ "bad-mcs-classmethod-argument",
383
+ "broad-except",
384
+ "c-extension-no-member",
385
+ "comparison-with-itself",
386
+ "consider-using-enumerate",
387
+ "import-error",
388
+ "import-outside-toplevel",
389
+ "invalid-name",
390
+ "invalid-unary-operand-type",
391
+ "line-too-long",
392
+ "no-else-continue",
393
+ "no-else-raise",
394
+ "no-else-return",
395
+ "no-member",
396
+ "no-name-in-module",
397
+ "not-an-iterable",
398
+ "overridden-final-method",
399
+ "pointless-statement",
400
+ "redundant-keyword-arg",
401
+ "singleton-comparison",
402
+ "too-many-ancestors",
403
+ "too-many-arguments",
404
+ "too-many-boolean-expressions",
405
+ "too-many-branches",
406
+ "too-many-function-args",
407
+ "too-many-instance-attributes",
408
+ "too-many-locals",
409
+ "too-many-nested-blocks",
410
+ "too-many-public-methods",
411
+ "too-many-return-statements",
412
+ "too-many-statements",
413
+ "unexpected-keyword-arg",
414
+ "ungrouped-imports",
415
+ "unsubscriptable-object",
416
+ "unsupported-assignment-operation",
417
+ "unsupported-membership-test",
418
+ "unused-import",
419
+ "use-dict-literal",
420
+ "use-implicit-booleaness-not-comparison",
421
+ "use-implicit-booleaness-not-len",
422
+ "wrong-import-order",
423
+ "wrong-import-position",
424
+ "redefined-loop-name",
425
+
426
+ # misc
427
+ "abstract-class-instantiated",
428
+ "no-value-for-parameter",
429
+ "undefined-variable",
430
+ "unpacking-non-sequence",
431
+ "used-before-assignment",
432
+
433
+ # pylint type "C": convention, for programming standard violation
434
+ "missing-class-docstring",
435
+ "missing-function-docstring",
436
+ "missing-module-docstring",
437
+ "superfluous-parens",
438
+ "too-many-lines",
439
+ "unidiomatic-typecheck",
440
+ "unnecessary-dunder-call",
441
+ "unnecessary-lambda-assignment",
442
+
443
+ # pylint type "R": refactor, for bad code smell
444
+ "consider-using-with",
445
+ "cyclic-import",
446
+ "duplicate-code",
447
+ "inconsistent-return-statements",
448
+ "redefined-argument-from-local",
449
+ "too-few-public-methods",
450
+
451
+ # pylint type "W": warning, for python specific problems
452
+ "abstract-method",
453
+ "arguments-differ",
454
+ "arguments-out-of-order",
455
+ "arguments-renamed",
456
+ "attribute-defined-outside-init",
457
+ "broad-exception-raised",
458
+ "comparison-with-callable",
459
+ "dangerous-default-value",
460
+ "deprecated-module",
461
+ "eval-used",
462
+ "expression-not-assigned",
463
+ "fixme",
464
+ "global-statement",
465
+ "invalid-overridden-method",
466
+ "keyword-arg-before-vararg",
467
+ "possibly-unused-variable",
468
+ "protected-access",
469
+ "raise-missing-from",
470
+ "redefined-builtin",
471
+ "redefined-outer-name",
472
+ "self-cls-assignment",
473
+ "signature-differs",
474
+ "super-init-not-called",
475
+ "try-except-raise",
476
+ "unnecessary-lambda",
477
+ "unused-argument",
478
+ "unused-variable",
479
+ "using-constant-test"
480
+ ]
481
+
482
+ [tool.pytest.ini_options]
483
+ # sync minversion with pyproject.toml & install.rst
484
+ minversion = "7.3.2"
485
+ addopts = "--strict-markers --strict-config --capture=no --durations=30 --junitxml=test-data.xml"
486
+ empty_parameter_set_mark = "fail_at_collect"
487
+ xfail_strict = true
488
+ testpaths = "pandas"
489
+ doctest_optionflags = [
490
+ "NORMALIZE_WHITESPACE",
491
+ "IGNORE_EXCEPTION_DETAIL",
492
+ "ELLIPSIS",
493
+ ]
494
+ filterwarnings = [
495
+ "error:::pandas",
496
+ "error::ResourceWarning",
497
+ "error::pytest.PytestUnraisableExceptionWarning",
498
+ # TODO(PY311-minimum): Specify EncodingWarning
499
+ # Ignore 3rd party EncodingWarning but raise on pandas'
500
+ "ignore:.*encoding.* argument not specified",
501
+ "error:.*encoding.* argument not specified::pandas",
502
+ "ignore:.*ssl.SSLSocket:pytest.PytestUnraisableExceptionWarning",
503
+ "ignore:.*ssl.SSLSocket:ResourceWarning",
504
+ # GH 44844: Can remove once minimum matplotlib version >= 3.7
505
+ "ignore:.*FileIO:pytest.PytestUnraisableExceptionWarning",
506
+ "ignore:.*BufferedRandom:ResourceWarning",
507
+ "ignore::ResourceWarning:asyncio",
508
+ # From plotting doctests
509
+ "ignore:More than 20 figures have been opened:RuntimeWarning",
510
+ # Will be fixed in numba 0.56: https://github.com/numba/numba/issues/7758
511
+ "ignore:`np.MachAr` is deprecated:DeprecationWarning:numba",
512
+ "ignore:.*urllib3:DeprecationWarning:botocore",
513
+ "ignore:Setuptools is replacing distutils.:UserWarning:_distutils_hack",
514
+ # https://github.com/PyTables/PyTables/issues/822
515
+ "ignore:a closed node found in the registry:UserWarning:tables",
516
+ "ignore:`np.object` is a deprecated:DeprecationWarning:tables",
517
+ "ignore:tostring:DeprecationWarning:tables",
518
+ "ignore:distutils Version classes are deprecated:DeprecationWarning:pandas_datareader",
519
+ "ignore:distutils Version classes are deprecated:DeprecationWarning:numexpr",
520
+ "ignore:distutils Version classes are deprecated:DeprecationWarning:fastparquet",
521
+ "ignore:distutils Version classes are deprecated:DeprecationWarning:fsspec",
522
+ # Can be removed once https://github.com/numpy/numpy/pull/24794 is merged
523
+ "ignore:.*In the future `np.long` will be defined as.*:FutureWarning",
524
+ ]
525
+ junit_family = "xunit2"
526
+ markers = [
527
+ "single_cpu: tests that should run on a single cpu only",
528
+ "slow: mark a test as slow",
529
+ "network: mark a test as network",
530
+ "db: tests requiring a database (mysql or postgres)",
531
+ "clipboard: mark a pd.read_clipboard test",
532
+ "arm_slow: mark a test as slow for arm64 architecture",
533
+ "skip_ubsan: Tests known to fail UBSAN check",
534
+ # TODO: someone should investigate this ...
535
+ # these tests only fail in the wheel builder and don't fail in regular
536
+ # ARM CI
537
+ "fails_arm_wheels: Tests that fail in the ARM wheel build only",
538
+ ]
539
+
540
+ [tool.mypy]
541
+ # Import discovery
542
+ mypy_path = "typings"
543
+ files = ["pandas", "typings"]
544
+ namespace_packages = false
545
+ explicit_package_bases = false
546
+ ignore_missing_imports = true
547
+ follow_imports = "normal"
548
+ follow_imports_for_stubs = false
549
+ no_site_packages = false
550
+ no_silence_site_packages = false
551
+ # Platform configuration
552
+ python_version = "3.11"
553
+ platform = "linux-64"
554
+ # Disallow dynamic typing
555
+ disallow_any_unimported = false # TODO
556
+ disallow_any_expr = false # TODO
557
+ disallow_any_decorated = false # TODO
558
+ disallow_any_explicit = false # TODO
559
+ disallow_any_generics = false # TODO
560
+ disallow_subclassing_any = false # TODO
561
+ # Untyped definitions and calls
562
+ disallow_untyped_calls = true
563
+ disallow_untyped_defs = true
564
+ disallow_incomplete_defs = true
565
+ check_untyped_defs = true
566
+ disallow_untyped_decorators = true
567
+ # None and Optional handling
568
+ no_implicit_optional = true
569
+ strict_optional = true
570
+ # Configuring warnings
571
+ warn_redundant_casts = true
572
+ warn_unused_ignores = true
573
+ warn_no_return = true
574
+ warn_return_any = false # TODO
575
+ warn_unreachable = false # GH#27396
576
+ # Suppressing errors
577
+ ignore_errors = false
578
+ enable_error_code = "ignore-without-code"
579
+ # Miscellaneous strictness flags
580
+ allow_untyped_globals = false
581
+ allow_redefinition = false
582
+ local_partial_types = false
583
+ implicit_reexport = true
584
+ strict_equality = true
585
+ # Configuring error messages
586
+ show_error_context = false
587
+ show_column_numbers = false
588
+ show_error_codes = true
589
+
590
+ [[tool.mypy.overrides]]
591
+ module = [
592
+ "pandas._config.config", # TODO
593
+ "pandas._libs.*",
594
+ "pandas._testing.*", # TODO
595
+ "pandas.arrays", # TODO
596
+ "pandas.compat.numpy.function", # TODO
597
+ "pandas.compat._optional", # TODO
598
+ "pandas.compat.compressors", # TODO
599
+ "pandas.compat.pickle_compat", # TODO
600
+ "pandas.core._numba.executor", # TODO
601
+ "pandas.core.array_algos.datetimelike_accumulations", # TODO
602
+ "pandas.core.array_algos.masked_accumulations", # TODO
603
+ "pandas.core.array_algos.masked_reductions", # TODO
604
+ "pandas.core.array_algos.putmask", # TODO
605
+ "pandas.core.array_algos.quantile", # TODO
606
+ "pandas.core.array_algos.replace", # TODO
607
+ "pandas.core.array_algos.take", # TODO
608
+ "pandas.core.arrays.*", # TODO
609
+ "pandas.core.computation.*", # TODO
610
+ "pandas.core.dtypes.astype", # TODO
611
+ "pandas.core.dtypes.cast", # TODO
612
+ "pandas.core.dtypes.common", # TODO
613
+ "pandas.core.dtypes.concat", # TODO
614
+ "pandas.core.dtypes.dtypes", # TODO
615
+ "pandas.core.dtypes.generic", # TODO
616
+ "pandas.core.dtypes.inference", # TODO
617
+ "pandas.core.dtypes.missing", # TODO
618
+ "pandas.core.groupby.categorical", # TODO
619
+ "pandas.core.groupby.generic", # TODO
620
+ "pandas.core.groupby.grouper", # TODO
621
+ "pandas.core.groupby.groupby", # TODO
622
+ "pandas.core.groupby.ops", # TODO
623
+ "pandas.core.indexers.*", # TODO
624
+ "pandas.core.indexes.*", # TODO
625
+ "pandas.core.interchange.column", # TODO
626
+ "pandas.core.interchange.dataframe_protocol", # TODO
627
+ "pandas.core.interchange.from_dataframe", # TODO
628
+ "pandas.core.internals.*", # TODO
629
+ "pandas.core.methods.*", # TODO
630
+ "pandas.core.ops.array_ops", # TODO
631
+ "pandas.core.ops.common", # TODO
632
+ "pandas.core.ops.invalid", # TODO
633
+ "pandas.core.ops.mask_ops", # TODO
634
+ "pandas.core.ops.missing", # TODO
635
+ "pandas.core.reshape.*", # TODO
636
+ "pandas.core.strings.*", # TODO
637
+ "pandas.core.tools.*", # TODO
638
+ "pandas.core.window.common", # TODO
639
+ "pandas.core.window.ewm", # TODO
640
+ "pandas.core.window.expanding", # TODO
641
+ "pandas.core.window.numba_", # TODO
642
+ "pandas.core.window.online", # TODO
643
+ "pandas.core.window.rolling", # TODO
644
+ "pandas.core.accessor", # TODO
645
+ "pandas.core.algorithms", # TODO
646
+ "pandas.core.apply", # TODO
647
+ "pandas.core.arraylike", # TODO
648
+ "pandas.core.base", # TODO
649
+ "pandas.core.common", # TODO
650
+ "pandas.core.config_init", # TODO
651
+ "pandas.core.construction", # TODO
652
+ "pandas.core.flags", # TODO
653
+ "pandas.core.frame", # TODO
654
+ "pandas.core.generic", # TODO
655
+ "pandas.core.indexing", # TODO
656
+ "pandas.core.missing", # TODO
657
+ "pandas.core.nanops", # TODO
658
+ "pandas.core.resample", # TODO
659
+ "pandas.core.roperator", # TODO
660
+ "pandas.core.sample", # TODO
661
+ "pandas.core.series", # TODO
662
+ "pandas.core.sorting", # TODO
663
+ "pandas.errors", # TODO
664
+ "pandas.io.clipboard", # TODO
665
+ "pandas.io.excel._base", # TODO
666
+ "pandas.io.excel._odfreader", # TODO
667
+ "pandas.io.excel._odswriter", # TODO
668
+ "pandas.io.excel._openpyxl", # TODO
669
+ "pandas.io.excel._pyxlsb", # TODO
670
+ "pandas.io.excel._xlrd", # TODO
671
+ "pandas.io.excel._xlsxwriter", # TODO
672
+ "pandas.io.formats.console", # TODO
673
+ "pandas.io.formats.css", # TODO
674
+ "pandas.io.formats.excel", # TODO
675
+ "pandas.io.formats.format", # TODO
676
+ "pandas.io.formats.info", # TODO
677
+ "pandas.io.formats.printing", # TODO
678
+ "pandas.io.formats.style", # TODO
679
+ "pandas.io.formats.style_render", # TODO
680
+ "pandas.io.formats.xml", # TODO
681
+ "pandas.io.json.*", # TODO
682
+ "pandas.io.parsers.*", # TODO
683
+ "pandas.io.sas.sas_xport", # TODO
684
+ "pandas.io.sas.sas7bdat", # TODO
685
+ "pandas.io.clipboards", # TODO
686
+ "pandas.io.common", # TODO
687
+ "pandas.io.gbq", # TODO
688
+ "pandas.io.html", # TODO
689
+ "pandas.io.gbq", # TODO
690
+ "pandas.io.parquet", # TODO
691
+ "pandas.io.pytables", # TODO
692
+ "pandas.io.sql", # TODO
693
+ "pandas.io.stata", # TODO
694
+ "pandas.io.xml", # TODO
695
+ "pandas.plotting.*", # TODO
696
+ "pandas.tests.*",
697
+ "pandas.tseries.frequencies", # TODO
698
+ "pandas.tseries.holiday", # TODO
699
+ "pandas.util._decorators", # TODO
700
+ "pandas.util._doctools", # TODO
701
+ "pandas.util._print_versions", # TODO
702
+ "pandas.util._test_decorators", # TODO
703
+ "pandas.util._validators", # TODO
704
+ "pandas.util", # TODO
705
+ "pandas._version",
706
+ "pandas.conftest",
707
+ "pandas"
708
+ ]
709
+ disallow_untyped_calls = false
710
+ disallow_untyped_defs = false
711
+ disallow_incomplete_defs = false
712
+
713
+ [[tool.mypy.overrides]]
714
+ module = [
715
+ "pandas.tests.*",
716
+ "pandas._version",
717
+ "pandas.io.clipboard",
718
+ ]
719
+ check_untyped_defs = false
720
+
721
+ [[tool.mypy.overrides]]
722
+ module = [
723
+ "pandas.tests.apply.test_series_apply",
724
+ "pandas.tests.arithmetic.conftest",
725
+ "pandas.tests.arrays.sparse.test_combine_concat",
726
+ "pandas.tests.dtypes.test_common",
727
+ "pandas.tests.frame.methods.test_to_records",
728
+ "pandas.tests.groupby.test_rank",
729
+ "pandas.tests.groupby.transform.test_transform",
730
+ "pandas.tests.indexes.interval.test_interval",
731
+ "pandas.tests.indexing.test_categorical",
732
+ "pandas.tests.io.excel.test_writers",
733
+ "pandas.tests.reductions.test_reductions",
734
+ "pandas.tests.test_expressions",
735
+ ]
736
+ ignore_errors = true
737
+
738
+ # To be kept consistent with "Import Formatting" section in contributing.rst
739
+ [tool.isort]
740
+ known_pre_libs = "pandas._config"
741
+ known_pre_core = ["pandas._libs", "pandas._typing", "pandas.util._*", "pandas.compat", "pandas.errors"]
742
+ known_dtypes = "pandas.core.dtypes"
743
+ known_post_core = ["pandas.tseries", "pandas.io", "pandas.plotting"]
744
+ sections = ["FUTURE", "STDLIB", "THIRDPARTY" ,"PRE_LIBS" , "PRE_CORE", "DTYPES", "FIRSTPARTY", "POST_CORE", "LOCALFOLDER"]
745
+ profile = "black"
746
+ combine_as_imports = true
747
+ force_grid_wrap = 2
748
+ force_sort_within_sections = true
749
+ skip_glob = "env"
750
+ skip = "pandas/__init__.py"
751
+
752
+ [tool.pyright]
753
+ pythonVersion = "3.11"
754
+ typeCheckingMode = "basic"
755
+ useLibraryCodeForTypes = false
756
+ include = ["pandas", "typings"]
757
+ exclude = ["pandas/tests", "pandas/io/clipboard", "pandas/util/version", "pandas/core/_numba/extensions.py"]
758
+ # enable subset of "strict"
759
+ reportDuplicateImport = true
760
+ reportInconsistentConstructor = true
761
+ reportInvalidStubStatement = true
762
+ reportOverlappingOverload = true
763
+ reportPropertyTypeMismatch = true
764
+ reportUntypedClassDecorator = true
765
+ reportUntypedFunctionDecorator = true
766
+ reportUntypedNamedTuple = true
767
+ reportUnusedImport = true
768
+ disableBytesTypePromotions = true
769
+ # disable subset of "basic"
770
+ reportGeneralTypeIssues = false
771
+ reportMissingModuleSource = false
772
+ reportOptionalCall = false
773
+ reportOptionalIterable = false
774
+ reportOptionalMemberAccess = false
775
+ reportOptionalOperand = false
776
+ reportOptionalSubscript = false
777
+ reportPrivateImportUsage = false
778
+ reportUnboundVariable = false
779
+
780
+ [tool.coverage.run]
781
+ branch = true
782
+ omit = ["pandas/_typing.py", "pandas/_version.py"]
783
+ plugins = ["Cython.Coverage"]
784
+ source = ["pandas"]
785
+
786
+ [tool.coverage.report]
787
+ ignore_errors = false
788
+ show_missing = true
789
+ omit = ["pandas/_version.py"]
790
+ exclude_lines = [
791
+ # Have to re-enable the standard pragma
792
+ "pragma: no cover",
793
+ # Don't complain about missing debug-only code:s
794
+ "def __repr__",
795
+ "if self.debug",
796
+ # Don't complain if tests don't hit defensive assertion code:
797
+ "raise AssertionError",
798
+ "raise NotImplementedError",
799
+ "AbstractMethodError",
800
+ # Don't complain if non-runnable code isn't run:
801
+ "if 0:",
802
+ "if __name__ == .__main__.:",
803
+ "if TYPE_CHECKING:",
804
+ ]
805
+
806
+ [tool.coverage.html]
807
+ directory = "coverage_html_report"
808
+
809
+ [tool.codespell]
810
+ ignore-words-list = "blocs, coo, hist, nd, sav, ser, recuse, nin, timere, expec, expecs"
811
+ ignore-regex = 'https://([\w/\.])+'
lib/python3.12/site-packages/pandas/testing.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Public testing utility functions.
3
+ """
4
+
5
+
6
+ from pandas._testing import (
7
+ assert_extension_array_equal,
8
+ assert_frame_equal,
9
+ assert_index_equal,
10
+ assert_series_equal,
11
+ )
12
+
13
+ __all__ = [
14
+ "assert_extension_array_equal",
15
+ "assert_frame_equal",
16
+ "assert_series_equal",
17
+ "assert_index_equal",
18
+ ]
lib/python3.12/site-packages/pandas/tests/__init__.py ADDED
File without changes
lib/python3.12/site-packages/pandas/tests/computation/__init__.py ADDED
File without changes
lib/python3.12/site-packages/pandas/tests/computation/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (203 Bytes). View file
 
lib/python3.12/site-packages/pandas/tests/computation/__pycache__/test_compat.cpython-312.pyc ADDED
Binary file (1.57 kB). View file
 
lib/python3.12/site-packages/pandas/tests/computation/test_compat.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+
3
+ from pandas.compat._optional import VERSIONS
4
+
5
+ import pandas as pd
6
+ from pandas.core.computation import expr
7
+ from pandas.core.computation.engines import ENGINES
8
+ from pandas.util.version import Version
9
+
10
+
11
+ def test_compat():
12
+ # test we have compat with our version of numexpr
13
+
14
+ from pandas.core.computation.check import NUMEXPR_INSTALLED
15
+
16
+ ne = pytest.importorskip("numexpr")
17
+
18
+ ver = ne.__version__
19
+ if Version(ver) < Version(VERSIONS["numexpr"]):
20
+ assert not NUMEXPR_INSTALLED
21
+ else:
22
+ assert NUMEXPR_INSTALLED
23
+
24
+
25
+ @pytest.mark.parametrize("engine", ENGINES)
26
+ @pytest.mark.parametrize("parser", expr.PARSERS)
27
+ def test_invalid_numexpr_version(engine, parser):
28
+ if engine == "numexpr":
29
+ pytest.importorskip("numexpr")
30
+ a, b = 1, 2 # noqa: F841
31
+ res = pd.eval("a + b", engine=engine, parser=parser)
32
+ assert res == 3
lib/python3.12/site-packages/pandas/tests/computation/test_eval.py ADDED
@@ -0,0 +1,2001 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from functools import reduce
4
+ from itertools import product
5
+ import operator
6
+
7
+ import numpy as np
8
+ import pytest
9
+
10
+ from pandas.compat import PY312
11
+ from pandas.errors import (
12
+ NumExprClobberingError,
13
+ PerformanceWarning,
14
+ UndefinedVariableError,
15
+ )
16
+ import pandas.util._test_decorators as td
17
+
18
+ from pandas.core.dtypes.common import (
19
+ is_bool,
20
+ is_float,
21
+ is_list_like,
22
+ is_scalar,
23
+ )
24
+
25
+ import pandas as pd
26
+ from pandas import (
27
+ DataFrame,
28
+ Index,
29
+ Series,
30
+ date_range,
31
+ period_range,
32
+ timedelta_range,
33
+ )
34
+ import pandas._testing as tm
35
+ from pandas.core.computation import (
36
+ expr,
37
+ pytables,
38
+ )
39
+ from pandas.core.computation.engines import ENGINES
40
+ from pandas.core.computation.expr import (
41
+ BaseExprVisitor,
42
+ PandasExprVisitor,
43
+ PythonExprVisitor,
44
+ )
45
+ from pandas.core.computation.expressions import (
46
+ NUMEXPR_INSTALLED,
47
+ USE_NUMEXPR,
48
+ )
49
+ from pandas.core.computation.ops import (
50
+ ARITH_OPS_SYMS,
51
+ SPECIAL_CASE_ARITH_OPS_SYMS,
52
+ _binary_math_ops,
53
+ _binary_ops_dict,
54
+ _unary_math_ops,
55
+ )
56
+ from pandas.core.computation.scope import DEFAULT_GLOBALS
57
+
58
+
59
+ @pytest.fixture(
60
+ params=(
61
+ pytest.param(
62
+ engine,
63
+ marks=[
64
+ pytest.mark.skipif(
65
+ engine == "numexpr" and not USE_NUMEXPR,
66
+ reason=f"numexpr enabled->{USE_NUMEXPR}, "
67
+ f"installed->{NUMEXPR_INSTALLED}",
68
+ ),
69
+ td.skip_if_no("numexpr"),
70
+ ],
71
+ )
72
+ for engine in ENGINES
73
+ )
74
+ )
75
+ def engine(request):
76
+ return request.param
77
+
78
+
79
+ @pytest.fixture(params=expr.PARSERS)
80
+ def parser(request):
81
+ return request.param
82
+
83
+
84
+ def _eval_single_bin(lhs, cmp1, rhs, engine):
85
+ c = _binary_ops_dict[cmp1]
86
+ if ENGINES[engine].has_neg_frac:
87
+ try:
88
+ return c(lhs, rhs)
89
+ except ValueError as e:
90
+ if str(e).startswith(
91
+ "negative number cannot be raised to a fractional power"
92
+ ):
93
+ return np.nan
94
+ raise
95
+ return c(lhs, rhs)
96
+
97
+
98
+ # TODO: using range(5) here is a kludge
99
+ @pytest.fixture(
100
+ params=list(range(5)),
101
+ ids=["DataFrame", "Series", "SeriesNaN", "DataFrameNaN", "float"],
102
+ )
103
+ def lhs(request):
104
+ nan_df1 = DataFrame(np.random.default_rng(2).standard_normal((10, 5)))
105
+ nan_df1[nan_df1 > 0.5] = np.nan
106
+
107
+ opts = (
108
+ DataFrame(np.random.default_rng(2).standard_normal((10, 5))),
109
+ Series(np.random.default_rng(2).standard_normal(5)),
110
+ Series([1, 2, np.nan, np.nan, 5]),
111
+ nan_df1,
112
+ np.random.default_rng(2).standard_normal(),
113
+ )
114
+ return opts[request.param]
115
+
116
+
117
+ rhs = lhs
118
+ midhs = lhs
119
+
120
+
121
+ @pytest.fixture
122
+ def idx_func_dict():
123
+ return {
124
+ "i": lambda n: Index(np.arange(n), dtype=np.int64),
125
+ "f": lambda n: Index(np.arange(n), dtype=np.float64),
126
+ "s": lambda n: Index([f"{i}_{chr(i)}" for i in range(97, 97 + n)]),
127
+ "dt": lambda n: date_range("2020-01-01", periods=n),
128
+ "td": lambda n: timedelta_range("1 day", periods=n),
129
+ "p": lambda n: period_range("2020-01-01", periods=n, freq="D"),
130
+ }
131
+
132
+
133
+ class TestEval:
134
+ @pytest.mark.parametrize(
135
+ "cmp1",
136
+ ["!=", "==", "<=", ">=", "<", ">"],
137
+ ids=["ne", "eq", "le", "ge", "lt", "gt"],
138
+ )
139
+ @pytest.mark.parametrize("cmp2", [">", "<"], ids=["gt", "lt"])
140
+ @pytest.mark.parametrize("binop", expr.BOOL_OPS_SYMS)
141
+ def test_complex_cmp_ops(self, cmp1, cmp2, binop, lhs, rhs, engine, parser):
142
+ if parser == "python" and binop in ["and", "or"]:
143
+ msg = "'BoolOp' nodes are not implemented"
144
+ with pytest.raises(NotImplementedError, match=msg):
145
+ ex = f"(lhs {cmp1} rhs) {binop} (lhs {cmp2} rhs)"
146
+ pd.eval(ex, engine=engine, parser=parser)
147
+ return
148
+
149
+ lhs_new = _eval_single_bin(lhs, cmp1, rhs, engine)
150
+ rhs_new = _eval_single_bin(lhs, cmp2, rhs, engine)
151
+ expected = _eval_single_bin(lhs_new, binop, rhs_new, engine)
152
+
153
+ ex = f"(lhs {cmp1} rhs) {binop} (lhs {cmp2} rhs)"
154
+ result = pd.eval(ex, engine=engine, parser=parser)
155
+ tm.assert_equal(result, expected)
156
+
157
+ @pytest.mark.parametrize("cmp_op", expr.CMP_OPS_SYMS)
158
+ def test_simple_cmp_ops(self, cmp_op, lhs, rhs, engine, parser):
159
+ lhs = lhs < 0
160
+ rhs = rhs < 0
161
+
162
+ if parser == "python" and cmp_op in ["in", "not in"]:
163
+ msg = "'(In|NotIn)' nodes are not implemented"
164
+
165
+ with pytest.raises(NotImplementedError, match=msg):
166
+ ex = f"lhs {cmp_op} rhs"
167
+ pd.eval(ex, engine=engine, parser=parser)
168
+ return
169
+
170
+ ex = f"lhs {cmp_op} rhs"
171
+ msg = "|".join(
172
+ [
173
+ r"only list-like( or dict-like)? objects are allowed to be "
174
+ r"passed to (DataFrame\.)?isin\(\), you passed a "
175
+ r"(`|')bool(`|')",
176
+ "argument of type 'bool' is not iterable",
177
+ ]
178
+ )
179
+ if cmp_op in ("in", "not in") and not is_list_like(rhs):
180
+ with pytest.raises(TypeError, match=msg):
181
+ pd.eval(
182
+ ex,
183
+ engine=engine,
184
+ parser=parser,
185
+ local_dict={"lhs": lhs, "rhs": rhs},
186
+ )
187
+ else:
188
+ expected = _eval_single_bin(lhs, cmp_op, rhs, engine)
189
+ result = pd.eval(ex, engine=engine, parser=parser)
190
+ tm.assert_equal(result, expected)
191
+
192
+ @pytest.mark.parametrize("op", expr.CMP_OPS_SYMS)
193
+ def test_compound_invert_op(self, op, lhs, rhs, request, engine, parser):
194
+ if parser == "python" and op in ["in", "not in"]:
195
+ msg = "'(In|NotIn)' nodes are not implemented"
196
+ with pytest.raises(NotImplementedError, match=msg):
197
+ ex = f"~(lhs {op} rhs)"
198
+ pd.eval(ex, engine=engine, parser=parser)
199
+ return
200
+
201
+ if (
202
+ is_float(lhs)
203
+ and not is_float(rhs)
204
+ and op in ["in", "not in"]
205
+ and engine == "python"
206
+ and parser == "pandas"
207
+ ):
208
+ mark = pytest.mark.xfail(
209
+ reason="Looks like expected is negative, unclear whether "
210
+ "expected is incorrect or result is incorrect"
211
+ )
212
+ request.applymarker(mark)
213
+ skip_these = ["in", "not in"]
214
+ ex = f"~(lhs {op} rhs)"
215
+
216
+ msg = "|".join(
217
+ [
218
+ r"only list-like( or dict-like)? objects are allowed to be "
219
+ r"passed to (DataFrame\.)?isin\(\), you passed a "
220
+ r"(`|')float(`|')",
221
+ "argument of type 'float' is not iterable",
222
+ ]
223
+ )
224
+ if is_scalar(rhs) and op in skip_these:
225
+ with pytest.raises(TypeError, match=msg):
226
+ pd.eval(
227
+ ex,
228
+ engine=engine,
229
+ parser=parser,
230
+ local_dict={"lhs": lhs, "rhs": rhs},
231
+ )
232
+ else:
233
+ # compound
234
+ if is_scalar(lhs) and is_scalar(rhs):
235
+ lhs, rhs = (np.array([x]) for x in (lhs, rhs))
236
+ expected = _eval_single_bin(lhs, op, rhs, engine)
237
+ if is_scalar(expected):
238
+ expected = not expected
239
+ else:
240
+ expected = ~expected
241
+ result = pd.eval(ex, engine=engine, parser=parser)
242
+ tm.assert_almost_equal(expected, result)
243
+
244
+ @pytest.mark.parametrize("cmp1", ["<", ">"])
245
+ @pytest.mark.parametrize("cmp2", ["<", ">"])
246
+ def test_chained_cmp_op(self, cmp1, cmp2, lhs, midhs, rhs, engine, parser):
247
+ mid = midhs
248
+ if parser == "python":
249
+ ex1 = f"lhs {cmp1} mid {cmp2} rhs"
250
+ msg = "'BoolOp' nodes are not implemented"
251
+ with pytest.raises(NotImplementedError, match=msg):
252
+ pd.eval(ex1, engine=engine, parser=parser)
253
+ return
254
+
255
+ lhs_new = _eval_single_bin(lhs, cmp1, mid, engine)
256
+ rhs_new = _eval_single_bin(mid, cmp2, rhs, engine)
257
+
258
+ if lhs_new is not None and rhs_new is not None:
259
+ ex1 = f"lhs {cmp1} mid {cmp2} rhs"
260
+ ex2 = f"lhs {cmp1} mid and mid {cmp2} rhs"
261
+ ex3 = f"(lhs {cmp1} mid) & (mid {cmp2} rhs)"
262
+ expected = _eval_single_bin(lhs_new, "&", rhs_new, engine)
263
+
264
+ for ex in (ex1, ex2, ex3):
265
+ result = pd.eval(ex, engine=engine, parser=parser)
266
+
267
+ tm.assert_almost_equal(result, expected)
268
+
269
+ @pytest.mark.parametrize(
270
+ "arith1", sorted(set(ARITH_OPS_SYMS).difference(SPECIAL_CASE_ARITH_OPS_SYMS))
271
+ )
272
+ def test_binary_arith_ops(self, arith1, lhs, rhs, engine, parser):
273
+ ex = f"lhs {arith1} rhs"
274
+ result = pd.eval(ex, engine=engine, parser=parser)
275
+ expected = _eval_single_bin(lhs, arith1, rhs, engine)
276
+
277
+ tm.assert_almost_equal(result, expected)
278
+ ex = f"lhs {arith1} rhs {arith1} rhs"
279
+ result = pd.eval(ex, engine=engine, parser=parser)
280
+ nlhs = _eval_single_bin(lhs, arith1, rhs, engine)
281
+ try:
282
+ nlhs, ghs = nlhs.align(rhs)
283
+ except (ValueError, TypeError, AttributeError):
284
+ # ValueError: series frame or frame series align
285
+ # TypeError, AttributeError: series or frame with scalar align
286
+ return
287
+ else:
288
+ if engine == "numexpr":
289
+ import numexpr as ne
290
+
291
+ # direct numpy comparison
292
+ expected = ne.evaluate(f"nlhs {arith1} ghs")
293
+ # Update assert statement due to unreliable numerical
294
+ # precision component (GH37328)
295
+ # TODO: update testing code so that assert_almost_equal statement
296
+ # can be replaced again by the assert_numpy_array_equal statement
297
+ tm.assert_almost_equal(result.values, expected)
298
+ else:
299
+ expected = eval(f"nlhs {arith1} ghs")
300
+ tm.assert_almost_equal(result, expected)
301
+
302
+ # modulus, pow, and floor division require special casing
303
+
304
+ def test_modulus(self, lhs, rhs, engine, parser):
305
+ ex = r"lhs % rhs"
306
+ result = pd.eval(ex, engine=engine, parser=parser)
307
+ expected = lhs % rhs
308
+ tm.assert_almost_equal(result, expected)
309
+
310
+ if engine == "numexpr":
311
+ import numexpr as ne
312
+
313
+ expected = ne.evaluate(r"expected % rhs")
314
+ if isinstance(result, (DataFrame, Series)):
315
+ tm.assert_almost_equal(result.values, expected)
316
+ else:
317
+ tm.assert_almost_equal(result, expected.item())
318
+ else:
319
+ expected = _eval_single_bin(expected, "%", rhs, engine)
320
+ tm.assert_almost_equal(result, expected)
321
+
322
+ def test_floor_division(self, lhs, rhs, engine, parser):
323
+ ex = "lhs // rhs"
324
+
325
+ if engine == "python":
326
+ res = pd.eval(ex, engine=engine, parser=parser)
327
+ expected = lhs // rhs
328
+ tm.assert_equal(res, expected)
329
+ else:
330
+ msg = (
331
+ r"unsupported operand type\(s\) for //: 'VariableNode' and "
332
+ "'VariableNode'"
333
+ )
334
+ with pytest.raises(TypeError, match=msg):
335
+ pd.eval(
336
+ ex,
337
+ local_dict={"lhs": lhs, "rhs": rhs},
338
+ engine=engine,
339
+ parser=parser,
340
+ )
341
+
342
+ @td.skip_if_windows
343
+ def test_pow(self, lhs, rhs, engine, parser):
344
+ # odd failure on win32 platform, so skip
345
+ ex = "lhs ** rhs"
346
+ expected = _eval_single_bin(lhs, "**", rhs, engine)
347
+ result = pd.eval(ex, engine=engine, parser=parser)
348
+
349
+ if (
350
+ is_scalar(lhs)
351
+ and is_scalar(rhs)
352
+ and isinstance(expected, (complex, np.complexfloating))
353
+ and np.isnan(result)
354
+ ):
355
+ msg = "(DataFrame.columns|numpy array) are different"
356
+ with pytest.raises(AssertionError, match=msg):
357
+ tm.assert_numpy_array_equal(result, expected)
358
+ else:
359
+ tm.assert_almost_equal(result, expected)
360
+
361
+ ex = "(lhs ** rhs) ** rhs"
362
+ result = pd.eval(ex, engine=engine, parser=parser)
363
+
364
+ middle = _eval_single_bin(lhs, "**", rhs, engine)
365
+ expected = _eval_single_bin(middle, "**", rhs, engine)
366
+ tm.assert_almost_equal(result, expected)
367
+
368
+ def test_check_single_invert_op(self, lhs, engine, parser):
369
+ # simple
370
+ try:
371
+ elb = lhs.astype(bool)
372
+ except AttributeError:
373
+ elb = np.array([bool(lhs)])
374
+ expected = ~elb
375
+ result = pd.eval("~elb", engine=engine, parser=parser)
376
+ tm.assert_almost_equal(expected, result)
377
+
378
+ def test_frame_invert(self, engine, parser):
379
+ expr = "~lhs"
380
+
381
+ # ~ ##
382
+ # frame
383
+ # float always raises
384
+ lhs = DataFrame(np.random.default_rng(2).standard_normal((5, 2)))
385
+ if engine == "numexpr":
386
+ msg = "couldn't find matching opcode for 'invert_dd'"
387
+ with pytest.raises(NotImplementedError, match=msg):
388
+ pd.eval(expr, engine=engine, parser=parser)
389
+ else:
390
+ msg = "ufunc 'invert' not supported for the input types"
391
+ with pytest.raises(TypeError, match=msg):
392
+ pd.eval(expr, engine=engine, parser=parser)
393
+
394
+ # int raises on numexpr
395
+ lhs = DataFrame(np.random.default_rng(2).integers(5, size=(5, 2)))
396
+ if engine == "numexpr":
397
+ msg = "couldn't find matching opcode for 'invert"
398
+ with pytest.raises(NotImplementedError, match=msg):
399
+ pd.eval(expr, engine=engine, parser=parser)
400
+ else:
401
+ expect = ~lhs
402
+ result = pd.eval(expr, engine=engine, parser=parser)
403
+ tm.assert_frame_equal(expect, result)
404
+
405
+ # bool always works
406
+ lhs = DataFrame(np.random.default_rng(2).standard_normal((5, 2)) > 0.5)
407
+ expect = ~lhs
408
+ result = pd.eval(expr, engine=engine, parser=parser)
409
+ tm.assert_frame_equal(expect, result)
410
+
411
+ # object raises
412
+ lhs = DataFrame(
413
+ {"b": ["a", 1, 2.0], "c": np.random.default_rng(2).standard_normal(3) > 0.5}
414
+ )
415
+ if engine == "numexpr":
416
+ with pytest.raises(ValueError, match="unknown type object"):
417
+ pd.eval(expr, engine=engine, parser=parser)
418
+ else:
419
+ msg = "bad operand type for unary ~: 'str'"
420
+ with pytest.raises(TypeError, match=msg):
421
+ pd.eval(expr, engine=engine, parser=parser)
422
+
423
+ def test_series_invert(self, engine, parser):
424
+ # ~ ####
425
+ expr = "~lhs"
426
+
427
+ # series
428
+ # float raises
429
+ lhs = Series(np.random.default_rng(2).standard_normal(5))
430
+ if engine == "numexpr":
431
+ msg = "couldn't find matching opcode for 'invert_dd'"
432
+ with pytest.raises(NotImplementedError, match=msg):
433
+ result = pd.eval(expr, engine=engine, parser=parser)
434
+ else:
435
+ msg = "ufunc 'invert' not supported for the input types"
436
+ with pytest.raises(TypeError, match=msg):
437
+ pd.eval(expr, engine=engine, parser=parser)
438
+
439
+ # int raises on numexpr
440
+ lhs = Series(np.random.default_rng(2).integers(5, size=5))
441
+ if engine == "numexpr":
442
+ msg = "couldn't find matching opcode for 'invert"
443
+ with pytest.raises(NotImplementedError, match=msg):
444
+ pd.eval(expr, engine=engine, parser=parser)
445
+ else:
446
+ expect = ~lhs
447
+ result = pd.eval(expr, engine=engine, parser=parser)
448
+ tm.assert_series_equal(expect, result)
449
+
450
+ # bool
451
+ lhs = Series(np.random.default_rng(2).standard_normal(5) > 0.5)
452
+ expect = ~lhs
453
+ result = pd.eval(expr, engine=engine, parser=parser)
454
+ tm.assert_series_equal(expect, result)
455
+
456
+ # float
457
+ # int
458
+ # bool
459
+
460
+ # object
461
+ lhs = Series(["a", 1, 2.0])
462
+ if engine == "numexpr":
463
+ with pytest.raises(ValueError, match="unknown type object"):
464
+ pd.eval(expr, engine=engine, parser=parser)
465
+ else:
466
+ msg = "bad operand type for unary ~: 'str'"
467
+ with pytest.raises(TypeError, match=msg):
468
+ pd.eval(expr, engine=engine, parser=parser)
469
+
470
+ def test_frame_negate(self, engine, parser):
471
+ expr = "-lhs"
472
+
473
+ # float
474
+ lhs = DataFrame(np.random.default_rng(2).standard_normal((5, 2)))
475
+ expect = -lhs
476
+ result = pd.eval(expr, engine=engine, parser=parser)
477
+ tm.assert_frame_equal(expect, result)
478
+
479
+ # int
480
+ lhs = DataFrame(np.random.default_rng(2).integers(5, size=(5, 2)))
481
+ expect = -lhs
482
+ result = pd.eval(expr, engine=engine, parser=parser)
483
+ tm.assert_frame_equal(expect, result)
484
+
485
+ # bool doesn't work with numexpr but works elsewhere
486
+ lhs = DataFrame(np.random.default_rng(2).standard_normal((5, 2)) > 0.5)
487
+ if engine == "numexpr":
488
+ msg = "couldn't find matching opcode for 'neg_bb'"
489
+ with pytest.raises(NotImplementedError, match=msg):
490
+ pd.eval(expr, engine=engine, parser=parser)
491
+ else:
492
+ expect = -lhs
493
+ result = pd.eval(expr, engine=engine, parser=parser)
494
+ tm.assert_frame_equal(expect, result)
495
+
496
+ def test_series_negate(self, engine, parser):
497
+ expr = "-lhs"
498
+
499
+ # float
500
+ lhs = Series(np.random.default_rng(2).standard_normal(5))
501
+ expect = -lhs
502
+ result = pd.eval(expr, engine=engine, parser=parser)
503
+ tm.assert_series_equal(expect, result)
504
+
505
+ # int
506
+ lhs = Series(np.random.default_rng(2).integers(5, size=5))
507
+ expect = -lhs
508
+ result = pd.eval(expr, engine=engine, parser=parser)
509
+ tm.assert_series_equal(expect, result)
510
+
511
+ # bool doesn't work with numexpr but works elsewhere
512
+ lhs = Series(np.random.default_rng(2).standard_normal(5) > 0.5)
513
+ if engine == "numexpr":
514
+ msg = "couldn't find matching opcode for 'neg_bb'"
515
+ with pytest.raises(NotImplementedError, match=msg):
516
+ pd.eval(expr, engine=engine, parser=parser)
517
+ else:
518
+ expect = -lhs
519
+ result = pd.eval(expr, engine=engine, parser=parser)
520
+ tm.assert_series_equal(expect, result)
521
+
522
+ @pytest.mark.parametrize(
523
+ "lhs",
524
+ [
525
+ # Float
526
+ DataFrame(np.random.default_rng(2).standard_normal((5, 2))),
527
+ # Int
528
+ DataFrame(np.random.default_rng(2).integers(5, size=(5, 2))),
529
+ # bool doesn't work with numexpr but works elsewhere
530
+ DataFrame(np.random.default_rng(2).standard_normal((5, 2)) > 0.5),
531
+ ],
532
+ )
533
+ def test_frame_pos(self, lhs, engine, parser):
534
+ expr = "+lhs"
535
+ expect = lhs
536
+
537
+ result = pd.eval(expr, engine=engine, parser=parser)
538
+ tm.assert_frame_equal(expect, result)
539
+
540
+ @pytest.mark.parametrize(
541
+ "lhs",
542
+ [
543
+ # Float
544
+ Series(np.random.default_rng(2).standard_normal(5)),
545
+ # Int
546
+ Series(np.random.default_rng(2).integers(5, size=5)),
547
+ # bool doesn't work with numexpr but works elsewhere
548
+ Series(np.random.default_rng(2).standard_normal(5) > 0.5),
549
+ ],
550
+ )
551
+ def test_series_pos(self, lhs, engine, parser):
552
+ expr = "+lhs"
553
+ expect = lhs
554
+
555
+ result = pd.eval(expr, engine=engine, parser=parser)
556
+ tm.assert_series_equal(expect, result)
557
+
558
+ def test_scalar_unary(self, engine, parser):
559
+ msg = "bad operand type for unary ~: 'float'"
560
+ warn = None
561
+ if PY312 and not (engine == "numexpr" and parser == "pandas"):
562
+ warn = DeprecationWarning
563
+ with pytest.raises(TypeError, match=msg):
564
+ pd.eval("~1.0", engine=engine, parser=parser)
565
+
566
+ assert pd.eval("-1.0", parser=parser, engine=engine) == -1.0
567
+ assert pd.eval("+1.0", parser=parser, engine=engine) == +1.0
568
+ assert pd.eval("~1", parser=parser, engine=engine) == ~1
569
+ assert pd.eval("-1", parser=parser, engine=engine) == -1
570
+ assert pd.eval("+1", parser=parser, engine=engine) == +1
571
+ with tm.assert_produces_warning(
572
+ warn, match="Bitwise inversion", check_stacklevel=False
573
+ ):
574
+ assert pd.eval("~True", parser=parser, engine=engine) == ~True
575
+ with tm.assert_produces_warning(
576
+ warn, match="Bitwise inversion", check_stacklevel=False
577
+ ):
578
+ assert pd.eval("~False", parser=parser, engine=engine) == ~False
579
+ assert pd.eval("-True", parser=parser, engine=engine) == -True
580
+ assert pd.eval("-False", parser=parser, engine=engine) == -False
581
+ assert pd.eval("+True", parser=parser, engine=engine) == +True
582
+ assert pd.eval("+False", parser=parser, engine=engine) == +False
583
+
584
+ def test_unary_in_array(self):
585
+ # GH 11235
586
+ # TODO: 2022-01-29: result return list with numexpr 2.7.3 in CI
587
+ # but cannot reproduce locally
588
+ result = np.array(
589
+ pd.eval("[-True, True, +True, -False, False, +False, -37, 37, ~37, +37]"),
590
+ dtype=np.object_,
591
+ )
592
+ expected = np.array(
593
+ [
594
+ -True,
595
+ True,
596
+ +True,
597
+ -False,
598
+ False,
599
+ +False,
600
+ -37,
601
+ 37,
602
+ ~37,
603
+ +37,
604
+ ],
605
+ dtype=np.object_,
606
+ )
607
+ tm.assert_numpy_array_equal(result, expected)
608
+
609
+ @pytest.mark.parametrize("dtype", [np.float32, np.float64])
610
+ @pytest.mark.parametrize("expr", ["x < -0.1", "-5 > x"])
611
+ def test_float_comparison_bin_op(self, dtype, expr):
612
+ # GH 16363
613
+ df = DataFrame({"x": np.array([0], dtype=dtype)})
614
+ res = df.eval(expr)
615
+ assert res.values == np.array([False])
616
+
617
+ def test_unary_in_function(self):
618
+ # GH 46471
619
+ df = DataFrame({"x": [0, 1, np.nan]})
620
+
621
+ result = df.eval("x.fillna(-1)")
622
+ expected = df.x.fillna(-1)
623
+ # column name becomes None if using numexpr
624
+ # only check names when the engine is not numexpr
625
+ tm.assert_series_equal(result, expected, check_names=not USE_NUMEXPR)
626
+
627
+ result = df.eval("x.shift(1, fill_value=-1)")
628
+ expected = df.x.shift(1, fill_value=-1)
629
+ tm.assert_series_equal(result, expected, check_names=not USE_NUMEXPR)
630
+
631
+ @pytest.mark.parametrize(
632
+ "ex",
633
+ (
634
+ "1 or 2",
635
+ "1 and 2",
636
+ "a and b",
637
+ "a or b",
638
+ "1 or 2 and (3 + 2) > 3",
639
+ "2 * x > 2 or 1 and 2",
640
+ "2 * df > 3 and 1 or a",
641
+ ),
642
+ )
643
+ def test_disallow_scalar_bool_ops(self, ex, engine, parser):
644
+ x, a, b = np.random.default_rng(2).standard_normal(3), 1, 2 # noqa: F841
645
+ df = DataFrame(np.random.default_rng(2).standard_normal((3, 2))) # noqa: F841
646
+
647
+ msg = "cannot evaluate scalar only bool ops|'BoolOp' nodes are not"
648
+ with pytest.raises(NotImplementedError, match=msg):
649
+ pd.eval(ex, engine=engine, parser=parser)
650
+
651
+ def test_identical(self, engine, parser):
652
+ # see gh-10546
653
+ x = 1
654
+ result = pd.eval("x", engine=engine, parser=parser)
655
+ assert result == 1
656
+ assert is_scalar(result)
657
+
658
+ x = 1.5
659
+ result = pd.eval("x", engine=engine, parser=parser)
660
+ assert result == 1.5
661
+ assert is_scalar(result)
662
+
663
+ x = False
664
+ result = pd.eval("x", engine=engine, parser=parser)
665
+ assert not result
666
+ assert is_bool(result)
667
+ assert is_scalar(result)
668
+
669
+ x = np.array([1])
670
+ result = pd.eval("x", engine=engine, parser=parser)
671
+ tm.assert_numpy_array_equal(result, np.array([1]))
672
+ assert result.shape == (1,)
673
+
674
+ x = np.array([1.5])
675
+ result = pd.eval("x", engine=engine, parser=parser)
676
+ tm.assert_numpy_array_equal(result, np.array([1.5]))
677
+ assert result.shape == (1,)
678
+
679
+ x = np.array([False]) # noqa: F841
680
+ result = pd.eval("x", engine=engine, parser=parser)
681
+ tm.assert_numpy_array_equal(result, np.array([False]))
682
+ assert result.shape == (1,)
683
+
684
+ def test_line_continuation(self, engine, parser):
685
+ # GH 11149
686
+ exp = """1 + 2 * \
687
+ 5 - 1 + 2 """
688
+ result = pd.eval(exp, engine=engine, parser=parser)
689
+ assert result == 12
690
+
691
+ def test_float_truncation(self, engine, parser):
692
+ # GH 14241
693
+ exp = "1000000000.006"
694
+ result = pd.eval(exp, engine=engine, parser=parser)
695
+ expected = np.float64(exp)
696
+ assert result == expected
697
+
698
+ df = DataFrame({"A": [1000000000.0009, 1000000000.0011, 1000000000.0015]})
699
+ cutoff = 1000000000.0006
700
+ result = df.query(f"A < {cutoff:.4f}")
701
+ assert result.empty
702
+
703
+ cutoff = 1000000000.0010
704
+ result = df.query(f"A > {cutoff:.4f}")
705
+ expected = df.loc[[1, 2], :]
706
+ tm.assert_frame_equal(expected, result)
707
+
708
+ exact = 1000000000.0011
709
+ result = df.query(f"A == {exact:.4f}")
710
+ expected = df.loc[[1], :]
711
+ tm.assert_frame_equal(expected, result)
712
+
713
+ def test_disallow_python_keywords(self):
714
+ # GH 18221
715
+ df = DataFrame([[0, 0, 0]], columns=["foo", "bar", "class"])
716
+ msg = "Python keyword not valid identifier in numexpr query"
717
+ with pytest.raises(SyntaxError, match=msg):
718
+ df.query("class == 0")
719
+
720
+ df = DataFrame()
721
+ df.index.name = "lambda"
722
+ with pytest.raises(SyntaxError, match=msg):
723
+ df.query("lambda == 0")
724
+
725
+ def test_true_false_logic(self):
726
+ # GH 25823
727
+ # This behavior is deprecated in Python 3.12
728
+ with tm.maybe_produces_warning(
729
+ DeprecationWarning, PY312, check_stacklevel=False
730
+ ):
731
+ assert pd.eval("not True") == -2
732
+ assert pd.eval("not False") == -1
733
+ assert pd.eval("True and not True") == 0
734
+
735
+ def test_and_logic_string_match(self):
736
+ # GH 25823
737
+ event = Series({"a": "hello"})
738
+ assert pd.eval(f"{event.str.match('hello').a}")
739
+ assert pd.eval(f"{event.str.match('hello').a and event.str.match('hello').a}")
740
+
741
+
742
+ # -------------------------------------
743
+ # gh-12388: Typecasting rules consistency with python
744
+
745
+
746
+ class TestTypeCasting:
747
+ @pytest.mark.parametrize("op", ["+", "-", "*", "**", "/"])
748
+ # maybe someday... numexpr has too many upcasting rules now
749
+ # chain(*(np.core.sctypes[x] for x in ['uint', 'int', 'float']))
750
+ @pytest.mark.parametrize("left_right", [("df", "3"), ("3", "df")])
751
+ def test_binop_typecasting(
752
+ self, engine, parser, op, complex_or_float_dtype, left_right, request
753
+ ):
754
+ # GH#21374
755
+ dtype = complex_or_float_dtype
756
+ df = DataFrame(np.random.default_rng(2).standard_normal((5, 3)), dtype=dtype)
757
+ left, right = left_right
758
+ s = f"{left} {op} {right}"
759
+ res = pd.eval(s, engine=engine, parser=parser)
760
+ if dtype == "complex64" and engine == "numexpr":
761
+ mark = pytest.mark.xfail(
762
+ reason="numexpr issue with complex that are upcast "
763
+ "to complex 128 "
764
+ "https://github.com/pydata/numexpr/issues/492"
765
+ )
766
+ request.applymarker(mark)
767
+ assert df.values.dtype == dtype
768
+ assert res.values.dtype == dtype
769
+ tm.assert_frame_equal(res, eval(s), check_exact=False)
770
+
771
+
772
+ # -------------------------------------
773
+ # Basic and complex alignment
774
+
775
+
776
+ def should_warn(*args):
777
+ not_mono = not any(map(operator.attrgetter("is_monotonic_increasing"), args))
778
+ only_one_dt = reduce(
779
+ operator.xor, (issubclass(x.dtype.type, np.datetime64) for x in args)
780
+ )
781
+ return not_mono and only_one_dt
782
+
783
+
784
+ class TestAlignment:
785
+ index_types = ["i", "s", "dt"]
786
+ lhs_index_types = index_types + ["s"] # 'p'
787
+
788
+ def test_align_nested_unary_op(self, engine, parser):
789
+ s = "df * ~2"
790
+ df = DataFrame(np.random.default_rng(2).standard_normal((5, 3)))
791
+ res = pd.eval(s, engine=engine, parser=parser)
792
+ tm.assert_frame_equal(res, df * ~2)
793
+
794
+ @pytest.mark.filterwarnings("always::RuntimeWarning")
795
+ @pytest.mark.parametrize("lr_idx_type", lhs_index_types)
796
+ @pytest.mark.parametrize("rr_idx_type", index_types)
797
+ @pytest.mark.parametrize("c_idx_type", index_types)
798
+ def test_basic_frame_alignment(
799
+ self, engine, parser, lr_idx_type, rr_idx_type, c_idx_type, idx_func_dict
800
+ ):
801
+ df = DataFrame(
802
+ np.random.default_rng(2).standard_normal((10, 10)),
803
+ index=idx_func_dict[lr_idx_type](10),
804
+ columns=idx_func_dict[c_idx_type](10),
805
+ )
806
+ df2 = DataFrame(
807
+ np.random.default_rng(2).standard_normal((20, 10)),
808
+ index=idx_func_dict[rr_idx_type](20),
809
+ columns=idx_func_dict[c_idx_type](10),
810
+ )
811
+ # only warns if not monotonic and not sortable
812
+ if should_warn(df.index, df2.index):
813
+ with tm.assert_produces_warning(RuntimeWarning):
814
+ res = pd.eval("df + df2", engine=engine, parser=parser)
815
+ else:
816
+ res = pd.eval("df + df2", engine=engine, parser=parser)
817
+ tm.assert_frame_equal(res, df + df2)
818
+
819
+ @pytest.mark.parametrize("r_idx_type", lhs_index_types)
820
+ @pytest.mark.parametrize("c_idx_type", lhs_index_types)
821
+ def test_frame_comparison(
822
+ self, engine, parser, r_idx_type, c_idx_type, idx_func_dict
823
+ ):
824
+ df = DataFrame(
825
+ np.random.default_rng(2).standard_normal((10, 10)),
826
+ index=idx_func_dict[r_idx_type](10),
827
+ columns=idx_func_dict[c_idx_type](10),
828
+ )
829
+ res = pd.eval("df < 2", engine=engine, parser=parser)
830
+ tm.assert_frame_equal(res, df < 2)
831
+
832
+ df3 = DataFrame(
833
+ np.random.default_rng(2).standard_normal(df.shape),
834
+ index=df.index,
835
+ columns=df.columns,
836
+ )
837
+ res = pd.eval("df < df3", engine=engine, parser=parser)
838
+ tm.assert_frame_equal(res, df < df3)
839
+
840
+ @pytest.mark.filterwarnings("ignore::RuntimeWarning")
841
+ @pytest.mark.parametrize("r1", lhs_index_types)
842
+ @pytest.mark.parametrize("c1", index_types)
843
+ @pytest.mark.parametrize("r2", index_types)
844
+ @pytest.mark.parametrize("c2", index_types)
845
+ def test_medium_complex_frame_alignment(
846
+ self, engine, parser, r1, c1, r2, c2, idx_func_dict
847
+ ):
848
+ df = DataFrame(
849
+ np.random.default_rng(2).standard_normal((3, 2)),
850
+ index=idx_func_dict[r1](3),
851
+ columns=idx_func_dict[c1](2),
852
+ )
853
+ df2 = DataFrame(
854
+ np.random.default_rng(2).standard_normal((4, 2)),
855
+ index=idx_func_dict[r2](4),
856
+ columns=idx_func_dict[c2](2),
857
+ )
858
+ df3 = DataFrame(
859
+ np.random.default_rng(2).standard_normal((5, 2)),
860
+ index=idx_func_dict[r2](5),
861
+ columns=idx_func_dict[c2](2),
862
+ )
863
+ if should_warn(df.index, df2.index, df3.index):
864
+ with tm.assert_produces_warning(RuntimeWarning):
865
+ res = pd.eval("df + df2 + df3", engine=engine, parser=parser)
866
+ else:
867
+ res = pd.eval("df + df2 + df3", engine=engine, parser=parser)
868
+ tm.assert_frame_equal(res, df + df2 + df3)
869
+
870
+ @pytest.mark.filterwarnings("ignore::RuntimeWarning")
871
+ @pytest.mark.parametrize("index_name", ["index", "columns"])
872
+ @pytest.mark.parametrize("c_idx_type", index_types)
873
+ @pytest.mark.parametrize("r_idx_type", lhs_index_types)
874
+ def test_basic_frame_series_alignment(
875
+ self, engine, parser, index_name, r_idx_type, c_idx_type, idx_func_dict
876
+ ):
877
+ df = DataFrame(
878
+ np.random.default_rng(2).standard_normal((10, 10)),
879
+ index=idx_func_dict[r_idx_type](10),
880
+ columns=idx_func_dict[c_idx_type](10),
881
+ )
882
+ index = getattr(df, index_name)
883
+ s = Series(np.random.default_rng(2).standard_normal(5), index[:5])
884
+
885
+ if should_warn(df.index, s.index):
886
+ with tm.assert_produces_warning(RuntimeWarning):
887
+ res = pd.eval("df + s", engine=engine, parser=parser)
888
+ else:
889
+ res = pd.eval("df + s", engine=engine, parser=parser)
890
+
891
+ if r_idx_type == "dt" or c_idx_type == "dt":
892
+ expected = df.add(s) if engine == "numexpr" else df + s
893
+ else:
894
+ expected = df + s
895
+ tm.assert_frame_equal(res, expected)
896
+
897
+ @pytest.mark.parametrize("index_name", ["index", "columns"])
898
+ @pytest.mark.parametrize(
899
+ "r_idx_type, c_idx_type",
900
+ list(product(["i", "s"], ["i", "s"])) + [("dt", "dt")],
901
+ )
902
+ @pytest.mark.filterwarnings("ignore::RuntimeWarning")
903
+ def test_basic_series_frame_alignment(
904
+ self, request, engine, parser, index_name, r_idx_type, c_idx_type, idx_func_dict
905
+ ):
906
+ if (
907
+ engine == "numexpr"
908
+ and parser in ("pandas", "python")
909
+ and index_name == "index"
910
+ and r_idx_type == "i"
911
+ and c_idx_type == "s"
912
+ ):
913
+ reason = (
914
+ f"Flaky column ordering when engine={engine}, "
915
+ f"parser={parser}, index_name={index_name}, "
916
+ f"r_idx_type={r_idx_type}, c_idx_type={c_idx_type}"
917
+ )
918
+ request.applymarker(pytest.mark.xfail(reason=reason, strict=False))
919
+ df = DataFrame(
920
+ np.random.default_rng(2).standard_normal((10, 7)),
921
+ index=idx_func_dict[r_idx_type](10),
922
+ columns=idx_func_dict[c_idx_type](7),
923
+ )
924
+ index = getattr(df, index_name)
925
+ s = Series(np.random.default_rng(2).standard_normal(5), index[:5])
926
+ if should_warn(s.index, df.index):
927
+ with tm.assert_produces_warning(RuntimeWarning):
928
+ res = pd.eval("s + df", engine=engine, parser=parser)
929
+ else:
930
+ res = pd.eval("s + df", engine=engine, parser=parser)
931
+
932
+ if r_idx_type == "dt" or c_idx_type == "dt":
933
+ expected = df.add(s) if engine == "numexpr" else s + df
934
+ else:
935
+ expected = s + df
936
+ tm.assert_frame_equal(res, expected)
937
+
938
+ @pytest.mark.filterwarnings("ignore::RuntimeWarning")
939
+ @pytest.mark.parametrize("c_idx_type", index_types)
940
+ @pytest.mark.parametrize("r_idx_type", lhs_index_types)
941
+ @pytest.mark.parametrize("index_name", ["index", "columns"])
942
+ @pytest.mark.parametrize("op", ["+", "*"])
943
+ def test_series_frame_commutativity(
944
+ self, engine, parser, index_name, op, r_idx_type, c_idx_type, idx_func_dict
945
+ ):
946
+ df = DataFrame(
947
+ np.random.default_rng(2).standard_normal((10, 10)),
948
+ index=idx_func_dict[r_idx_type](10),
949
+ columns=idx_func_dict[c_idx_type](10),
950
+ )
951
+ index = getattr(df, index_name)
952
+ s = Series(np.random.default_rng(2).standard_normal(5), index[:5])
953
+
954
+ lhs = f"s {op} df"
955
+ rhs = f"df {op} s"
956
+ if should_warn(df.index, s.index):
957
+ with tm.assert_produces_warning(RuntimeWarning):
958
+ a = pd.eval(lhs, engine=engine, parser=parser)
959
+ with tm.assert_produces_warning(RuntimeWarning):
960
+ b = pd.eval(rhs, engine=engine, parser=parser)
961
+ else:
962
+ a = pd.eval(lhs, engine=engine, parser=parser)
963
+ b = pd.eval(rhs, engine=engine, parser=parser)
964
+
965
+ if r_idx_type != "dt" and c_idx_type != "dt":
966
+ if engine == "numexpr":
967
+ tm.assert_frame_equal(a, b)
968
+
969
+ @pytest.mark.filterwarnings("always::RuntimeWarning")
970
+ @pytest.mark.parametrize("r1", lhs_index_types)
971
+ @pytest.mark.parametrize("c1", index_types)
972
+ @pytest.mark.parametrize("r2", index_types)
973
+ @pytest.mark.parametrize("c2", index_types)
974
+ def test_complex_series_frame_alignment(
975
+ self, engine, parser, r1, c1, r2, c2, idx_func_dict
976
+ ):
977
+ n = 3
978
+ m1 = 5
979
+ m2 = 2 * m1
980
+ df = DataFrame(
981
+ np.random.default_rng(2).standard_normal((m1, n)),
982
+ index=idx_func_dict[r1](m1),
983
+ columns=idx_func_dict[c1](n),
984
+ )
985
+ df2 = DataFrame(
986
+ np.random.default_rng(2).standard_normal((m2, n)),
987
+ index=idx_func_dict[r2](m2),
988
+ columns=idx_func_dict[c2](n),
989
+ )
990
+ index = df2.columns
991
+ ser = Series(np.random.default_rng(2).standard_normal(n), index[:n])
992
+
993
+ if r2 == "dt" or c2 == "dt":
994
+ if engine == "numexpr":
995
+ expected2 = df2.add(ser)
996
+ else:
997
+ expected2 = df2 + ser
998
+ else:
999
+ expected2 = df2 + ser
1000
+
1001
+ if r1 == "dt" or c1 == "dt":
1002
+ if engine == "numexpr":
1003
+ expected = expected2.add(df)
1004
+ else:
1005
+ expected = expected2 + df
1006
+ else:
1007
+ expected = expected2 + df
1008
+
1009
+ if should_warn(df2.index, ser.index, df.index):
1010
+ with tm.assert_produces_warning(RuntimeWarning):
1011
+ res = pd.eval("df2 + ser + df", engine=engine, parser=parser)
1012
+ else:
1013
+ res = pd.eval("df2 + ser + df", engine=engine, parser=parser)
1014
+ assert res.shape == expected.shape
1015
+ tm.assert_frame_equal(res, expected)
1016
+
1017
+ def test_performance_warning_for_poor_alignment(self, engine, parser):
1018
+ df = DataFrame(np.random.default_rng(2).standard_normal((1000, 10)))
1019
+ s = Series(np.random.default_rng(2).standard_normal(10000))
1020
+ if engine == "numexpr":
1021
+ seen = PerformanceWarning
1022
+ else:
1023
+ seen = False
1024
+
1025
+ with tm.assert_produces_warning(seen):
1026
+ pd.eval("df + s", engine=engine, parser=parser)
1027
+
1028
+ s = Series(np.random.default_rng(2).standard_normal(1000))
1029
+ with tm.assert_produces_warning(False):
1030
+ pd.eval("df + s", engine=engine, parser=parser)
1031
+
1032
+ df = DataFrame(np.random.default_rng(2).standard_normal((10, 10000)))
1033
+ s = Series(np.random.default_rng(2).standard_normal(10000))
1034
+ with tm.assert_produces_warning(False):
1035
+ pd.eval("df + s", engine=engine, parser=parser)
1036
+
1037
+ df = DataFrame(np.random.default_rng(2).standard_normal((10, 10)))
1038
+ s = Series(np.random.default_rng(2).standard_normal(10000))
1039
+
1040
+ is_python_engine = engine == "python"
1041
+
1042
+ if not is_python_engine:
1043
+ wrn = PerformanceWarning
1044
+ else:
1045
+ wrn = False
1046
+
1047
+ with tm.assert_produces_warning(wrn) as w:
1048
+ pd.eval("df + s", engine=engine, parser=parser)
1049
+
1050
+ if not is_python_engine:
1051
+ assert len(w) == 1
1052
+ msg = str(w[0].message)
1053
+ logged = np.log10(s.size - df.shape[1])
1054
+ expected = (
1055
+ f"Alignment difference on axis 1 is larger "
1056
+ f"than an order of magnitude on term 'df', "
1057
+ f"by more than {logged:.4g}; performance may suffer."
1058
+ )
1059
+ assert msg == expected
1060
+
1061
+
1062
+ # ------------------------------------
1063
+ # Slightly more complex ops
1064
+
1065
+
1066
+ class TestOperations:
1067
+ def eval(self, *args, **kwargs):
1068
+ kwargs["level"] = kwargs.pop("level", 0) + 1
1069
+ return pd.eval(*args, **kwargs)
1070
+
1071
+ def test_simple_arith_ops(self, engine, parser):
1072
+ exclude_arith = []
1073
+ if parser == "python":
1074
+ exclude_arith = ["in", "not in"]
1075
+
1076
+ arith_ops = [
1077
+ op
1078
+ for op in expr.ARITH_OPS_SYMS + expr.CMP_OPS_SYMS
1079
+ if op not in exclude_arith
1080
+ ]
1081
+
1082
+ ops = (op for op in arith_ops if op != "//")
1083
+
1084
+ for op in ops:
1085
+ ex = f"1 {op} 1"
1086
+ ex2 = f"x {op} 1"
1087
+ ex3 = f"1 {op} (x + 1)"
1088
+
1089
+ if op in ("in", "not in"):
1090
+ msg = "argument of type 'int' is not iterable"
1091
+ with pytest.raises(TypeError, match=msg):
1092
+ pd.eval(ex, engine=engine, parser=parser)
1093
+ else:
1094
+ expec = _eval_single_bin(1, op, 1, engine)
1095
+ x = self.eval(ex, engine=engine, parser=parser)
1096
+ assert x == expec
1097
+
1098
+ expec = _eval_single_bin(x, op, 1, engine)
1099
+ y = self.eval(ex2, local_dict={"x": x}, engine=engine, parser=parser)
1100
+ assert y == expec
1101
+
1102
+ expec = _eval_single_bin(1, op, x + 1, engine)
1103
+ y = self.eval(ex3, local_dict={"x": x}, engine=engine, parser=parser)
1104
+ assert y == expec
1105
+
1106
+ @pytest.mark.parametrize("rhs", [True, False])
1107
+ @pytest.mark.parametrize("lhs", [True, False])
1108
+ @pytest.mark.parametrize("op", expr.BOOL_OPS_SYMS)
1109
+ def test_simple_bool_ops(self, rhs, lhs, op):
1110
+ ex = f"{lhs} {op} {rhs}"
1111
+
1112
+ if parser == "python" and op in ["and", "or"]:
1113
+ msg = "'BoolOp' nodes are not implemented"
1114
+ with pytest.raises(NotImplementedError, match=msg):
1115
+ self.eval(ex)
1116
+ return
1117
+
1118
+ res = self.eval(ex)
1119
+ exp = eval(ex)
1120
+ assert res == exp
1121
+
1122
+ @pytest.mark.parametrize("rhs", [True, False])
1123
+ @pytest.mark.parametrize("lhs", [True, False])
1124
+ @pytest.mark.parametrize("op", expr.BOOL_OPS_SYMS)
1125
+ def test_bool_ops_with_constants(self, rhs, lhs, op):
1126
+ ex = f"{lhs} {op} {rhs}"
1127
+
1128
+ if parser == "python" and op in ["and", "or"]:
1129
+ msg = "'BoolOp' nodes are not implemented"
1130
+ with pytest.raises(NotImplementedError, match=msg):
1131
+ self.eval(ex)
1132
+ return
1133
+
1134
+ res = self.eval(ex)
1135
+ exp = eval(ex)
1136
+ assert res == exp
1137
+
1138
+ def test_4d_ndarray_fails(self):
1139
+ x = np.random.default_rng(2).standard_normal((3, 4, 5, 6))
1140
+ y = Series(np.random.default_rng(2).standard_normal(10))
1141
+ msg = "N-dimensional objects, where N > 2, are not supported with eval"
1142
+ with pytest.raises(NotImplementedError, match=msg):
1143
+ self.eval("x + y", local_dict={"x": x, "y": y})
1144
+
1145
+ def test_constant(self):
1146
+ x = self.eval("1")
1147
+ assert x == 1
1148
+
1149
+ def test_single_variable(self):
1150
+ df = DataFrame(np.random.default_rng(2).standard_normal((10, 2)))
1151
+ df2 = self.eval("df", local_dict={"df": df})
1152
+ tm.assert_frame_equal(df, df2)
1153
+
1154
+ def test_failing_subscript_with_name_error(self):
1155
+ df = DataFrame(np.random.default_rng(2).standard_normal((5, 3))) # noqa: F841
1156
+ with pytest.raises(NameError, match="name 'x' is not defined"):
1157
+ self.eval("df[x > 2] > 2")
1158
+
1159
+ def test_lhs_expression_subscript(self):
1160
+ df = DataFrame(np.random.default_rng(2).standard_normal((5, 3)))
1161
+ result = self.eval("(df + 1)[df > 2]", local_dict={"df": df})
1162
+ expected = (df + 1)[df > 2]
1163
+ tm.assert_frame_equal(result, expected)
1164
+
1165
+ def test_attr_expression(self):
1166
+ df = DataFrame(
1167
+ np.random.default_rng(2).standard_normal((5, 3)), columns=list("abc")
1168
+ )
1169
+ expr1 = "df.a < df.b"
1170
+ expec1 = df.a < df.b
1171
+ expr2 = "df.a + df.b + df.c"
1172
+ expec2 = df.a + df.b + df.c
1173
+ expr3 = "df.a + df.b + df.c[df.b < 0]"
1174
+ expec3 = df.a + df.b + df.c[df.b < 0]
1175
+ exprs = expr1, expr2, expr3
1176
+ expecs = expec1, expec2, expec3
1177
+ for e, expec in zip(exprs, expecs):
1178
+ tm.assert_series_equal(expec, self.eval(e, local_dict={"df": df}))
1179
+
1180
+ def test_assignment_fails(self):
1181
+ df = DataFrame(
1182
+ np.random.default_rng(2).standard_normal((5, 3)), columns=list("abc")
1183
+ )
1184
+ df2 = DataFrame(np.random.default_rng(2).standard_normal((5, 3)))
1185
+ expr1 = "df = df2"
1186
+ msg = "cannot assign without a target object"
1187
+ with pytest.raises(ValueError, match=msg):
1188
+ self.eval(expr1, local_dict={"df": df, "df2": df2})
1189
+
1190
+ def test_assignment_column_multiple_raise(self):
1191
+ df = DataFrame(
1192
+ np.random.default_rng(2).standard_normal((5, 2)), columns=list("ab")
1193
+ )
1194
+ # multiple assignees
1195
+ with pytest.raises(SyntaxError, match="invalid syntax"):
1196
+ df.eval("d c = a + b")
1197
+
1198
+ def test_assignment_column_invalid_assign(self):
1199
+ df = DataFrame(
1200
+ np.random.default_rng(2).standard_normal((5, 2)), columns=list("ab")
1201
+ )
1202
+ # invalid assignees
1203
+ msg = "left hand side of an assignment must be a single name"
1204
+ with pytest.raises(SyntaxError, match=msg):
1205
+ df.eval("d,c = a + b")
1206
+
1207
+ def test_assignment_column_invalid_assign_function_call(self):
1208
+ df = DataFrame(
1209
+ np.random.default_rng(2).standard_normal((5, 2)), columns=list("ab")
1210
+ )
1211
+ msg = "cannot assign to function call"
1212
+ with pytest.raises(SyntaxError, match=msg):
1213
+ df.eval('Timestamp("20131001") = a + b')
1214
+
1215
+ def test_assignment_single_assign_existing(self):
1216
+ df = DataFrame(
1217
+ np.random.default_rng(2).standard_normal((5, 2)), columns=list("ab")
1218
+ )
1219
+ # single assignment - existing variable
1220
+ expected = df.copy()
1221
+ expected["a"] = expected["a"] + expected["b"]
1222
+ df.eval("a = a + b", inplace=True)
1223
+ tm.assert_frame_equal(df, expected)
1224
+
1225
+ def test_assignment_single_assign_new(self):
1226
+ df = DataFrame(
1227
+ np.random.default_rng(2).standard_normal((5, 2)), columns=list("ab")
1228
+ )
1229
+ # single assignment - new variable
1230
+ expected = df.copy()
1231
+ expected["c"] = expected["a"] + expected["b"]
1232
+ df.eval("c = a + b", inplace=True)
1233
+ tm.assert_frame_equal(df, expected)
1234
+
1235
+ def test_assignment_single_assign_local_overlap(self):
1236
+ df = DataFrame(
1237
+ np.random.default_rng(2).standard_normal((5, 2)), columns=list("ab")
1238
+ )
1239
+ df = df.copy()
1240
+ a = 1 # noqa: F841
1241
+ df.eval("a = 1 + b", inplace=True)
1242
+
1243
+ expected = df.copy()
1244
+ expected["a"] = 1 + expected["b"]
1245
+ tm.assert_frame_equal(df, expected)
1246
+
1247
+ def test_assignment_single_assign_name(self):
1248
+ df = DataFrame(
1249
+ np.random.default_rng(2).standard_normal((5, 2)), columns=list("ab")
1250
+ )
1251
+
1252
+ a = 1 # noqa: F841
1253
+ old_a = df.a.copy()
1254
+ df.eval("a = a + b", inplace=True)
1255
+ result = old_a + df.b
1256
+ tm.assert_series_equal(result, df.a, check_names=False)
1257
+ assert result.name is None
1258
+
1259
+ def test_assignment_multiple_raises(self):
1260
+ df = DataFrame(
1261
+ np.random.default_rng(2).standard_normal((5, 2)), columns=list("ab")
1262
+ )
1263
+ # multiple assignment
1264
+ df.eval("c = a + b", inplace=True)
1265
+ msg = "can only assign a single expression"
1266
+ with pytest.raises(SyntaxError, match=msg):
1267
+ df.eval("c = a = b")
1268
+
1269
+ def test_assignment_explicit(self):
1270
+ df = DataFrame(
1271
+ np.random.default_rng(2).standard_normal((5, 2)), columns=list("ab")
1272
+ )
1273
+ # explicit targets
1274
+ self.eval("c = df.a + df.b", local_dict={"df": df}, target=df, inplace=True)
1275
+ expected = df.copy()
1276
+ expected["c"] = expected["a"] + expected["b"]
1277
+ tm.assert_frame_equal(df, expected)
1278
+
1279
+ def test_column_in(self):
1280
+ # GH 11235
1281
+ df = DataFrame({"a": [11], "b": [-32]})
1282
+ result = df.eval("a in [11, -32]")
1283
+ expected = Series([True])
1284
+ # TODO: 2022-01-29: Name check failed with numexpr 2.7.3 in CI
1285
+ # but cannot reproduce locally
1286
+ tm.assert_series_equal(result, expected, check_names=False)
1287
+
1288
+ @pytest.mark.xfail(reason="Unknown: Omitted test_ in name prior.")
1289
+ def test_assignment_not_inplace(self):
1290
+ # see gh-9297
1291
+ df = DataFrame(
1292
+ np.random.default_rng(2).standard_normal((5, 2)), columns=list("ab")
1293
+ )
1294
+
1295
+ actual = df.eval("c = a + b", inplace=False)
1296
+ assert actual is not None
1297
+
1298
+ expected = df.copy()
1299
+ expected["c"] = expected["a"] + expected["b"]
1300
+ tm.assert_frame_equal(df, expected)
1301
+
1302
+ def test_multi_line_expression(self, warn_copy_on_write):
1303
+ # GH 11149
1304
+ df = DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
1305
+ expected = df.copy()
1306
+
1307
+ expected["c"] = expected["a"] + expected["b"]
1308
+ expected["d"] = expected["c"] + expected["b"]
1309
+ answer = df.eval(
1310
+ """
1311
+ c = a + b
1312
+ d = c + b""",
1313
+ inplace=True,
1314
+ )
1315
+ tm.assert_frame_equal(expected, df)
1316
+ assert answer is None
1317
+
1318
+ expected["a"] = expected["a"] - 1
1319
+ expected["e"] = expected["a"] + 2
1320
+ answer = df.eval(
1321
+ """
1322
+ a = a - 1
1323
+ e = a + 2""",
1324
+ inplace=True,
1325
+ )
1326
+ tm.assert_frame_equal(expected, df)
1327
+ assert answer is None
1328
+
1329
+ # multi-line not valid if not all assignments
1330
+ msg = "Multi-line expressions are only valid if all expressions contain"
1331
+ with pytest.raises(ValueError, match=msg):
1332
+ df.eval(
1333
+ """
1334
+ a = b + 2
1335
+ b - 2""",
1336
+ inplace=False,
1337
+ )
1338
+
1339
+ def test_multi_line_expression_not_inplace(self):
1340
+ # GH 11149
1341
+ df = DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
1342
+ expected = df.copy()
1343
+
1344
+ expected["c"] = expected["a"] + expected["b"]
1345
+ expected["d"] = expected["c"] + expected["b"]
1346
+ df = df.eval(
1347
+ """
1348
+ c = a + b
1349
+ d = c + b""",
1350
+ inplace=False,
1351
+ )
1352
+ tm.assert_frame_equal(expected, df)
1353
+
1354
+ expected["a"] = expected["a"] - 1
1355
+ expected["e"] = expected["a"] + 2
1356
+ df = df.eval(
1357
+ """
1358
+ a = a - 1
1359
+ e = a + 2""",
1360
+ inplace=False,
1361
+ )
1362
+ tm.assert_frame_equal(expected, df)
1363
+
1364
+ def test_multi_line_expression_local_variable(self):
1365
+ # GH 15342
1366
+ df = DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
1367
+ expected = df.copy()
1368
+
1369
+ local_var = 7
1370
+ expected["c"] = expected["a"] * local_var
1371
+ expected["d"] = expected["c"] + local_var
1372
+ answer = df.eval(
1373
+ """
1374
+ c = a * @local_var
1375
+ d = c + @local_var
1376
+ """,
1377
+ inplace=True,
1378
+ )
1379
+ tm.assert_frame_equal(expected, df)
1380
+ assert answer is None
1381
+
1382
+ def test_multi_line_expression_callable_local_variable(self):
1383
+ # 26426
1384
+ df = DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
1385
+
1386
+ def local_func(a, b):
1387
+ return b
1388
+
1389
+ expected = df.copy()
1390
+ expected["c"] = expected["a"] * local_func(1, 7)
1391
+ expected["d"] = expected["c"] + local_func(1, 7)
1392
+ answer = df.eval(
1393
+ """
1394
+ c = a * @local_func(1, 7)
1395
+ d = c + @local_func(1, 7)
1396
+ """,
1397
+ inplace=True,
1398
+ )
1399
+ tm.assert_frame_equal(expected, df)
1400
+ assert answer is None
1401
+
1402
+ def test_multi_line_expression_callable_local_variable_with_kwargs(self):
1403
+ # 26426
1404
+ df = DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
1405
+
1406
+ def local_func(a, b):
1407
+ return b
1408
+
1409
+ expected = df.copy()
1410
+ expected["c"] = expected["a"] * local_func(b=7, a=1)
1411
+ expected["d"] = expected["c"] + local_func(b=7, a=1)
1412
+ answer = df.eval(
1413
+ """
1414
+ c = a * @local_func(b=7, a=1)
1415
+ d = c + @local_func(b=7, a=1)
1416
+ """,
1417
+ inplace=True,
1418
+ )
1419
+ tm.assert_frame_equal(expected, df)
1420
+ assert answer is None
1421
+
1422
+ def test_assignment_in_query(self):
1423
+ # GH 8664
1424
+ df = DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
1425
+ df_orig = df.copy()
1426
+ msg = "cannot assign without a target object"
1427
+ with pytest.raises(ValueError, match=msg):
1428
+ df.query("a = 1")
1429
+ tm.assert_frame_equal(df, df_orig)
1430
+
1431
+ def test_query_inplace(self):
1432
+ # see gh-11149
1433
+ df = DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
1434
+ expected = df.copy()
1435
+ expected = expected[expected["a"] == 2]
1436
+ df.query("a == 2", inplace=True)
1437
+ tm.assert_frame_equal(expected, df)
1438
+
1439
+ df = {}
1440
+ expected = {"a": 3}
1441
+
1442
+ self.eval("a = 1 + 2", target=df, inplace=True)
1443
+ tm.assert_dict_equal(df, expected)
1444
+
1445
+ @pytest.mark.parametrize("invalid_target", [1, "cat", [1, 2], np.array([]), (1, 3)])
1446
+ def test_cannot_item_assign(self, invalid_target):
1447
+ msg = "Cannot assign expression output to target"
1448
+ expression = "a = 1 + 2"
1449
+
1450
+ with pytest.raises(ValueError, match=msg):
1451
+ self.eval(expression, target=invalid_target, inplace=True)
1452
+
1453
+ if hasattr(invalid_target, "copy"):
1454
+ with pytest.raises(ValueError, match=msg):
1455
+ self.eval(expression, target=invalid_target, inplace=False)
1456
+
1457
+ @pytest.mark.parametrize("invalid_target", [1, "cat", (1, 3)])
1458
+ def test_cannot_copy_item(self, invalid_target):
1459
+ msg = "Cannot return a copy of the target"
1460
+ expression = "a = 1 + 2"
1461
+
1462
+ with pytest.raises(ValueError, match=msg):
1463
+ self.eval(expression, target=invalid_target, inplace=False)
1464
+
1465
+ @pytest.mark.parametrize("target", [1, "cat", [1, 2], np.array([]), (1, 3), {1: 2}])
1466
+ def test_inplace_no_assignment(self, target):
1467
+ expression = "1 + 2"
1468
+
1469
+ assert self.eval(expression, target=target, inplace=False) == 3
1470
+
1471
+ msg = "Cannot operate inplace if there is no assignment"
1472
+ with pytest.raises(ValueError, match=msg):
1473
+ self.eval(expression, target=target, inplace=True)
1474
+
1475
+ def test_basic_period_index_boolean_expression(self):
1476
+ df = DataFrame(
1477
+ np.random.default_rng(2).standard_normal((2, 2)),
1478
+ columns=period_range("2020-01-01", freq="D", periods=2),
1479
+ )
1480
+ e = df < 2
1481
+ r = self.eval("df < 2", local_dict={"df": df})
1482
+ x = df < 2
1483
+
1484
+ tm.assert_frame_equal(r, e)
1485
+ tm.assert_frame_equal(x, e)
1486
+
1487
+ def test_basic_period_index_subscript_expression(self):
1488
+ df = DataFrame(
1489
+ np.random.default_rng(2).standard_normal((2, 2)),
1490
+ columns=period_range("2020-01-01", freq="D", periods=2),
1491
+ )
1492
+ r = self.eval("df[df < 2 + 3]", local_dict={"df": df})
1493
+ e = df[df < 2 + 3]
1494
+ tm.assert_frame_equal(r, e)
1495
+
1496
+ def test_nested_period_index_subscript_expression(self):
1497
+ df = DataFrame(
1498
+ np.random.default_rng(2).standard_normal((2, 2)),
1499
+ columns=period_range("2020-01-01", freq="D", periods=2),
1500
+ )
1501
+ r = self.eval("df[df[df < 2] < 2] + df * 2", local_dict={"df": df})
1502
+ e = df[df[df < 2] < 2] + df * 2
1503
+ tm.assert_frame_equal(r, e)
1504
+
1505
+ def test_date_boolean(self, engine, parser):
1506
+ df = DataFrame(np.random.default_rng(2).standard_normal((5, 3)))
1507
+ df["dates1"] = date_range("1/1/2012", periods=5)
1508
+ res = self.eval(
1509
+ "df.dates1 < 20130101",
1510
+ local_dict={"df": df},
1511
+ engine=engine,
1512
+ parser=parser,
1513
+ )
1514
+ expec = df.dates1 < "20130101"
1515
+ tm.assert_series_equal(res, expec, check_names=False)
1516
+
1517
+ def test_simple_in_ops(self, engine, parser):
1518
+ if parser != "python":
1519
+ res = pd.eval("1 in [1, 2]", engine=engine, parser=parser)
1520
+ assert res
1521
+
1522
+ res = pd.eval("2 in (1, 2)", engine=engine, parser=parser)
1523
+ assert res
1524
+
1525
+ res = pd.eval("3 in (1, 2)", engine=engine, parser=parser)
1526
+ assert not res
1527
+
1528
+ res = pd.eval("3 not in (1, 2)", engine=engine, parser=parser)
1529
+ assert res
1530
+
1531
+ res = pd.eval("[3] not in (1, 2)", engine=engine, parser=parser)
1532
+ assert res
1533
+
1534
+ res = pd.eval("[3] in ([3], 2)", engine=engine, parser=parser)
1535
+ assert res
1536
+
1537
+ res = pd.eval("[[3]] in [[[3]], 2]", engine=engine, parser=parser)
1538
+ assert res
1539
+
1540
+ res = pd.eval("(3,) in [(3,), 2]", engine=engine, parser=parser)
1541
+ assert res
1542
+
1543
+ res = pd.eval("(3,) not in [(3,), 2]", engine=engine, parser=parser)
1544
+ assert not res
1545
+
1546
+ res = pd.eval("[(3,)] in [[(3,)], 2]", engine=engine, parser=parser)
1547
+ assert res
1548
+ else:
1549
+ msg = "'In' nodes are not implemented"
1550
+ with pytest.raises(NotImplementedError, match=msg):
1551
+ pd.eval("1 in [1, 2]", engine=engine, parser=parser)
1552
+ with pytest.raises(NotImplementedError, match=msg):
1553
+ pd.eval("2 in (1, 2)", engine=engine, parser=parser)
1554
+ with pytest.raises(NotImplementedError, match=msg):
1555
+ pd.eval("3 in (1, 2)", engine=engine, parser=parser)
1556
+ with pytest.raises(NotImplementedError, match=msg):
1557
+ pd.eval("[(3,)] in (1, 2, [(3,)])", engine=engine, parser=parser)
1558
+ msg = "'NotIn' nodes are not implemented"
1559
+ with pytest.raises(NotImplementedError, match=msg):
1560
+ pd.eval("3 not in (1, 2)", engine=engine, parser=parser)
1561
+ with pytest.raises(NotImplementedError, match=msg):
1562
+ pd.eval("[3] not in (1, 2, [[3]])", engine=engine, parser=parser)
1563
+
1564
+ def test_check_many_exprs(self, engine, parser):
1565
+ a = 1 # noqa: F841
1566
+ expr = " * ".join("a" * 33)
1567
+ expected = 1
1568
+ res = pd.eval(expr, engine=engine, parser=parser)
1569
+ assert res == expected
1570
+
1571
+ @pytest.mark.parametrize(
1572
+ "expr",
1573
+ [
1574
+ "df > 2 and df > 3",
1575
+ "df > 2 or df > 3",
1576
+ "not df > 2",
1577
+ ],
1578
+ )
1579
+ def test_fails_and_or_not(self, expr, engine, parser):
1580
+ df = DataFrame(np.random.default_rng(2).standard_normal((5, 3)))
1581
+ if parser == "python":
1582
+ msg = "'BoolOp' nodes are not implemented"
1583
+ if "not" in expr:
1584
+ msg = "'Not' nodes are not implemented"
1585
+
1586
+ with pytest.raises(NotImplementedError, match=msg):
1587
+ pd.eval(
1588
+ expr,
1589
+ local_dict={"df": df},
1590
+ parser=parser,
1591
+ engine=engine,
1592
+ )
1593
+ else:
1594
+ # smoke-test, should not raise
1595
+ pd.eval(
1596
+ expr,
1597
+ local_dict={"df": df},
1598
+ parser=parser,
1599
+ engine=engine,
1600
+ )
1601
+
1602
+ @pytest.mark.parametrize("char", ["|", "&"])
1603
+ def test_fails_ampersand_pipe(self, char, engine, parser):
1604
+ df = DataFrame(np.random.default_rng(2).standard_normal((5, 3))) # noqa: F841
1605
+ ex = f"(df + 2)[df > 1] > 0 {char} (df > 0)"
1606
+ if parser == "python":
1607
+ msg = "cannot evaluate scalar only bool ops"
1608
+ with pytest.raises(NotImplementedError, match=msg):
1609
+ pd.eval(ex, parser=parser, engine=engine)
1610
+ else:
1611
+ # smoke-test, should not raise
1612
+ pd.eval(ex, parser=parser, engine=engine)
1613
+
1614
+
1615
+ class TestMath:
1616
+ def eval(self, *args, **kwargs):
1617
+ kwargs["level"] = kwargs.pop("level", 0) + 1
1618
+ return pd.eval(*args, **kwargs)
1619
+
1620
+ @pytest.mark.skipif(
1621
+ not NUMEXPR_INSTALLED, reason="Unary ops only implemented for numexpr"
1622
+ )
1623
+ @pytest.mark.parametrize("fn", _unary_math_ops)
1624
+ def test_unary_functions(self, fn):
1625
+ df = DataFrame({"a": np.random.default_rng(2).standard_normal(10)})
1626
+ a = df.a
1627
+
1628
+ expr = f"{fn}(a)"
1629
+ got = self.eval(expr)
1630
+ with np.errstate(all="ignore"):
1631
+ expect = getattr(np, fn)(a)
1632
+ tm.assert_series_equal(got, expect, check_names=False)
1633
+
1634
+ @pytest.mark.parametrize("fn", _binary_math_ops)
1635
+ def test_binary_functions(self, fn):
1636
+ df = DataFrame(
1637
+ {
1638
+ "a": np.random.default_rng(2).standard_normal(10),
1639
+ "b": np.random.default_rng(2).standard_normal(10),
1640
+ }
1641
+ )
1642
+ a = df.a
1643
+ b = df.b
1644
+
1645
+ expr = f"{fn}(a, b)"
1646
+ got = self.eval(expr)
1647
+ with np.errstate(all="ignore"):
1648
+ expect = getattr(np, fn)(a, b)
1649
+ tm.assert_almost_equal(got, expect, check_names=False)
1650
+
1651
+ def test_df_use_case(self, engine, parser):
1652
+ df = DataFrame(
1653
+ {
1654
+ "a": np.random.default_rng(2).standard_normal(10),
1655
+ "b": np.random.default_rng(2).standard_normal(10),
1656
+ }
1657
+ )
1658
+ df.eval(
1659
+ "e = arctan2(sin(a), b)",
1660
+ engine=engine,
1661
+ parser=parser,
1662
+ inplace=True,
1663
+ )
1664
+ got = df.e
1665
+ expect = np.arctan2(np.sin(df.a), df.b)
1666
+ tm.assert_series_equal(got, expect, check_names=False)
1667
+
1668
+ def test_df_arithmetic_subexpression(self, engine, parser):
1669
+ df = DataFrame(
1670
+ {
1671
+ "a": np.random.default_rng(2).standard_normal(10),
1672
+ "b": np.random.default_rng(2).standard_normal(10),
1673
+ }
1674
+ )
1675
+ df.eval("e = sin(a + b)", engine=engine, parser=parser, inplace=True)
1676
+ got = df.e
1677
+ expect = np.sin(df.a + df.b)
1678
+ tm.assert_series_equal(got, expect, check_names=False)
1679
+
1680
+ @pytest.mark.parametrize(
1681
+ "dtype, expect_dtype",
1682
+ [
1683
+ (np.int32, np.float64),
1684
+ (np.int64, np.float64),
1685
+ (np.float32, np.float32),
1686
+ (np.float64, np.float64),
1687
+ pytest.param(np.complex128, np.complex128, marks=td.skip_if_windows),
1688
+ ],
1689
+ )
1690
+ def test_result_types(self, dtype, expect_dtype, engine, parser):
1691
+ # xref https://github.com/pandas-dev/pandas/issues/12293
1692
+ # this fails on Windows, apparently a floating point precision issue
1693
+
1694
+ # Did not test complex64 because DataFrame is converting it to
1695
+ # complex128. Due to https://github.com/pandas-dev/pandas/issues/10952
1696
+ df = DataFrame(
1697
+ {"a": np.random.default_rng(2).standard_normal(10).astype(dtype)}
1698
+ )
1699
+ assert df.a.dtype == dtype
1700
+ df.eval("b = sin(a)", engine=engine, parser=parser, inplace=True)
1701
+ got = df.b
1702
+ expect = np.sin(df.a)
1703
+ assert expect.dtype == got.dtype
1704
+ assert expect_dtype == got.dtype
1705
+ tm.assert_series_equal(got, expect, check_names=False)
1706
+
1707
+ def test_undefined_func(self, engine, parser):
1708
+ df = DataFrame({"a": np.random.default_rng(2).standard_normal(10)})
1709
+ msg = '"mysin" is not a supported function'
1710
+
1711
+ with pytest.raises(ValueError, match=msg):
1712
+ df.eval("mysin(a)", engine=engine, parser=parser)
1713
+
1714
+ def test_keyword_arg(self, engine, parser):
1715
+ df = DataFrame({"a": np.random.default_rng(2).standard_normal(10)})
1716
+ msg = 'Function "sin" does not support keyword arguments'
1717
+
1718
+ with pytest.raises(TypeError, match=msg):
1719
+ df.eval("sin(x=a)", engine=engine, parser=parser)
1720
+
1721
+
1722
+ _var_s = np.random.default_rng(2).standard_normal(10)
1723
+
1724
+
1725
+ class TestScope:
1726
+ def test_global_scope(self, engine, parser):
1727
+ e = "_var_s * 2"
1728
+ tm.assert_numpy_array_equal(
1729
+ _var_s * 2, pd.eval(e, engine=engine, parser=parser)
1730
+ )
1731
+
1732
+ def test_no_new_locals(self, engine, parser):
1733
+ x = 1
1734
+ lcls = locals().copy()
1735
+ pd.eval("x + 1", local_dict=lcls, engine=engine, parser=parser)
1736
+ lcls2 = locals().copy()
1737
+ lcls2.pop("lcls")
1738
+ assert lcls == lcls2
1739
+
1740
+ def test_no_new_globals(self, engine, parser):
1741
+ x = 1 # noqa: F841
1742
+ gbls = globals().copy()
1743
+ pd.eval("x + 1", engine=engine, parser=parser)
1744
+ gbls2 = globals().copy()
1745
+ assert gbls == gbls2
1746
+
1747
+ def test_empty_locals(self, engine, parser):
1748
+ # GH 47084
1749
+ x = 1 # noqa: F841
1750
+ msg = "name 'x' is not defined"
1751
+ with pytest.raises(UndefinedVariableError, match=msg):
1752
+ pd.eval("x + 1", engine=engine, parser=parser, local_dict={})
1753
+
1754
+ def test_empty_globals(self, engine, parser):
1755
+ # GH 47084
1756
+ msg = "name '_var_s' is not defined"
1757
+ e = "_var_s * 2"
1758
+ with pytest.raises(UndefinedVariableError, match=msg):
1759
+ pd.eval(e, engine=engine, parser=parser, global_dict={})
1760
+
1761
+
1762
+ @td.skip_if_no("numexpr")
1763
+ def test_invalid_engine():
1764
+ msg = "Invalid engine 'asdf' passed"
1765
+ with pytest.raises(KeyError, match=msg):
1766
+ pd.eval("x + y", local_dict={"x": 1, "y": 2}, engine="asdf")
1767
+
1768
+
1769
+ @td.skip_if_no("numexpr")
1770
+ @pytest.mark.parametrize(
1771
+ ("use_numexpr", "expected"),
1772
+ (
1773
+ (True, "numexpr"),
1774
+ (False, "python"),
1775
+ ),
1776
+ )
1777
+ def test_numexpr_option_respected(use_numexpr, expected):
1778
+ # GH 32556
1779
+ from pandas.core.computation.eval import _check_engine
1780
+
1781
+ with pd.option_context("compute.use_numexpr", use_numexpr):
1782
+ result = _check_engine(None)
1783
+ assert result == expected
1784
+
1785
+
1786
+ @td.skip_if_no("numexpr")
1787
+ def test_numexpr_option_incompatible_op():
1788
+ # GH 32556
1789
+ with pd.option_context("compute.use_numexpr", False):
1790
+ df = DataFrame(
1791
+ {"A": [True, False, True, False, None, None], "B": [1, 2, 3, 4, 5, 6]}
1792
+ )
1793
+ result = df.query("A.isnull()")
1794
+ expected = DataFrame({"A": [None, None], "B": [5, 6]}, index=[4, 5])
1795
+ tm.assert_frame_equal(result, expected)
1796
+
1797
+
1798
+ @td.skip_if_no("numexpr")
1799
+ def test_invalid_parser():
1800
+ msg = "Invalid parser 'asdf' passed"
1801
+ with pytest.raises(KeyError, match=msg):
1802
+ pd.eval("x + y", local_dict={"x": 1, "y": 2}, parser="asdf")
1803
+
1804
+
1805
+ _parsers: dict[str, type[BaseExprVisitor]] = {
1806
+ "python": PythonExprVisitor,
1807
+ "pytables": pytables.PyTablesExprVisitor,
1808
+ "pandas": PandasExprVisitor,
1809
+ }
1810
+
1811
+
1812
+ @pytest.mark.parametrize("engine", ENGINES)
1813
+ @pytest.mark.parametrize("parser", _parsers)
1814
+ def test_disallowed_nodes(engine, parser):
1815
+ VisitorClass = _parsers[parser]
1816
+ inst = VisitorClass("x + 1", engine, parser)
1817
+
1818
+ for ops in VisitorClass.unsupported_nodes:
1819
+ msg = "nodes are not implemented"
1820
+ with pytest.raises(NotImplementedError, match=msg):
1821
+ getattr(inst, ops)()
1822
+
1823
+
1824
+ def test_syntax_error_exprs(engine, parser):
1825
+ e = "s +"
1826
+ with pytest.raises(SyntaxError, match="invalid syntax"):
1827
+ pd.eval(e, engine=engine, parser=parser)
1828
+
1829
+
1830
+ def test_name_error_exprs(engine, parser):
1831
+ e = "s + t"
1832
+ msg = "name 's' is not defined"
1833
+ with pytest.raises(NameError, match=msg):
1834
+ pd.eval(e, engine=engine, parser=parser)
1835
+
1836
+
1837
+ @pytest.mark.parametrize("express", ["a + @b", "@a + b", "@a + @b"])
1838
+ def test_invalid_local_variable_reference(engine, parser, express):
1839
+ a, b = 1, 2 # noqa: F841
1840
+
1841
+ if parser != "pandas":
1842
+ with pytest.raises(SyntaxError, match="The '@' prefix is only"):
1843
+ pd.eval(express, engine=engine, parser=parser)
1844
+ else:
1845
+ with pytest.raises(SyntaxError, match="The '@' prefix is not"):
1846
+ pd.eval(express, engine=engine, parser=parser)
1847
+
1848
+
1849
+ def test_numexpr_builtin_raises(engine, parser):
1850
+ sin, dotted_line = 1, 2
1851
+ if engine == "numexpr":
1852
+ msg = "Variables in expression .+"
1853
+ with pytest.raises(NumExprClobberingError, match=msg):
1854
+ pd.eval("sin + dotted_line", engine=engine, parser=parser)
1855
+ else:
1856
+ res = pd.eval("sin + dotted_line", engine=engine, parser=parser)
1857
+ assert res == sin + dotted_line
1858
+
1859
+
1860
+ def test_bad_resolver_raises(engine, parser):
1861
+ cannot_resolve = 42, 3.0
1862
+ with pytest.raises(TypeError, match="Resolver of type .+"):
1863
+ pd.eval("1 + 2", resolvers=cannot_resolve, engine=engine, parser=parser)
1864
+
1865
+
1866
+ def test_empty_string_raises(engine, parser):
1867
+ # GH 13139
1868
+ with pytest.raises(ValueError, match="expr cannot be an empty string"):
1869
+ pd.eval("", engine=engine, parser=parser)
1870
+
1871
+
1872
+ def test_more_than_one_expression_raises(engine, parser):
1873
+ with pytest.raises(SyntaxError, match="only a single expression is allowed"):
1874
+ pd.eval("1 + 1; 2 + 2", engine=engine, parser=parser)
1875
+
1876
+
1877
+ @pytest.mark.parametrize("cmp", ("and", "or"))
1878
+ @pytest.mark.parametrize("lhs", (int, float))
1879
+ @pytest.mark.parametrize("rhs", (int, float))
1880
+ def test_bool_ops_fails_on_scalars(lhs, cmp, rhs, engine, parser):
1881
+ gen = {
1882
+ int: lambda: np.random.default_rng(2).integers(10),
1883
+ float: np.random.default_rng(2).standard_normal,
1884
+ }
1885
+
1886
+ mid = gen[lhs]() # noqa: F841
1887
+ lhs = gen[lhs]()
1888
+ rhs = gen[rhs]()
1889
+
1890
+ ex1 = f"lhs {cmp} mid {cmp} rhs"
1891
+ ex2 = f"lhs {cmp} mid and mid {cmp} rhs"
1892
+ ex3 = f"(lhs {cmp} mid) & (mid {cmp} rhs)"
1893
+ for ex in (ex1, ex2, ex3):
1894
+ msg = "cannot evaluate scalar only bool ops|'BoolOp' nodes are not"
1895
+ with pytest.raises(NotImplementedError, match=msg):
1896
+ pd.eval(ex, engine=engine, parser=parser)
1897
+
1898
+
1899
+ @pytest.mark.parametrize(
1900
+ "other",
1901
+ [
1902
+ "'x'",
1903
+ "...",
1904
+ ],
1905
+ )
1906
+ def test_equals_various(other):
1907
+ df = DataFrame({"A": ["a", "b", "c"]}, dtype=object)
1908
+ result = df.eval(f"A == {other}")
1909
+ expected = Series([False, False, False], name="A")
1910
+ if USE_NUMEXPR:
1911
+ # https://github.com/pandas-dev/pandas/issues/10239
1912
+ # lose name with numexpr engine. Remove when that's fixed.
1913
+ expected.name = None
1914
+ tm.assert_series_equal(result, expected)
1915
+
1916
+
1917
+ def test_inf(engine, parser):
1918
+ s = "inf + 1"
1919
+ expected = np.inf
1920
+ result = pd.eval(s, engine=engine, parser=parser)
1921
+ assert result == expected
1922
+
1923
+
1924
+ @pytest.mark.parametrize("column", ["Temp(°C)", "Capacitance(μF)"])
1925
+ def test_query_token(engine, column):
1926
+ # See: https://github.com/pandas-dev/pandas/pull/42826
1927
+ df = DataFrame(
1928
+ np.random.default_rng(2).standard_normal((5, 2)), columns=[column, "b"]
1929
+ )
1930
+ expected = df[df[column] > 5]
1931
+ query_string = f"`{column}` > 5"
1932
+ result = df.query(query_string, engine=engine)
1933
+ tm.assert_frame_equal(result, expected)
1934
+
1935
+
1936
+ def test_negate_lt_eq_le(engine, parser):
1937
+ df = DataFrame([[0, 10], [1, 20]], columns=["cat", "count"])
1938
+ expected = df[~(df.cat > 0)]
1939
+
1940
+ result = df.query("~(cat > 0)", engine=engine, parser=parser)
1941
+ tm.assert_frame_equal(result, expected)
1942
+
1943
+ if parser == "python":
1944
+ msg = "'Not' nodes are not implemented"
1945
+ with pytest.raises(NotImplementedError, match=msg):
1946
+ df.query("not (cat > 0)", engine=engine, parser=parser)
1947
+ else:
1948
+ result = df.query("not (cat > 0)", engine=engine, parser=parser)
1949
+ tm.assert_frame_equal(result, expected)
1950
+
1951
+
1952
+ @pytest.mark.parametrize(
1953
+ "column",
1954
+ DEFAULT_GLOBALS.keys(),
1955
+ )
1956
+ def test_eval_no_support_column_name(request, column):
1957
+ # GH 44603
1958
+ if column in ["True", "False", "inf", "Inf"]:
1959
+ request.applymarker(
1960
+ pytest.mark.xfail(
1961
+ raises=KeyError,
1962
+ reason=f"GH 47859 DataFrame eval not supported with {column}",
1963
+ )
1964
+ )
1965
+
1966
+ df = DataFrame(
1967
+ np.random.default_rng(2).integers(0, 100, size=(10, 2)),
1968
+ columns=[column, "col1"],
1969
+ )
1970
+ expected = df[df[column] > 6]
1971
+ result = df.query(f"{column}>6")
1972
+
1973
+ tm.assert_frame_equal(result, expected)
1974
+
1975
+
1976
+ def test_set_inplace(using_copy_on_write, warn_copy_on_write):
1977
+ # https://github.com/pandas-dev/pandas/issues/47449
1978
+ # Ensure we don't only update the DataFrame inplace, but also the actual
1979
+ # column values, such that references to this column also get updated
1980
+ df = DataFrame({"A": [1, 2, 3], "B": [4, 5, 6], "C": [7, 8, 9]})
1981
+ result_view = df[:]
1982
+ ser = df["A"]
1983
+ with tm.assert_cow_warning(warn_copy_on_write):
1984
+ df.eval("A = B + C", inplace=True)
1985
+ expected = DataFrame({"A": [11, 13, 15], "B": [4, 5, 6], "C": [7, 8, 9]})
1986
+ tm.assert_frame_equal(df, expected)
1987
+ if not using_copy_on_write:
1988
+ tm.assert_series_equal(ser, expected["A"])
1989
+ tm.assert_series_equal(result_view["A"], expected["A"])
1990
+ else:
1991
+ expected = Series([1, 2, 3], name="A")
1992
+ tm.assert_series_equal(ser, expected)
1993
+ tm.assert_series_equal(result_view["A"], expected)
1994
+
1995
+
1996
+ class TestValidate:
1997
+ @pytest.mark.parametrize("value", [1, "True", [1, 2, 3], 5.0])
1998
+ def test_validate_bool_args(self, value):
1999
+ msg = 'For argument "inplace" expected type bool, received type'
2000
+ with pytest.raises(ValueError, match=msg):
2001
+ pd.eval("2+2", inplace=value)
lib/python3.12/site-packages/pandas/tests/indexes/__init__.py ADDED
File without changes
lib/python3.12/site-packages/pandas/tests/indexes/categorical/__init__.py ADDED
File without changes
lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (211 Bytes). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/test_append.cpython-312.pyc ADDED
Binary file (4.22 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/test_astype.cpython-312.pyc ADDED
Binary file (4.36 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/test_category.cpython-312.pyc ADDED
Binary file (22 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/test_constructors.cpython-312.pyc ADDED
Binary file (8.06 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/test_equals.cpython-312.pyc ADDED
Binary file (6.18 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/test_fillna.cpython-312.pyc ADDED
Binary file (2.95 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/test_formats.cpython-312.pyc ADDED
Binary file (6.65 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/test_indexing.cpython-312.pyc ADDED
Binary file (24.7 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/test_map.cpython-312.pyc ADDED
Binary file (7.45 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/test_reindex.cpython-312.pyc ADDED
Binary file (4.95 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/categorical/__pycache__/test_setops.cpython-312.pyc ADDED
Binary file (1.02 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/categorical/test_append.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+
3
+ from pandas import (
4
+ CategoricalIndex,
5
+ Index,
6
+ )
7
+ import pandas._testing as tm
8
+
9
+
10
+ class TestAppend:
11
+ @pytest.fixture
12
+ def ci(self):
13
+ categories = list("cab")
14
+ return CategoricalIndex(list("aabbca"), categories=categories, ordered=False)
15
+
16
+ def test_append(self, ci):
17
+ # append cats with the same categories
18
+ result = ci[:3].append(ci[3:])
19
+ tm.assert_index_equal(result, ci, exact=True)
20
+
21
+ foos = [ci[:1], ci[1:3], ci[3:]]
22
+ result = foos[0].append(foos[1:])
23
+ tm.assert_index_equal(result, ci, exact=True)
24
+
25
+ def test_append_empty(self, ci):
26
+ # empty
27
+ result = ci.append([])
28
+ tm.assert_index_equal(result, ci, exact=True)
29
+
30
+ def test_append_mismatched_categories(self, ci):
31
+ # appending with different categories or reordered is not ok
32
+ msg = "all inputs must be Index"
33
+ with pytest.raises(TypeError, match=msg):
34
+ ci.append(ci.values.set_categories(list("abcd")))
35
+ with pytest.raises(TypeError, match=msg):
36
+ ci.append(ci.values.reorder_categories(list("abc")))
37
+
38
+ def test_append_category_objects(self, ci):
39
+ # with objects
40
+ result = ci.append(Index(["c", "a"]))
41
+ expected = CategoricalIndex(list("aabbcaca"), categories=ci.categories)
42
+ tm.assert_index_equal(result, expected, exact=True)
43
+
44
+ def test_append_non_categories(self, ci):
45
+ # invalid objects -> cast to object via concat_compat
46
+ result = ci.append(Index(["a", "d"]))
47
+ expected = Index(["a", "a", "b", "b", "c", "a", "a", "d"])
48
+ tm.assert_index_equal(result, expected, exact=True)
49
+
50
+ def test_append_object(self, ci):
51
+ # GH#14298 - if base object is not categorical -> coerce to object
52
+ result = Index(["c", "a"]).append(ci)
53
+ expected = Index(list("caaabbca"))
54
+ tm.assert_index_equal(result, expected, exact=True)
55
+
56
+ def test_append_to_another(self):
57
+ # hits Index._concat
58
+ fst = Index(["a", "b"])
59
+ snd = CategoricalIndex(["d", "e"])
60
+ result = fst.append(snd)
61
+ expected = Index(["a", "b", "d", "e"])
62
+ tm.assert_index_equal(result, expected)
lib/python3.12/site-packages/pandas/tests/indexes/categorical/test_astype.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import date
2
+
3
+ import numpy as np
4
+ import pytest
5
+
6
+ from pandas import (
7
+ Categorical,
8
+ CategoricalDtype,
9
+ CategoricalIndex,
10
+ Index,
11
+ IntervalIndex,
12
+ )
13
+ import pandas._testing as tm
14
+
15
+
16
+ class TestAstype:
17
+ def test_astype(self):
18
+ ci = CategoricalIndex(list("aabbca"), categories=list("cab"), ordered=False)
19
+
20
+ result = ci.astype(object)
21
+ tm.assert_index_equal(result, Index(np.array(ci), dtype=object))
22
+
23
+ # this IS equal, but not the same class
24
+ assert result.equals(ci)
25
+ assert isinstance(result, Index)
26
+ assert not isinstance(result, CategoricalIndex)
27
+
28
+ # interval
29
+ ii = IntervalIndex.from_arrays(left=[-0.001, 2.0], right=[2, 4], closed="right")
30
+
31
+ ci = CategoricalIndex(
32
+ Categorical.from_codes([0, 1, -1], categories=ii, ordered=True)
33
+ )
34
+
35
+ result = ci.astype("interval")
36
+ expected = ii.take([0, 1, -1], allow_fill=True, fill_value=np.nan)
37
+ tm.assert_index_equal(result, expected)
38
+
39
+ result = IntervalIndex(result.values)
40
+ tm.assert_index_equal(result, expected)
41
+
42
+ @pytest.mark.parametrize("name", [None, "foo"])
43
+ @pytest.mark.parametrize("dtype_ordered", [True, False])
44
+ @pytest.mark.parametrize("index_ordered", [True, False])
45
+ def test_astype_category(self, name, dtype_ordered, index_ordered):
46
+ # GH#18630
47
+ index = CategoricalIndex(
48
+ list("aabbca"), categories=list("cab"), ordered=index_ordered
49
+ )
50
+ if name:
51
+ index = index.rename(name)
52
+
53
+ # standard categories
54
+ dtype = CategoricalDtype(ordered=dtype_ordered)
55
+ result = index.astype(dtype)
56
+ expected = CategoricalIndex(
57
+ index.tolist(),
58
+ name=name,
59
+ categories=index.categories,
60
+ ordered=dtype_ordered,
61
+ )
62
+ tm.assert_index_equal(result, expected)
63
+
64
+ # non-standard categories
65
+ dtype = CategoricalDtype(index.unique().tolist()[:-1], dtype_ordered)
66
+ result = index.astype(dtype)
67
+ expected = CategoricalIndex(index.tolist(), name=name, dtype=dtype)
68
+ tm.assert_index_equal(result, expected)
69
+
70
+ if dtype_ordered is False:
71
+ # dtype='category' can't specify ordered, so only test once
72
+ result = index.astype("category")
73
+ expected = index
74
+ tm.assert_index_equal(result, expected)
75
+
76
+ @pytest.mark.parametrize("box", [True, False])
77
+ def test_categorical_date_roundtrip(self, box):
78
+ # astype to categorical and back should preserve date objects
79
+ v = date.today()
80
+
81
+ obj = Index([v, v])
82
+ assert obj.dtype == object
83
+ if box:
84
+ obj = obj.array
85
+
86
+ cat = obj.astype("category")
87
+
88
+ rtrip = cat.astype(object)
89
+ assert rtrip.dtype == object
90
+ assert type(rtrip[0]) is date
lib/python3.12/site-packages/pandas/tests/indexes/categorical/test_category.py ADDED
@@ -0,0 +1,394 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from pandas._config import using_pyarrow_string_dtype
5
+
6
+ from pandas._libs import index as libindex
7
+ from pandas._libs.arrays import NDArrayBacked
8
+
9
+ import pandas as pd
10
+ from pandas import (
11
+ Categorical,
12
+ CategoricalDtype,
13
+ )
14
+ import pandas._testing as tm
15
+ from pandas.core.indexes.api import (
16
+ CategoricalIndex,
17
+ Index,
18
+ )
19
+
20
+
21
+ class TestCategoricalIndex:
22
+ @pytest.fixture
23
+ def simple_index(self) -> CategoricalIndex:
24
+ return CategoricalIndex(list("aabbca"), categories=list("cab"), ordered=False)
25
+
26
+ def test_can_hold_identifiers(self):
27
+ idx = CategoricalIndex(list("aabbca"), categories=None, ordered=False)
28
+ key = idx[0]
29
+ assert idx._can_hold_identifiers_and_holds_name(key) is True
30
+
31
+ def test_insert(self, simple_index):
32
+ ci = simple_index
33
+ categories = ci.categories
34
+
35
+ # test 0th element
36
+ result = ci.insert(0, "a")
37
+ expected = CategoricalIndex(list("aaabbca"), categories=categories)
38
+ tm.assert_index_equal(result, expected, exact=True)
39
+
40
+ # test Nth element that follows Python list behavior
41
+ result = ci.insert(-1, "a")
42
+ expected = CategoricalIndex(list("aabbcaa"), categories=categories)
43
+ tm.assert_index_equal(result, expected, exact=True)
44
+
45
+ # test empty
46
+ result = CategoricalIndex([], categories=categories).insert(0, "a")
47
+ expected = CategoricalIndex(["a"], categories=categories)
48
+ tm.assert_index_equal(result, expected, exact=True)
49
+
50
+ # invalid -> cast to object
51
+ expected = ci.astype(object).insert(0, "d")
52
+ result = ci.insert(0, "d").astype(object)
53
+ tm.assert_index_equal(result, expected, exact=True)
54
+
55
+ # GH 18295 (test missing)
56
+ expected = CategoricalIndex(["a", np.nan, "a", "b", "c", "b"])
57
+ for na in (np.nan, pd.NaT, None):
58
+ result = CategoricalIndex(list("aabcb")).insert(1, na)
59
+ tm.assert_index_equal(result, expected)
60
+
61
+ def test_insert_na_mismatched_dtype(self):
62
+ ci = CategoricalIndex([0, 1, 1])
63
+ result = ci.insert(0, pd.NaT)
64
+ expected = Index([pd.NaT, 0, 1, 1], dtype=object)
65
+ tm.assert_index_equal(result, expected)
66
+
67
+ def test_delete(self, simple_index):
68
+ ci = simple_index
69
+ categories = ci.categories
70
+
71
+ result = ci.delete(0)
72
+ expected = CategoricalIndex(list("abbca"), categories=categories)
73
+ tm.assert_index_equal(result, expected, exact=True)
74
+
75
+ result = ci.delete(-1)
76
+ expected = CategoricalIndex(list("aabbc"), categories=categories)
77
+ tm.assert_index_equal(result, expected, exact=True)
78
+
79
+ with tm.external_error_raised((IndexError, ValueError)):
80
+ # Either depending on NumPy version
81
+ ci.delete(10)
82
+
83
+ @pytest.mark.parametrize(
84
+ "data, non_lexsorted_data",
85
+ [[[1, 2, 3], [9, 0, 1, 2, 3]], [list("abc"), list("fabcd")]],
86
+ )
87
+ def test_is_monotonic(self, data, non_lexsorted_data):
88
+ c = CategoricalIndex(data)
89
+ assert c.is_monotonic_increasing is True
90
+ assert c.is_monotonic_decreasing is False
91
+
92
+ c = CategoricalIndex(data, ordered=True)
93
+ assert c.is_monotonic_increasing is True
94
+ assert c.is_monotonic_decreasing is False
95
+
96
+ c = CategoricalIndex(data, categories=reversed(data))
97
+ assert c.is_monotonic_increasing is False
98
+ assert c.is_monotonic_decreasing is True
99
+
100
+ c = CategoricalIndex(data, categories=reversed(data), ordered=True)
101
+ assert c.is_monotonic_increasing is False
102
+ assert c.is_monotonic_decreasing is True
103
+
104
+ # test when data is neither monotonic increasing nor decreasing
105
+ reordered_data = [data[0], data[2], data[1]]
106
+ c = CategoricalIndex(reordered_data, categories=reversed(data))
107
+ assert c.is_monotonic_increasing is False
108
+ assert c.is_monotonic_decreasing is False
109
+
110
+ # non lexsorted categories
111
+ categories = non_lexsorted_data
112
+
113
+ c = CategoricalIndex(categories[:2], categories=categories)
114
+ assert c.is_monotonic_increasing is True
115
+ assert c.is_monotonic_decreasing is False
116
+
117
+ c = CategoricalIndex(categories[1:3], categories=categories)
118
+ assert c.is_monotonic_increasing is True
119
+ assert c.is_monotonic_decreasing is False
120
+
121
+ def test_has_duplicates(self):
122
+ idx = CategoricalIndex([0, 0, 0], name="foo")
123
+ assert idx.is_unique is False
124
+ assert idx.has_duplicates is True
125
+
126
+ idx = CategoricalIndex([0, 1], categories=[2, 3], name="foo")
127
+ assert idx.is_unique is False
128
+ assert idx.has_duplicates is True
129
+
130
+ idx = CategoricalIndex([0, 1, 2, 3], categories=[1, 2, 3], name="foo")
131
+ assert idx.is_unique is True
132
+ assert idx.has_duplicates is False
133
+
134
+ @pytest.mark.parametrize(
135
+ "data, categories, expected",
136
+ [
137
+ (
138
+ [1, 1, 1],
139
+ [1, 2, 3],
140
+ {
141
+ "first": np.array([False, True, True]),
142
+ "last": np.array([True, True, False]),
143
+ False: np.array([True, True, True]),
144
+ },
145
+ ),
146
+ (
147
+ [1, 1, 1],
148
+ list("abc"),
149
+ {
150
+ "first": np.array([False, True, True]),
151
+ "last": np.array([True, True, False]),
152
+ False: np.array([True, True, True]),
153
+ },
154
+ ),
155
+ (
156
+ [2, "a", "b"],
157
+ list("abc"),
158
+ {
159
+ "first": np.zeros(shape=(3), dtype=np.bool_),
160
+ "last": np.zeros(shape=(3), dtype=np.bool_),
161
+ False: np.zeros(shape=(3), dtype=np.bool_),
162
+ },
163
+ ),
164
+ (
165
+ list("abb"),
166
+ list("abc"),
167
+ {
168
+ "first": np.array([False, False, True]),
169
+ "last": np.array([False, True, False]),
170
+ False: np.array([False, True, True]),
171
+ },
172
+ ),
173
+ ],
174
+ )
175
+ def test_drop_duplicates(self, data, categories, expected):
176
+ idx = CategoricalIndex(data, categories=categories, name="foo")
177
+ for keep, e in expected.items():
178
+ tm.assert_numpy_array_equal(idx.duplicated(keep=keep), e)
179
+ e = idx[~e]
180
+ result = idx.drop_duplicates(keep=keep)
181
+ tm.assert_index_equal(result, e)
182
+
183
+ @pytest.mark.parametrize(
184
+ "data, categories, expected_data",
185
+ [
186
+ ([1, 1, 1], [1, 2, 3], [1]),
187
+ ([1, 1, 1], list("abc"), [np.nan]),
188
+ ([1, 2, "a"], [1, 2, 3], [1, 2, np.nan]),
189
+ ([2, "a", "b"], list("abc"), [np.nan, "a", "b"]),
190
+ ],
191
+ )
192
+ def test_unique(self, data, categories, expected_data, ordered):
193
+ dtype = CategoricalDtype(categories, ordered=ordered)
194
+
195
+ idx = CategoricalIndex(data, dtype=dtype)
196
+ expected = CategoricalIndex(expected_data, dtype=dtype)
197
+ tm.assert_index_equal(idx.unique(), expected)
198
+
199
+ @pytest.mark.xfail(using_pyarrow_string_dtype(), reason="repr doesn't roundtrip")
200
+ def test_repr_roundtrip(self):
201
+ ci = CategoricalIndex(["a", "b"], categories=["a", "b"], ordered=True)
202
+ str(ci)
203
+ tm.assert_index_equal(eval(repr(ci)), ci, exact=True)
204
+
205
+ # formatting
206
+ str(ci)
207
+
208
+ # long format
209
+ # this is not reprable
210
+ ci = CategoricalIndex(np.random.default_rng(2).integers(0, 5, size=100))
211
+ str(ci)
212
+
213
+ def test_isin(self):
214
+ ci = CategoricalIndex(list("aabca") + [np.nan], categories=["c", "a", "b"])
215
+ tm.assert_numpy_array_equal(
216
+ ci.isin(["c"]), np.array([False, False, False, True, False, False])
217
+ )
218
+ tm.assert_numpy_array_equal(
219
+ ci.isin(["c", "a", "b"]), np.array([True] * 5 + [False])
220
+ )
221
+ tm.assert_numpy_array_equal(
222
+ ci.isin(["c", "a", "b", np.nan]), np.array([True] * 6)
223
+ )
224
+
225
+ # mismatched categorical -> coerced to ndarray so doesn't matter
226
+ result = ci.isin(ci.set_categories(list("abcdefghi")))
227
+ expected = np.array([True] * 6)
228
+ tm.assert_numpy_array_equal(result, expected)
229
+
230
+ result = ci.isin(ci.set_categories(list("defghi")))
231
+ expected = np.array([False] * 5 + [True])
232
+ tm.assert_numpy_array_equal(result, expected)
233
+
234
+ def test_isin_overlapping_intervals(self):
235
+ # GH 34974
236
+ idx = pd.IntervalIndex([pd.Interval(0, 2), pd.Interval(0, 1)])
237
+ result = CategoricalIndex(idx).isin(idx)
238
+ expected = np.array([True, True])
239
+ tm.assert_numpy_array_equal(result, expected)
240
+
241
+ def test_identical(self):
242
+ ci1 = CategoricalIndex(["a", "b"], categories=["a", "b"], ordered=True)
243
+ ci2 = CategoricalIndex(["a", "b"], categories=["a", "b", "c"], ordered=True)
244
+ assert ci1.identical(ci1)
245
+ assert ci1.identical(ci1.copy())
246
+ assert not ci1.identical(ci2)
247
+
248
+ def test_ensure_copied_data(self):
249
+ # gh-12309: Check the "copy" argument of each
250
+ # Index.__new__ is honored.
251
+ #
252
+ # Must be tested separately from other indexes because
253
+ # self.values is not an ndarray.
254
+ index = CategoricalIndex(list("ab") * 5)
255
+
256
+ result = CategoricalIndex(index.values, copy=True)
257
+ tm.assert_index_equal(index, result)
258
+ assert not np.shares_memory(result._data._codes, index._data._codes)
259
+
260
+ result = CategoricalIndex(index.values, copy=False)
261
+ assert result._data._codes is index._data._codes
262
+
263
+
264
+ class TestCategoricalIndex2:
265
+ def test_view_i8(self):
266
+ # GH#25464
267
+ ci = CategoricalIndex(list("ab") * 50)
268
+ msg = "When changing to a larger dtype, its size must be a divisor"
269
+ with pytest.raises(ValueError, match=msg):
270
+ ci.view("i8")
271
+ with pytest.raises(ValueError, match=msg):
272
+ ci._data.view("i8")
273
+
274
+ ci = ci[:-4] # length divisible by 8
275
+
276
+ res = ci.view("i8")
277
+ expected = ci._data.codes.view("i8")
278
+ tm.assert_numpy_array_equal(res, expected)
279
+
280
+ cat = ci._data
281
+ tm.assert_numpy_array_equal(cat.view("i8"), expected)
282
+
283
+ @pytest.mark.parametrize(
284
+ "dtype, engine_type",
285
+ [
286
+ (np.int8, libindex.Int8Engine),
287
+ (np.int16, libindex.Int16Engine),
288
+ (np.int32, libindex.Int32Engine),
289
+ (np.int64, libindex.Int64Engine),
290
+ ],
291
+ )
292
+ def test_engine_type(self, dtype, engine_type):
293
+ if dtype != np.int64:
294
+ # num. of uniques required to push CategoricalIndex.codes to a
295
+ # dtype (128 categories required for .codes dtype to be int16 etc.)
296
+ num_uniques = {np.int8: 1, np.int16: 128, np.int32: 32768}[dtype]
297
+ ci = CategoricalIndex(range(num_uniques))
298
+ else:
299
+ # having 2**32 - 2**31 categories would be very memory-intensive,
300
+ # so we cheat a bit with the dtype
301
+ ci = CategoricalIndex(range(32768)) # == 2**16 - 2**(16 - 1)
302
+ arr = ci.values._ndarray.astype("int64")
303
+ NDArrayBacked.__init__(ci._data, arr, ci.dtype)
304
+ assert np.issubdtype(ci.codes.dtype, dtype)
305
+ assert isinstance(ci._engine, engine_type)
306
+
307
+ @pytest.mark.parametrize(
308
+ "func,op_name",
309
+ [
310
+ (lambda idx: idx - idx, "__sub__"),
311
+ (lambda idx: idx + idx, "__add__"),
312
+ (lambda idx: idx - ["a", "b"], "__sub__"),
313
+ (lambda idx: idx + ["a", "b"], "__add__"),
314
+ (lambda idx: ["a", "b"] - idx, "__rsub__"),
315
+ (lambda idx: ["a", "b"] + idx, "__radd__"),
316
+ ],
317
+ )
318
+ def test_disallow_addsub_ops(self, func, op_name):
319
+ # GH 10039
320
+ # set ops (+/-) raise TypeError
321
+ idx = Index(Categorical(["a", "b"]))
322
+ cat_or_list = "'(Categorical|list)' and '(Categorical|list)'"
323
+ msg = "|".join(
324
+ [
325
+ f"cannot perform {op_name} with this index type: CategoricalIndex",
326
+ "can only concatenate list",
327
+ rf"unsupported operand type\(s\) for [\+-]: {cat_or_list}",
328
+ ]
329
+ )
330
+ with pytest.raises(TypeError, match=msg):
331
+ func(idx)
332
+
333
+ def test_method_delegation(self):
334
+ ci = CategoricalIndex(list("aabbca"), categories=list("cabdef"))
335
+ result = ci.set_categories(list("cab"))
336
+ tm.assert_index_equal(
337
+ result, CategoricalIndex(list("aabbca"), categories=list("cab"))
338
+ )
339
+
340
+ ci = CategoricalIndex(list("aabbca"), categories=list("cab"))
341
+ result = ci.rename_categories(list("efg"))
342
+ tm.assert_index_equal(
343
+ result, CategoricalIndex(list("ffggef"), categories=list("efg"))
344
+ )
345
+
346
+ # GH18862 (let rename_categories take callables)
347
+ result = ci.rename_categories(lambda x: x.upper())
348
+ tm.assert_index_equal(
349
+ result, CategoricalIndex(list("AABBCA"), categories=list("CAB"))
350
+ )
351
+
352
+ ci = CategoricalIndex(list("aabbca"), categories=list("cab"))
353
+ result = ci.add_categories(["d"])
354
+ tm.assert_index_equal(
355
+ result, CategoricalIndex(list("aabbca"), categories=list("cabd"))
356
+ )
357
+
358
+ ci = CategoricalIndex(list("aabbca"), categories=list("cab"))
359
+ result = ci.remove_categories(["c"])
360
+ tm.assert_index_equal(
361
+ result,
362
+ CategoricalIndex(list("aabb") + [np.nan] + ["a"], categories=list("ab")),
363
+ )
364
+
365
+ ci = CategoricalIndex(list("aabbca"), categories=list("cabdef"))
366
+ result = ci.as_unordered()
367
+ tm.assert_index_equal(result, ci)
368
+
369
+ ci = CategoricalIndex(list("aabbca"), categories=list("cabdef"))
370
+ result = ci.as_ordered()
371
+ tm.assert_index_equal(
372
+ result,
373
+ CategoricalIndex(list("aabbca"), categories=list("cabdef"), ordered=True),
374
+ )
375
+
376
+ # invalid
377
+ msg = "cannot use inplace with CategoricalIndex"
378
+ with pytest.raises(ValueError, match=msg):
379
+ ci.set_categories(list("cab"), inplace=True)
380
+
381
+ def test_remove_maintains_order(self):
382
+ ci = CategoricalIndex(list("abcdda"), categories=list("abcd"))
383
+ result = ci.reorder_categories(["d", "c", "b", "a"], ordered=True)
384
+ tm.assert_index_equal(
385
+ result,
386
+ CategoricalIndex(list("abcdda"), categories=list("dcba"), ordered=True),
387
+ )
388
+ result = result.remove_categories(["c"])
389
+ tm.assert_index_equal(
390
+ result,
391
+ CategoricalIndex(
392
+ ["a", "b", np.nan, "d", "d", "a"], categories=list("dba"), ordered=True
393
+ ),
394
+ )
lib/python3.12/site-packages/pandas/tests/indexes/categorical/test_constructors.py ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from pandas import (
5
+ Categorical,
6
+ CategoricalDtype,
7
+ CategoricalIndex,
8
+ Index,
9
+ )
10
+ import pandas._testing as tm
11
+
12
+
13
+ class TestCategoricalIndexConstructors:
14
+ def test_construction_disallows_scalar(self):
15
+ msg = "must be called with a collection of some kind"
16
+ with pytest.raises(TypeError, match=msg):
17
+ CategoricalIndex(data=1, categories=list("abcd"), ordered=False)
18
+ with pytest.raises(TypeError, match=msg):
19
+ CategoricalIndex(categories=list("abcd"), ordered=False)
20
+
21
+ def test_construction(self):
22
+ ci = CategoricalIndex(list("aabbca"), categories=list("abcd"), ordered=False)
23
+ categories = ci.categories
24
+
25
+ result = Index(ci)
26
+ tm.assert_index_equal(result, ci, exact=True)
27
+ assert not result.ordered
28
+
29
+ result = Index(ci.values)
30
+ tm.assert_index_equal(result, ci, exact=True)
31
+ assert not result.ordered
32
+
33
+ # empty
34
+ result = CategoricalIndex([], categories=categories)
35
+ tm.assert_index_equal(result.categories, Index(categories))
36
+ tm.assert_numpy_array_equal(result.codes, np.array([], dtype="int8"))
37
+ assert not result.ordered
38
+
39
+ # passing categories
40
+ result = CategoricalIndex(list("aabbca"), categories=categories)
41
+ tm.assert_index_equal(result.categories, Index(categories))
42
+ tm.assert_numpy_array_equal(
43
+ result.codes, np.array([0, 0, 1, 1, 2, 0], dtype="int8")
44
+ )
45
+
46
+ c = Categorical(list("aabbca"))
47
+ result = CategoricalIndex(c)
48
+ tm.assert_index_equal(result.categories, Index(list("abc")))
49
+ tm.assert_numpy_array_equal(
50
+ result.codes, np.array([0, 0, 1, 1, 2, 0], dtype="int8")
51
+ )
52
+ assert not result.ordered
53
+
54
+ result = CategoricalIndex(c, categories=categories)
55
+ tm.assert_index_equal(result.categories, Index(categories))
56
+ tm.assert_numpy_array_equal(
57
+ result.codes, np.array([0, 0, 1, 1, 2, 0], dtype="int8")
58
+ )
59
+ assert not result.ordered
60
+
61
+ ci = CategoricalIndex(c, categories=list("abcd"))
62
+ result = CategoricalIndex(ci)
63
+ tm.assert_index_equal(result.categories, Index(categories))
64
+ tm.assert_numpy_array_equal(
65
+ result.codes, np.array([0, 0, 1, 1, 2, 0], dtype="int8")
66
+ )
67
+ assert not result.ordered
68
+
69
+ result = CategoricalIndex(ci, categories=list("ab"))
70
+ tm.assert_index_equal(result.categories, Index(list("ab")))
71
+ tm.assert_numpy_array_equal(
72
+ result.codes, np.array([0, 0, 1, 1, -1, 0], dtype="int8")
73
+ )
74
+ assert not result.ordered
75
+
76
+ result = CategoricalIndex(ci, categories=list("ab"), ordered=True)
77
+ tm.assert_index_equal(result.categories, Index(list("ab")))
78
+ tm.assert_numpy_array_equal(
79
+ result.codes, np.array([0, 0, 1, 1, -1, 0], dtype="int8")
80
+ )
81
+ assert result.ordered
82
+
83
+ result = CategoricalIndex(ci, categories=list("ab"), ordered=True)
84
+ expected = CategoricalIndex(
85
+ ci, categories=list("ab"), ordered=True, dtype="category"
86
+ )
87
+ tm.assert_index_equal(result, expected, exact=True)
88
+
89
+ # turn me to an Index
90
+ result = Index(np.array(ci))
91
+ assert isinstance(result, Index)
92
+ assert not isinstance(result, CategoricalIndex)
93
+
94
+ def test_construction_with_dtype(self):
95
+ # specify dtype
96
+ ci = CategoricalIndex(list("aabbca"), categories=list("abc"), ordered=False)
97
+
98
+ result = Index(np.array(ci), dtype="category")
99
+ tm.assert_index_equal(result, ci, exact=True)
100
+
101
+ result = Index(np.array(ci).tolist(), dtype="category")
102
+ tm.assert_index_equal(result, ci, exact=True)
103
+
104
+ # these are generally only equal when the categories are reordered
105
+ ci = CategoricalIndex(list("aabbca"), categories=list("cab"), ordered=False)
106
+
107
+ result = Index(np.array(ci), dtype="category").reorder_categories(ci.categories)
108
+ tm.assert_index_equal(result, ci, exact=True)
109
+
110
+ # make sure indexes are handled
111
+ idx = Index(range(3))
112
+ expected = CategoricalIndex([0, 1, 2], categories=idx, ordered=True)
113
+ result = CategoricalIndex(idx, categories=idx, ordered=True)
114
+ tm.assert_index_equal(result, expected, exact=True)
115
+
116
+ def test_construction_empty_with_bool_categories(self):
117
+ # see GH#22702
118
+ cat = CategoricalIndex([], categories=[True, False])
119
+ categories = sorted(cat.categories.tolist())
120
+ assert categories == [False, True]
121
+
122
+ def test_construction_with_categorical_dtype(self):
123
+ # construction with CategoricalDtype
124
+ # GH#18109
125
+ data, cats, ordered = "a a b b".split(), "c b a".split(), True
126
+ dtype = CategoricalDtype(categories=cats, ordered=ordered)
127
+
128
+ result = CategoricalIndex(data, dtype=dtype)
129
+ expected = CategoricalIndex(data, categories=cats, ordered=ordered)
130
+ tm.assert_index_equal(result, expected, exact=True)
131
+
132
+ # GH#19032
133
+ result = Index(data, dtype=dtype)
134
+ tm.assert_index_equal(result, expected, exact=True)
135
+
136
+ # error when combining categories/ordered and dtype kwargs
137
+ msg = "Cannot specify `categories` or `ordered` together with `dtype`."
138
+ with pytest.raises(ValueError, match=msg):
139
+ CategoricalIndex(data, categories=cats, dtype=dtype)
140
+
141
+ with pytest.raises(ValueError, match=msg):
142
+ CategoricalIndex(data, ordered=ordered, dtype=dtype)
lib/python3.12/site-packages/pandas/tests/indexes/categorical/test_equals.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from pandas import (
5
+ Categorical,
6
+ CategoricalIndex,
7
+ Index,
8
+ MultiIndex,
9
+ )
10
+
11
+
12
+ class TestEquals:
13
+ def test_equals_categorical(self):
14
+ ci1 = CategoricalIndex(["a", "b"], categories=["a", "b"], ordered=True)
15
+ ci2 = CategoricalIndex(["a", "b"], categories=["a", "b", "c"], ordered=True)
16
+
17
+ assert ci1.equals(ci1)
18
+ assert not ci1.equals(ci2)
19
+ assert ci1.equals(ci1.astype(object))
20
+ assert ci1.astype(object).equals(ci1)
21
+
22
+ assert (ci1 == ci1).all()
23
+ assert not (ci1 != ci1).all()
24
+ assert not (ci1 > ci1).all()
25
+ assert not (ci1 < ci1).all()
26
+ assert (ci1 <= ci1).all()
27
+ assert (ci1 >= ci1).all()
28
+
29
+ assert not (ci1 == 1).all()
30
+ assert (ci1 == Index(["a", "b"])).all()
31
+ assert (ci1 == ci1.values).all()
32
+
33
+ # invalid comparisons
34
+ with pytest.raises(ValueError, match="Lengths must match"):
35
+ ci1 == Index(["a", "b", "c"])
36
+
37
+ msg = "Categoricals can only be compared if 'categories' are the same"
38
+ with pytest.raises(TypeError, match=msg):
39
+ ci1 == ci2
40
+ with pytest.raises(TypeError, match=msg):
41
+ ci1 == Categorical(ci1.values, ordered=False)
42
+ with pytest.raises(TypeError, match=msg):
43
+ ci1 == Categorical(ci1.values, categories=list("abc"))
44
+
45
+ # tests
46
+ # make sure that we are testing for category inclusion properly
47
+ ci = CategoricalIndex(list("aabca"), categories=["c", "a", "b"])
48
+ assert not ci.equals(list("aabca"))
49
+ # Same categories, but different order
50
+ # Unordered
51
+ assert ci.equals(CategoricalIndex(list("aabca")))
52
+ # Ordered
53
+ assert not ci.equals(CategoricalIndex(list("aabca"), ordered=True))
54
+ assert ci.equals(ci.copy())
55
+
56
+ ci = CategoricalIndex(list("aabca") + [np.nan], categories=["c", "a", "b"])
57
+ assert not ci.equals(list("aabca"))
58
+ assert not ci.equals(CategoricalIndex(list("aabca")))
59
+ assert ci.equals(ci.copy())
60
+
61
+ ci = CategoricalIndex(list("aabca") + [np.nan], categories=["c", "a", "b"])
62
+ assert not ci.equals(list("aabca") + [np.nan])
63
+ assert ci.equals(CategoricalIndex(list("aabca") + [np.nan]))
64
+ assert not ci.equals(CategoricalIndex(list("aabca") + [np.nan], ordered=True))
65
+ assert ci.equals(ci.copy())
66
+
67
+ def test_equals_categorical_unordered(self):
68
+ # https://github.com/pandas-dev/pandas/issues/16603
69
+ a = CategoricalIndex(["A"], categories=["A", "B"])
70
+ b = CategoricalIndex(["A"], categories=["B", "A"])
71
+ c = CategoricalIndex(["C"], categories=["B", "A"])
72
+ assert a.equals(b)
73
+ assert not a.equals(c)
74
+ assert not b.equals(c)
75
+
76
+ def test_equals_non_category(self):
77
+ # GH#37667 Case where other contains a value not among ci's
78
+ # categories ("D") and also contains np.nan
79
+ ci = CategoricalIndex(["A", "B", np.nan, np.nan])
80
+ other = Index(["A", "B", "D", np.nan])
81
+
82
+ assert not ci.equals(other)
83
+
84
+ def test_equals_multiindex(self):
85
+ # dont raise NotImplementedError when calling is_dtype_compat
86
+
87
+ mi = MultiIndex.from_arrays([["A", "B", "C", "D"], range(4)])
88
+ ci = mi.to_flat_index().astype("category")
89
+
90
+ assert not ci.equals(mi)
91
+
92
+ def test_equals_string_dtype(self, any_string_dtype):
93
+ # GH#55364
94
+ idx = CategoricalIndex(list("abc"), name="B")
95
+ other = Index(["a", "b", "c"], name="B", dtype=any_string_dtype)
96
+ assert idx.equals(other)
lib/python3.12/site-packages/pandas/tests/indexes/categorical/test_fillna.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from pandas import CategoricalIndex
5
+ import pandas._testing as tm
6
+
7
+
8
+ class TestFillNA:
9
+ def test_fillna_categorical(self):
10
+ # GH#11343
11
+ idx = CategoricalIndex([1.0, np.nan, 3.0, 1.0], name="x")
12
+ # fill by value in categories
13
+ exp = CategoricalIndex([1.0, 1.0, 3.0, 1.0], name="x")
14
+ tm.assert_index_equal(idx.fillna(1.0), exp)
15
+
16
+ cat = idx._data
17
+
18
+ # fill by value not in categories raises TypeError on EA, casts on CI
19
+ msg = "Cannot setitem on a Categorical with a new category"
20
+ with pytest.raises(TypeError, match=msg):
21
+ cat.fillna(2.0)
22
+
23
+ result = idx.fillna(2.0)
24
+ expected = idx.astype(object).fillna(2.0)
25
+ tm.assert_index_equal(result, expected)
26
+
27
+ def test_fillna_copies_with_no_nas(self):
28
+ # Nothing to fill, should still get a copy for the Categorical method,
29
+ # but OK to get a view on CategoricalIndex method
30
+ ci = CategoricalIndex([0, 1, 1])
31
+ result = ci.fillna(0)
32
+ assert result is not ci
33
+ assert tm.shares_memory(result, ci)
34
+
35
+ # But at the EA level we always get a copy.
36
+ cat = ci._data
37
+ result = cat.fillna(0)
38
+ assert result._ndarray is not cat._ndarray
39
+ assert result._ndarray.base is None
40
+ assert not tm.shares_memory(result, cat)
41
+
42
+ def test_fillna_validates_with_no_nas(self):
43
+ # We validate the fill value even if fillna is a no-op
44
+ ci = CategoricalIndex([2, 3, 3])
45
+ cat = ci._data
46
+
47
+ msg = "Cannot setitem on a Categorical with a new category"
48
+ res = ci.fillna(False)
49
+ # nothing to fill, so we dont cast
50
+ tm.assert_index_equal(res, ci)
51
+
52
+ # Same check directly on the Categorical
53
+ with pytest.raises(TypeError, match=msg):
54
+ cat.fillna(False)
lib/python3.12/site-packages/pandas/tests/indexes/categorical/test_formats.py ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Tests for CategoricalIndex.__repr__ and related methods.
3
+ """
4
+ import pytest
5
+
6
+ from pandas._config import using_pyarrow_string_dtype
7
+ import pandas._config.config as cf
8
+
9
+ from pandas import CategoricalIndex
10
+ import pandas._testing as tm
11
+
12
+
13
+ class TestCategoricalIndexRepr:
14
+ def test_format_different_scalar_lengths(self):
15
+ # GH#35439
16
+ idx = CategoricalIndex(["aaaaaaaaa", "b"])
17
+ expected = ["aaaaaaaaa", "b"]
18
+ msg = r"CategoricalIndex\.format is deprecated"
19
+ with tm.assert_produces_warning(FutureWarning, match=msg):
20
+ assert idx.format() == expected
21
+
22
+ @pytest.mark.xfail(using_pyarrow_string_dtype(), reason="repr different")
23
+ def test_string_categorical_index_repr(self):
24
+ # short
25
+ idx = CategoricalIndex(["a", "bb", "ccc"])
26
+ expected = """CategoricalIndex(['a', 'bb', 'ccc'], categories=['a', 'bb', 'ccc'], ordered=False, dtype='category')""" # noqa: E501
27
+ assert repr(idx) == expected
28
+
29
+ # multiple lines
30
+ idx = CategoricalIndex(["a", "bb", "ccc"] * 10)
31
+ expected = """CategoricalIndex(['a', 'bb', 'ccc', 'a', 'bb', 'ccc', 'a', 'bb', 'ccc', 'a',
32
+ 'bb', 'ccc', 'a', 'bb', 'ccc', 'a', 'bb', 'ccc', 'a', 'bb',
33
+ 'ccc', 'a', 'bb', 'ccc', 'a', 'bb', 'ccc', 'a', 'bb', 'ccc'],
34
+ categories=['a', 'bb', 'ccc'], ordered=False, dtype='category')""" # noqa: E501
35
+
36
+ assert repr(idx) == expected
37
+
38
+ # truncated
39
+ idx = CategoricalIndex(["a", "bb", "ccc"] * 100)
40
+ expected = """CategoricalIndex(['a', 'bb', 'ccc', 'a', 'bb', 'ccc', 'a', 'bb', 'ccc', 'a',
41
+ ...
42
+ 'ccc', 'a', 'bb', 'ccc', 'a', 'bb', 'ccc', 'a', 'bb', 'ccc'],
43
+ categories=['a', 'bb', 'ccc'], ordered=False, dtype='category', length=300)""" # noqa: E501
44
+
45
+ assert repr(idx) == expected
46
+
47
+ # larger categories
48
+ idx = CategoricalIndex(list("abcdefghijklmmo"))
49
+ expected = """CategoricalIndex(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
50
+ 'm', 'm', 'o'],
51
+ categories=['a', 'b', 'c', 'd', ..., 'k', 'l', 'm', 'o'], ordered=False, dtype='category')""" # noqa: E501
52
+
53
+ assert repr(idx) == expected
54
+
55
+ # short
56
+ idx = CategoricalIndex(["あ", "いい", "ううう"])
57
+ expected = """CategoricalIndex(['あ', 'いい', 'ううう'], categories=['あ', 'いい', 'ううう'], ordered=False, dtype='category')""" # noqa: E501
58
+ assert repr(idx) == expected
59
+
60
+ # multiple lines
61
+ idx = CategoricalIndex(["あ", "いい", "ううう"] * 10)
62
+ expected = """CategoricalIndex(['あ', 'いい', 'ううう', 'あ', 'いい', 'ううう', 'あ', 'いい', 'ううう', 'あ',
63
+ 'いい', 'ううう', 'あ', 'いい', 'ううう', 'あ', 'いい', 'ううう', 'あ', 'いい',
64
+ 'ううう', 'あ', 'いい', 'ううう', 'あ', 'いい', 'ううう', 'あ', 'いい', 'ううう'],
65
+ categories=['あ', 'いい', 'ううう'], ordered=False, dtype='category')""" # noqa: E501
66
+
67
+ assert repr(idx) == expected
68
+
69
+ # truncated
70
+ idx = CategoricalIndex(["あ", "いい", "ううう"] * 100)
71
+ expected = """CategoricalIndex(['あ', 'いい', 'ううう', 'あ', 'いい', 'ううう', 'あ', 'いい', 'ううう', 'あ',
72
+ ...
73
+ 'ううう', 'あ', 'いい', 'ううう', 'あ', 'いい', 'ううう', 'あ', 'いい', 'ううう'],
74
+ categories=['あ', 'いい', 'ううう'], ordered=False, dtype='category', length=300)""" # noqa: E501
75
+
76
+ assert repr(idx) == expected
77
+
78
+ # larger categories
79
+ idx = CategoricalIndex(list("あいうえおかきくけこさしすせそ"))
80
+ expected = """CategoricalIndex(['あ', 'い', 'う', 'え', 'お', 'か', 'き', 'く', 'け', 'こ', 'さ', 'し',
81
+ 'す', 'せ', 'そ'],
82
+ categories=['あ', 'い', 'う', 'え', ..., 'し', 'す', 'せ', 'そ'], ordered=False, dtype='category')""" # noqa: E501
83
+
84
+ assert repr(idx) == expected
85
+
86
+ # Enable Unicode option -----------------------------------------
87
+ with cf.option_context("display.unicode.east_asian_width", True):
88
+ # short
89
+ idx = CategoricalIndex(["あ", "いい", "ううう"])
90
+ expected = """CategoricalIndex(['あ', 'いい', 'ううう'], categories=['あ', 'いい', 'ううう'], ordered=False, dtype='category')""" # noqa: E501
91
+ assert repr(idx) == expected
92
+
93
+ # multiple lines
94
+ idx = CategoricalIndex(["あ", "いい", "ううう"] * 10)
95
+ expected = """CategoricalIndex(['あ', 'いい', 'ううう', 'あ', 'いい', 'ううう', 'あ', 'いい',
96
+ 'ううう', 'あ', 'いい', 'ううう', 'あ', 'いい', 'ううう',
97
+ 'あ', 'いい', 'ううう', 'あ', 'いい', 'ううう', 'あ', 'いい',
98
+ 'ううう', 'あ', 'いい', 'ううう', 'あ', 'いい', 'ううう'],
99
+ categories=['あ', 'いい', 'ううう'], ordered=False, dtype='category')""" # noqa: E501
100
+
101
+ assert repr(idx) == expected
102
+
103
+ # truncated
104
+ idx = CategoricalIndex(["あ", "いい", "ううう"] * 100)
105
+ expected = """CategoricalIndex(['あ', 'いい', 'ううう', 'あ', 'いい', 'ううう', 'あ', 'いい',
106
+ 'ううう', 'あ',
107
+ ...
108
+ 'ううう', 'あ', 'いい', 'ううう', 'あ', 'いい', 'ううう',
109
+ 'あ', 'いい', 'ううう'],
110
+ categories=['あ', 'いい', 'ううう'], ordered=False, dtype='category', length=300)""" # noqa: E501
111
+
112
+ assert repr(idx) == expected
113
+
114
+ # larger categories
115
+ idx = CategoricalIndex(list("あいうえおかきくけこさしすせそ"))
116
+ expected = """CategoricalIndex(['あ', 'い', 'う', 'え', 'お', 'か', 'き', 'く', 'け', 'こ',
117
+ 'さ', 'し', 'す', 'せ', 'そ'],
118
+ categories=['あ', 'い', 'う', 'え', ..., 'し', 'す', 'せ', 'そ'], ordered=False, dtype='category')""" # noqa: E501
119
+
120
+ assert repr(idx) == expected
lib/python3.12/site-packages/pandas/tests/indexes/categorical/test_indexing.py ADDED
@@ -0,0 +1,420 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from pandas.errors import InvalidIndexError
5
+
6
+ import pandas as pd
7
+ from pandas import (
8
+ CategoricalIndex,
9
+ Index,
10
+ IntervalIndex,
11
+ Timestamp,
12
+ )
13
+ import pandas._testing as tm
14
+
15
+
16
+ class TestTake:
17
+ def test_take_fill_value(self):
18
+ # GH 12631
19
+
20
+ # numeric category
21
+ idx = CategoricalIndex([1, 2, 3], name="xxx")
22
+ result = idx.take(np.array([1, 0, -1]))
23
+ expected = CategoricalIndex([2, 1, 3], name="xxx")
24
+ tm.assert_index_equal(result, expected)
25
+ tm.assert_categorical_equal(result.values, expected.values)
26
+
27
+ # fill_value
28
+ result = idx.take(np.array([1, 0, -1]), fill_value=True)
29
+ expected = CategoricalIndex([2, 1, np.nan], categories=[1, 2, 3], name="xxx")
30
+ tm.assert_index_equal(result, expected)
31
+ tm.assert_categorical_equal(result.values, expected.values)
32
+
33
+ # allow_fill=False
34
+ result = idx.take(np.array([1, 0, -1]), allow_fill=False, fill_value=True)
35
+ expected = CategoricalIndex([2, 1, 3], name="xxx")
36
+ tm.assert_index_equal(result, expected)
37
+ tm.assert_categorical_equal(result.values, expected.values)
38
+
39
+ # object category
40
+ idx = CategoricalIndex(
41
+ list("CBA"), categories=list("ABC"), ordered=True, name="xxx"
42
+ )
43
+ result = idx.take(np.array([1, 0, -1]))
44
+ expected = CategoricalIndex(
45
+ list("BCA"), categories=list("ABC"), ordered=True, name="xxx"
46
+ )
47
+ tm.assert_index_equal(result, expected)
48
+ tm.assert_categorical_equal(result.values, expected.values)
49
+
50
+ # fill_value
51
+ result = idx.take(np.array([1, 0, -1]), fill_value=True)
52
+ expected = CategoricalIndex(
53
+ ["B", "C", np.nan], categories=list("ABC"), ordered=True, name="xxx"
54
+ )
55
+ tm.assert_index_equal(result, expected)
56
+ tm.assert_categorical_equal(result.values, expected.values)
57
+
58
+ # allow_fill=False
59
+ result = idx.take(np.array([1, 0, -1]), allow_fill=False, fill_value=True)
60
+ expected = CategoricalIndex(
61
+ list("BCA"), categories=list("ABC"), ordered=True, name="xxx"
62
+ )
63
+ tm.assert_index_equal(result, expected)
64
+ tm.assert_categorical_equal(result.values, expected.values)
65
+
66
+ msg = (
67
+ "When allow_fill=True and fill_value is not None, "
68
+ "all indices must be >= -1"
69
+ )
70
+ with pytest.raises(ValueError, match=msg):
71
+ idx.take(np.array([1, 0, -2]), fill_value=True)
72
+ with pytest.raises(ValueError, match=msg):
73
+ idx.take(np.array([1, 0, -5]), fill_value=True)
74
+
75
+ msg = "index -5 is out of bounds for (axis 0 with )?size 3"
76
+ with pytest.raises(IndexError, match=msg):
77
+ idx.take(np.array([1, -5]))
78
+
79
+ def test_take_fill_value_datetime(self):
80
+ # datetime category
81
+ idx = pd.DatetimeIndex(["2011-01-01", "2011-02-01", "2011-03-01"], name="xxx")
82
+ idx = CategoricalIndex(idx)
83
+ result = idx.take(np.array([1, 0, -1]))
84
+ expected = pd.DatetimeIndex(
85
+ ["2011-02-01", "2011-01-01", "2011-03-01"], name="xxx"
86
+ )
87
+ expected = CategoricalIndex(expected)
88
+ tm.assert_index_equal(result, expected)
89
+
90
+ # fill_value
91
+ result = idx.take(np.array([1, 0, -1]), fill_value=True)
92
+ expected = pd.DatetimeIndex(["2011-02-01", "2011-01-01", "NaT"], name="xxx")
93
+ exp_cats = pd.DatetimeIndex(["2011-01-01", "2011-02-01", "2011-03-01"])
94
+ expected = CategoricalIndex(expected, categories=exp_cats)
95
+ tm.assert_index_equal(result, expected)
96
+
97
+ # allow_fill=False
98
+ result = idx.take(np.array([1, 0, -1]), allow_fill=False, fill_value=True)
99
+ expected = pd.DatetimeIndex(
100
+ ["2011-02-01", "2011-01-01", "2011-03-01"], name="xxx"
101
+ )
102
+ expected = CategoricalIndex(expected)
103
+ tm.assert_index_equal(result, expected)
104
+
105
+ msg = (
106
+ "When allow_fill=True and fill_value is not None, "
107
+ "all indices must be >= -1"
108
+ )
109
+ with pytest.raises(ValueError, match=msg):
110
+ idx.take(np.array([1, 0, -2]), fill_value=True)
111
+ with pytest.raises(ValueError, match=msg):
112
+ idx.take(np.array([1, 0, -5]), fill_value=True)
113
+
114
+ msg = "index -5 is out of bounds for (axis 0 with )?size 3"
115
+ with pytest.raises(IndexError, match=msg):
116
+ idx.take(np.array([1, -5]))
117
+
118
+ def test_take_invalid_kwargs(self):
119
+ idx = CategoricalIndex([1, 2, 3], name="foo")
120
+ indices = [1, 0, -1]
121
+
122
+ msg = r"take\(\) got an unexpected keyword argument 'foo'"
123
+ with pytest.raises(TypeError, match=msg):
124
+ idx.take(indices, foo=2)
125
+
126
+ msg = "the 'out' parameter is not supported"
127
+ with pytest.raises(ValueError, match=msg):
128
+ idx.take(indices, out=indices)
129
+
130
+ msg = "the 'mode' parameter is not supported"
131
+ with pytest.raises(ValueError, match=msg):
132
+ idx.take(indices, mode="clip")
133
+
134
+
135
+ class TestGetLoc:
136
+ def test_get_loc(self):
137
+ # GH 12531
138
+ cidx1 = CategoricalIndex(list("abcde"), categories=list("edabc"))
139
+ idx1 = Index(list("abcde"))
140
+ assert cidx1.get_loc("a") == idx1.get_loc("a")
141
+ assert cidx1.get_loc("e") == idx1.get_loc("e")
142
+
143
+ for i in [cidx1, idx1]:
144
+ with pytest.raises(KeyError, match="'NOT-EXIST'"):
145
+ i.get_loc("NOT-EXIST")
146
+
147
+ # non-unique
148
+ cidx2 = CategoricalIndex(list("aacded"), categories=list("edabc"))
149
+ idx2 = Index(list("aacded"))
150
+
151
+ # results in bool array
152
+ res = cidx2.get_loc("d")
153
+ tm.assert_numpy_array_equal(res, idx2.get_loc("d"))
154
+ tm.assert_numpy_array_equal(
155
+ res, np.array([False, False, False, True, False, True])
156
+ )
157
+ # unique element results in scalar
158
+ res = cidx2.get_loc("e")
159
+ assert res == idx2.get_loc("e")
160
+ assert res == 4
161
+
162
+ for i in [cidx2, idx2]:
163
+ with pytest.raises(KeyError, match="'NOT-EXIST'"):
164
+ i.get_loc("NOT-EXIST")
165
+
166
+ # non-unique, sliceable
167
+ cidx3 = CategoricalIndex(list("aabbb"), categories=list("abc"))
168
+ idx3 = Index(list("aabbb"))
169
+
170
+ # results in slice
171
+ res = cidx3.get_loc("a")
172
+ assert res == idx3.get_loc("a")
173
+ assert res == slice(0, 2, None)
174
+
175
+ res = cidx3.get_loc("b")
176
+ assert res == idx3.get_loc("b")
177
+ assert res == slice(2, 5, None)
178
+
179
+ for i in [cidx3, idx3]:
180
+ with pytest.raises(KeyError, match="'c'"):
181
+ i.get_loc("c")
182
+
183
+ def test_get_loc_unique(self):
184
+ cidx = CategoricalIndex(list("abc"))
185
+ result = cidx.get_loc("b")
186
+ assert result == 1
187
+
188
+ def test_get_loc_monotonic_nonunique(self):
189
+ cidx = CategoricalIndex(list("abbc"))
190
+ result = cidx.get_loc("b")
191
+ expected = slice(1, 3, None)
192
+ assert result == expected
193
+
194
+ def test_get_loc_nonmonotonic_nonunique(self):
195
+ cidx = CategoricalIndex(list("abcb"))
196
+ result = cidx.get_loc("b")
197
+ expected = np.array([False, True, False, True], dtype=bool)
198
+ tm.assert_numpy_array_equal(result, expected)
199
+
200
+ def test_get_loc_nan(self):
201
+ # GH#41933
202
+ ci = CategoricalIndex(["A", "B", np.nan])
203
+ res = ci.get_loc(np.nan)
204
+
205
+ assert res == 2
206
+
207
+
208
+ class TestGetIndexer:
209
+ def test_get_indexer_base(self):
210
+ # Determined by cat ordering.
211
+ idx = CategoricalIndex(list("cab"), categories=list("cab"))
212
+ expected = np.arange(len(idx), dtype=np.intp)
213
+
214
+ actual = idx.get_indexer(idx)
215
+ tm.assert_numpy_array_equal(expected, actual)
216
+
217
+ with pytest.raises(ValueError, match="Invalid fill method"):
218
+ idx.get_indexer(idx, method="invalid")
219
+
220
+ def test_get_indexer_requires_unique(self):
221
+ ci = CategoricalIndex(list("aabbca"), categories=list("cab"), ordered=False)
222
+ oidx = Index(np.array(ci))
223
+
224
+ msg = "Reindexing only valid with uniquely valued Index objects"
225
+
226
+ for n in [1, 2, 5, len(ci)]:
227
+ finder = oidx[np.random.default_rng(2).integers(0, len(ci), size=n)]
228
+
229
+ with pytest.raises(InvalidIndexError, match=msg):
230
+ ci.get_indexer(finder)
231
+
232
+ # see gh-17323
233
+ #
234
+ # Even when indexer is equal to the
235
+ # members in the index, we should
236
+ # respect duplicates instead of taking
237
+ # the fast-track path.
238
+ for finder in [list("aabbca"), list("aababca")]:
239
+ with pytest.raises(InvalidIndexError, match=msg):
240
+ ci.get_indexer(finder)
241
+
242
+ def test_get_indexer_non_unique(self):
243
+ idx1 = CategoricalIndex(list("aabcde"), categories=list("edabc"))
244
+ idx2 = CategoricalIndex(list("abf"))
245
+
246
+ for indexer in [idx2, list("abf"), Index(list("abf"))]:
247
+ msg = "Reindexing only valid with uniquely valued Index objects"
248
+ with pytest.raises(InvalidIndexError, match=msg):
249
+ idx1.get_indexer(indexer)
250
+
251
+ r1, _ = idx1.get_indexer_non_unique(indexer)
252
+ expected = np.array([0, 1, 2, -1], dtype=np.intp)
253
+ tm.assert_almost_equal(r1, expected)
254
+
255
+ def test_get_indexer_method(self):
256
+ idx1 = CategoricalIndex(list("aabcde"), categories=list("edabc"))
257
+ idx2 = CategoricalIndex(list("abf"))
258
+
259
+ msg = "method pad not yet implemented for CategoricalIndex"
260
+ with pytest.raises(NotImplementedError, match=msg):
261
+ idx2.get_indexer(idx1, method="pad")
262
+ msg = "method backfill not yet implemented for CategoricalIndex"
263
+ with pytest.raises(NotImplementedError, match=msg):
264
+ idx2.get_indexer(idx1, method="backfill")
265
+
266
+ msg = "method nearest not yet implemented for CategoricalIndex"
267
+ with pytest.raises(NotImplementedError, match=msg):
268
+ idx2.get_indexer(idx1, method="nearest")
269
+
270
+ def test_get_indexer_array(self):
271
+ arr = np.array(
272
+ [Timestamp("1999-12-31 00:00:00"), Timestamp("2000-12-31 00:00:00")],
273
+ dtype=object,
274
+ )
275
+ cats = [Timestamp("1999-12-31 00:00:00"), Timestamp("2000-12-31 00:00:00")]
276
+ ci = CategoricalIndex(cats, categories=cats, ordered=False, dtype="category")
277
+ result = ci.get_indexer(arr)
278
+ expected = np.array([0, 1], dtype="intp")
279
+ tm.assert_numpy_array_equal(result, expected)
280
+
281
+ def test_get_indexer_same_categories_same_order(self):
282
+ ci = CategoricalIndex(["a", "b"], categories=["a", "b"])
283
+
284
+ result = ci.get_indexer(CategoricalIndex(["b", "b"], categories=["a", "b"]))
285
+ expected = np.array([1, 1], dtype="intp")
286
+ tm.assert_numpy_array_equal(result, expected)
287
+
288
+ def test_get_indexer_same_categories_different_order(self):
289
+ # https://github.com/pandas-dev/pandas/issues/19551
290
+ ci = CategoricalIndex(["a", "b"], categories=["a", "b"])
291
+
292
+ result = ci.get_indexer(CategoricalIndex(["b", "b"], categories=["b", "a"]))
293
+ expected = np.array([1, 1], dtype="intp")
294
+ tm.assert_numpy_array_equal(result, expected)
295
+
296
+ def test_get_indexer_nans_in_index_and_target(self):
297
+ # GH 45361
298
+ ci = CategoricalIndex([1, 2, np.nan, 3])
299
+ other1 = [2, 3, 4, np.nan]
300
+ res1 = ci.get_indexer(other1)
301
+ expected1 = np.array([1, 3, -1, 2], dtype=np.intp)
302
+ tm.assert_numpy_array_equal(res1, expected1)
303
+ other2 = [1, 4, 2, 3]
304
+ res2 = ci.get_indexer(other2)
305
+ expected2 = np.array([0, -1, 1, 3], dtype=np.intp)
306
+ tm.assert_numpy_array_equal(res2, expected2)
307
+
308
+
309
+ class TestWhere:
310
+ def test_where(self, listlike_box):
311
+ klass = listlike_box
312
+
313
+ i = CategoricalIndex(list("aabbca"), categories=list("cab"), ordered=False)
314
+ cond = [True] * len(i)
315
+ expected = i
316
+ result = i.where(klass(cond))
317
+ tm.assert_index_equal(result, expected)
318
+
319
+ cond = [False] + [True] * (len(i) - 1)
320
+ expected = CategoricalIndex([np.nan] + i[1:].tolist(), categories=i.categories)
321
+ result = i.where(klass(cond))
322
+ tm.assert_index_equal(result, expected)
323
+
324
+ def test_where_non_categories(self):
325
+ ci = CategoricalIndex(["a", "b", "c", "d"])
326
+ mask = np.array([True, False, True, False])
327
+
328
+ result = ci.where(mask, 2)
329
+ expected = Index(["a", 2, "c", 2], dtype=object)
330
+ tm.assert_index_equal(result, expected)
331
+
332
+ msg = "Cannot setitem on a Categorical with a new category"
333
+ with pytest.raises(TypeError, match=msg):
334
+ # Test the Categorical method directly
335
+ ci._data._where(mask, 2)
336
+
337
+
338
+ class TestContains:
339
+ def test_contains(self):
340
+ ci = CategoricalIndex(list("aabbca"), categories=list("cabdef"), ordered=False)
341
+
342
+ assert "a" in ci
343
+ assert "z" not in ci
344
+ assert "e" not in ci
345
+ assert np.nan not in ci
346
+
347
+ # assert codes NOT in index
348
+ assert 0 not in ci
349
+ assert 1 not in ci
350
+
351
+ def test_contains_nan(self):
352
+ ci = CategoricalIndex(list("aabbca") + [np.nan], categories=list("cabdef"))
353
+ assert np.nan in ci
354
+
355
+ @pytest.mark.parametrize("unwrap", [True, False])
356
+ def test_contains_na_dtype(self, unwrap):
357
+ dti = pd.date_range("2016-01-01", periods=100).insert(0, pd.NaT)
358
+ pi = dti.to_period("D")
359
+ tdi = dti - dti[-1]
360
+ ci = CategoricalIndex(dti)
361
+
362
+ obj = ci
363
+ if unwrap:
364
+ obj = ci._data
365
+
366
+ assert np.nan in obj
367
+ assert None in obj
368
+ assert pd.NaT in obj
369
+ assert np.datetime64("NaT") in obj
370
+ assert np.timedelta64("NaT") not in obj
371
+
372
+ obj2 = CategoricalIndex(tdi)
373
+ if unwrap:
374
+ obj2 = obj2._data
375
+
376
+ assert np.nan in obj2
377
+ assert None in obj2
378
+ assert pd.NaT in obj2
379
+ assert np.datetime64("NaT") not in obj2
380
+ assert np.timedelta64("NaT") in obj2
381
+
382
+ obj3 = CategoricalIndex(pi)
383
+ if unwrap:
384
+ obj3 = obj3._data
385
+
386
+ assert np.nan in obj3
387
+ assert None in obj3
388
+ assert pd.NaT in obj3
389
+ assert np.datetime64("NaT") not in obj3
390
+ assert np.timedelta64("NaT") not in obj3
391
+
392
+ @pytest.mark.parametrize(
393
+ "item, expected",
394
+ [
395
+ (pd.Interval(0, 1), True),
396
+ (1.5, True),
397
+ (pd.Interval(0.5, 1.5), False),
398
+ ("a", False),
399
+ (Timestamp(1), False),
400
+ (pd.Timedelta(1), False),
401
+ ],
402
+ ids=str,
403
+ )
404
+ def test_contains_interval(self, item, expected):
405
+ # GH 23705
406
+ ci = CategoricalIndex(IntervalIndex.from_breaks(range(3)))
407
+ result = item in ci
408
+ assert result is expected
409
+
410
+ def test_contains_list(self):
411
+ # GH#21729
412
+ idx = CategoricalIndex([1, 2, 3])
413
+
414
+ assert "a" not in idx
415
+
416
+ with pytest.raises(TypeError, match="unhashable type"):
417
+ ["a"] in idx
418
+
419
+ with pytest.raises(TypeError, match="unhashable type"):
420
+ ["a", "b"] in idx
lib/python3.12/site-packages/pandas/tests/indexes/categorical/test_map.py ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import pytest
3
+
4
+ import pandas as pd
5
+ from pandas import (
6
+ CategoricalIndex,
7
+ Index,
8
+ Series,
9
+ )
10
+ import pandas._testing as tm
11
+
12
+
13
+ @pytest.mark.parametrize(
14
+ "data, categories",
15
+ [
16
+ (list("abcbca"), list("cab")),
17
+ (pd.interval_range(0, 3).repeat(3), pd.interval_range(0, 3)),
18
+ ],
19
+ ids=["string", "interval"],
20
+ )
21
+ def test_map_str(data, categories, ordered):
22
+ # GH 31202 - override base class since we want to maintain categorical/ordered
23
+ index = CategoricalIndex(data, categories=categories, ordered=ordered)
24
+ result = index.map(str)
25
+ expected = CategoricalIndex(
26
+ map(str, data), categories=map(str, categories), ordered=ordered
27
+ )
28
+ tm.assert_index_equal(result, expected)
29
+
30
+
31
+ def test_map():
32
+ ci = CategoricalIndex(list("ABABC"), categories=list("CBA"), ordered=True)
33
+ result = ci.map(lambda x: x.lower())
34
+ exp = CategoricalIndex(list("ababc"), categories=list("cba"), ordered=True)
35
+ tm.assert_index_equal(result, exp)
36
+
37
+ ci = CategoricalIndex(
38
+ list("ABABC"), categories=list("BAC"), ordered=False, name="XXX"
39
+ )
40
+ result = ci.map(lambda x: x.lower())
41
+ exp = CategoricalIndex(
42
+ list("ababc"), categories=list("bac"), ordered=False, name="XXX"
43
+ )
44
+ tm.assert_index_equal(result, exp)
45
+
46
+ # GH 12766: Return an index not an array
47
+ tm.assert_index_equal(
48
+ ci.map(lambda x: 1), Index(np.array([1] * 5, dtype=np.int64), name="XXX")
49
+ )
50
+
51
+ # change categories dtype
52
+ ci = CategoricalIndex(list("ABABC"), categories=list("BAC"), ordered=False)
53
+
54
+ def f(x):
55
+ return {"A": 10, "B": 20, "C": 30}.get(x)
56
+
57
+ result = ci.map(f)
58
+ exp = CategoricalIndex([10, 20, 10, 20, 30], categories=[20, 10, 30], ordered=False)
59
+ tm.assert_index_equal(result, exp)
60
+
61
+ result = ci.map(Series([10, 20, 30], index=["A", "B", "C"]))
62
+ tm.assert_index_equal(result, exp)
63
+
64
+ result = ci.map({"A": 10, "B": 20, "C": 30})
65
+ tm.assert_index_equal(result, exp)
66
+
67
+
68
+ def test_map_with_categorical_series():
69
+ # GH 12756
70
+ a = Index([1, 2, 3, 4])
71
+ b = Series(["even", "odd", "even", "odd"], dtype="category")
72
+ c = Series(["even", "odd", "even", "odd"])
73
+
74
+ exp = CategoricalIndex(["odd", "even", "odd", np.nan])
75
+ tm.assert_index_equal(a.map(b), exp)
76
+ exp = Index(["odd", "even", "odd", np.nan])
77
+ tm.assert_index_equal(a.map(c), exp)
78
+
79
+
80
+ @pytest.mark.parametrize(
81
+ ("data", "f", "expected"),
82
+ (
83
+ ([1, 1, np.nan], pd.isna, CategoricalIndex([False, False, np.nan])),
84
+ ([1, 2, np.nan], pd.isna, Index([False, False, np.nan])),
85
+ ([1, 1, np.nan], {1: False}, CategoricalIndex([False, False, np.nan])),
86
+ ([1, 2, np.nan], {1: False, 2: False}, Index([False, False, np.nan])),
87
+ (
88
+ [1, 1, np.nan],
89
+ Series([False, False]),
90
+ CategoricalIndex([False, False, np.nan]),
91
+ ),
92
+ (
93
+ [1, 2, np.nan],
94
+ Series([False, False, False]),
95
+ Index([False, False, np.nan]),
96
+ ),
97
+ ),
98
+ )
99
+ def test_map_with_nan_ignore(data, f, expected): # GH 24241
100
+ values = CategoricalIndex(data)
101
+ result = values.map(f, na_action="ignore")
102
+ tm.assert_index_equal(result, expected)
103
+
104
+
105
+ @pytest.mark.parametrize(
106
+ ("data", "f", "expected"),
107
+ (
108
+ ([1, 1, np.nan], pd.isna, Index([False, False, True])),
109
+ ([1, 2, np.nan], pd.isna, Index([False, False, True])),
110
+ ([1, 1, np.nan], {1: False}, CategoricalIndex([False, False, np.nan])),
111
+ ([1, 2, np.nan], {1: False, 2: False}, Index([False, False, np.nan])),
112
+ (
113
+ [1, 1, np.nan],
114
+ Series([False, False]),
115
+ CategoricalIndex([False, False, np.nan]),
116
+ ),
117
+ (
118
+ [1, 2, np.nan],
119
+ Series([False, False, False]),
120
+ Index([False, False, np.nan]),
121
+ ),
122
+ ),
123
+ )
124
+ def test_map_with_nan_none(data, f, expected): # GH 24241
125
+ values = CategoricalIndex(data)
126
+ result = values.map(f, na_action=None)
127
+ tm.assert_index_equal(result, expected)
128
+
129
+
130
+ def test_map_with_dict_or_series():
131
+ orig_values = ["a", "B", 1, "a"]
132
+ new_values = ["one", 2, 3.0, "one"]
133
+ cur_index = CategoricalIndex(orig_values, name="XXX")
134
+ expected = CategoricalIndex(new_values, name="XXX", categories=[3.0, 2, "one"])
135
+
136
+ mapper = Series(new_values[:-1], index=orig_values[:-1])
137
+ result = cur_index.map(mapper)
138
+ # Order of categories in result can be different
139
+ tm.assert_index_equal(result, expected)
140
+
141
+ mapper = dict(zip(orig_values[:-1], new_values[:-1]))
142
+ result = cur_index.map(mapper)
143
+ # Order of categories in result can be different
144
+ tm.assert_index_equal(result, expected)
lib/python3.12/site-packages/pandas/tests/indexes/categorical/test_reindex.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from pandas import (
5
+ Categorical,
6
+ CategoricalIndex,
7
+ Index,
8
+ Interval,
9
+ )
10
+ import pandas._testing as tm
11
+
12
+
13
+ class TestReindex:
14
+ def test_reindex_list_non_unique(self):
15
+ # GH#11586
16
+ msg = "cannot reindex on an axis with duplicate labels"
17
+ ci = CategoricalIndex(["a", "b", "c", "a"])
18
+ with pytest.raises(ValueError, match=msg):
19
+ ci.reindex(["a", "c"])
20
+
21
+ def test_reindex_categorical_non_unique(self):
22
+ msg = "cannot reindex on an axis with duplicate labels"
23
+ ci = CategoricalIndex(["a", "b", "c", "a"])
24
+ with pytest.raises(ValueError, match=msg):
25
+ ci.reindex(Categorical(["a", "c"]))
26
+
27
+ def test_reindex_list_non_unique_unused_category(self):
28
+ msg = "cannot reindex on an axis with duplicate labels"
29
+ ci = CategoricalIndex(["a", "b", "c", "a"], categories=["a", "b", "c", "d"])
30
+ with pytest.raises(ValueError, match=msg):
31
+ ci.reindex(["a", "c"])
32
+
33
+ def test_reindex_categorical_non_unique_unused_category(self):
34
+ msg = "cannot reindex on an axis with duplicate labels"
35
+ ci = CategoricalIndex(["a", "b", "c", "a"], categories=["a", "b", "c", "d"])
36
+ with pytest.raises(ValueError, match=msg):
37
+ ci.reindex(Categorical(["a", "c"]))
38
+
39
+ def test_reindex_duplicate_target(self):
40
+ # See GH25459
41
+ cat = CategoricalIndex(["a", "b", "c"], categories=["a", "b", "c", "d"])
42
+ res, indexer = cat.reindex(["a", "c", "c"])
43
+ exp = Index(["a", "c", "c"])
44
+ tm.assert_index_equal(res, exp, exact=True)
45
+ tm.assert_numpy_array_equal(indexer, np.array([0, 2, 2], dtype=np.intp))
46
+
47
+ res, indexer = cat.reindex(
48
+ CategoricalIndex(["a", "c", "c"], categories=["a", "b", "c", "d"])
49
+ )
50
+ exp = CategoricalIndex(["a", "c", "c"], categories=["a", "b", "c", "d"])
51
+ tm.assert_index_equal(res, exp, exact=True)
52
+ tm.assert_numpy_array_equal(indexer, np.array([0, 2, 2], dtype=np.intp))
53
+
54
+ def test_reindex_empty_index(self):
55
+ # See GH16770
56
+ c = CategoricalIndex([])
57
+ res, indexer = c.reindex(["a", "b"])
58
+ tm.assert_index_equal(res, Index(["a", "b"]), exact=True)
59
+ tm.assert_numpy_array_equal(indexer, np.array([-1, -1], dtype=np.intp))
60
+
61
+ def test_reindex_categorical_added_category(self):
62
+ # GH 42424
63
+ ci = CategoricalIndex(
64
+ [Interval(0, 1, closed="right"), Interval(1, 2, closed="right")],
65
+ ordered=True,
66
+ )
67
+ ci_add = CategoricalIndex(
68
+ [
69
+ Interval(0, 1, closed="right"),
70
+ Interval(1, 2, closed="right"),
71
+ Interval(2, 3, closed="right"),
72
+ Interval(3, 4, closed="right"),
73
+ ],
74
+ ordered=True,
75
+ )
76
+ result, _ = ci.reindex(ci_add)
77
+ expected = ci_add
78
+ tm.assert_index_equal(expected, result)
lib/python3.12/site-packages/pandas/tests/indexes/categorical/test_setops.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from pandas import (
5
+ CategoricalIndex,
6
+ Index,
7
+ )
8
+ import pandas._testing as tm
9
+
10
+
11
+ @pytest.mark.parametrize("na_value", [None, np.nan])
12
+ def test_difference_with_na(na_value):
13
+ # GH 57318
14
+ ci = CategoricalIndex(["a", "b", "c", None])
15
+ other = Index(["c", na_value])
16
+ result = ci.difference(other)
17
+ expected = CategoricalIndex(["a", "b"], categories=["a", "b", "c"])
18
+ tm.assert_index_equal(result, expected)
lib/python3.12/site-packages/pandas/tests/indexes/conftest.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from pandas import (
5
+ Series,
6
+ array,
7
+ )
8
+
9
+
10
+ @pytest.fixture(params=[None, False])
11
+ def sort(request):
12
+ """
13
+ Valid values for the 'sort' parameter used in the Index
14
+ setops methods (intersection, union, etc.)
15
+
16
+ Caution:
17
+ Don't confuse this one with the "sort" fixture used
18
+ for DataFrame.append or concat. That one has
19
+ parameters [True, False].
20
+
21
+ We can't combine them as sort=True is not permitted
22
+ in the Index setops methods.
23
+ """
24
+ return request.param
25
+
26
+
27
+ @pytest.fixture(params=["D", "3D", "-3D", "h", "2h", "-2h", "min", "2min", "s", "-3s"])
28
+ def freq_sample(request):
29
+ """
30
+ Valid values for 'freq' parameter used to create date_range and
31
+ timedelta_range..
32
+ """
33
+ return request.param
34
+
35
+
36
+ @pytest.fixture(params=[list, tuple, np.array, array, Series])
37
+ def listlike_box(request):
38
+ """
39
+ Types that may be passed as the indexer to searchsorted.
40
+ """
41
+ return request.param
lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__init__.py ADDED
File without changes
lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (209 Bytes). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_arithmetic.cpython-312.pyc ADDED
Binary file (2.75 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_constructors.cpython-312.pyc ADDED
Binary file (57.3 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_date_range.cpython-312.pyc ADDED
Binary file (79.7 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_datetime.cpython-312.pyc ADDED
Binary file (10.8 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_formats.cpython-312.pyc ADDED
Binary file (17.2 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_freq_attr.cpython-312.pyc ADDED
Binary file (2.83 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_indexing.cpython-312.pyc ADDED
Binary file (41.4 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_iter.cpython-312.pyc ADDED
Binary file (4.13 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_join.cpython-312.pyc ADDED
Binary file (8.25 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_npfuncs.cpython-312.pyc ADDED
Binary file (1.02 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_ops.cpython-312.pyc ADDED
Binary file (3.07 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_partial_slicing.cpython-312.pyc ADDED
Binary file (23 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_pickle.cpython-312.pyc ADDED
Binary file (2.69 kB). View file
 
lib/python3.12/site-packages/pandas/tests/indexes/datetimes/__pycache__/test_reindex.cpython-312.pyc ADDED
Binary file (2.96 kB). View file