Repository: lincc-frameworks/nested-pandas

Issue:
reduce(append_columns=True) has a wrong behavior for non-unique indexes
**Bug report**

This should be `pd.concat([self, resultd_df], axis=1)`:
https://github.com/lincc-frameworks/nested-pandas/blob/1eeda4bafab06582e90b1266a36f6923ccf31fdc/src/nested_pandas/nestedframe/core.py#L1340

See lsdb's implementation:
https://github.com/astronomy-commons/lsdb/blob/943438654f2d53b6c6c683ceebe4385667c9401b/src/lsdb/catalog/dataset/healpix_dataset.py#L1017

**Before submitting**
Please check the following:

- [ ] I have described the situation in which the bug arose, including what code was executed, information about my environment, and any applicable data others will need to reproduce the problem.
- [ ] I have included available evidence of the unexpected behavior (including error messages, screenshots, and/or plots) as well as a description of what I expected instead.
- [x] If I have a solution in mind, I have provided an explanation and/or pseudocode and/or task list.


Discussion:
github-actions[bot]: | Before [1eeda4ba]    | After [195ab94c]    |   Ratio | Benchmark (Parameter)                               |
|----------------------|---------------------|---------|-----------------------------------------------------|
| 10.5±0.1ms           | 10.6±0.08ms         |    1.01 | benchmarks.NestedFrameAddNested.time_run            |
| 246M                 | 246M                |    1    | benchmarks.AssignSingleDfToNestedSeries.peakmem_run |
| 27.6±0.5ms           | 27.7±0.7ms          |    1    | benchmarks.AssignSingleDfToNestedSeries.time_run    |
| 135M                 | 134M                |    1    | benchmarks.CountNestedBy.peakmem_run                |
| 65.4±0.7ms           | 65.5±0.2ms          |    1    | benchmarks.CountNestedBy.time_run                   |
| 101M                 | 101M                |    1    | benchmarks.NestedFrameAddNested.peakmem_run         |
| 105M                 | 104M                |    1    | benchmarks.NestedFrameQuery.peakmem_run             |
| 105M                 | 104M                |    1    | benchmarks.NestedFrameReduce.peakmem_run            |
| 267M                 | 267M                |    1    | benchmarks.ReassignHalfOfNestedSeries.peakmem_run   |
| 10.5±0.03ms          | 10.4±0.04ms         |    0.99 | benchmarks.NestedFrameQuery.time_run                |


Click [here](https://github.com/lincc-frameworks/nested-pandas/actions/runs/16033893278/job/45240881298#step:10:1) to view all benchmarks.

Fix (patch):
diff --git a/src/nested_pandas/nestedframe/core.py b/src/nested_pandas/nestedframe/core.py
index 582a482..2848e59 100644
--- a/src/nested_pandas/nestedframe/core.py
+++ b/src/nested_pandas/nestedframe/core.py
@@ -1337,7 +1337,7 @@ class NestedFrame(pd.DataFrame):
 
         if append_columns:
             # Append the results to the original NestedFrame
-            return self.join(results_nf)
+            return pd.concat([self, results_nf], axis=1)
 
         # Otherwise, return the results as a new NestedFrame
         return results_nf


Test changes:
diff --git a/tests/nested_pandas/nestedframe/test_nestedframe.py b/tests/nested_pandas/nestedframe/test_nestedframe.py
index 756a360..919c088 100644
--- a/tests/nested_pandas/nestedframe/test_nestedframe.py
+++ b/tests/nested_pandas/nestedframe/test_nestedframe.py
@@ -1090,7 +1090,10 @@ def test_reduce():
     result = nf.reduce(make_id, "b", prefix_str="some_id_")
     assert result[0][1] == "some_id_4"
 
-    # Verify that append_columns=True works as expected
+    # Verify that append_columns=True works as expected.
+    # Ensure that even with non-unique indexes, the final result retains
+    # the original index (nested-pandas#301)
+    nf.index = pd.Index([0, 1, 1], name="non-unique")
     result = nf.reduce(get_max, "packed.c", "packed.d", append_columns=True)
     assert len(result) == len(nf)
     assert isinstance(result, NestedFrame)
@@ -1099,7 +1102,8 @@ def test_reduce():
     # The result should have the original columns plus the new max columns
     assert result_c[: len(nf_c)] == nf_c
     assert result_c[len(nf_c) :] == ["max_col1", "max_col2"]
-    assert result.index.name == "idx"
+    assert result.index.name == "non-unique"
+    assert list(result.index) == [0, 1, 1]
     for i in range(len(result)):
         assert result["max_col1"].values[i] == expected_max_c[i]
         assert result["max_col2"].values[i] == expected_max_d[i]