Repository: pandas-dev/pandas

Issue:
BUG: Formerly useful behavior now raises.
I did this:

In [1]: df1 = DataFrame([1, 2, 3, 4, 5], index=[1, 2, 1, 2, 3])

In [2]: df2 = DataFrame([1, 2, 3], index=[1, 2, 3])

In [3]: df1.sub(df2)
Out[3]: 
   0
1  0
1  2
2  0
2  2
3  2

which is possible up to and including [this commit](https://github.com/jreback/pandas/commit/725b1951249a795fe01896dff4ce46bd9206021f). Then, [this "bug fix"](https://github.com/jreback/pandas/commit/3f76205f1d4a42ba971ff5be8770b52172b9f822#diff-0) seems to have broken the above usage. It now raises like so:

```
ValueError                                Traceback (most recent call last)
<ipython-input-12-e7e549a4e818> in <module>()
----> 1 df1.sub(df2)

/Users/danielallan/Documents/Repos/pandas-danielballan/pandas/core/ops.pyc in f(self, other, axis, level, fill_value)
    710     def f(self, other, axis=default_axis, level=None, fill_value=None):
    711         if isinstance(other, pd.DataFrame):    # Another DataFrame
--> 712             return self._combine_frame(other, na_op, fill_value, level)
    713         elif isinstance(other, pd.Series):
    714             return self._combine_series(other, na_op, fill_value, axis, level)

/Users/danielallan/Documents/Repos/pandas-danielballan/pandas/core/frame.pyc in _combine_frame(self, other, func, fill_value, level)
   2759 
   2760     def _combine_frame(self, other, func, fill_value=None, level=None):
-> 2761         this, other = self.align(other, join='outer', level=level, copy=False)
   2762         new_index, new_columns = this.index, this.columns
   2763 

/Users/danielallan/Documents/Repos/pandas-danielballan/pandas/core/generic.pyc in align(self, other, join, axis, level, copy, fill_value, method, limit, fill_axis)
   2331                                      copy=copy, fill_value=fill_value,
   2332                                      method=method, limit=limit,
-> 2333                                      fill_axis=fill_axis)
   2334         elif isinstance(other, Series):
   2335             return self._align_series(other, join=join, axis=axis, level=level,

/Users/danielallan/Documents/Repos/pandas-danielballan/pandas/core/generic.pyc in _align_frame(self, other, join, axis, level, copy, fill_value, method, limit, fill_axis)
   2362         left = self._reindex_with_indexers({0: [join_index,   ilidx],
   2363                                             1: [join_columns, clidx]},
-> 2364                                            copy=copy, fill_value=fill_value)
   2365         right = other._reindex_with_indexers({0: [join_index,   iridx],
   2366                                               1: [join_columns, cridx]},

/Users/danielallan/Documents/Repos/pandas-danielballan/pandas/core/generic.pyc in _reindex_with_indexers(self, reindexers, method, fill_value, limit, copy, allow_dups)
   1320                 indexer = com._ensure_int64(indexer)
   1321                 new_data = new_data.reindex_indexer(index, indexer, axis=baxis,
-> 1322                                                     fill_value=fill_value, allow_dups=allow_dups)
   1323 
   1324             elif baxis == 0 and index is not None and index is not new_data.axes[baxis]:

/Users/danielallan/Documents/Repos/pandas-danielballan/pandas/core/internals.pyc in reindex_indexer(self, new_axis, indexer, axis, fill_value, allow_dups)
   2884         # trying to reindex on an axis with duplicates
   2885         if not allow_dups and not self.axes[axis].is_unique:
-> 2886             raise ValueError("cannot reindex from a duplicate axis")
   2887 
   2888         if axis == 0:

ValueError: cannot reindex from a duplicate axis
```

I found this very handy. Should it really raise? Can you suggest what I should be doing instead? Thanks.



Discussion:
that is disappointing! let me take a look


Resolving patch:
diff --git a/pandas/core/generic.py b/pandas/core/generic.py
--- a/pandas/core/generic.py
+++ b/pandas/core/generic.py
@@ -2455,10 +2455,12 @@ def _align_frame(self, other, join='outer', axis=None, level=None,
 
         left = self._reindex_with_indexers({0: [join_index,   ilidx],
                                             1: [join_columns, clidx]},
-                                           copy=copy, fill_value=fill_value)
+                                           copy=copy, fill_value=fill_value,
+                                           allow_dups=True)
         right = other._reindex_with_indexers({0: [join_index,   iridx],
                                               1: [join_columns, cridx]},
-                                             copy=copy, fill_value=fill_value)
+                                             copy=copy, fill_value=fill_value,
+                                             allow_dups=True)
 
         if method is not None:
             left = left.fillna(axis=fill_axis, method=method, limit=limit)
diff --git a/pandas/sparse/frame.py b/pandas/sparse/frame.py
--- a/pandas/sparse/frame.py
+++ b/pandas/sparse/frame.py
@@ -572,7 +572,8 @@ def _reindex_columns(self, columns, copy, level, fill_value, limit=None,
         return SparseDataFrame(sdict, index=self.index, columns=columns,
                                default_fill_value=self._default_fill_value)
 
-    def _reindex_with_indexers(self, reindexers, method=None, fill_value=None, limit=None, copy=False):
+    def _reindex_with_indexers(self, reindexers, method=None, fill_value=None, limit=None,
+                               copy=False, allow_dups=False):
 
         if method is not None or limit is not None:
             raise NotImplementedError("cannot reindex with a method or limit with sparse")


Test patch:
diff --git a/pandas/tests/test_frame.py b/pandas/tests/test_frame.py
--- a/pandas/tests/test_frame.py
+++ b/pandas/tests/test_frame.py
@@ -3209,6 +3209,14 @@ def check(result, expected=None):
         df = DataFrame(np.arange(12).reshape(3,4), columns=dups,dtype='float64')
         self.assertRaises(ValueError, lambda : df[df.A > 6])
 
+        # dup aligining operations should work
+        # GH 5185
+        df1 = DataFrame([1, 2, 3, 4, 5], index=[1, 2, 1, 2, 3])
+        df2 = DataFrame([1, 2, 3], index=[1, 2, 3])
+        expected = DataFrame([0,2,0,2,2],index=[1,1,2,2,3])
+        result = df1.sub(df2)
+        assert_frame_equal(result,expected)
+
     def test_insert_benchmark(self):
         # from the vb_suite/frame_methods/frame_insert_columns
         N = 10