如何通过正则表达式从数据框中选择列


75

我在python大熊猫中有一个数据框。数据框的结构如下:

   a    b    c    d1   d2   d3 
   10   14   12   44  45    78

我想选择以d开头的列。有没有一种简单的方法可以在python中实现这一点。

Answers:


140

您可以使用DataFrame.filter这种方式:

import pandas as pd

df = pd.DataFrame(np.array([[2,4,4],[4,3,3],[5,9,1]]),columns=['d','t','didi'])
>>
   d  t  didi
0  2  4     4
1  4  3     3
2  5  9     1

df.filter(regex=("d.*"))

>>
   d  didi
0  2     4
1  4     3
2  5     1

这个想法是通过选择列 regex


20

用途select

import pandas as pd

df = pd.DataFrame([[10, 14, 12, 44, 45, 78]], columns=['a', 'b', 'c', 'd1', 'd2', 'd3'])

df.select(lambda col: col.startswith('d'), axis=1)

结果:

   d1  d2  d3
0  44  45  78

如果您对正则表达式不满意,这是一个很好的解决方案。


5
当心选择现在已被弃用
Venkat Ramana

9

尤其是在较大的数据集上,矢量化方法实际上要快得多(超过两个数量级),而且可读性更高。我提供屏幕截图作为证明。(注意:除了我在底部写的最后几行用向量化的方法使我的观点清楚之外,其他代码均来自@Alexander的答案。)

在此处输入图片说明

这是供参考的代码:

import pandas as pd
import numpy as np
n = 10000
cols = ['{0}_{1}'.format(letters, number) 
        for number in range(n) for letters in ('d', 't', 'didi')]
df = pd.DataFrame(np.random.randn(30000, n * 3), columns=cols)

%timeit df[[c for c in df if c[0] == 'd']]

%timeit df[[c for c in df if c.startswith('d')]]

%timeit df.select(lambda col: col.startswith('d'), axis=1)

%timeit df.filter(regex=("d.*"))

%timeit df.filter(like='d')

%timeit df.filter(like='d', axis=1)

%timeit df.filter(regex=("d.*"), axis=1)

%timeit df.columns.map(lambda x: x.startswith("d"))

columnVals = df.columns.map(lambda x: x.startswith("d"))

%timeit df.filter(columnVals, axis=1)

1
我无法使用您的方法来过滤数据框,使用最后两行,我的结果为空...没有列...此方法仍然有效吗?
拉赫·奥德怀

@RachOdwyer我认为它应该可以工作,除非他们进行了重大更改。如果是这种情况,请告诉我。
devinbost

5

您可以使用列表推导来遍历DataFrame中的所有列名df,然后仅选择以'd'开头的列名。

df = pd.DataFrame({'a': {0: 10}, 'b': {0: 14}, 'c': {0: 12},
                   'd1': {0: 44}, 'd2': {0: 45}, 'd3': {0: 78}})

使用列表推导来遍历数据框中的列并返回其名称(c以下是表示列名称的局部变量)。

>>> [c for c in df]
['a', 'b', 'c', 'd1', 'd2', 'd3']

然后仅选择以“ d”开头的那些。

>>> [c for c in df if c[0] == 'd']  # As an alternative to c[0], use c.startswith(...)
['d1', 'd2', 'd3']

最后,将此列列表传递给DataFrame。

df[[c for c in df if c.startswith('d')]]
>>> df
   d1  d2  d3
0  44  45  78

=================================================== ========================

TIMINGSdevinbost在2018年2月发表的每条评论都声称此方法很慢...)

首先,让我们创建一个包含30k列的数据框:

n = 10000
cols = ['{0}_{1}'.format(letters, number) 
        for number in range(n) for letters in ('d', 't', 'didi')]
df = pd.DataFrame(np.random.randn(3, n * 3), columns=cols)
>>> df.shape
(3, 30000)

>>> %timeit df[[c for c in df if c[0] == 'd']]  # Simple list comprehension.
# 10 loops, best of 3: 16.4 ms per loop

>>> %timeit df[[c for c in df if c.startswith('d')]]  # More 'pythonic'?
# 10 loops, best of 3: 29.2 ms per loop

>>> %timeit df.select(lambda col: col.startswith('d'), axis=1)  # Solution of gbrener.
# 10 loops, best of 3: 21.4 ms per loop

>>> %timeit df.filter(regex=("d.*"))  # Accepted solution.
# 10 loops, best of 3: 40 ms per loop

我没有代码。那里的c是什么。并测试了代码,请提供一些解释。
严松

1
c.startswith('d')可能更pythonic。无论哪种方式,我都喜欢!
亚当·斯密

这非常慢。向量化方法将是非常可取的。
devinbost

@devinbost您的要求是可悲的便宜,是在OP提出问题近两年后提出的。OP询问“是否有一种简单的方法可以在python中实现此目标”,我的答复在大多数情况下都适用。如果您有一个特定的要求,即调用具有大量列或许多数据帧的数据帧,那么我建议您提出一个更符合您需求的问题。
亚历山大

1
@devinbost,您发布的链接指的是逐行优化,而这篇文章明确地按询问了选择的内容,因此您对社区最佳实践的抱怨确实不合适。对于常见数据分析,列很少会超过一百,并且不需要向量化。
Jacquot

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.