Best way to subset a pandas dataframe

11,038

Solution 1

Consider the following sample DF:

In [307]: df
Out[307]:
  sex  age     name
0   M   40      Max
1   F   35     Anna
2   M   29      Joe
3   F   18    Maria
4   F   23  Natalie

There are quite a few good reasons to prefer .query() method.

  • it might be much shorter and cleaner compared to boolean indexing:

    In [308]: df.query("20 <= age <= 30 and sex=='F'")
    Out[308]:
      sex  age     name
    4   F   23  Natalie
    
    In [309]: df[(df['age']>=20) & (df['age']<=30) & (df['sex']=='F')]
    Out[309]:
      sex  age     name
    4   F   23  Natalie
    
  • you can prepare conditions (queries) programmatically:

    In [315]: conditions = {'name':'Joe', 'sex':'M'}
    
    In [316]: q = ' and '.join(['{}=="{}"'.format(k,v) for k,v in conditions.items()])
    
    In [317]: q
    Out[317]: 'name=="Joe" and sex=="M"'
    
    In [318]: df.query(q)
    Out[318]:
      sex  age name
    2   M   29  Joe
    

PS there are also some disadvantages:

  • we can't use .query() method for columns containing spaces or columns that consist only from digits
  • not all functions can be applied or in some cases we have to use engine='python' instead of default engine='numexpr' (which is faster)

NOTE: Jeff (one of the main Pandas contributors and a member of Pandas core team) once said:

Note that in reality .query is just a nice-to-have interface, in fact it has very specific guarantees, meaning its meant to parse like a query language, and not a fully general interface.

Solution 2

Some other interesting usages in the documentation.

Reuseable

A use case for query() is when you have a collection of DataFrame objects that have a subset of column names (or index levels/names) in common. You can pass the same query to both frames without having to specify which frame you’re interested in querying -- (Source)

Example:

dfA = pd.DataFrame([[1,2,3], [4,5,6]], columns=["X", "Y", "Z"])
dfB = pd.DataFrame([[1,3,3], [4,1,6]], columns=["X", "Y", "Z"])
q = "(X > 3) & (Y < 10)"

print(dfA.query(q))
print(dfB.query(q))

   X  Y  Z
1  4  5  6
   X  Y  Z
1  4  1  6

More flexible syntax

df.query('a < b and b < c')  # understand a bit more English

Support in operator and not in (alternative to isin)

df.query('a in [3, 4, 5]') # select rows whose value of column a is in [2, 3, 4]

Special usage of == and != (similar to in/not in)

df.query('a == [1, 3, 5]') # select whose value of column a is in [1, 3, 5]
# equivalent to df.query('a in [1, 3, 5]')
Share:
11,038

Related videos on Youtube

Pierre-Eric Garcia
Author by

Pierre-Eric Garcia

Computer science &amp; Rap music 💘

Updated on September 15, 2022

Comments

  • Pierre-Eric Garcia
    Pierre-Eric Garcia over 1 year

    Hey I'm new to Pandas and I just came across df.query().

    Why people would use df.query() when you can directly filter your Dataframes using brackets notation ? The official pandas tutorial also seems to prefer the latter approach.

    With brackets notation :

    df[df['age'] <= 21]
    

    With pandas query method :

    df.query('age <= 21')
    

    Besides some of the stylistic or flexibility differences that have been mentioned, is one canonically preferred - namely for performance of operations on large dataframes?

    • Willem Van Onsem
      Willem Van Onsem over 6 years
      Because some people can not easily construct the correct filtering and aggregates, but know how to write something like SQL?
    • kindall
      kindall over 6 years
      Because you might want to use a query obtained from a user or read from a file, or generated programmatically in some way.
    • ayhan
      ayhan over 6 years
      When they were first released they had some performance benefits thanks to numexpr but I don't think they outperform boolean indexing now.
  • ayhan
    ayhan over 6 years
    Maybe it is also worth noting Jeff's comment here: "Note that in reality .query is just a nice-to-have interface, in fact it has very specific guarantees, meaning its meant to parse like a query language, and not a fully general interface."