pandas无法从大StringIO对象读取
我正在使用pandas来管理一个很大的8字节整数数组。这些整数作为一个列中的空格分隔元素,包含在一个用逗号分隔的CSV文件里,数组的大小大约是10000x10000。
pandas可以快速读取前几列的逗号分隔数据,并将空格分隔的字符串轻松存储到另一个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
替换成 c.getvalue()
,但这样做时,解释器会试图在终端打印 c
的内容!肯定有办法解决这个问题。
提前感谢你的帮助。
1 个回答
这里有两个问题,一个是基础问题,另一个是你可能还没遇到过的。:^)
首先,在你写入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