42

I want to use a boolean to select the columns with more than 4000 entries from a dataframe comb which has over 1,000 columns. This expression gives me a Boolean (True/False) result:

criteria = comb.ix[:,'c_0327':].count()>4000

I want to use it to select only the True columns to a new Dataframe.
The following just gives me "Unalignable boolean Series key provided":

comb.loc[criteria,]

I also tried:

comb.ix[:, comb.ix[:,'c_0327':].count()>4000] 

Similar to this question answer dataframe boolean selection along columns instead of row but that gives me the same error: "Unalignable boolean Series key provided"

comb.ix[:,'c_0327':].count()>4000

yields:

c_0327    False
c_0328    False
c_0329    False
c_0330    False
c_0331    False
c_0332    False
c_0333    False
c_0334    False
c_0335    False
c_0336    False
c_0337     True
c_0338    False
.....
smci
  • 32,567
  • 20
  • 113
  • 146
dartdog
  • 10,432
  • 21
  • 72
  • 121

7 Answers7

44

What is returned is a Series with the column names as the index and the boolean values as the row values.

I think actually you want:

this should now work:

comb[criteria.index[criteria]]

Basically this uses the index values from criteria and the boolean values to mask them, this will return an array of column names, we can use this to select the columns of interest from the orig df.

dartdog
  • 10,432
  • 21
  • 72
  • 121
EdChum
  • 376,765
  • 198
  • 813
  • 562
  • 3
    I am surprised to see, there is no shorter (more straightforward ) way of doing this. – Areza Jan 23 '19 at 09:56
  • 2
    There is, this answer is 5 years old and outdated. See [my answer](https://stackoverflow.com/a/57577991/11715606) below for the straightforward way – johnDanger Apr 16 '20 at 22:00
  • @johnDanger Nice answer, but I'm not sure I'd agree that going from "`m[f]` for row filtering" to "`m.loc[:,f]` for column filtering" is straightforward. – c z Mar 09 '21 at 11:37
  • 1
    The "straightforwardness" in @johnDanger's answer is that you only need `criteria` once, and hence you do not need to define the variable separately (but can just use the expression itself ion `m.loc[:, expression_of_criteria_itself]`). – chickenNinja123 Jun 10 '21 at 09:13
32

In pandas 0.25:

comb.loc[:, criteria]

Returns a DataFrame with columns selected by the Boolean list or Series.

For multiple criteria:

comb.loc[:, criteria1 & criteria2]

And for selecting rows with an index criteria:

comb[criteria]

Note: The bit-wise operator & is required (not and). See Logical operators for boolean indexing in Pandas.

Other Note: If the criteria is an expression (e.g., comb.columnX > 3), and multiple criteria are used, remember to enclose each expression in parentheses! This is because &, | have higher precedence than >, ==, ect. (whereas and, or are lower precedence).

johnDanger
  • 1,990
  • 16
  • 22
7

You can also use:

# To filter columns (assuming criteria length is equal to the number of columns of comb)
comb.ix[:, criteria]
comb.iloc[:, criteria.values]

# To filter rows (assuming criteria length is equal to the number of rows of comb)
comb[criteria]
jberrio
  • 972
  • 2
  • 9
  • 20
Yohan Obadia
  • 2,552
  • 2
  • 24
  • 31
  • 2
    The first answer looks the most elegant for masked column selection. The only trick is that one needs to do `comb.iloc[:, criteria.values]`, as a series is not a valid argument into `iloc` slicing of this type – Mischa Lisovyi Aug 17 '18 at 13:47
  • I should have specified that I expected criteria to be a boolean list. Good catch. – Yohan Obadia Aug 24 '18 at 09:02
3

You can pass a boolean array to loc to indicate which columns should be kept and which not.

For example,

>>> df
    A   B   C   D    E
0  73  15  55  33  foo
1  63  64  11  11  bar
2  56  72  57  55  foo

>>> df.loc[:, [True, True, False, False, True]]
    A   B    E
0  73  15  foo
1  63  64  bar
2  56  72  foo
Giorgos Myrianthous
  • 36,235
  • 20
  • 134
  • 156
1

I'm using this, it's cleaner

comb.values[:,criteria]

credit: https://stackoverflow.com/a/43291257/815677

Krishna
  • 415
  • 1
  • 4
  • 11
-1

Another solution is to transpose comb to make its columns act as its index, then transpose on the resulting subset:

comb.T[criteria].T

Again, not particularly elegant, but at least shorter/less repetitive than the leading solution.

Seth Johnson
  • 14,762
  • 6
  • 59
  • 85
  • 1
    There are already proposed solutions which are shorter/less repetitive than the accepted solution but also more elegant. – Jean Paul Oct 22 '20 at 15:34
  • 1
    Seconding @JeanPaul... best to avoid transposes – william_grisaitis Mar 15 '22 at 20:05
  • @william_grisaitis What's the problem with transposes? Are they memory/compute intensive, or do you just find the `T` aesthetically displeasing, or ...? – Seth Johnson Mar 29 '22 at 20:46
  • 1
    @SethJohnson they can be really slow. not an expert, but that's my experience. if i had to guess, it's reallocating memory under the hood for everything (not a zero-copy operation). – william_grisaitis Mar 30 '22 at 21:24
-1

Another approach is to use Python's built-in filter function:

def satisfies_criteria(column):
    return comb[column].count() > 4000


cols = filter(satisfies_criteria, df.columns)
df[cols]
william_grisaitis
  • 5,170
  • 3
  • 33
  • 40