数据框中的字符串,但dtype是对象。

157

尽管我已经进行了显式转换,但为什么Pandas告诉我我有对象,而所选列中的每个项都是字符串?

这是我的DataFrame:

<class 'pandas.core.frame.DataFrame'>
Int64Index: 56992 entries, 0 to 56991
Data columns (total 7 columns):
id            56992  non-null values
attr1         56992  non-null values
attr2         56992  non-null values
attr3         56992  non-null values
attr4         56992  non-null values
attr5         56992  non-null values
attr6         56992  non-null values
dtypes: int64(2), object(5)

其中五个是dtype object。我显式地将这些对象转换为字符串:

for c in df.columns:
    if df[c].dtype == object:
        print "convert ", df[c].name, " to string"
        df[c] = df[c].astype(str)

然后,df["attr2"]仍然有dtype object,虽然type(df["attr2"].ix[0]显示str,这是正确的。

Pandas区分int64float64object。当没有dtype str时,这背后的逻辑是什么?为什么strobject覆盖?


来这里是因为连接失败,原因是“对象类型”,尽管每个“is”字符串都正确。 - Monica Heddneck
4个回答

200
dtype对象来源于NumPy,它描述了ndarray中元素的类型。在ndarray中,每个元素的字节大小必须相同。对于int64float64,它们占用8个字节。但是对于字符串,字符串的长度不固定。因此,Pandas使用一个对象ndarray来保存指向字符串对象的指针,而不是直接将字符串的字节保存在ndarray中。因为这个原因,这种类型的ndarraydtype是对象。
以下是一个例子:
  • int64数组包含4个int64值。
  • 对象数组包含4个指向3个字符串对象的指针。

enter image description here


7
注意,然而拥有“对象”类型的列对DataFrame读写操作的性能有重大影响。 - erwanp
4
我能以字符串形式返回数据类型吗?我知道我可以始终使用type(df["column"].iloc[0]),但可能会出现它是nan的情况。 - user1953366

63

@HYRY的回答很好,我只想提供一些更多的背景信息。

数组将数据存储在连续的固定大小的内存块中。这些属性的组合是使数组对数据访问极快的原因。例如,考虑计算机如何存储一个包含32位整数的数组[3,0,1]

enter image description here

如果您要求计算机获取数组中的第三个元素,则它将从开头开始,并跨越64位以到达第三个元素。确切地知道要跨越多少位是使数组快速的关键

现在考虑字符串序列['hello', 'i', 'am', 'a', 'banana']。字符串是大小变化的对象,因此如果尝试将它们存储在连续的内存块中,结果会像这样。

enter image description here

现在您的计算机没有快速访问随机请求的元素的方法。克服这个问题的关键是使用指针。基本上,将每个字符串存储在某个随机内存位置,并用每个字符串的内存地址填充数组。(内存地址只是整数。)现在,情况看起来像这样:

在此输入图片描述

现在,如果您请求计算机获取第三个元素,就像以前一样,它可以跨越64位(假设内存地址是32位整数),然后再走一步去获取字符串。

但是,NumPy面临的挑战是没有保证指针实际上指向字符串。这就是为什么它将dtype报告为“object”的原因。

我要不要不要羞耻地推销一下我自己的NumPy课程,我最初就是在其中讨论了这个问题。


4
谢谢您的提问。不相关的问题:您是如何在答案中创建图形和动画的? - hpy
8
@hpy 苹果发布会 - Ben

21
接受的答案很好。我只想参考文档。文档中说:

Pandas使用object dtype来存储字符串。

接受的答案非常好地解释了“为什么”,即字符串是可变长度的:

但对于字符串而言,字符串的长度不是固定的。

但正如接受的答案中的领先评论所说:“不用担心这个问题;它应该是这样的。”

1
为什么我需要将我传递的每一列转换为scipy或sklearn astype(str)才能接受它?看起来我应该最初就能将其应用于所有列。 - Tinkinc
我不明白,@Tinkinc如果您将列转换为字符串会发生什么?而这个答案似乎是一种将所有列转换为astype(str)的优雅方式,尽管我仍然想知道是否需要进行字符串转换。 - Nate Anderson
1
我无法使用fillna(0)将数据框中的所有对象填充为0,而是仍然保持为(1, nan),而不是(1, 0)。 - Tinkinc
抱歉@Tinkinc,我还是不太明白;我想帮忙,但你的问题听起来比一个Stack Overflow评论更复杂。考虑提出一个问题或加入我的聊天室(我刚邀请了你)。 - Nate Anderson

11

自2020年1月版的pandas(版本1.0.0)起,它引入了一项试验性功能,通过pandas.StringDtype,提供对字符串类型的一流支持。

尽管默认情况下仍然会看到object,但新类型可以通过指定dtypepd.StringDtype或简单地使用'string'来使用:

>>> pd.Series(['abc', None, 'def'])
0     abc
1    None
2     def
dtype: object
>>> pd.Series(['abc', None, 'def'], dtype=pd.StringDtype())
0     abc
1    <NA>
2     def
dtype: string
>>> pd.Series(['abc', None, 'def']).astype('string')
0     abc
1    <NA>
2     def
dtype: string

5
暂时不要使用这个......就像他们所说的,“实现可能会在没有警告的情况下更改。”这意味着新的更新将破坏您的旧程序。 - NoName
2
这完全取决于你将用它来做什么。如果你想在一个需要持续包升级的生产系统中使用它,并且API破坏会导致无法接受的维护负担,那么确实要密切关注“实验性”这个词,但如果你正在使用pandas执行探索性分析,而脚本的寿命不超过一天的工作时间,那么这些问题对你来说应该意义不大。 - fuglede
4
从 Pandas 1.1 开始,API 已经稳定下来了,所有的数据类型现在都可以转换为 StringDtype - D3f0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接