pandas无法从大型StringIO对象中读取数据。

10

我正在使用pandas处理一个包含大量8字节整数的数组。这些整数以空格分隔的元素形式包含在逗号分隔的CSV文件的一列中,数组大小约为10000x10000。

Pandas可以快速将逗号分隔的数据从前几列读取为DataFrame,并且也可以轻松地将空格分隔的字符串存储在另一个DataFrame中,但当我尝试将表格从单列空格分隔的字符串转换为8位整数的DataFrame时,问题就出现了。

我已经尝试了以下方法:

intdata = pd.DataFrame(strdata.columnname.str.split().tolist(), dtype='uint8')

但内存使用量无法承受——价值10MB的整数消耗了2GB的内存。我被告知这是语言的限制,在这种情况下我无能为力。

作为一种可能的解决方法,人们建议我将字符串数据保存到CSV文件中,然后重新加载CSV文件以成为空格分隔的整数DataFrame。这个方法很有效,但为了避免写入磁盘而带来的减速,我尝试把数据写入StringIO对象中。

以下是一个最小的不起作用的示例:

import numpy as np
import pandas as pd
from cStringIO import StringIO

a = np.random.randint(0,256,(10000,10000)).astype('uint8')
b = pd.DataFrame(a)
c = StringIO()
b.to_csv(c, delimiter=' ', header=False, index=False)
d = pd.io.parsers.read_csv(c, delimiter=' ', header=None, dtype='uint8')

会产生以下错误信息:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python2.7/site-packages/pandas/io/parsers.py", line 443, in parser_f
    return _read(filepath_or_buffer, kwds)
  File "/usr/lib64/python2.7/site-packages/pandas/io/parsers.py", line 228, in _read
    parser = TextFileReader(filepath_or_buffer, **kwds)
  File "/usr/lib64/python2.7/site-packages/pandas/io/parsers.py", line 533, in __init__
    self._make_engine(self.engine)
  File "/usr/lib64/python2.7/site-packages/pandas/io/parsers.py", line 670, in _make_engine
    self._engine = CParserWrapper(self.f, **self.options)
  File "/usr/lib64/python2.7/site-packages/pandas/io/parsers.py", line 1032, in __init__
    self._reader = _parser.TextReader(src, **kwds)
  File "parser.pyx", line 486, in pandas.parser.TextReader.__cinit__ (pandas/parser.c:4494)
ValueError: No columns to parse from file

这很令人困惑,因为如果我用'c.csv'而不是c运行完全相同的代码时,代码可以完美地工作。此外,如果我使用以下片段:

file = open('c.csv', 'w')
file.write(c.getvalue())

CSV文件成功保存,因此向StringIO对象写入内容并非问题所在。

我可能需要在read_csv行中使用c.getvalue() 替换 c,但这样做会导致解释器在终端上打印 c 的内容!肯定有方法可以解决这个问题。

提前感谢您的帮助。

1个回答

16

这里有两个问题,一个是根本性的,一个是你还没有遇到的。: ^)

首先,在你写入 c 后,你已经到达了(虚拟)文件的末尾。你需要 seek 回到开头。我们将使用一个较小的网格作为例子:

>>> a = np.random.randint(0,256,(10,10)).astype('uint8')
>>> b = pd.DataFrame(a)
>>> c = StringIO()
>>> b.to_csv(c, delimiter=' ', header=False, index=False)
>>> next(c)
Traceback (most recent call last):
  File "<ipython-input-57-73b012f9653f>", line 1, in <module>
    next(c)
StopIteration

如果我们先使用seek,就可以避免生成“没有列”的错误:

>>> c.seek(0)
>>> next(c)
'103,3,171,239,150,35,224,190,225,57\n'

但是现在你会注意到第二个问题——逗号?我想我们请求的是空格分隔符吧?但是to_csv只接受sep,而不是delimiter。对我来说,它应该要么接受它,要么拒绝它并报错,但是默默地忽略它感觉像一个bug。无论如何,如果我们使用sep(或delim_whitespace=True):

>>> a = np.random.randint(0,256,(10,10)).astype('uint8')
>>> b = pd.DataFrame(a)
>>> c = StringIO()
>>> b.to_csv(c, sep=' ', header=False, index=False)
>>> c.seek(0)
>>> d = pd.read_csv(c, sep=' ', header=None, dtype='uint8')
>>> d
     0    1    2    3    4    5    6    7    8    9
0  209   65  218  242  178  213  187   63  137  145
1  161  222   50   92  157   31   49   62  218   30
2  182  255  146  249  115   91  160   53  200  252
3  192  116   87   85  164   46  192  228  104  113
4   89  137  142  188  183  199  106  128  110    1
5  208  140  116   50   66  208  116   72  158  169
6   50  221   82  235   16   31  222    9   95  111
7   88   36  204   96  186  205  210  223   22  235
8  136  221   98  191   31  174   83  208  226  150
9   62   93  168  181   26  128  116   92   68  153

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