<p dir="auto">Opening a new issue so this isn't lost.</p>
<p dir="auto">In <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="283691748" data-permission-text="Title is private" data-url="https://github.com/pandas-dev/pandas/issues/18882" data-hovercard-type="pull_request" data-hovercard-url="/pandas-dev/pandas/pull/18882/hovercard" href="https://github.com/pandas-dev/pandas/pull/18882">#18882</a> banned duplicate names in a MultiIndex. I think this is a good change since allowing duplicates hit a lot of edge cases when you went to actually do something. I want to make sure we understand all the cases that actually produce duplicate names in the MI though, specifically groupby.apply.</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="In [1]: import dask.dataframe as dd

In [2]: import pandas as pd

In [3]:     pdf = pd.DataFrame({'a': [1, 2, 3, 4, 5, 6, 7, 8, 9],
   ...:                         'b': [4, 5, 6, 3, 2, 1, 0, 0, 0]},
   ...:                        index=[0, 1, 3, 5, 6, 8, 9, 9, 9]).set_index(&quot;a&quot;)
   ...:
   ...:

In [4]: pdf.groupby(pdf.index).apply(lambda x: x.b)"><pre class="notranslate"><span class="pl-v">In</span> [<span class="pl-c1">1</span>]: <span class="pl-s1">import</span> <span class="pl-s1">dask</span>.<span class="pl-s1">dataframe</span> <span class="pl-k">as</span> <span class="pl-s1">dd</span>

<span class="pl-v">In</span> [<span class="pl-c1">2</span>]: <span class="pl-s1">import</span> <span class="pl-s1">pandas</span> <span class="pl-k">as</span> <span class="pl-s1">pd</span>

<span class="pl-v">In</span> [<span class="pl-c1">3</span>]:     <span class="pl-s1">pdf</span> <span class="pl-c1">=</span> <span class="pl-s1">pd</span>.<span class="pl-v">DataFrame</span>({<span class="pl-s">'a'</span>: [<span class="pl-c1">1</span>, <span class="pl-c1">2</span>, <span class="pl-c1">3</span>, <span class="pl-c1">4</span>, <span class="pl-c1">5</span>, <span class="pl-c1">6</span>, <span class="pl-c1">7</span>, <span class="pl-c1">8</span>, <span class="pl-c1">9</span>],
   ...:                         <span class="pl-s">'b'</span>: [<span class="pl-c1">4</span>, <span class="pl-c1">5</span>, <span class="pl-c1">6</span>, <span class="pl-c1">3</span>, <span class="pl-c1">2</span>, <span class="pl-c1">1</span>, <span class="pl-c1">0</span>, <span class="pl-c1">0</span>, <span class="pl-c1">0</span>]},
   ...:                        <span class="pl-s1">index</span><span class="pl-c1">=</span>[<span class="pl-c1">0</span>, <span class="pl-c1">1</span>, <span class="pl-c1">3</span>, <span class="pl-c1">5</span>, <span class="pl-c1">6</span>, <span class="pl-c1">8</span>, <span class="pl-c1">9</span>, <span class="pl-c1">9</span>, <span class="pl-c1">9</span>]).<span class="pl-en">set_index</span>(<span class="pl-s">"a"</span>)
   ...:
   ...:

<span class="pl-v">In</span> [<span class="pl-c1">4</span>]: <span class="pl-s1">pdf</span>.<span class="pl-en">groupby</span>(<span class="pl-s1">pdf</span>.<span class="pl-s1">index</span>).<span class="pl-en">apply</span>(<span class="pl-k">lambda</span> <span class="pl-s1">x</span>: <span class="pl-s1">x</span>.<span class="pl-s1">b</span>)</pre></div>
<p dir="auto">Another, more realistic example: groupwise drop_duplicates:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="In [18]: df = pd.DataFrame({&quot;B&quot;: [0, 0, 0, 1, 1, 1, 2, 2, 2]}, index=pd.Index([0, 1, 1, 2, 2, 2, 0, 0, 1], name='a'))

In [19]: df
Out[19]:
   B
a
0  0
1  0
1  0
2  1
2  1
2  1
0  2
0  2
1  2

In [20]: df.groupby('a').apply(pd.DataFrame.drop_duplicates)
Out[20]:
     B
a a
0 0  0
  0  2
1 1  0
  1  2
2 2  1"><pre class="notranslate"><span class="pl-v">In</span> [<span class="pl-c1">18</span>]: <span class="pl-s1">df</span> <span class="pl-c1">=</span> <span class="pl-s1">pd</span>.<span class="pl-v">DataFrame</span>({<span class="pl-s">"B"</span>: [<span class="pl-c1">0</span>, <span class="pl-c1">0</span>, <span class="pl-c1">0</span>, <span class="pl-c1">1</span>, <span class="pl-c1">1</span>, <span class="pl-c1">1</span>, <span class="pl-c1">2</span>, <span class="pl-c1">2</span>, <span class="pl-c1">2</span>]}, <span class="pl-s1">index</span><span class="pl-c1">=</span><span class="pl-s1">pd</span>.<span class="pl-v">Index</span>([<span class="pl-c1">0</span>, <span class="pl-c1">1</span>, <span class="pl-c1">1</span>, <span class="pl-c1">2</span>, <span class="pl-c1">2</span>, <span class="pl-c1">2</span>, <span class="pl-c1">0</span>, <span class="pl-c1">0</span>, <span class="pl-c1">1</span>], <span class="pl-s1">name</span><span class="pl-c1">=</span><span class="pl-s">'a'</span>))

