在熊猫中执行read_csv调用后无法重用输入流

时间:2019-08-13 13:46:42

标签: python pandas

我有一个这样的输入文件:

a
1,100
2,200
3,300
b
1,100,200
2,200,300
3,300,400
c
...

我想使用以下代码将文件读取到多个数据帧中(为简化问题,我们假设每个表的行数是固定的):

import pandas as pd

with open("file.csv", "r") as f:
    while True:
        table_name = f.readline()
        if table_name:
            table_df = pd.read_csv(f, nrows=3)
            # Do other stuff
        else:
            break

我最初的期望是pd.read_csv(f, nrows=3)仅消耗输入流中有限数量的行,并且下一个f.readline()调用将继续进行。但是,事实证明,在第一次read_csv调用之后,f的流位置被设置为文件的末尾,并且我无法再从同一流f中读取。我的熊猫版本是0.25.0。这是错误还是预期的行为?有什么方法可以重用相同的输入流来读取多个数据帧?

3 个答案:

答案 0 :(得分:3)

pandas.read_csv将从filepath_or_buffer参数一次创建文件 reader 对象,而nrows= param仅提供获取阅读器的一个切片(它无法从同一文件对象重新实例化新的阅读器)

  

通过类似文件的对象,我们使用read()方法引用对象,例如   作为文件处理程序(例如,通过内置的open函数)或StringIO

根据您的输入文件格式,假设将table_name视为具有单个字符串且不带分隔符,(即ab)的行。 您可以通过手动将一部分行传递到read_csv构造函数来实现所需的结果:

import pandas as pd
import io
from itertools import islice

with open("file.csv", "r") as f:
    dfs = []
    while True:
        table_name = f.readline().strip()
        if table_name and ',' not in table_name:
            data = ''.join(islice(f, 3)).strip()
            table_df = pd.read_csv(io.StringIO(data), sep=',', header=None)
            dfs.append([table_name, table_df])
        else:
            break

# check results
for t_name, df in dfs:
    print('---', t_name)
    print(df) 

示例输出:

--- a
   0    1
0  1  100
1  2  200
2  3  300
--- b
   0    1    2
0  1  100  200
1  2  200  300
2  3  300  400

答案 1 :(得分:1)

使用Python标准库中的csv模块,并使用send指示生成函数所需的行数:

import csv
import pandas as pd

def csvreader(filename):
    with open(filename) as csvfile:
        reader = csv.DictReader(csvfile)

        count = yield

        while True:
            rows = []
            for n,row in enumerate(reader):
                rows.append(row)
                if n == count:
                    break

            count = yield(pd.DataFrame(rows))

testfile.csv:

i, j, k
1, 2, 4
2, 4, 8
3, 6, 12
4, 8, 16
. . . 

设置发电机

x = csvreader(s)
next(x)

请求后2行:

x.send(2)
               #returned DataFrame
                    i   j   k
               0    1   2   4
               1    2   4   8

请求下3行:

x.send(3)
               #returned DataFrame

                    i   j   k
               0    3   6   12
               1    4   8   16
               2    5   10  20

请注意,索引每次都会重新开始。可以通过指定一列作为索引来解决此问题(如果需要,可以在每行中添加一个运行计数器):

count = yield(pd.DataFrame(rows), index=<some column name>)

答案 2 :(得分:0)

不知道为什么我以前没有想到这个。 设置iterator=True将在csv文件上返回一个迭代器。然后使用get_chunk()选择要读取的行数:

reader = pd.read_csv(f, iterator=True)
​
reader.get_chunk(2)
                            i   j   k
        returns ->     0    1   2   4
                       1    2   4   8
reader.get_chunk(3)
                            i   j   k
                       2    3   6   12
        returns ->     3    4   8   16
                       4    5   10  20