title: Pandas: Knowing when an operation affects the original dataframe
body: <p>I love pandas and have been using it for years and feel pretty confident I have a good handle on how to subset dataframes and deal with views vs copies appropriately (though I use a lot of assertions to be sure). I also know that there have been tons of questions about SettingWithCopyWarning, e.g. <a href="https://stackoverflow.com/questions/20625582/how-to-deal-with-settingwithcopywarning-in-pandas">How to deal with SettingWithCopyWarning in Pandas?</a>
  and some great recent guides on wrapping your head around when it happens, e.g. <a href="https://www.dataquest.io/blog/settingwithcopywarning/" rel="noreferrer">Understanding SettingWithCopyWarning in pandas</a>. </p>
  
  <p>But I also know specific things like the quote from <a href="https://stackoverflow.com/a/17961468/8022335">this answer</a> are no longer in the most recent docs (<code>0.22.0</code>) and that many things have been deprecated over the years (leading to some inappropriate old SO answers), and that things are <a href="https://stackoverflow.com/a/34908742/8022335">continuing to change</a>. </p>
  
  <p>Recently after teaching pandas to complete newcomers with very basic general Python knowledge about things like avoiding chained-indexing (and using <code>.iloc</code>/<code>.loc</code>), I've still struggled to provide <em>general rules of thumb</em> to know when it's important to pay attention to the <code>SettingWithCopyWarning</code> (e.g. when it's safe to ignore it). </p>
  
  <p>I've personally found that the specific pattern of subsetting a dataframe according so some rule (e.g. slicing or boolean operation) and then modifying that subset, <em>independent of the original dataframe</em>, is a much more common operation than the docs suggest. In this situation we want to <em>modify the copy not the original</em> and the warning is confusing/scary to newcomers.</p>
  
  <p>I know it's not trivial to know ahead of time  when a view vs a copy is returned, e.g.<br>
  <a href="https://stackoverflow.com/questions/23296282/what-rules-does-pandas-use-to-generate-a-view-vs-a-copy">What rules does Pandas use to generate a view vs a copy?</a><br>
  <a href="https://stackoverflow.com/questions/26879073/checking-whether-data-frame-is-copy-or-view-in-pandas">Checking whether data frame is copy or view in Pandas</a></p>
  
  <p>So instead I'm looking for the answer to a more general (beginner friendly) question: <strong>when does performing an operation on a subsetted dataframe affect the original dataframe from which it was created, and when are they independent?</strong>. </p>
  
  <p>I've created some cases below that I think seem reasonable, but I'm not sure if there's a "gotcha" I'm missing or if there's any easier way to think/check this. I was hoping someone could confirm that my intuitions about the following use cases are correct as the pertain to my question above.</p>
  
  <pre><code>import pandas as pd
  df1 = pd.DataFrame({'A':[2,4,6,8,10],'B':[1,3,5,7,9],'C':[10,20,30,40,50]})
  </code></pre>
  
  <p>1) Warning: No<br>
     Original changed: No  </p>
  
  <pre><code># df1 will be unaffected because we use .copy() method explicitly 
  df2 = df1.copy()
  #
  # Reference: docs
  df2.iloc[0,1] = 100
  </code></pre>
  
  <p>2) Warning: Yes (I don't really understood why)<br>
     Original changed: No  </p>
  
  <pre><code># df1 will be unaffected because .query() always returns a copy
  #
  # Reference:
  # https://stackoverflow.com/a/23296545/8022335
  df2 = df1.query('A &lt; 10')
  df2.iloc[0,1] = 100
  </code></pre>
  
  <p>3) Warning: Yes<br>
     Original changed: No</p>
  
  <pre><code># df1 will be unaffected because boolean indexing with .loc
  # always returns a copy
  #
  # Reference:
  # https://stackoverflow.com/a/17961468/8022335
  df2 = df1.loc[df1['A'] &lt; 10,:]
  df2.iloc[0,1] = 100
  </code></pre>
  
  <p>4) Warning: No<br>
     Original changed: No</p>
  
  <pre><code># df1 will be unaffected because list indexing with .loc (or .iloc)
  # always returns a copy
  #
  # Reference:
  # Same as 4)
  df2 = df1.loc[[0,3,4],:]
  df2.iloc[0,1] = 100
  </code></pre>
  
  <p>5) Warning: No<br>
     Original changed: Yes (confusing to newcomers but makes sense)  </p>
  
  <pre><code># df1 will be affected because scalar/slice indexing with .iloc/.loc
  # always references the original dataframe, but may sometimes 
  # provide a view and sometimes provide a copy
  #
  # Reference: docs
  df2 = df1.loc[:10,:]
  df2.iloc[0,1] = 100
  </code></pre>
  
  <p><strong>tl;dr</strong>
  When creating a new dataframe from the original, changing the new dataframe:<br>
  Will change the original when <strong>scalar/slice indexing with .loc/.iloc is used to create the new dataframe</strong>.<br>
  Will <em>not</em> change the original when <strong>boolean indexing with .loc, <code>.query()</code>, or <code>.copy()</code> is used to create the new dataframe</strong></p>