Python 为什么数据帧单元的id会随着每次执行而改变?

Python 为什么数据帧单元的id会随着每次执行而改变?,python,python-3.x,pandas,dataframe,Python,Python 3.x,Pandas,Dataframe,当我试图确定数据帧视图的某些属性时,遇到了这个问题 假设我有一个定义为:df=pd.dataframe(columns=list('abc'),data=np.arange(18).重塑(6,3))的数据帧,这个数据帧的视图定义为:df1=df.iloc[:3,:]。我们现在有两个数据帧,如下所示: print(df) a b c 0 0 1 2 1 3 4 5 2 6 7 8 3 9 10 11 4 12 13 14 5 15

当我试图确定数据帧视图的某些属性时,遇到了这个问题

假设我有一个定义为:
df=pd.dataframe(columns=list('abc'),data=np.arange(18).重塑(6,3))
的数据帧,这个数据帧的视图定义为:
df1=df.iloc[:3,:]
。我们现在有两个数据帧,如下所示:

print(df)
    a   b   c
0   0   1   2
1   3   4   5
2   6   7   8
3   9  10  11
4  12  13  14
5  15  16  17

print(df1)

   a  b  c
0  0  1  2
1  3  4  5
2  6  7  8
现在我想输出这两个数据帧的特定单元格的id:

print(id(df.loc[0, 'a']))
print(id(df1.loc[0, 'a']))
我的输出为:

140114943491408
140114943491408
奇怪的是,如果我连续执行这两行“打印id”代码,id也会改变:

140114943491480
140114943491480
我必须强调,当我执行这两个“打印id”代码时,我没有执行“df定义”代码,因此df和df1没有被重新定义。那么,在我看来,数据帧中每个元素的内存地址应该是固定的,那么输出如何变化呢

当我继续执行这两行“打印id”代码时,会发生更奇怪的事情。在一些罕见的情况下,这两个ID甚至不相等:

140114943181088
140114943181112
但是如果我同时执行
id(df.loc[0,'a'])==id(df1.loc[0,'a'])
,python仍然会输出
True
。我知道,因为df1是df的一个视图,所以它们的单元应该共享一个内存,但是为什么它们的ID的输出有时会不同呢

那些奇怪的行为使我完全不知所措。有人能解释这些行为吗?它们是由于数据帧的特性还是python中的id函数造成的?谢谢


仅供参考,我使用的是
Python3.5.2

您没有获得“单元格”的id,而是获得
.loc
访问器返回的对象的
id
,它是基础数据的盒装版本

所以

但由于Python中的所有内容都是对象,因此
loc
方法必须返回一个对象:

>>> x = df.loc[0, 'a']
>>> x
0
>>> type(x)
<class 'numpy.int64'>
>>> isinstance(x, object)
True
发生的原因是,在Python中,对象可以自由地重新使用最近回收的对象的内存地址。实际上,当您创建
id
的元组时,
loc
返回的对象只存在足够长的时间,通过第一次调用
id
,第二次使用
loc
,已经解除分配的对象只会重新使用相同的内存。您可以在任何Python对象上看到相同的行为,如
列表

>>> id([]), id([])
(4545276872, 4545276872)
基本上,
id
只保证在对象的生命周期内是唯一的。了解更多关于这种现象的信息。但是,请注意,在以下情况下,情况总是不同的:

>>> x = df.loc[0, 'a']
>>> x2 = df.loc[0, 'a']
>>> id(x), id(x2)
(4539673432, 4539673408)
由于您在周围维护引用,因此对象不会被回收,并且需要新的内存

注意,对于许多不可变对象,解释器可以自由地优化并返回相同的精确对象。在CPython中,“小整数”就是这种情况,即所谓的小整数缓存:

>>> x = 2
>>> y = 2
>>> id(x), id(y)
(4304820368, 4304820368)
但这是一个不应该依赖的实现细节

如果您想向自己证明您的数据帧共享相同的底层缓冲区,只需对它们进行变异,您就会看到视图中反映的相同更改:

>>> df
    a   b   c
0   0   1   2
1   3   4   5
2   6   7   8
3   9  10  11
4  12  13  14
5  15  16  17
>>> df1
   a  b  c
0  0  1  2
1  3  4  5
2  6  7  8
>>> df.loc[0, 'a'] = 99
>>> df
    a   b   c
0  99   1   2
1   3   4   5
2   6   7   8
3   9  10  11
4  12  13  14
5  15  16  17
>>> df1
    a  b  c
0  99  1  2
1   3  4  5
2   6  7  8

您没有获取“单元格”的id,而是获取
.loc
访问器返回的对象的
id
,它是基础数据的盒装版本。我尝试运行相同的程序。每次打印ID时,我都会得到相等的ID。>>打印(id(df.loc[0,'a'])4402589368>>>打印(id(df1.loc[0,'a'])4402589368>>打印(id(df.loc[0,'a'])4402589368>>打印(id(df1.loc[0,'a'])4402589368>>打印(id(df1.loc[0,'a']))4402589368>>打印(id(df1.loc[0,'a'])4402589368>>我想你一定是重新定义的df1.loc了。或者运行program@ShubhamAgrawal读我的答案。重新定义数据帧绝对是可能的。如果你真的理解了发生了什么,那么应该感到惊讶的是ID是相同的,而不是它们是不同的,因为你得到的是一个副本,而不是一个视图。@smci的副本否,这不是问题所在。看我的答案,变异
df
影响
df1
。很好的解释!我相信它回答了这个问题的所有方面。我还不清楚的一个小细节是你在回答中提到的“从其他语言借用术语”的“盒装”程序。你能进一步解释一下那是什么吗?如果您使用C/C++术语来解释,对我来说没问题。谢谢@因此,Python没有原始数据类型。相反,一切都是一个对象。您可以将
numpy.ndarray
对象视为基本数组周围的面向对象包装器。由于实际的底层缓冲区包含基本数据类型,为了将其引入Python解释器级别,需要将其“装箱”到Python对象中。每次从numpy数组中选择一个元素时都会发生这种情况,即使它是同一个元素。
>>> x = 2
>>> y = 2
>>> id(x), id(y)
(4304820368, 4304820368)
>>> df
    a   b   c
0   0   1   2
1   3   4   5
2   6   7   8
3   9  10  11
4  12  13  14
5  15  16  17
>>> df1
   a  b  c
0  0  1  2
1  3  4  5
2  6  7  8
>>> df.loc[0, 'a'] = 99
>>> df
    a   b   c
0  99   1   2
1   3   4   5
2   6   7   8
3   9  10  11
4  12  13  14
5  15  16  17
>>> df1
    a  b  c
0  99  1  2
1   3  4  5
2   6  7  8