【问题标题】:Why do we lose data when assigned to transpose of dataframe?为什么我们在分配数据帧转置时会丢失数据?
【发布时间】:2017-11-22 12:47:58
【问题描述】:

假设我有一个像

这样的数据框
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 C 3 3

我做df.T['C'] = 3时都没有发生

编辑:正如@Zero 提到的,我们可能不得不这样做

df = df.T.assign(C=3).T # Which is like df.loc['C',:] = 3

【问题讨论】:

  • df.T['C'] = 3 的预期结果是什么?
  • 改用df.T.assign(C=3)
  • 投反对票的我可以知道为什么吗?这真的是那么愚蠢的问题吗?
  • @Zero 先生,是的,我很想知道为什么数据会丢失。也许正如 Ayhan 所说,因为没有变量指向它。
  • 问一下,是df = df.T['C'] = 3 还是df.T['C'] = 3?下面的答案谈到了这一点。

标签: python pandas dataframe transpose


【解决方案1】:

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   

当您调用任何返回新 DataFrame 的方法时,也会发生同样的事情。 df.drop(0)['C'] = 2df.reset_index()['C'] = 3df.drop_duplicates()['C'] = 3。原始 DataFrame 始终保持不变。创建了另一个 DataFrame,并为其分配了确切的行,但是一旦执行该语句,它就变得不可访问,因为您没有任何变量指向它。关于CPython的垃圾回收,有一些有用的资料here


从@Bharath 编辑:

(我的一位老师给出的解释)

T returns a copy。这意味着分配新内存来存储新对象。如果你查看 python 垃圾收集,你会发现内存中的每个对象都有一个计数器,用于记录有多少指针指向它。

当垃圾收集运行时,它会在内存中找到这个对象,并看到它的指针为零。因为它的指针为零,所以垃圾收集器将回收内存并且对象永远消失了。

因此建议通过分配名称(或变量)来保持指向对象的单个指针。

【讨论】:

  • 我知道复制,但垃圾收集器对我来说是新的。我很好奇它为什么会丢失。为什么没有显示任何错误。我们应该向熊猫报告吗?
  • @Bharath 在某种意义上它就像链式分配,但这可能并不常见。如果您执行df.drop(0)['C'] = 2,它也不会发出任何警告(它也不会修改df)。由于您是即时执行这些操作,而没有将其分配给变量,它甚至可能没有机会发出警告。至于垃圾收集,this 可能会有所帮助。对于引用计数,您不仅要考虑创建的引用;大熊猫创造的那些也很重要,而且有些未知(你需要深入挖掘)。
  • @Bharath 不,当然不是。 Python 处理变量/名称的方式与许多语言不同,大多数人都在为这些引用而苦恼。我不会过多地阅读反对票。可能有数百万个原因。它们可能是合乎逻辑的或不合逻辑的。除非选民自己解释,否则我不会在意。
  • @ElisByberi 所以如果我想计算一个总和,我应该继续修改原始数据帧吗?那肯定会打乱我的工作流程。我的一半代码将包含copy()s。
  • @ElisByberi 我认为我们在绕圈子。 :) 你为什么要写filter(groupby(reset_index(set_index(drop(rename(df, arg), arg), arg), arg), arg), arg) 而你可以写df.rename(arg).drop(arg).set_index(arg).reset_index(arg).groupby(arg).filter(arg)。这是不直观的(您以相反的顺序调用函数)并且很难阅读/跟踪。
【解决方案2】:

方法T 执行return super(DataFrame, self).transpose(1, 0, **kwargs)
它将创建另一个 DataFrame。

【讨论】:

  • @Bharath 这不是解释,而是提示:DataFrame 的父类中的方法transpose 这样做而不是return self._constructor(new_values, **new_axes).__finalize__(self) 这是就地转置。
  • @Bharath 这是 pandas 的方法 T(): DataFrame.T, Transpose index and columns 的文档。这是荒唐的!哈哈哈哈!
【解决方案3】:

除了现有的答案,我想提请您注意 -

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 lists 类似的情况 -

l = [1, 2, 3, 4, 5]
l[:].append(6)

l
[1, 2, 3, 4, 5]

在这两种情况下都会发生一个新对象!然后将该操作应用于 那个 新创建的对象,之后,该对象被垃圾回收,因为没有指向它的活动引用。你看这个 -

import sys

sys.getrefcount(df.T)
1

对该对象只有一个引用(该时间点的引用,随后丢失)。一旦你接受df.T 返回一个全新的对象这一事实,这将变得很容易理解(我已经说过了,但我想强调这一点)-

id(df.T)
4612098928

id(df.T)
4612098872

id(df.T)
4612098592

总而言之,您正在尝试修改一个您没有参考的新鲜对象,并且您看不到对原始对象的任何更改,因为您没有进行任何更改。

【讨论】:

  • 显示代码和解释发生了什么的 sn-p 总是一个很棒的答案。谢谢
  • @Bharath 事实是根本没有对新创建的对象的引用。垃圾收集器不应该责怪您确实“丢失”了对象。我将创建一个问题并自己给出答案(也欢迎其他同行的答案)。我真的厌倦了一遍又一遍地向任何询问这个问题的人解释这一点。我的这个问题对每个人来说都是一个很好的参考。当它准备好时,我会 ping 你。
  • @ElisByberi The truth is that there is no reference at all to the newly created object。这是 Ahyan 在他的回答中所说的以及我的老师所说的话。我很好奇你会放什么。
  • @ElisByberi 为什么你认为 Bharath 不理解并且需要你重申已经提到的内容?
  • @cᴏʟᴅsᴘᴇᴇᴅ 我想他明白了(我从他在这里的最后评论中明白了)。我确实向 Bharath 重复过,在这种特殊情况下,谈论垃圾收集器是题外话,而不是多余的。我将在不久的将来在我的一个问题中解释它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多