<span class="pl-v">In</span> [<span class="pl-c1">19</span>]: <span class="pl-s1">df</span>
<span class="pl-v">Out</span>[<span class="pl-c1">19</span>]:
   <span class="pl-v">B</span>
<span class="pl-s1">a</span>
<span class="pl-c1">0</span>  <span class="pl-c1">0</span>
<span class="pl-c1">1</span>  <span class="pl-c1">0</span>
<span class="pl-c1">1</span>  <span class="pl-c1">0</span>
<span class="pl-c1">2</span>  <span class="pl-c1">1</span>
<span class="pl-c1">2</span>  <span class="pl-c1">1</span>
<span class="pl-c1">2</span>  <span class="pl-c1">1</span>
<span class="pl-c1">0</span>  <span class="pl-c1">2</span>
<span class="pl-c1">0</span>  <span class="pl-c1">2</span>
<span class="pl-c1">1</span>  <span class="pl-c1">2</span>

<span class="pl-v">In</span> [<span class="pl-c1">20</span>]: <span class="pl-s1">df</span>.<span class="pl-en">groupby</span>(<span class="pl-s">'a'</span>).<span class="pl-en">apply</span>(<span class="pl-s1">pd</span>.<span class="pl-v">DataFrame</span>.<span class="pl-s1">drop_duplicates</span>)
<span class="pl-v">Out</span>[<span class="pl-c1">20</span>]:
     <span class="pl-v">B</span>
<span class="pl-s1">a</span> <span class="pl-s1">a</span>
<span class="pl-c1">0</span> <span class="pl-c1">0</span>  <span class="pl-c1">0</span>
  <span class="pl-c1">0</span>  <span class="pl-c1">2</span>
<span class="pl-c1">1</span> <span class="pl-c1">1</span>  <span class="pl-c1">0</span>
  <span class="pl-c1">1</span>  <span class="pl-c1">2</span>
<span class="pl-c1">2</span> <span class="pl-c1">2</span>  <span class="pl-c1">1</span></pre></div>
<p dir="auto">Is it possible to throw a warning on this for now, in case duplicate names are more common than we thought?</p>
<p dir="auto">I think the <code class="notranslate">replace</code> docstring is lacking in many ways (<a href="https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.replace.html" rel="nofollow">https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.replace.html</a>):</p>
<ul dir="auto">
<li>The explanation of <code class="notranslate">to_replace</code> keyword is both way too complex and lacking an explanation of the simple cases:
<ul dir="auto">
<li>the most simple case of a scalar value is not clearly mentioned (like <code class="notranslate">df.replace(to_replace=0, replace=1)</code>, it is mentioned in the 'str' explanation, but it is not specific to strings)</li>
<li>the simplest dict case of <code class="notranslate">df.replace({to_replace: replacement})</code> is not mentioned (the dict explanation starts with explanation of nested dicts)</li>
<li>I would personally rewrite this whole explanation of this keyword, start with basic cases, and only after that (or in the notes) explain the complex cases.</li>
</ul>
</li>
<li>There is a reference to the examples section for "examples of each of those", but there is no examples section. We should add one.</li>
<li>In the 'see also' section it references <code class="notranslate">reindex</code>, <code class="notranslate">asfreq</code> and <code class="notranslate">fillna</code>. <code class="notranslate">fillna</code> is fine, but I fail to see the link with the first two. I would rather add a reference to <code class="notranslate">where</code> to replace values based on a boolean condition (and the 'see also' should not just refer to the other methods, but also include a sentence on why / the difference)</li>
<li>The docstring also uses <code class="notranslate">NDFrame</code>, and this should never be in a public docstring (failing substituion of docstring in <code class="notranslate">generic</code>)</li>
<li>I would personally also write separate docstrings for the series and dataframe case. This will give some duplication, but I think this gives room to simplify the docstring (or certainly for the simpler Series.replace case). (xref <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="168481230" data-permission-text="Title is private" data-url="https://github.com/pandas-dev/pandas/issues/13852" data-hovercard-type="issue" data-hovercard-url="/pandas-dev/pandas/issues/13852/hovercard" href="https://github.com/pandas-dev/pandas/issues/13852">#13852</a>)</li>
</ul>
<p dir="auto">See the tutorial docs (<a href="https://pandas.pydata.org/pandas-docs/stable/missing_data.html#replacing-generic-values" rel="nofollow">https://pandas.pydata.org/pandas-docs/stable/missing_data.html#replacing-generic-values</a>) with some actual examples.</p>
<p dir="auto">Underlying reason is that this function of course can do way too many things at the same time (or the same things in too many different ways) ... (orthogonal to this, we could maybe also think if certain functionality could be moved into its own function).</p>