title: pandas .at versus .loc
body: <p>I've been exploring how to optimize my code and ran across <code>pandas</code> <code>.at</code> method. Per the <a href="http://pandas.pydata.org/pandas-docs/version/0.17.0/generated/pandas.DataFrame.at.html#pandas.DataFrame.at" rel="noreferrer">documentation</a></p>
  <blockquote>
  <p>Fast label-based scalar accessor</p>
  <p>Similarly to loc, at provides label based scalar lookups. You can also set using these indexers.</p>
  </blockquote>
  <p>So I ran some samples:</p>
  <h1>Setup</h1>
  <pre><code>import pandas as pd
  import numpy as np
  from string import letters, lowercase, uppercase
  
  lt = list(letters)
  lc = list(lowercase)
  uc = list(uppercase)
  
  def gdf(rows, cols, seed=None):
      &quot;&quot;&quot;rows and cols are what you'd pass
      to pd.MultiIndex.from_product()&quot;&quot;&quot;
      gmi = pd.MultiIndex.from_product
      df = pd.DataFrame(index=gmi(rows), columns=gmi(cols))
      np.random.seed(seed)
      df.iloc[:, :] = np.random.rand(*df.shape)
      return df
  
  seed = [3, 1415]
  df = gdf([lc, uc], [lc, uc], seed)
  
  print df.head().T.head().T
  </code></pre>
  <p><code>df</code> looks like:</p>
  <pre><code>            a                                        
              A         B         C         D         E
  a A  0.444939  0.407554  0.460148  0.465239  0.462691
    B  0.032746  0.485650  0.503892  0.351520  0.061569
    C  0.777350  0.047677  0.250667  0.602878  0.570528
    D  0.927783  0.653868  0.381103  0.959544  0.033253
    E  0.191985  0.304597  0.195106  0.370921  0.631576
  </code></pre>
  <p>Lets use <code>.at</code> and <code>.loc</code> and ensure I get the same thing</p>
  <pre><code>print &quot;using .loc&quot;, df.loc[('a', 'A'), ('c', 'C')]
  print &quot;using .at &quot;, df.at[('a', 'A'), ('c', 'C')]
  
  using .loc 0.37374090276
  using .at  0.37374090276
  </code></pre>
  <p>Test speed using <code>.loc</code></p>
  <pre><code>%%timeit
  df.loc[('a', 'A'), ('c', 'C')]
  
  10000 loops, best of 3: 180 µs per loop
  </code></pre>
  <p>Test speed using <code>.at</code></p>
  <pre><code>%%timeit
  df.at[('a', 'A'), ('c', 'C')]
  
  The slowest run took 6.11 times longer than the fastest. This could mean that an intermediate result is being cached.
  100000 loops, best of 3: 8 µs per loop
  </code></pre>
  <p>This looks to be a huge speed increase.  Even at the caching stage <code>6.11 * 8</code> is a lot faster than <code>180</code></p>
  <h1>Question</h1>
  <p>What are the limitations of <code>.at</code>?  I'm motivated to use it.  The documentation says it's similar to <code>.loc</code> but it doesn't behave similarly.  Example:</p>
  <pre><code># small df
  sdf = gdf([lc[:2]], [uc[:2]], seed)
  
  print sdf.loc[:, :]
  
            A         B
  a  0.444939  0.407554
  b  0.460148  0.465239
  </code></pre>
  <p>where as <code>print sdf.at[:, :]</code> results in <code>TypeError: unhashable type</code></p>
  <p>So obviously not the same even if the intent is to be similar.</p>
  <p>That said, who can provide guidance on what can and cannot be done with the <code>.at</code> method?</p>