title: How to replace a string in a pandas multiindex?
body: <p>I have a dataframe with a large multiindex, sourced from a vast number of csv files. Some of those files have errors in the various labels, ie. "window" is missspelled as "winZZw", which then causes problems when I select all windows with <code>df.xs('window', level='middle', axis=1)</code>.</p>
  
  <p>So I need a way to simply replace <code>winZZw</code> with <code>window</code>.</p>
  
  <p>Here's a very minimal sample df: (lets assume the data and the <code>'roof', 'window'…</code> strings come from some convoluted text reader)</p>
  
  <pre><code>header = pd.MultiIndex.from_product(['roof', 'window', 'basement'], names = ['top', 'middle', 'bottom'])
  dates = pd.date_range('01/01/2000','01/12/2010', freq='MS')
  data = np.random.randn(len(dates))
  df = pd.DataFrame(data, index=dates, columns=header)
  header2 = pd.MultiIndex.from_product(['roof', 'winZZw', 'basement'], names = ['top', 'middle', 'bottom'])
  data = 3*(np.random.randn(len(dates)))
  df2 = pd.DataFrame(data, index=dates, columns=header2)
  df = pd.concat([df, df2], axis=1)
  header3 = pd.MultiIndex.from_product(['roof', 'door', 'basement'], names = ['top', 'middle', 'bottom'])
  data = 2*(np.random.randn(len(dates)))
  df3 = pd.DataFrame(data, index=dates, columns=header3)
  df = pd.concat([df, df3], axis=1)
  </code></pre>
  
  <p>Now I want to <code>xs</code> a new dataframe for all the <em>houses</em> that have a window at their middle level: <code>windf = df.xs('window', level='middle', axis=1)</code> </p>
  
  <p>But this obviously misses the misspelled winZZw.</p>
  
  <p><strong>So, how I replace <code>winZZw</code> with <code>window</code>?</strong></p>
  
  <p>The only way I found was to use <a href="https://pandas.pydata.org/pandas-docs/stable/generated/pandas.MultiIndex.set_levels.html?highlight=index%20set_levels#pandas.MultiIndex.set_levels" rel="nofollow noreferrer">set_levels</a>, but if I understood that correctly, I need to feed it the whole level, ie </p>
  
  <pre><code>df.columns.set_levels([u'window',u'window', u'door'], level='middle',inplace=True)
  </code></pre>
  
  <p>but this has two issues:</p>
  
  <ul>
  <li>I need to pass it the whole index, which is easy in this sample, but impossible/stupid for a thousand column df with hundreds of labels.</li>
  <li>It seems to need the list backwards (now, my first entry in the df has door in the middle, instead of the window it had). That can probably be fixed, but it seems weird</li>
  </ul>
  
  <p>I can work around these issues by <code>xs</code>ing a new df of only <code>winZZw</code>s, and then setting the levels with <code>set_levels(df.shape[1]*[u'window'], level='middle')</code> and then concatting it together again, but I'd like to have something more straightforward analog to <code>str.replace('winZZw', 'window')</code>, but I can't figure out how.</p>