【问题标题】:pandas dataframe view vs copy, how do I tell?熊猫数据框视图与副本,我怎么知道?
【发布时间】:2015-02-06 15:59:22
【问题描述】:

两者有什么区别:

熊猫df.loc[:,('col_a','col_b')]

df.loc[:,['col_a','col_b']]

下面的链接没有提到后者,尽管它有效。两者都拉视图吗?第一个拉视图,第二个拉副本吗?喜欢学习熊猫。

http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy

谢谢

【问题讨论】:

  • 两者都是一样的,都会返回一个视图
  • 除了成为熊猫绝地武士外,你怎么知道
  • 文档说明是这样的,另外,如果您尝试类似 df[:][['a','b']] = 5 的内容,它会发出警告

标签: python pandas


【解决方案1】:

如果您的 DataFrame 具有简单的列索引,则没有区别。 例如,

In [8]: df = pd.DataFrame(np.arange(12).reshape(4,3), columns=list('ABC'))

In [9]: df.loc[:, ['A','B']]
Out[9]: 
   A   B
0  0   1
1  3   4
2  6   7
3  9  10

In [10]: df.loc[:, ('A','B')]
Out[10]: 
   A   B
0  0   1
1  3   4
2  6   7
3  9  10

但是如果DataFrame有MultiIndex的话,可能会有很大的不同:

df = pd.DataFrame(np.random.randint(10, size=(5,4)),
                  columns=pd.MultiIndex.from_arrays([['foo']*2+['bar']*2,
                                                     list('ABAB')]),
                  index=pd.MultiIndex.from_arrays([['baz']*2+['qux']*3,
                                                   list('CDCDC')]))

#       foo    bar   
#         A  B   A  B
# baz C   7  9   9  9
#     D   7  5   5  4
# qux C   5  0   5  1
#     D   1  7   7  4
#     C   6  4   3  5

In [27]: df.loc[:, ('foo','B')]
Out[27]: 
baz  C    9
     D    5
qux  C    0
     D    7
     C    4
Name: (foo, B), dtype: int64

In [28]: df.loc[:, ['foo','B']]
KeyError: 'MultiIndex Slicing requires the index to be fully lexsorted tuple len (1), lexsort depth (0)'

KeyError 表示必须对 MultiIndex 进行 lexsorted。如果我们这样做,那么我们仍然会得到不同的结果:

In [29]: df.sortlevel(axis=1).loc[:, ('foo','B')]
Out[29]: 
baz  C    9
     D    5
qux  C    0
     D    7
     C    4
Name: (foo, B), dtype: int64

In [30]: df.sortlevel(axis=1).loc[:, ['foo','B']]
Out[30]: 
      foo   
        A  B
baz C   7  9
    D   7  5
qux C   5  0
    D   1  7
    C   6  4

这是为什么呢? df.sortlevel(axis=1).loc[:, ('foo','B')] 正在选择第一列级别等于foo,第二列级别等于B 的列。

相比之下,df.sortlevel(axis=1).loc[:, ['foo','B']] 正在选择第一列级别为fooB 的列。对于第一列级别,没有B 列,但有两个foo 列。

我认为 Pandas 的操作原理是,如果您使用 df.loc[...] 作为 表达式,您应该假设df.loc 可能会返回一个副本或一个视图。 Pandas 文档没有指定您应该期望的任何规则。 但是,如果您对表单进行赋值

df.loc[...] = value

那么你就可以相信 Pandas 会改变 df 本身。

文档警告视图和副本之间的区别的原因是为了让您意识到使用表单的链分配的陷阱

df.loc[...][...] = value

这里,Pandas 首先评估df.loc[...],它可能是视图或副本。现在如果是副本,那么

df.loc[...][...] = value

正在更改df 的某些部分的副本,因此对df 本身没有影响。雪上加霜的是,对副本的影响也会丢失,因为没有对副本的引用,因此在赋值语句完成后无法访问副本,并且(至少在 CPython 中)因此很快- 待收集垃圾。


我不知道确定df.loc[...] 是否会返回视图或副本的实用万无一失的先验方法。

但是,有一些经验法则可能有助于引导您的直觉(但请注意,我们在这里讨论的是实现细节,因此无法保证 Pandas 将来需要这样做):

  • 如果生成的 NDFrame 不能表示为 底层 NumPy 数组,那么它可能是一个副本。因此,选择任意行或列将导致复制。选择连续的行和/或连续的列(可以表示为切片)可能会返回一个视图。
  • 如果生成的 NDFrame 具有不同 dtype 的列,则 df.loc 可能会再次返回一份副本。

但是,有一个简单的方法可以确定x = df.loc[..] 是否是一个视图后验:只需查看更改x 中的值是否会影响df。如果是,它是一个视图,如果不是,x 是一个副本。

【讨论】:

  • 如果我们只是使用像df.loc[..] 这样的表达式并且结果是一个副本,那么为什么像df.loc[..] = ... 这样的东西会以某种方式成为一个视图?好像很腥但是非常感谢你的解释。现在感觉好多了。
  • 当用作表达式时,df.loc[...] 正在调用df.loc.__getitem__(...)。当用作赋值时,df.loc[...] = value 正在调用 df.loc.__setitem__(..., value)__getitem__ 方法可能会返回视图或副本。但是__setitem__ 方法总是会修改df 本身。毕竟,这是__setitem__ 唯一合理的做法。
  • @user3659451 不要担心某事是否是视图。只有在某些情况下它可以是一个视图,但没有保证。只需使用.loc 设置即可。视图是基础数据的内存布局及其选择方式的函数。
  • 也谢谢你。这是有道理的。我想我担心出于记忆目的的东西。我正在处理超过 2MM 的记录,大约 40 列。有时事情很奇怪。数据类型会根据我的处理方式而改变(例如,如果苹果是 int64 列,df.loc[:,'apple'] 会变成 float64 列,这很糟糕,因为我必须将 astype 重新转换为 int64。我我想我必须解决这个问题,或者只是打电话/设置df.loc[:,['apple']],我正在一点一点地得到它。
  • @user3659451:这让我感到惊讶,而且可能是一个错误。如果您可以生成一个示例来演示 int64float64 的行为,那将是一个有价值的 SO 问题,甚至可以提升为 an issue
猜你喜欢
  • 2016-12-30
  • 2013-07-31
  • 2018-09-30
  • 1970-01-01
  • 2019-04-17
  • 1970-01-01
  • 2015-07-08
  • 1970-01-01
相关资源
最近更新 更多