Pandas读取CSV文件时将其作为字符串类型处理

74

我有一个包含字母数字键的数据框,我想将其保存为CSV并稍后读取。由于某些原因,我需要将这些键列明确地读取为字符串格式,我有一些键是严格数字甚至更糟糕的是像:1234E5 这样的键,Pandas将其解释为浮点数。这显然使该键完全无用。

问题在于当我为数据框或其中任何一列指定字符串dtype时,我只会得到垃圾返回。我在这里提供了一些示例代码:

df = pd.DataFrame(np.random.rand(2,2),
                  index=['1A', '1B'],
                  columns=['A', 'B'])
df.to_csv(savefile)

数据框如下:

           A         B
1A  0.209059  0.275554
1B  0.742666  0.721165

然后我这样阅读:

df_read = pd.read_csv(savefile, dtype=str, index_col=0)

结果是:

   A  B
B  (  <

这是我的电脑出了问题,还是我在做错什么,或者只是个bug?


如果您能说明想要将其保存为字符串的“各种原因”,那就太好了。上下文可能有助于找到更优雅的解决方案。 - Sami Start
这个回答解决了你的问题吗?将 Pandas 数据框中的列导入为字符串而不是整数 - sophros
这个回答解决了你的问题吗?使 pandas.read_csv 读取空值时变为空字符串而不是 NaN - dank8
5个回答

66

更新:问题已得到修复:从0.11.1版本开始,您传递str/np.str将等同于使用object

请使用object数据类型:

In [11]: pd.read_csv('a', dtype=object, index_col=0)
Out[11]:
                      A                     B
1A  0.35633069074776547     0.745585398803751
1B  0.20037376323337375  0.013921830784260236

或者更好的做法是,不指定dtype:
In [12]: pd.read_csv('a', index_col=0)
Out[12]:
           A         B
1A  0.356331  0.745585
1B  0.200374  0.013922

但是绕过类型检查器并真正只返回字符串需要使用converters的hacky方法:

In [13]: pd.read_csv('a', converters={i: str for i in range(100)})
Out[13]:
                      A                     B
1A  0.35633069074776547     0.745585398803751
1B  0.20037376323337375  0.013921830784260236

其中100是某个大于等于你的列总数的数字。

最好避免使用str类型,例如请参考这里


1
我认为“read_csv”应该a)在无效的传递dtype时引发错误,并且b)只需将“str” dtype转换为“object”,是否需要提出问题? - Jeff
1
谢谢,我会试一试的。我已经说过了,不能不指定类型就直接读取它,因为Pandas会把我需要当作字符串的数字键值解析成浮点数。就像我在例子中所说的那样,这样的一个键值:1234E5 被解析成了 1234.0x10^5,这对我查找时没有丝毫帮助。 - daver
@daver 这个问题在0.11.1版本中已经修复(即将发布)。谢谢! - Andy Hayden
1
只需包含 dtype=object(而不是 index_col),就可以解决所有前导零消失的问题。 - elPastor
3
仅设置 dtype 是不够的。例如,df.applymap(lambda x: x.strip()) 会抛出一个错误 AttributeError: ("'float' object has no attribute 'strip'", 'occurred at index A'),因为 Pandas 在某个地方将 object 强制转换为 float - Anton Tarasenko
显示剩余4条评论

22

小时 + 我自己的问题,让我找到它! - Francisco Cortes

13
正如Anton T在他的评论中提到的,pandas会使用其类型侦测器随机将object类型转换为float类型,即使您传递了dtype=objectdtype=strdtype=np.str也是如此。
由于您可以传递一个函数字典,其中键是列索引,值是转换器函数,因此您可以像这样做(例如100个列)。
pd.read_csv('some_file.csv', converters={i: str for i in range(0, 100)})

如果您不知道要读取多少列,甚至可以传递range(0, N),其中N比列数大得多。


我在版本“0.25.3”中收到了“IndexError: list index out of range”的错误。 - Julio Batista Silva

4

如果您不知道列名,可以使用适用于任何列的转换器:

import pandas as pd

class StringConverter(dict):
    def __contains__(self, item):
        return True

    def __getitem__(self, item):
        return str

    def get(self, default=None):
        return str

pd.read_csv(file_or_buffer, converters=StringConverter())

0

以上许多答案都很好,但既不太优雅又不够通用。如果你想将所有列都作为字符串读取,可以使用以下结构,而不必担心列的数量。

from collections import defaultdict
import pandas as pd

pd.read_csv(file_or_buffer, converters=defaultdict(lambda i: str))

defaultdict 会针对传入 converters 的每个索引返回一个 str


似乎没有任何影响。 - Sn3akyP3t3
@Sn3akyP3t3:你怎么知道它不是针对问题中受影响的pandas版本?你怎么知道它不起作用 - 你得到了什么相反的结果? - sophros
我可以确认这个例子只在某些情况下有效。我在本周早些时候应用了它,它绝对有效。然而,后来我发现另一种情况,应用了它却没有效果。(只有一个3列的df)我选择了在这个帖子中也提到的“StringConverter”类选项,它完美地工作了。为什么?我不知道,但就是这样。 - Codek
@Codek:运行时Python / pandas的版本有区别吗,还是只是不同的数据? - sophros
1
其实这是一个很好的观点。新项目中同样的解决方法可能有细微的不同版本,我明天会检查一下!我肯定会想知道这种奇怪现象的原因! - Codek

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