pandas: la mejor manera de seleccionar todas las columnas cuyos nombres comienzan con X

104

Tengo un 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],})

Quiero seleccionar valores de 1 en las columnas que comienzan con foo.. ¿Existe una mejor manera de hacerlo que no sea:

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

Algo similar a escribir algo como:

df2= df[df.STARTS_WITH_FOO == 1]

La respuesta debería imprimir un 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
fuente

Respuestas:

151

Simplemente realice una comprensión de lista para crear sus columnas:

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

Otro método es crear una serie a partir de las columnas y usar el método str vectorizado 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 lograr lo que desea, debe agregar lo siguiente para filtrar los valores que no cumplen con sus ==1criterios:

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

Bien, después de ver lo que quieres, la respuesta complicada es 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
fuente
66

Ahora que los índices de pandas admiten operaciones de cadenas, posiblemente la forma más sencilla y mejor de seleccionar columnas que comienzan con 'foo' es simplemente:

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

Alternativamente, puede filtrar etiquetas de columna (o fila) con df.filter(). Para especificar una expresión regular para que coincida con los nombres que comienzan con 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 seleccionar solo las filas requeridas (que contienen a 1) y las columnas, puede usar loc, seleccionar las columnas usando filter(o cualquier otro método) y las filas 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
fuente
7

La forma más sencilla es usar str directamente en los nombres de las columnas, no es necesario pd.Series

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

Mohammed Omar Elsiddieg
fuente
1

Según la respuesta de @ EdChum, puede probar la siguiente solución:

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

Esto será realmente útil en caso de que no todas las columnas que desea seleccionar comiencen foo. Este método selecciona todas las columnas que contienen la subcadena fooy podría colocarse en cualquier punto del nombre de una columna.

En esencia, reemplacé .startswith()con .contains().

Arturo Sbr
fuente
0

Mi solución. Puede ser más lento en rendimiento:

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
fuente
0

Otra opción para la selección de las entradas deseadas es utilizar map:

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

que le da todas las columnas para las filas que contienen un 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

La selección de fila se realiza mediante

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

como en la respuesta de @ ajcr que le da:

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

lo que significa que la fila 3y 4no contienen un 1y no se seleccionarán.

La selección de las columnas se realiza mediante indexación booleana como esta:

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

En el ejemplo anterior, esto devuelve

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

Entonces, si una columna no comienza con foo, Falsese devuelve y, por lo tanto, la columna no se selecciona.

Si solo desea devolver todas las filas que contienen un 1, como sugiere el resultado deseado, simplemente puede hacer

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

que regresa

   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
fuente
0

Puede probar la expresión regular aquí para filtrar las columnas que comienzan con "foo"

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

Si necesita tener la cadena foo en su columna, entonces

df.filter(regex='foo*')

sería apropiado.

Para el siguiente paso, puede usar

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

para filtrar las filas donde uno de los valores de la columna 'foo *' es 1.

Ricky
fuente
0

En mi caso necesitaba una lista de prefijos

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