From 0d96e9b93ade1e7b366cd7827a80327c9e5d7839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Thu, 8 May 2025 23:15:46 -0400 Subject: [PATCH 1/8] GH61405 Expose arguments in DataFrame.query --- pandas/core/frame.py | 65 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 3199733cfb85f..eb17c630dbdc0 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -4477,18 +4477,62 @@ def _get_item(self, item: Hashable) -> Series: @overload def query( - self, expr: str, *, inplace: Literal[False] = ..., **kwargs + self, + expr: str, + *, + parser: Literal["pandas", "python"] = ..., + engine: Literal["python", "numexpr"] | None = ..., + local_dict: dict[str, Any] | None = ..., + global_dict: dict[str, Any] | None = ..., + resolvers: list[Mapping] | None = ..., + level: int = ..., + target: None = ..., + inplace: Literal[False] = ..., ) -> DataFrame: ... @overload - def query(self, expr: str, *, inplace: Literal[True], **kwargs) -> None: ... + def query( + self, + expr: str, + *, + parser: Literal["pandas", "python"] = ..., + engine: Literal["python", "numexpr"] | None = ..., + local_dict: dict[str, Any] | None = ..., + global_dict: dict[str, Any] | None = ..., + resolvers: list[Mapping] | None = ..., + level: int = ..., + target: None = ..., + inplace: Literal[True], + ) -> None: ... @overload def query( - self, expr: str, *, inplace: bool = ..., **kwargs + self, + expr: str, + *, + parser: Literal["pandas", "python"] = ..., + engine: Literal["python", "numexpr"] | None = ..., + local_dict: dict[str, Any] | None = ..., + global_dict: dict[str, Any] | None = ..., + resolvers: list[Mapping] | None = ..., + level: int = ..., + target: None = ..., + inplace: bool = ..., ) -> DataFrame | None: ... - def query(self, expr: str, *, inplace: bool = False, **kwargs) -> DataFrame | None: + def query( + self, + expr: str, + *, + parser: Literal["pandas", "python"] = ..., + engine: Literal["python", "numexpr"] | None = None, + local_dict: dict[str, Any] | None = None, + global_dict: dict[str, Any] | None = None, + resolvers: list[Mapping] | None = None, + level: int = 0, + target: None = None, + inplace: bool = False, + ) -> DataFrame | None: """ Query the columns of a DataFrame with a boolean expression. @@ -4624,10 +4668,17 @@ def query(self, expr: str, *, inplace: bool = False, **kwargs) -> DataFrame | No if not isinstance(expr, str): msg = f"expr must be a string to be evaluated, {type(expr)} given" raise ValueError(msg) - kwargs["level"] = kwargs.pop("level", 0) + 1 - kwargs["target"] = None - res = self.eval(expr, **kwargs) + res = self.eval( + expr, + parser, + engine, + local_dict, + global_dict, + resolvers, + level + 1, + target, + ) try: result = self.loc[res] From b8267cbc1282f8945ad4961203e200c4dfa63614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Fri, 9 May 2025 18:39:52 -0400 Subject: [PATCH 2/8] GH61405 Expose arguments in DataFrame.query --- pandas/core/frame.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index eb17c630dbdc0..dbfe1b69cb111 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -4524,7 +4524,7 @@ def query( self, expr: str, *, - parser: Literal["pandas", "python"] = ..., + parser: Literal["pandas", "python"] = "pandas", engine: Literal["python", "numexpr"] | None = None, local_dict: dict[str, Any] | None = None, global_dict: dict[str, Any] | None = None, @@ -4669,16 +4669,17 @@ def query( msg = f"expr must be a string to be evaluated, {type(expr)} given" raise ValueError(msg) - res = self.eval( - expr, - parser, - engine, - local_dict, - global_dict, - resolvers, - level + 1, - target, - ) + kwargs = { + "parser": parser, + "engine": engine, + "local_dict": local_dict, + "global_dict": global_dict, + "resolvers": resolvers, + "level": level + 1, + "target": None, + } + + res = self.eval(expr, **kwargs) try: result = self.loc[res] From 183a50fa45ca96b461a98825a88f01a5421cf857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Fri, 9 May 2025 18:45:28 -0400 Subject: [PATCH 3/8] GH61405 Expose arguments in DataFrame.query --- pandas/core/frame.py | 44 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index dbfe1b69cb111..060911dac309e 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -4486,7 +4486,6 @@ def query( global_dict: dict[str, Any] | None = ..., resolvers: list[Mapping] | None = ..., level: int = ..., - target: None = ..., inplace: Literal[False] = ..., ) -> DataFrame: ... @@ -4501,7 +4500,6 @@ def query( global_dict: dict[str, Any] | None = ..., resolvers: list[Mapping] | None = ..., level: int = ..., - target: None = ..., inplace: Literal[True], ) -> None: ... @@ -4516,7 +4514,6 @@ def query( global_dict: dict[str, Any] | None = ..., resolvers: list[Mapping] | None = ..., level: int = ..., - target: None = ..., inplace: bool = ..., ) -> DataFrame | None: ... @@ -4530,7 +4527,6 @@ def query( global_dict: dict[str, Any] | None = None, resolvers: list[Mapping] | None = None, level: int = 0, - target: None = None, inplace: bool = False, ) -> DataFrame | None: """ @@ -4551,11 +4547,45 @@ def query( See the documentation for :meth:`DataFrame.eval` for details on referring to column names and variables in the query string. + parser : {'pandas', 'python'}, default 'pandas' + The parser to use to construct the syntax tree from the expression. The + default of ``'pandas'`` parses code slightly different than standard + Python. Alternatively, you can parse an expression using the + ``'python'`` parser to retain strict Python semantics. See the + :ref:`enhancing performance ` documentation for + more details. + engine : {'python', 'numexpr'}, default 'numexpr' + + The engine used to evaluate the expression. Supported engines are + + - None : tries to use ``numexpr``, falls back to ``python`` + - ``'numexpr'`` : This default engine evaluates pandas objects using + numexpr for large speed ups in complex expressions with large frames. + - ``'python'`` : Performs operations as if you had ``eval``'d in top + level python. This engine is generally not that useful. + + More backends may be available in the future. + local_dict : dict or None, optional + A dictionary of local variables, taken from locals() by default. + global_dict : dict or None, optional + A dictionary of global variables, taken from globals() by default. + resolvers : list of dict-like or None, optional + A list of objects implementing the ``__getitem__`` special method that + you can use to inject an additional collection of namespaces to use for + variable lookup. For example, this is used in the + :meth:`~DataFrame.query` method to inject the + ``DataFrame.index`` and ``DataFrame.columns`` + variables that refer to their respective :class:`~pandas.DataFrame` + instance attributes. + level : int, optional + The number of prior stack frames to traverse and add to the current + scope. Most users will **not** need to change this parameter. + inplace : bool, default False + If `target` is provided, and the expression mutates `target`, whether + to modify `target` inplace. Otherwise, return a copy of `target` with + the mutation. inplace : bool Whether to modify the DataFrame rather than creating a new one. - **kwargs - See the documentation for :func:`eval` for complete details - on the keyword arguments accepted by :meth:`DataFrame.query`. Returns ------- From 7998fd2b8c583cc9fb0dad0298bf4612623b002f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Fri, 9 May 2025 22:35:08 -0400 Subject: [PATCH 4/8] GH61405 Expose arguments in DataFrame.query --- pandas/core/frame.py | 237 +++++++++++++++++++++++++++++++------------ 1 file changed, 170 insertions(+), 67 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 060911dac309e..c15713662fdcd 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -4281,7 +4281,7 @@ def _setitem_frame(self, key, value) -> None: raise ValueError("Array conditional must be same shape as self") key = self._constructor(key, **self._construct_axes_dict(), copy=False) - if key.size and not all(is_bool_dtype(dtype) for dtype in key.dtypes): + if key.size and not all(is_bool_dtype(blk.dtype) for blk in key._mgr.blocks): raise TypeError( "Must pass DataFrame or 2-d ndarray with boolean values only" ) @@ -4580,10 +4580,6 @@ def query( level : int, optional The number of prior stack frames to traverse and add to the current scope. Most users will **not** need to change this parameter. - inplace : bool, default False - If `target` is provided, and the expression mutates `target`, whether - to modify `target` inplace. Otherwise, return a copy of `target` with - the mutation. inplace : bool Whether to modify the DataFrame rather than creating a new one. @@ -4698,16 +4694,14 @@ def query( if not isinstance(expr, str): msg = f"expr must be a string to be evaluated, {type(expr)} given" raise ValueError(msg) - - kwargs = { - "parser": parser, - "engine": engine, - "local_dict": local_dict, - "global_dict": global_dict, - "resolvers": resolvers, - "level": level + 1, - "target": None, - } + kwargs = {} + kwargs["level"] = level + 1 + kwargs["target"] = None + kwargs["parser"] = parser + kwargs["engine"] = engine + kwargs["local_dict"] = local_dict + kwargs["global_dict"] = global_dict + kwargs["resolvers"] = resolvers or () res = self.eval(expr, **kwargs) @@ -5375,16 +5369,16 @@ def drop( Parameters ---------- - labels : single label or list-like + labels : single label or iterable of labels Index or column labels to drop. A tuple will be used as a single - label and not treated as a list-like. + label and not treated as an iterable. axis : {0 or 'index', 1 or 'columns'}, default 0 Whether to drop labels from the index (0 or 'index') or columns (1 or 'columns'). - index : single label or list-like + index : single label or iterable of labels Alternative to specifying axis (``labels, axis=0`` is equivalent to ``index=labels``). - columns : single label or list-like + columns : single label or iterable of labels Alternative to specifying axis (``labels, axis=1`` is equivalent to ``columns=labels``). level : int or level name, optional @@ -5962,6 +5956,8 @@ def set_index( Delete columns to be used as the new index. append : bool, default False Whether to append columns to existing index. + Setting to True will add the new columns to existing index. + When set to False, the current index will be dropped from the DataFrame. inplace : bool, default False Whether to modify the DataFrame rather than creating a new one. verify_integrity : bool, default False @@ -6035,6 +6031,25 @@ def set_index( 2 4 4 2014 40 3 9 7 2013 84 4 16 10 2014 31 + + Append a column to the existing index: + + >>> df = df.set_index("month") + >>> df.set_index("year", append=True) + sale + month year + 1 2012 55 + 4 2014 40 + 7 2013 84 + 10 2014 31 + + >>> df.set_index("year", append=False) + sale + year + 2012 55 + 2014 40 + 2013 84 + 2014 31 """ inplace = validate_bool_kwarg(inplace, "inplace") self._check_inplace_and_allows_duplicate_labels(inplace) @@ -7940,7 +7955,7 @@ def _cmp_method(self, other, op): # See GH#4537 for discussion of scalar op behavior new_data = self._dispatch_frame_op(other, op, axis=axis) - return self._construct_result(new_data) + return self._construct_result(new_data, other=other) def _arith_method(self, other, op): if self._should_reindex_frame_op(other, op, 1, None, None): @@ -7953,7 +7968,7 @@ def _arith_method(self, other, op): with np.errstate(all="ignore"): new_data = self._dispatch_frame_op(other, op, axis=axis) - return self._construct_result(new_data) + return self._construct_result(new_data, other=other) _logical_method = _arith_method @@ -8149,8 +8164,7 @@ def _align_for_op( Parameters ---------- - left : DataFrame - right : Any + other : Any axis : int flex : bool or None, default False Whether this is a flex op, in which case we reindex. @@ -8269,7 +8283,6 @@ def to_series(right): level=level, ) right = left._maybe_align_series_as_frame(right, axis) - return left, right def _maybe_align_series_as_frame(self, series: Series, axis: AxisInt): @@ -8298,7 +8311,7 @@ def _maybe_align_series_as_frame(self, series: Series, axis: AxisInt): index=self.index, columns=self.columns, dtype=rvalues.dtype, - ) + ).__finalize__(series) def _flex_arith_method( self, other, op, *, axis: Axis = "columns", level=None, fill_value=None @@ -8330,9 +8343,9 @@ def _flex_arith_method( new_data = self._dispatch_frame_op(other, op) - return self._construct_result(new_data) + return self._construct_result(new_data, other=other) - def _construct_result(self, result) -> DataFrame: + def _construct_result(self, result, other) -> DataFrame: """ Wrap the result of an arithmetic, comparison, or logical operation. @@ -8349,6 +8362,7 @@ def _construct_result(self, result) -> DataFrame: # non-unique columns case out.columns = self.columns out.index = self.index + out = out.__finalize__(other) return out def __divmod__(self, other) -> tuple[DataFrame, DataFrame]: @@ -8369,7 +8383,7 @@ def _flex_cmp_method(self, other, op, *, axis: Axis = "columns", level=None): self, other = self._align_for_op(other, axis, flex=True, level=level) new_data = self._dispatch_frame_op(other, op, axis=axis) - return self._construct_result(new_data) + return self._construct_result(new_data, other=other) @Appender(ops.make_flex_doc("eq", "dataframe")) def eq(self, other, axis: Axis = "columns", level=None) -> DataFrame: @@ -9408,8 +9422,12 @@ def pivot( on the rows and columns. dropna : bool, default True Do not include columns whose entries are all NaN. If True, - rows with a NaN value in any column will be omitted before - computing margins. + + * rows with an NA value in any column will be omitted before computing + margins, + * index/column keys containing NA values will be dropped (see ``dropna`` + parameter in :meth:`DataFrame.groupby`). + margins_name : str, default 'All' Name of the row / column that will contain the totals when margins is True. @@ -10336,7 +10354,7 @@ def apply( result_type: Literal["expand", "reduce", "broadcast"] | None = None, args=(), by_row: Literal[False, "compat"] = "compat", - engine: Literal["python", "numba"] = "python", + engine: Callable | None | Literal["python", "numba"] = None, engine_kwargs: dict[str, bool] | None = None, **kwargs, ): @@ -10347,7 +10365,9 @@ def apply( either the DataFrame's index (``axis=0``) or the DataFrame's columns (``axis=1``). By default (``result_type=None``), the final return type is inferred from the return type of the applied function. Otherwise, - it depends on the `result_type` argument. + it depends on the `result_type` argument. The return type of the applied + function is inferred based on the first computed result obtained after + applying the function to a Series object. Parameters ---------- @@ -10398,28 +10418,24 @@ def apply( .. versionadded:: 2.1.0 - engine : {'python', 'numba'}, default 'python' - Choose between the python (default) engine or the numba engine in apply. - - The numba engine will attempt to JIT compile the passed function, - which may result in speedups for large DataFrames. - It also supports the following engine_kwargs : + engine : decorator or {'python', 'numba'}, optional + Choose the execution engine to use. If not provided the function + will be executed by the regular Python interpreter. - - nopython (compile the function in nopython mode) - - nogil (release the GIL inside the JIT compiled function) - - parallel (try to apply the function in parallel over the DataFrame) + Other options include JIT compilers such Numba and Bodo, which in some + cases can speed up the execution. To use an executor you can provide + the decorators ``numba.jit``, ``numba.njit`` or ``bodo.jit``. You can + also provide the decorator with parameters, like ``numba.jit(nogit=True)``. - Note: Due to limitations within numba/how pandas interfaces with numba, - you should only use this if raw=True + Not all functions can be executed with all execution engines. In general, + JIT compilers will require type stability in the function (no variable + should change data type during the execution). And not all pandas and + NumPy APIs are supported. Check the engine documentation [1]_ and [2]_ + for limitations. - Note: The numba compiler only supports a subset of - valid Python/numpy operations. + .. warning:: - Please read more about the `supported python features - `_ - and `supported numpy features - `_ - in numba to learn what you can or cannot use in the passed function. + String parameters will stop being supported in a future pandas version. .. versionadded:: 2.2.0 @@ -10427,6 +10443,7 @@ def apply( Pass keyword arguments to the engine. This is currently only used by the numba engine, see the documentation for the engine argument for more information. + **kwargs Additional keyword arguments to pass as keywords arguments to `func`. @@ -10449,6 +10466,13 @@ def apply( behavior or errors and are not supported. See :ref:`gotchas.udf-mutation` for more details. + References + ---------- + .. [1] `Numba documentation + `_ + .. [2] `Bodo documentation + `/ + Examples -------- >>> df = pd.DataFrame([[4, 9]] * 3, columns=["A", "B"]) @@ -10517,22 +10541,99 @@ def apply( 0 1 2 1 1 2 2 1 2 + + Advanced users can speed up their code by using a Just-in-time (JIT) compiler + with ``apply``. The main JIT compilers available for pandas are Numba and Bodo. + In general, JIT compilation is only possible when the function passed to + ``apply`` has type stability (variables in the function do not change their + type during the execution). + + >>> import bodo + >>> df.apply(lambda x: x.A + x.B, axis=1, engine=bodo.jit) + + Note that JIT compilation is only recommended for functions that take a + significant amount of time to run. Fast functions are unlikely to run faster + with JIT compilation. """ - from pandas.core.apply import frame_apply + if engine is None or isinstance(engine, str): + from pandas.core.apply import frame_apply - op = frame_apply( - self, - func=func, - axis=axis, - raw=raw, - result_type=result_type, - by_row=by_row, - engine=engine, - engine_kwargs=engine_kwargs, - args=args, - kwargs=kwargs, - ) - return op.apply().__finalize__(self, method="apply") + if engine is None: + engine = "python" + + if engine not in ["python", "numba"]: + raise ValueError(f"Unknown engine '{engine}'") + + op = frame_apply( + self, + func=func, + axis=axis, + raw=raw, + result_type=result_type, + by_row=by_row, + engine=engine, + engine_kwargs=engine_kwargs, + args=args, + kwargs=kwargs, + ) + return op.apply().__finalize__(self, method="apply") + elif hasattr(engine, "__pandas_udf__"): + if result_type is not None: + raise NotImplementedError( + f"{result_type=} only implemented for the default engine" + ) + + agg_axis = self._get_agg_axis(self._get_axis_number(axis)) + + # one axis is empty + if not all(self.shape): + func = cast(Callable, func) + try: + if axis == 0: + r = func(Series([], dtype=np.float64), *args, **kwargs) + else: + r = func( + Series(index=self.columns, dtype=np.float64), + *args, + **kwargs, + ) + except Exception: + pass + else: + if not isinstance(r, Series): + if len(agg_axis): + r = func(Series([], dtype=np.float64), *args, **kwargs) + else: + r = np.nan + + return self._constructor_sliced(r, index=agg_axis) + return self.copy() + + data: DataFrame | np.ndarray = self + if raw: + # This will upcast the whole DataFrame to the same type, + # and likely result in an object 2D array. + # We should probably pass a list of 1D arrays instead, at + # lest for ``axis=0`` + data = self.values + result = engine.__pandas_udf__.apply( + data=data, + func=func, + args=args, + kwargs=kwargs, + decorator=engine, + axis=axis, + ) + if raw: + if result.ndim == 2: + return self._constructor( + result, index=self.index, columns=self.columns + ) + else: + return self._constructor_sliced(result, index=agg_axis) + return result + else: + raise ValueError(f"Unknown engine {engine}") def map( self, func: PythonFuncType, na_action: Literal["ignore"] | None = None, **kwargs @@ -10649,9 +10750,11 @@ def _append( index = Index( [other.name], - name=self.index.names - if isinstance(self.index, MultiIndex) - else self.index.name, + name=( + self.index.names + if isinstance(self.index, MultiIndex) + else self.index.name + ), ) row_df = other.to_frame().T # infer_objects is needed for From b99d62eb82caef3091d9de8f569dab539352f0fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Fri, 9 May 2025 23:02:00 -0400 Subject: [PATCH 5/8] GH61405 Expose arguments in DataFrame.query --- pandas/core/frame.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index c15713662fdcd..106e4a74aa4c4 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -4694,14 +4694,15 @@ def query( if not isinstance(expr, str): msg = f"expr must be a string to be evaluated, {type(expr)} given" raise ValueError(msg) - kwargs = {} - kwargs["level"] = level + 1 - kwargs["target"] = None - kwargs["parser"] = parser - kwargs["engine"] = engine - kwargs["local_dict"] = local_dict - kwargs["global_dict"] = global_dict - kwargs["resolvers"] = resolvers or () + kwargs: Any = { + "level": level + 1, + "target": None, + "parser": parser, + "engine": engine, + "local_dict": local_dict, + "global_dict": global_dict, + "resolvers": resolvers or (), + } res = self.eval(expr, **kwargs) From 3f50ff91749e24da9bd7086290732792fcf21525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Fri, 9 May 2025 23:10:09 -0400 Subject: [PATCH 6/8] Fix pre-commit --- pandas/core/frame.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 106e4a74aa4c4..678edf51ee79b 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -4695,13 +4695,13 @@ def query( msg = f"expr must be a string to be evaluated, {type(expr)} given" raise ValueError(msg) kwargs: Any = { - "level": level + 1, - "target": None, - "parser": parser, - "engine": engine, - "local_dict": local_dict, - "global_dict": global_dict, - "resolvers": resolvers or (), + "level": level + 1, + "target": None, + "parser": parser, + "engine": engine, + "local_dict": local_dict, + "global_dict": global_dict, + "resolvers": resolvers or (), } res = self.eval(expr, **kwargs) From b0f90c2b8c362af91d93f5f2f002834b43e85909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Sat, 10 May 2025 08:22:54 -0400 Subject: [PATCH 7/8] GH61405 Add whatsnew docs --- doc/source/whatsnew/v3.0.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 8695e196c4f38..30c8e0da407a3 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -401,6 +401,7 @@ Other API changes - Index set operations (like union or intersection) will now ignore the dtype of an empty ``RangeIndex`` or empty ``Index`` with object dtype when determining the dtype of the resulting Index (:issue:`60797`) +- `DataFrame.query` does not accept `**kwargs` anymore and requires passing keywords for desired arguments (:issue:`61405`) .. --------------------------------------------------------------------------- .. _whatsnew_300.deprecations: From 64575591c20b71dd0b86193ac4e61799d43aea39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Sat, 10 May 2025 08:31:03 -0400 Subject: [PATCH 8/8] Fix pre-commit --- doc/source/whatsnew/v3.0.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 30c8e0da407a3..6f7fc58ad5084 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -401,7 +401,7 @@ Other API changes - Index set operations (like union or intersection) will now ignore the dtype of an empty ``RangeIndex`` or empty ``Index`` with object dtype when determining the dtype of the resulting Index (:issue:`60797`) -- `DataFrame.query` does not accept `**kwargs` anymore and requires passing keywords for desired arguments (:issue:`61405`) +- :meth:`DataFrame.query` does not accept ``**kwargs`` anymore and requires passing keywords for desired arguments (:issue:`61405`) .. --------------------------------------------------------------------------- .. _whatsnew_300.deprecations: