Skip to content

Commit 59b487d

Browse files
committed
Validate operand, parametrized tests, whatnew
GH21220
1 parent 1a0e5a2 commit 59b487d

File tree

3 files changed

+45
-22
lines changed

3 files changed

+45
-22
lines changed

doc/source/whatsnew/v0.23.1.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ New features
1616
~~~~~~~~~~~~
1717

1818
- :meth:`Index.droplevel` is now implemented also for flat indexes, for compatibility with MultiIndex (:issue:`21115`)
19+
- :func: merge now allows a ``DataFrame`` and a ``Series`` with a name as inputs (:issue:`21220`)
1920

2021

2122
.. _whatsnew_0231.deprecations:

pandas/core/reshape/merge.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -492,10 +492,8 @@ def __init__(self, left, right, how='inner', on=None,
492492
left_index=False, right_index=False, sort=True,
493493
suffixes=('_x', '_y'), copy=True, indicator=False,
494494
validate=None):
495-
if isinstance(left, Series):
496-
left = left.to_frame()
497-
if isinstance(right, Series):
498-
right = right.to_frame()
495+
left = validate_operand(left)
496+
right = validate_operand(right)
499497
self.left = self.orig_left = left
500498
self.right = self.orig_right = right
501499
self.how = how
@@ -522,13 +520,6 @@ def __init__(self, left, right, how='inner', on=None,
522520
raise ValueError(
523521
'indicator option can only accept boolean or string arguments')
524522

525-
if not isinstance(left, DataFrame):
526-
raise ValueError('can not merge DataFrame with instance of '
527-
'type {left}'.format(left=type(left)))
528-
if not isinstance(right, DataFrame):
529-
raise ValueError('can not merge DataFrame with instance of '
530-
'type {right}'.format(right=type(right)))
531-
532523
if not is_bool(left_index):
533524
raise ValueError(
534525
'left_index parameter must be of type bool, not '
@@ -1643,3 +1634,16 @@ def _should_fill(lname, rname):
16431634

16441635
def _any(x):
16451636
return x is not None and com._any_not_none(*x)
1637+
1638+
1639+
def validate_operand(obj):
1640+
if isinstance(obj, DataFrame):
1641+
return obj
1642+
elif isinstance(obj, Series):
1643+
if obj.name is None:
1644+
raise ValueError('Cannot merge a Series without a name')
1645+
else:
1646+
return obj.to_frame()
1647+
else:
1648+
raise ValueError('Cannot merge a DataFrame or a Series with '
1649+
'instance of type {obj}'.format(obj=type(obj)))

pandas/tests/reshape/merge/test_merge.py

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1866,22 +1866,40 @@ def test_merge_index_types(index):
18661866
assert_frame_equal(result, expected)
18671867

18681868

1869-
def test_merge_series():
1869+
@pytest.mark.parametrize("on,left_on,right_on,left_index,right_index,nms,nm", [
1870+
(['outer', 'inner'], None, None, False, False,
1871+
['outer', 'inner'], 'B'),
1872+
(None, None, None, True, True, ['outer', 'inner'],
1873+
'B'),
1874+
(None, ['outer', 'inner'], None, False, True, None,
1875+
'B'),
1876+
(None, None, ['outer', 'inner'], True, False, None,
1877+
'B'),
1878+
(['outer', 'inner'], None, None, False, False,
1879+
['outer', 'inner'], None),
1880+
(None, None, None, True, True, ['outer', 'inner'],
1881+
None),
1882+
(None, ['outer', 'inner'], None, False, True, None,
1883+
None),
1884+
(None, None, ['outer', 'inner'], True, False, None,
1885+
None),
1886+
])
1887+
def test_merge_series(on, left_on, right_on, left_index, right_index, nms, nm):
18701888
# GH 21220
18711889
a = pd.DataFrame({"A": [1, 2, 3, 4]},
18721890
index=pd.MultiIndex.from_product([['a', 'b'], [0, 1]],
18731891
names=['outer', 'inner']))
18741892
b = pd.Series([1, 2, 3, 4],
18751893
index=pd.MultiIndex.from_product([['a', 'b'], [1, 2]],
1876-
names=['outer', 'inner']), name='B')
1894+
names=['outer', 'inner']), name=nm)
18771895
expected = pd.DataFrame({"A": [2, 4], "B": [1, 3]},
18781896
index=pd.MultiIndex.from_product([['a', 'b'], [1]],
1879-
names=['outer', 'inner']))
1880-
1881-
# Test merge with a DataFrame and a Series 'converted-to-DataFrame' object
1882-
result = pd.merge(a, b.to_frame(), on=['outer', 'inner'])
1883-
tm.assert_frame_equal(result, expected)
1884-
1885-
# Test merge with a DataFrame and a Series object
1886-
result = pd.merge(a, b, on=['outer', 'inner'])
1887-
tm.assert_frame_equal(result, expected)
1897+
names=nms))
1898+
if nm is not None:
1899+
result = pd.merge(a, b, on=on, left_on=left_on, right_on=right_on,
1900+
left_index=left_index, right_index=right_index)
1901+
tm.assert_frame_equal(result, expected)
1902+
else:
1903+
with tm.assert_raises_regex(ValueError, 'a Series without a name'):
1904+
result = pd.merge(a, b, on=on, left_on=left_on, right_on=right_on,
1905+
left_index=left_index, right_index=right_index)

0 commit comments

Comments
 (0)