当前位置: 首页 > 知识库问答 >
问题:

熊猫表演:栏目选择

荣沈义
2023-03-14

我今天观察到,选择两列或更多列的数据帧可能比只选择一列慢得多。

如果我使用loc或iloc选择多个列,并且我使用list传递列名或索引,那么与使用iloc的单个列或多个列选择相比,性能会下降100倍(但没有传递列表)

示例:

df = pd.DataFrame(np.random.randn(10**7,10), columns=list('abcdefghij'))

单列选择:

%%timeit -n 100
df['b']
3.17 µs ± 147 ns per loop (mean ± std. dev. of 7 runs, 100 loops each)

%%timeit -n 100
df.iloc[:,1]
66.7 µs ± 5.95 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%%timeit -n 100
df.loc[:,'b']
44.2 µs ± 10.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

两列选择:

%%timeit -n 10
df[['b', 'c']]
96.4 ms ± 788 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit -n 10
df.loc[:,['b', 'c']]
99.4 ms ± 4.44 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit -n 10
df.iloc[:,[1,2]]
97.6 ms ± 1.79 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

只有此选择的工作方式与预期相同:[编辑]

%%timeit -n 100
df.iloc[:,1:3]
103 µs ± 17.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

机制上的差异是什么?为什么差异如此之大?

[编辑]:正如@run-out指出的,pd。系列似乎比pd处理得快得多。DataFrame,有人知道为什么会这样吗?

另一方面,它没有解释df.iloc[:,[1,2]]df.iloc[:,1:3]

共有2个答案

毕宇
2023-03-14

我发现这可能源于numpy。

