pandas: лучший способ выбрать все столбцы, имена которых начинаются с X

106

У меня есть 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],})

Я хочу выбрать значения 1 в столбцах, начиная с foo.. Есть ли лучший способ сделать это, кроме:

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

Что-то вроде написания чего-то вроде:

df2= df[df.STARTS_WITH_FOO == 1]

Ответ должен распечатать DataFrame следующим образом:

   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
источник

Ответы:

155

Просто выполните понимание списка, чтобы создать свои столбцы:

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

Другой метод - создать серию из столбцов и использовать векторизованный метод str 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

Чтобы добиться желаемого, вам необходимо добавить следующее, чтобы отфильтровать значения, не соответствующие вашим ==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

РЕДАКТИРОВАТЬ

Хорошо, увидев, что вы хотите, запутанный ответ таков:

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
источник
66

Теперь, когда индексы pandas поддерживают строковые операции, возможно, самый простой и лучший способ выбрать столбцы, начинающиеся с 'foo', - это просто:

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

Кроме того, вы можете отфильтровать метки столбцов (или строк) с помощью df.filter(). Чтобы указать регулярное выражение для соответствия именам, начинающимся с 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

Чтобы выбрать только необходимые строки (содержащие a 1) и столбцы, вы можете использовать loc, выбирая столбцы с помощью filter(или любым другим способом), а строки с помощью 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
Алекс Райли
источник
Это ответ, за которым я пришел, он соответствует названию вопроса. То, что на самом деле хотел OP, было больше похоже на «Лучший способ выбрать строки с помощью фильтра, основанного только на столбцах, начинающихся с x».
scign
7

Самый простой способ - использовать str непосредственно в именах столбцов, нет необходимости в pd.Series

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

Мохаммед Омар Эльсиддиг
источник
1

Основываясь на ответе @ EdChum, вы можете попробовать следующее решение:

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

Это будет действительно полезно, если не все столбцы, которые вы хотите выбрать, начинаются с foo. Этот метод выбирает все столбцы, содержащие подстроку, fooи ее можно поместить в любую точку имени столбца.

По сути, я заменил .startswith()на .contains().

Артуро Сбр
источник
0

Мое решение. Это может быть медленнее по производительности:

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
Робби Лю
источник
0

Другой вариант выбора нужных записей - использовать map:

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

который дает вам все столбцы для строк, содержащих 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

Выбор строки выполняется

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

как в ответе @ ajcr, который дает вам:

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

это означает , что ряд 3и 4не содержат 1и не будет выбран.

Выбор столбцов осуществляется с помощью булевой индексации , как это:

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

В приведенном выше примере это возвращает

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

Таким образом, если столбец не начинается с foo, Falseвозвращается, и поэтому столбец не выбран.

Если вы просто хотите вернуть все строки, содержащие a 1- как предполагает ваш желаемый результат - вы можете просто сделать

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

который возвращается

   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
источник
0

Вы можете попробовать регулярное выражение здесь, чтобы отфильтровать столбцы, начинающиеся с "foo"

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

Если вам нужно, чтобы в столбце была строка foo, тогда

df.filter(regex='foo*')

было бы уместно.

Для следующего шага вы можете использовать

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

чтобы отфильтровать строки, в которых одно из значений столбца 'foo *' равно 1.

Рики
источник
0

В моем случае мне понадобился список префиксов

colsToScale=["production", "test", "development"]
dc[dc.columns[dc.columns.str.startswith(tuple(colsToScale))]]
Флавио Соуза
источник