title: Pandas mask / where methods versus NumPy np.where
body: <p>I often use Pandas <a href="https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.mask.html" rel="noreferrer"><code>mask</code></a> and <a href="https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.where.html" rel="noreferrer"><code>where</code></a> methods for cleaner logic when updating values in a series conditionally. However, for relatively performance-critical code I notice a significant performance drop relative to <a href="https://docs.scipy.org/doc/numpy/reference/generated/numpy.where.html" rel="noreferrer"><code>numpy.where</code></a>.</p>
  
  <p>While I'm happy to accept this for specific cases, I'm interested to know:</p>
  
  <ol>
  <li>Do Pandas <code>mask</code> / <code>where</code> methods offer any additional functionality, <em>apart from</em> <code>inplace</code> / <code>errors</code> / <code>try-cast</code> parameters? I understand those 3 parameters but rarely use them. For example, I have no idea what the <code>level</code> parameter refers to.</li>
  <li>Is there any non-trivial counter-example where <code>mask</code> / <code>where</code> outperforms <code>numpy.where</code>? If such an example exists, it could influence how I choose appropriate methods going forwards.</li>
  </ol>
  
  <p>For reference, here's some benchmarking on Pandas 0.19.2 / Python 3.6.0:</p>
  
  <pre><code>np.random.seed(0)
  
  n = 10000000
  df = pd.DataFrame(np.random.random(n))
  
  assert (df[0].mask(df[0] &gt; 0.5, 1).values == np.where(df[0] &gt; 0.5, 1, df[0])).all()
  
  %timeit df[0].mask(df[0] &gt; 0.5, 1)       # 145 ms per loop
  %timeit np.where(df[0] &gt; 0.5, 1, df[0])  # 113 ms per loop
  </code></pre>
  
  <p>The performance appears to diverge <em>further</em> for non-scalar values:</p>
  
  <pre><code>%timeit df[0].mask(df[0] &gt; 0.5, df[0]*2)       # 338 ms per loop
  %timeit np.where(df[0] &gt; 0.5, df[0]*2, df[0])  # 153 ms per loop
  </code></pre>