为什么在分配给数据帧转置时我们会丢失数据?
Why do we lose data when assigned to transpose of dataframe?
假设我有一个像
这样的数据框
df = pd.DataFrame({'A':[1,2,3,4],'B':[1,3,4,7]})
甲乙
0 1 1
1 2 3
2 3 4
3 4 7
当我分配一些数据来转置数据帧时,没有错误,即
df.T['C'] = 3
在 运行 之后数据帧没有变化。
但问题是数据存储在哪里?为什么它没有给出任何错误?我期待这种分配的错误或像
这样的输出
甲乙
0 1 1
1 2 3
2 3 4
3 4 7
中 3 3
我做的时候都没有发生 df.T['C'] = 3
编辑:正如@Zero 提到的,我们可能需要做
df = df.T.assign(C=3).T # Which is like df.loc['C',:] = 3
df.T
是不同的对象。您所做的更改不会反映在原始 df 中。它在哪里?由于没有变量指向它,它要么已经被垃圾收集器收集了,要么正在等待收集。您无法访问它。
你可以做的是创建一个新变量
transposed = df.T
transposed['C'] = 3
transposed
Out:
0 1 2 3 C
A 1 2 3 4 3
B 1 3 4 7 3
当您调用任何 returns 新 DataFrame 的方法时,也会发生同样的事情。 df.drop(0)['C'] = 2
、df.reset_index()['C'] = 3
或 df.drop_duplicates()['C'] = 3
。原始 DataFrame 始终保持不变。创建了另一个 DataFrame 并为其分配了确切的行,但是一旦执行该语句就无法访问它,因为您没有任何指向它的变量。对于CPython的垃圾回收,有一些有用的信息here.
来自@Bharath 的编辑:
(一位老师的解释)
T returns a copy
。这意味着分配了新内存来存储新对象。如果你查看 python 垃圾回收,你会发现内存中的每个对象都有一个计数器,记录有多少指针指向它。
当垃圾回收为运行时,它会在内存中找到这个对象,看到它有零指针。因为它有零指针,所以垃圾收集将回收内存并且对象永远消失了。
因此建议通过分配给名称(或变量)来保持指向对象的单个指针。
方法 T
执行 return super(DataFrame, self).transpose(1, 0, **kwargs)
。
它将创建另一个 DataFrame。
除了现有答案之外,我想提请您注意 -
之间的巧妙相似性
df
A B
0 1 1
1 2 3
2 3 4
3 4 7
df.T['C'] = 3
df
A B
0 1 1
1 2 3
2 3 4
3 4 7
并且,与 python list
s 类似的情况 -
l = [1, 2, 3, 4, 5]
l[:].append(6)
l
[1, 2, 3, 4, 5]
两种情况下都会创建一个新对象!然后该操作应用于 that 新创建的对象,随后,该对象被垃圾收集,因为没有指向它的活动引用。你看到这个 -
import sys
sys.getrefcount(df.T)
1
只有一个对该对象的引用(当时的引用,随后丢失)。一旦你接受了 df.T
returns 一个 全新的对象 这一事实,这就很容易理解了(我已经说过了,但我正在努力推动回家点)-
id(df.T)
4612098928
id(df.T)
4612098872
id(df.T)
4612098592
总而言之,您正在尝试修改一个您没有引用的 新鲜 对象,并且您没有看到对原始对象的任何更改,因为您没有进行任何更改。
假设我有一个像
这样的数据框df = pd.DataFrame({'A':[1,2,3,4],'B':[1,3,4,7]})
甲乙 0 1 1 1 2 3 2 3 4 3 4 7
当我分配一些数据来转置数据帧时,没有错误,即
df.T['C'] = 3
在 运行 之后数据帧没有变化。
但问题是数据存储在哪里?为什么它没有给出任何错误?我期待这种分配的错误或像
这样的输出甲乙 0 1 1 1 2 3 2 3 4 3 4 7 中 3 3
我做的时候都没有发生 df.T['C'] = 3
编辑:正如@Zero 提到的,我们可能需要做
df = df.T.assign(C=3).T # Which is like df.loc['C',:] = 3
df.T
是不同的对象。您所做的更改不会反映在原始 df 中。它在哪里?由于没有变量指向它,它要么已经被垃圾收集器收集了,要么正在等待收集。您无法访问它。
你可以做的是创建一个新变量
transposed = df.T
transposed['C'] = 3
transposed
Out:
0 1 2 3 C
A 1 2 3 4 3
B 1 3 4 7 3
当您调用任何 returns 新 DataFrame 的方法时,也会发生同样的事情。 df.drop(0)['C'] = 2
、df.reset_index()['C'] = 3
或 df.drop_duplicates()['C'] = 3
。原始 DataFrame 始终保持不变。创建了另一个 DataFrame 并为其分配了确切的行,但是一旦执行该语句就无法访问它,因为您没有任何指向它的变量。对于CPython的垃圾回收,有一些有用的信息here.
来自@Bharath 的编辑:
(一位老师的解释)
T returns a copy
。这意味着分配了新内存来存储新对象。如果你查看 python 垃圾回收,你会发现内存中的每个对象都有一个计数器,记录有多少指针指向它。
当垃圾回收为运行时,它会在内存中找到这个对象,看到它有零指针。因为它有零指针,所以垃圾收集将回收内存并且对象永远消失了。
因此建议通过分配给名称(或变量)来保持指向对象的单个指针。
方法 T
执行 return super(DataFrame, self).transpose(1, 0, **kwargs)
。
它将创建另一个 DataFrame。
除了现有答案之外,我想提请您注意 -
之间的巧妙相似性df
A B
0 1 1
1 2 3
2 3 4
3 4 7
df.T['C'] = 3
df
A B
0 1 1
1 2 3
2 3 4
3 4 7
并且,与 python list
s 类似的情况 -
l = [1, 2, 3, 4, 5]
l[:].append(6)
l
[1, 2, 3, 4, 5]
两种情况下都会创建一个新对象!然后该操作应用于 that 新创建的对象,随后,该对象被垃圾收集,因为没有指向它的活动引用。你看到这个 -
import sys
sys.getrefcount(df.T)
1
只有一个对该对象的引用(当时的引用,随后丢失)。一旦你接受了 df.T
returns 一个 全新的对象 这一事实,这就很容易理解了(我已经说过了,但我正在努力推动回家点)-
id(df.T)
4612098928
id(df.T)
4612098872
id(df.T)
4612098592
总而言之,您正在尝试修改一个您没有引用的 新鲜 对象,并且您没有看到对原始对象的任何更改,因为您没有进行任何更改。