pandas: melhor maneira de selecionar todas as colunas cujos nomes começam com X

104

Eu tenho um DataFrame:

import pandas as pd
import numpy as np

df = pd.DataFrame({'foo.aa': [1, 2.1, np.nan, 4.7, 5.6, 6.8],
                   'foo.fighters': [0, 1, np.nan, 0, 0, 0],
                   'foo.bars': [0, 0, 0, 0, 0, 1],
                   'bar.baz': [5, 5, 6, 5, 5.6, 6.8],
                   'foo.fox': [2, 4, 1, 0, 0, 5],
                   'nas.foo': ['NA', 0, 1, 0, 0, 0],
                   'foo.manchu': ['NA', 0, 0, 0, 0, 0],})

Quero selecionar valores de 1 em colunas começando com foo.. Existe uma maneira melhor de fazer isso do que:

df2 = df[(df['foo.aa'] == 1)|
(df['foo.fighters'] == 1)|
(df['foo.bars'] == 1)|
(df['foo.fox'] == 1)|
(df['foo.manchu'] == 1)
]

Algo semelhante a escrever algo como:

df2= df[df.STARTS_WITH_FOO == 1]

A resposta deve imprimir um DataFrame como este:

   bar.baz  foo.aa  foo.bars  foo.fighters  foo.fox foo.manchu nas.foo
0      5.0     1.0         0             0        2         NA      NA
1      5.0     2.1         0             1        4          0       0
2      6.0     NaN         0           NaN        1          0       1
5      6.8     6.8         1             0        5          0       0

[4 rows x 7 columns]
ccsv
fonte

Respostas:

151

Basta realizar uma compreensão de lista para criar suas colunas:

In [28]:

filter_col = [col for col in df if col.startswith('foo')]
filter_col
Out[28]:
['foo.aa', 'foo.bars', 'foo.fighters', 'foo.fox', 'foo.manchu']
In [29]:

df[filter_col]
Out[29]:
   foo.aa  foo.bars  foo.fighters  foo.fox foo.manchu
0     1.0         0             0        2         NA
1     2.1         0             1        4          0
2     NaN         0           NaN        1          0
3     4.7         0             0        0          0
4     5.6         0             0        0          0
5     6.8         1             0        5          0

Outro método é criar uma série a partir das colunas e usar o método str vetorizado startswith:

In [33]:

df[df.columns[pd.Series(df.columns).str.startswith('foo')]]
Out[33]:
   foo.aa  foo.bars  foo.fighters  foo.fox foo.manchu
0     1.0         0             0        2         NA
1     2.1         0             1        4          0
2     NaN         0           NaN        1          0
3     4.7         0             0        0          0
4     5.6         0             0        0          0
5     6.8         1             0        5          0

Para alcançar o que deseja, você precisa adicionar o seguinte para filtrar os valores que não atendem aos seus ==1critérios:

In [36]:

df[df[df.columns[pd.Series(df.columns).str.startswith('foo')]]==1]
Out[36]:
   bar.baz  foo.aa  foo.bars  foo.fighters  foo.fox foo.manchu nas.foo
0      NaN       1       NaN           NaN      NaN        NaN     NaN
1      NaN     NaN       NaN             1      NaN        NaN     NaN
2      NaN     NaN       NaN           NaN        1        NaN     NaN
3      NaN     NaN       NaN           NaN      NaN        NaN     NaN
4      NaN     NaN       NaN           NaN      NaN        NaN     NaN
5      NaN     NaN         1           NaN      NaN        NaN     NaN

EDITAR

OK, depois de ver o que você quer, a resposta complicada é esta:

In [72]:

df.loc[df[df[df.columns[pd.Series(df.columns).str.startswith('foo')]] == 1].dropna(how='all', axis=0).index]
Out[72]:
   bar.baz  foo.aa  foo.bars  foo.fighters  foo.fox foo.manchu nas.foo
0      5.0     1.0         0             0        2         NA      NA
1      5.0     2.1         0             1        4          0       0
2      6.0     NaN         0           NaN        1          0       1
5      6.8     6.8         1             0        5          0       0
EdChum
fonte
66

Agora que os índices do pandas suportam operações de string, sem dúvida a melhor e mais simples maneira de selecionar colunas começando com 'foo' é apenas:

df.loc[:, df.columns.str.startswith('foo')]

