Skip to content

Commit 25f2535

Browse files
Merge branch 'fix-61099' of https://github.com/mayurkishorkumar/pandas into fix-61099
2 parents cb31eb1 + 2122bfe commit 25f2535

File tree

7 files changed

+71
-2
lines changed

7 files changed

+71
-2
lines changed

.github/workflows/wheels.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ jobs:
153153
run: echo "sdist_name=$(cd ./dist && ls -d */)" >> "$GITHUB_ENV"
154154

155155
- name: Build wheels
156-
uses: pypa/[email protected].1
156+
uses: pypa/[email protected].2
157157
with:
158158
package-dir: ./dist/${{ startsWith(matrix.buildplat[1], 'macosx') && env.sdist_name || needs.build_sdist.outputs.sdist_file }}
159159
env:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
.tags
2424
.cache/
2525
.vscode/
26+
.venv/
2627

2728
# Compiled source #
2829
###################

doc/source/whatsnew/v3.0.0.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,7 @@ Indexing
706706
- Bug in :meth:`MultiIndex.insert` when a new value inserted to a datetime-like level gets cast to ``NaT`` and fails indexing (:issue:`60388`)
707707
- Bug in printing :attr:`Index.names` and :attr:`MultiIndex.levels` would not escape single quotes (:issue:`60190`)
708708
- Bug in reindexing of :class:`DataFrame` with :class:`PeriodDtype` columns in case of consolidated block (:issue:`60980`, :issue:`60273`)
709+
- Bug in :meth:`Series.equals` and :meth:`Series.__eq__` where equality comparison would fail if the index values were equal but had differing dtypes (e.g. ``object`` vs ``string``). (:issue:`61099`)
709710

710711
Missing
711712
^^^^^^^
@@ -825,6 +826,7 @@ Other
825826
- Bug in :class:`DataFrame` when passing a ``dict`` with a NA scalar and ``columns`` that would always return ``np.nan`` (:issue:`57205`)
826827
- Bug in :class:`Series` ignoring errors when trying to convert :class:`Series` input data to the given ``dtype`` (:issue:`60728`)
827828
- Bug in :func:`eval` on :class:`ExtensionArray` on including division ``/`` failed with a ``TypeError``. (:issue:`58748`)
829+
- Bug in :func:`eval` where method calls on binary operations like ``(x + y).dropna()`` would raise ``AttributeError: 'BinOp' object has no attribute 'value'`` (:issue:`61175`)
828830
- Bug in :func:`eval` where the names of the :class:`Series` were not preserved when using ``engine="numexpr"``. (:issue:`10239`)
829831
- Bug in :func:`eval` with ``engine="numexpr"`` returning unexpected result for float division. (:issue:`59736`)
830832
- Bug in :func:`to_numeric` raising ``TypeError`` when ``arg`` is a :class:`Timedelta` or :class:`Timestamp` scalar. (:issue:`59944`)

pandas/core/computation/expr.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,11 @@ def visit_Attribute(self, node, **kwargs):
644644
ctx = node.ctx
645645
if isinstance(ctx, ast.Load):
646646
# resolve the value
647-
resolved = self.visit(value).value
647+
visited_value = self.visit(value)
648+
if hasattr(visited_value, "value"):
649+
resolved = visited_value.value
650+
else:
651+
resolved = visited_value(self.env)
648652
try:
649653
v = getattr(resolved, attr)
650654
name = self.env.add_tmp(v)

pandas/core/indexes/base.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5471,6 +5471,11 @@ def equals(self, other: Any) -> bool:
54715471
# quickly return if the lengths are different
54725472
return False
54735473

5474+
if (isinstance(self.dtype, StringDtype) or is_object_dtype(self.dtype)) and (
5475+
isinstance(other.dtype, StringDtype) or is_object_dtype(other.dtype)
5476+
):
5477+
return array_equivalent(self._values, other._values)
5478+
54745479
if (
54755480
isinstance(self.dtype, StringDtype)
54765481
and self.dtype.na_value is np.nan

pandas/tests/computation/test_eval.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2006,3 +2006,24 @@ def test_eval_float_div_numexpr():
20062006
result = pd.eval("1 / 2", engine="numexpr")
20072007
expected = 0.5
20082008
assert result == expected
2009+
2010+
2011+
def test_method_calls_on_binop():
2012+
# GH 61175
2013+
x = Series([1, 2, 3, 5])
2014+
y = Series([2, 3, 4])
2015+
2016+
# Method call on binary operation result
2017+
result = pd.eval("(x + y).dropna()")
2018+
expected = (x + y).dropna()
2019+
tm.assert_series_equal(result, expected)
2020+
2021+
# Test with other binary operations
2022+
result = pd.eval("(x * y).dropna()")
2023+
expected = (x * y).dropna()
2024+
tm.assert_series_equal(result, expected)
2025+
2026+
# Test with method chaining
2027+
result = pd.eval("(x + y).dropna().reset_index(drop=True)")
2028+
expected = (x + y).dropna().reset_index(drop=True)
2029+
tm.assert_series_equal(result, expected)

pandas/tests/indexes/test_base.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
ensure_index,
4141
ensure_index_from_sequences,
4242
)
43+
from pandas.testing import assert_series_equal
4344

4445

4546
class TestIndex:
@@ -1717,3 +1718,38 @@ def test_is_monotonic_pyarrow_list_type():
17171718
idx = Index([[1], [2, 3]], dtype=pd.ArrowDtype(pa.list_(pa.int64())))
17181719
assert not idx.is_monotonic_increasing
17191720
assert not idx.is_monotonic_decreasing
1721+
1722+
1723+
def test_index_equals_string_vs_object():
1724+
# GH 61099
1725+
idx1 = Index(["a", "b", "c"])
1726+
idx2 = Index(["a", "b", "c"], dtype="string")
1727+
assert idx1.equals(idx2)
1728+
assert idx2.equals(idx1)
1729+
1730+
1731+
def test_compare_string_vs_object_index_equality():
1732+
# GH 61099
1733+
s1 = Series([1, 2, 3], index=["a", "b", "c"]) # dtype=object
1734+
s2 = Series([0, 1, 2], index=Index(["a", "b", "c"], dtype="string")) # dtype=string
1735+
result = s1 > s2
1736+
expected = Series([True, True, True], index=["a", "b", "c"])
1737+
assert_series_equal(result, expected)
1738+
1739+
1740+
def test_align_string_vs_object_index():
1741+
# GH 61099
1742+
s1 = Series([1, 2, 3], index=["a", "b", "c"]) # object
1743+
s2 = Series([1, 2, 3], index=Index(["a", "b", "c"], dtype="string")) # string
1744+
s1_aligned, s2_aligned = s1.align(s2)
1745+
assert list(s1_aligned.index) == list(s2_aligned.index)
1746+
1747+
1748+
def test_comparison_without_manual_casting():
1749+
# GH 61099
1750+
s1 = Series([1, 2, 3], index=["a", "b", "c"]) # object index
1751+
s2 = Series([1, 2, 3], index=Index(["a", "b", "c"], dtype="string"))
1752+
# Should not raise
1753+
result = s1 == s2
1754+
expected = Series([True, True, True], index=["a", "b", "c"])
1755+
assert_series_equal(result, expected)

0 commit comments

Comments
 (0)