numpy有两种索引:

  1. 基本索引像[1:3]
  2. 高级索引像[[1,2]

根据留档,

高级索引始终返回数据的副本(与返回视图的基本切片不同)。

所以如果你检查一下

a=df.values
%timeit -n2 a[:,0:3]
%timeit -n2 a[:,[0,1,2]]

你有

The slowest run took 5.06 times longer than the fastest. This could mean that an intermediate result is being cached.
1.57 µs ± 1.3 µs per loop (mean ± std. dev. of 7 runs, 2 loops each)
188 ms ± 2.17 ms per loop (mean ± std. dev. of 7 runs, 2 loops each)

数据帧的行为非常相似

葛兴发
2023-03-14

Pandas以Pandas.Series的形式处理单行或列,这将比在DataFrame体系结构中处理更快。

熊猫和熊猫一起工作。系列,当你要求:

%%timeit -n 10
df['b']
2.31 µs ± 1.59 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

但是,我可以通过将同一列放入列表来html" target="_blank">调用该列的数据帧。然后你会得到:

%%timeit -n 10
df[['b']]
90.7 ms ± 1.73 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

从上面可以看出,它的系列性能优于DataFrame。

以下是熊猫如何处理b栏。

type(df['b'])
pandas.core.series.Series

type(df[['b']])
pandas.core.frame.DataFrame

编辑:我在扩展我的答案,因为OP想更深入地了解为什么pd.series和pd.dataframe有更高的速度。同时,这也是一个很好的问题,可以帮助我/我们加深对底层技术工作原理的理解。那些有更多专业知识的人请插话。

首先让我们从numpy开始,因为它是熊猫的一个组成部分。根据pandas和Python for Data Analysis的作者Wes McKinney的说法,numpy的性能优于Python:

This is based partly on performance differences having to do with the
cache hierarchy of the CPU; operations accessing contiguous blocks of memory (e.g.,
summing the rows of a C order array) will generally be the fastest because the mem‐
ory subsystem will buffer the appropriate blocks of memory into the ultrafast L1 or
L2 CPU cache. 

让我们看看这个例子的速度差。让我们从数据帧的“b”列创建一个numpy数组。

a = np.array(df['b'])

现在做性能测试:

%%timeit -n 10
a

结果是:

32.5 ns ± 28.2 ns per loop (mean ± std. dev. of 7 runs, 10 loops each)

在2.31µs的pd.series时间内,性能大幅提高。

性能提升的另一个主要原因是numpy索引直接进入numpy C扩展,但是当您索引到一个系列中时,会有很多python内容,这要慢得多。(阅读本文)

让我们看看为什么会这样的问题:

df.iloc[:,1:3]

显著优于:

df.iloc[:,[1,2]]

我们的第一个大线索是不正确的是在下面的代码:

df.iloc[:,1:3] is df.iloc[:,[1,2]]
False

它们给出相同的结果,但是不同的对象。我做了一次深入的研究,试图找出两者的区别。我在互联网上或我的图书馆里找不到这方面的参考资料。

看看源代码,我们可以开始看到一些不同。我指的是index.py。

在类_iLocIndexer我们可以找到一些额外的工作熊猫正在做的列表在一个iloc切片。

在检查输入时,我们立即遇到以下两个差异:

if isinstance(key, slice):
            return

vs.

elif is_list_like_indexer(key):
            # check that the key does not exceed the maximum size of the index
            arr = np.array(key)
            l = len(self.obj._get_axis(axis))

            if len(arr) and (arr.max() >= l or arr.min() < -l):
                raise IndexError("positional indexers are out-of-bounds")

仅仅这一点就足以导致性能下降吗?我不知道。

虽然.loc略有不同,但在使用值列表时,它也会影响性能。查看index.py,查看def _getitem_轴(self,key,axis=None):--

处理列表输入的类索引器(键)的代码段相当长,包括大量开销。它包含以下注释:

# convert various list-like indexers
# to a list of keys
# we will use the *values* of the object
# and NOT the index if its a PandasObject

当然,在处理值或整数列表时,会有足够的额外开销,然后直接对切片进行处理,从而导致处理延迟。

其余的代码超过了我的工资等级。如果有人能看一下并敲响它,那将是最受欢迎的

 类似资料:
  • 我只是试图通过整数访问命名的熊猫列。 您可以使用按位置选择行。 但是如何通过整数选择列呢? 我的数据框:

  • 问题内容: 我有以下数据(2列4行): 我正在尝试将列合并为一列,看起来像这样(1列,8行): 我使用的熊猫数据框,并使用不同的功能,但没有成功(试过,等)。非常感激任何的帮助! 问题答案: 更新资料 熊猫为此有一个内置的方法,它可以根据您的意愿看到其他答案。 这是我很多年前才知道的第一个答案:

  • 问题内容: 如果我有以下数据帧,派生如下: 有没有一种有效的方法“cumsum”行有限制并且每次都有这个限制 已到达,开始新的“cumsum”。在达到每个极限后(不管有多少 rows),则创建一个包含总累计和的行。 下面我创建了一个这样做的函数的例子,但是它非常 速度很慢,尤其是当数据帧变得非常大时。我不喜欢这样,我的朋友 函数是循环的,我正在寻找一种方法使它更快(我猜 没有环路)。 If you

  • 问题内容: 我有一个数据框,其中的一列作为字典。我想将其解包成多列(即,代码,金额为以下Raw列格式的单独列)。以下代码曾经与pandas v0.22(现在是(0.23))一起工作,给出了索引错误: 我在Google / Stack溢出中搜索了数小时,之前提出的其他解决方案均无法正常工作。 原始列格式: 有没有人有什么建议? 谢谢 问题答案: 设定 与 确保先 和 相同的想法,但跳过

  • ...给出一个错误: .../site-packages/pandas/io/parsers . py:1130:dtype warning:列(4,5,7,16)具有混合类型。请在导入时指定dtype选项,或者设置low_memory=False。 为什么选项与相关,为什么可能有帮助?

  • 问题内容: 所以我有一个数据框df1,看起来像下面的样子: 我想按A列分组,然后对B列求和,同时将值保留在C列中。 问题是,当我说df.groupby(’A’)。sum()列C被删除时,返回 当我进行分组和求和时,如何解决这个问题并保留C列? 问题答案: 唯一的方法是将C包含在groupby中(groupby函数可以接受列表)。 试试看: 需要注意的另一件事是,如果需要在聚合后使用df,则还可以使