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

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 columnas que comiencen con foo. . ¿Hay 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 parecido a escribir algo como:

 df2= df[df.STARTS_WITH_FOO == 1] 

La respuesta debe 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] 

Simplemente realice una lista de comprensión 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 startswith que 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 criterios ==1 :

 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 después de ver lo que quiere, la respuesta enrevesada es la siguiente:

 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 

Ahora que los índices de pandas son compatibles con las operaciones de cadena, podría decirse que 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 las tags de columna (o fila) con df.filter() . Para especificar una expresión regular 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 un 1 ) y las columnas, puede usar loc , seleccionando las columnas usando el 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 

Mi solución. Puede ser más lento en el 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 

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

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

lo 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 la fila se realiza por

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

como en la respuesta de @ajcr que te da:

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

lo que significa que las filas 3 y 4 no contienen un 1 y no se seleccionarán.

La selección de las columnas se realiza utilizando una 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 , se devuelve False y, por lo tanto, la columna no está seleccionada.

Si solo desea devolver todas las filas que contienen un 1 , como sugiere su salida deseada, simplemente puede hacer

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

que devuelve

  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