如何在 pandas DataFrame 中选择名称以 X 开头的所有列
- 2025-03-18 08:55:00
- admin 原创
- 90
问题描述:
我有一个数据框:
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]
解决方案 1:
只需执行列表理解即可创建您的列:
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
解决方案 2:
既然 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
要仅选择所需的行(包含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
解决方案 3:
最简单的方法是直接在列名上使用 str,不需要pd.Series
df.loc[:,df.columns.str.startswith("foo")]
解决方案 4:
filter
您可以使用带有参数的方法like
:
df.filter(like='foo')
解决方案 5:
就我而言,我需要一个前缀列表
colsToScale=["production", "test", "development"]
dc[dc.columns[dc.columns.str.startswith(tuple(colsToScale))]]
解决方案 6:
您可以在此处尝试使用正则表达式来过滤以“foo”开头的列
df.filter(regex='^foo*')
如果你需要在列中包含字符串 foo,那么
df.filter(regex='foo*')
是合适的。
对于下一步,您可以使用
df[df.filter(regex='^foo*').values==1]
过滤掉'foo*'列的值之一为 1 的行。
解决方案 7:
根据@EdChum 的回答,您可以尝试以下解决方案:
df[df.columns[pd.Series(df.columns).str.contains("foo")]]
如果您要选择的列并非全部都以 开头,那么这将非常有用foo
。此方法选择包含子字符串的所有列foo
,并且可以将其放置在列名称的任何位置。
本质上,我.startswith()
用替换了.contains()
。
解决方案 8:
甚至您可以尝试使用多个前缀:
temp = df.loc[:, df.columns.str.startswith(('prefix1','prefix2','prefix3'))]
解决方案 9:
选择所需条目的另一种方法是使用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
则返回,因此不会选择该列。
如果你只是想返回所有包含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
解决方案 10:
我不喜欢其他解决方案要求我们引用 DataFrame 两次;如果您只有一个名为的框架df
,那么可能没问题,但情况往往并非如此(并且您的实际名称可能要长得多)。让我们滥用 pandas 索引功能来减少输入,并使代码更具可读性。没有什么可以阻止我们使用类似这样的方法:
df.loc[:, columns.startswith('foo')]
因为索引器可以是任意的Callable
。我们甚至可以将这个伪索引器分配给一个变量,并将其用于多个框架:
foo_columns = columns.startswith('foo')
df_1.loc[:, foo_columns]
df_2.loc[:, foo_columns]
我们甚至可以让它漂亮地打印出来:
> foo_columns
<function __main__.PandasIndexer:columns.str.startswith(pat='foo')()>
我们可以使用str
访问器的任何其他方法,例如columns.contains(r'bard', regex=True)
,同时获取有用的签名:
> columns.contains
<function __main__.PandasIndexer:columns.str.contains(pat, case=True, flags=0, na=None, regex=True)>
只需遵循这个简短的魔法代码:
from pandas import Series
from inspect import signature, Signature
class PandasIndexer:
def __init__(self, axis_name, accessor='str'):
"""
Args:
- axis_name: `columns` or `index`
- accessor: e.g. `str`, or `dt`
"""
self._axis_name = axis_name
self._accessor = accessor
self._dummy_series = Series(dtype=object)
def _create_indexer(self, attribute):
dummy_accessor = getattr(self._dummy_series, self._accessor)
dummy_attr = getattr(dummy_accessor, attribute)
name = f'PandasIndexer:{self._axis_name}.{self._accessor}.{attribute}'
def indexer_factory(*args, **kwargs):
def indexer(df):
axis = getattr(df, self._axis_name)
accessor = getattr(axis, self._accessor)
method = getattr(accessor, attribute)
return method(*args, **kwargs)
bound_arguments = signature(dummy_attr).bind(*args, **kwargs)
indexer.__qualname__ = (
name + str(bound_arguments).replace('<BoundArguments ', '')[:-1]
)
indexer.__signature__ = Signature()
return indexer
indexer_factory.__name__ = name
indexer_factory.__qualname__ = name
indexer_factory.__signature__ = signature(dummy_attr)
return indexer_factory
def __getattr__(self, attribute):
return self._create_indexer(attribute)
def __dir__(self):
"""Make it work with auto-complete in IPython"""
return dir(getattr(self._dummy_series, self._accessor))
columns = PandasIndexer('columns')
解决方案 11:
我的解决方案。这可能会降低性能:
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
解决方案 12:
一个选项是使用pyjanitor 的选择功能:
# pip install pyjanitor
import janitor
import pandas as pd
df.select(columns='foo*')
Out[32]:
foo.aa foo.fighters foo.bars foo.fox foo.manchu
0 1.0 0.0 0 2 NA
1 2.1 1.0 0 4 0
2 NaN NaN 0 1 0
3 4.7 0.0 0 0 0
4 5.6 0.0 0 0 0
5 6.8 0.0 1 5 0
要获得行和列的答案:
df.select(rows=df.select(columns='foo*').eq(1).any(axis=1), columns='foo*')
Out[36]:
foo.aa foo.fighters foo.bars foo.fox foo.manchu
0 1.0 0.0 0 2 NA
1 2.1 1.0 0 4 0
2 NaN NaN 0 1 0
5 6.8 0.0 1 5 0
当然,您只需将布尔值传递给.loc
:
df.loc[df.select(columns='foo*').eq(1).any(axis=1)]
Out[38]:
foo.aa foo.fighters foo.bars bar.baz foo.fox nas.foo foo.manchu
0 1.0 0.0 0 5.0 2 NA NA
1 2.1 1.0 0 5.0 4 0 0
2 NaN NaN 0 6.0 1 1 0
5 6.8 0.0 1 6.8 5 0 0
扫码咨询,免费领取项目管理大礼包!