Como alternativa, você pode filtrar rótulos de coluna (ou linha) com df.filter(). Para especificar uma expressão regular para corresponder aos nomes que começam com foo.:

>>> df.filter(regex=r'^foo\.', axis=1)
   foo.aa  foo.bars  foo.fighters  foo.fox foo.manchu
0     1.0         0             0        2         NA
1     2.1         0             1        4          0
2     NaN         0           NaN        1          0
3     4.7         0             0        0          0
4     5.6         0             0        0          0
5     6.8         1             0        5          0

Para selecionar apenas as linhas necessárias (contendo um 1) e as colunas, você pode usar loc, selecionando as colunas usando filter(ou qualquer outro método) e as linhas usando any:

>>> df.loc[(df == 1).any(axis=1), df.filter(regex=r'^foo\.', axis=1).columns]
   foo.aa  foo.bars  foo.fighters  foo.fox foo.manchu
0     1.0         0             0        2         NA
1     2.1         0             1        4          0
2     NaN         0           NaN        1          0
5     6.8         1             0        5          0
Alex Riley
fonte
7

A maneira mais simples é usar str diretamente nos nomes das colunas, não há necessidade de pd.Series

df.loc[:,df.columns.str.startswith("foo")]

Mohammed Omar Elsiddieg
fonte
1

Com base na resposta de @EdChum, você pode tentar a seguinte solução:

df[df.columns[pd.Series(df.columns).str.contains("foo")]]

Isso será muito útil caso nem todas as colunas que você deseja selecionar comecem foo. Este método seleciona todas as colunas que contêm a substring fooe pode ser colocado em qualquer ponto do nome de uma coluna.

Em essência, eu substituí .startswith()com .contains().

Arturo Sbr
fonte
0

Minha solução. Pode ser mais lento no desempenho:

a = pd.concat(df[df[c] == 1] for c in df.columns if c.startswith('foo'))
a.sort_index()


   bar.baz  foo.aa  foo.bars  foo.fighters  foo.fox foo.manchu nas.foo
0      5.0     1.0         0             0        2         NA      NA
1      5.0     2.1         0             1        4          0       0
2      6.0     NaN         0           NaN        1          0       1
5      6.8     6.8         1             0        5          0       0
Robbie Liu
fonte
0

Outra opção para a seleção das entradas desejadas é utilizar map:

df.loc[(df == 1).any(axis=1), df.columns.map(lambda x: x.startswith('foo'))]

que fornece todas as colunas para linhas que contêm 1:

   foo.aa  foo.bars  foo.fighters  foo.fox foo.manchu
0     1.0         0             0        2         NA
1     2.1         0             1        4          0
2     NaN         0           NaN        1          0
5     6.8         1             0        5          0

A seleção da linha é feita por

(df == 1).any(axis=1)

como na resposta de @ ajcr que dá a você:

0     True
1     True
2     True
3    False
4    False
5     True
dtype: bool

o que significa que a linha 3e 4não contém um 1e não será selecionado.

A seleção das colunas é feita usando indexação booleana como esta:

df.columns.map(lambda x: x.startswith('foo'))

No exemplo acima, isso retorna

array([False,  True,  True,  True,  True,  True, False], dtype=bool)

Portanto, se uma coluna não começar com foo, Falseserá retornado e a coluna não será selecionada.

Se você deseja apenas retornar todas as linhas que contêm um 1- como sua saída desejada sugere - você pode simplesmente fazer

df.loc[(df == 1).any(axis=1)]

que retorna

   bar.baz  foo.aa  foo.bars  foo.fighters  foo.fox foo.manchu nas.foo
0      5.0     1.0         0             0        2         NA      NA
1      5.0     2.1         0             1        4          0       0
2      6.0     NaN         0           NaN        1          0       1
5      6.8     6.8         1             0        5          0       0
Cleb
fonte
0

Você pode tentar o regex aqui para filtrar as colunas que começam com "foo"

df.filter(regex='^foo*')

Se você precisa ter a string foo em sua coluna, então

df.filter(regex='foo*')

seria apropriado.

Para a próxima etapa, você pode usar

df[df.filter(regex='^foo*').values==1]

para filtrar as linhas em que um dos valores da coluna 'foo *' é 1.

Ricky
fonte
0

No meu caso, eu precisava de uma lista de prefixos

colsToScale=["production", "test", "development"]
dc[dc.columns[dc.columns.str.startswith(tuple(colsToScale))]]
Flavio Sousa
fonte