Skip to content

Commit e36b000

Browse files
authored
BUG: fix combine_first reorders columns (#60791)
* Add test * Fix combine_first reorders columns * Add whatsnew * Fix corner case when self is empty and future.infer_string is True * Update
1 parent 84bf1ef commit e36b000

File tree

3 files changed

+15
-3
lines changed

3 files changed

+15
-3
lines changed

doc/source/whatsnew/v3.0.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,7 @@ Groupby/resample/rolling
758758
Reshaping
759759
^^^^^^^^^
760760
- Bug in :func:`qcut` where values at the quantile boundaries could be incorrectly assigned (:issue:`59355`)
761+
- Bug in :meth:`DataFrame.combine_first` not preserving the column order (:issue:`60427`)
761762
- Bug in :meth:`DataFrame.join` inconsistently setting result index name (:issue:`55815`)
762763
- Bug in :meth:`DataFrame.join` when a :class:`DataFrame` with a :class:`MultiIndex` would raise an ``AssertionError`` when :attr:`MultiIndex.names` contained ``None``. (:issue:`58721`)
763764
- Bug in :meth:`DataFrame.merge` where merging on a column containing only ``NaN`` values resulted in an out-of-bounds array access (:issue:`59421`)

pandas/core/frame.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -8671,6 +8671,7 @@ def combine(
86718671
2 NaN 3.0 1.0
86728672
"""
86738673
other_idxlen = len(other.index) # save for compare
8674+
other_columns = other.columns
86748675

86758676
this, other = self.align(other)
86768677
new_index = this.index
@@ -8681,8 +8682,8 @@ def combine(
86818682
if self.empty and len(other) == other_idxlen:
86828683
return other.copy()
86838684

8684-
# sorts if possible; otherwise align above ensures that these are set-equal
8685-
new_columns = this.columns.union(other.columns)
8685+
# preserve column order
8686+
new_columns = self.columns.union(other_columns, sort=False)
86868687
do_fill = fill_value is not None
86878688
result = {}
86888689
for col in new_columns:

pandas/tests/frame/methods/test_combine_first.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ def test_combine_first_with_asymmetric_other(self, val):
380380
df2 = DataFrame({"isBool": [True]})
381381

382382
res = df1.combine_first(df2)
383-
exp = DataFrame({"isBool": [True], "isNum": [val]})
383+
exp = DataFrame({"isNum": [val], "isBool": [True]})
384384

385385
tm.assert_frame_equal(res, exp)
386386

@@ -555,3 +555,13 @@ def test_combine_first_empty_columns():
555555
result = left.combine_first(right)
556556
expected = DataFrame(columns=["a", "b", "c"])
557557
tm.assert_frame_equal(result, expected)
558+
559+
560+
def test_combine_first_preserve_column_order():
561+
# GH#60427
562+
df1 = DataFrame({"B": [1, 2, 3], "A": [4, None, 6]})
563+
df2 = DataFrame({"A": [5]}, index=[1])
564+
565+
result = df1.combine_first(df2)
566+
expected = DataFrame({"B": [1, 2, 3], "A": [4.0, 5.0, 6.0]})
567+
tm.assert_frame_equal(result, expected)

0 commit comments

Comments
 (0)