从csv文件中逐块读取和反转数据,然后复制到新的csv文件中

时间:2018-10-29 06:32:34

标签: python python-3.x pandas csv dataset

假设我正在处理一个非常大的csv文件。因此,我只能逐块读取数据到内存中。预期的事件流应如下:

  

1)使用熊猫从csv读取数据块(例如10行)。

     

2)颠倒数据顺序

     

3)反向将每一行复制到新的csv文件。所以每个块(10行)是        从一开始就以相反的顺序写入csv。

最后,csv文件应以相反的顺序进行,并且应在不将整个文件加载到Windows OS内存的情况下完成。

我正在尝试进行时间序列预测,因此我需要数据从旧到最新(第一行最早的条目)。我无法将整个文件加载到内存中,我正在寻找一种在可能的情况下一次处理每个块的方法。

我在kaggle的Rossmann dataset中的train.csv上尝试过的数据集。您可以从此github repo

获得它

我的尝试未将行正确复制到新的csv文件中。

下面显示的是我的代码:

import pandas as pd
import csv

def reverse():

    fields = ["Store","DayOfWeek","Date","Sales","Customers","Open","Promo","StateHoliday",
              "SchoolHoliday"]
    with open('processed_train.csv', mode='a') as stock_file:
        writer = csv.writer(stock_file,delimiter=',', quotechar='"', 
                                                quoting=csv.QUOTE_MINIMAL)
        writer.writerow(fields)

    for chunk in pd.read_csv("train.csv", chunksize=10):
        store_data = chunk.reindex(index=chunk.index[::-1])
        append_data_csv(store_data)

def append_data_csv(store_data):
    with open('processed_train.csv', mode='a') as store_file:
        writer = csv.writer(store_file,delimiter=',', quotechar='"',
                                           quoting=csv.QUOTE_MINIMAL)
        for index, row in store_data.iterrows():
            print(row)
            writer.writerow([row['Store'],row['DayOfWeek'],row['Date'],row['Sales'],
            row['Customers'],row['Open'],row['Promo'],
            row['StateHoliday'],row['SchoolHoliday']])

reverse()

提前谢谢

5 个答案:

答案 0 :(得分:4)

使用bash,您可以拖尾整个文件(第一行除外),然后将其反转并以此存储:

tail -n +2 train.csv  | tac > train_rev.csv

如果要将标题保留在反向文件中,请先将其写入,然后附加反向内容

head -1 train.csv > train_rev.csv; tail -n +2 train.csv  | tac >> train_rev.csv

答案 1 :(得分:0)

如果有足够的硬盘空间,则可以分块读取,反向存储。然后以相反的顺序拾取存储的块并写入新的csv文件。

下面是Pandas的示例,该示例还使用了pickle(提高性能)和gzip(提高存储效率)。

import pandas as pd, numpy as np

# create a dataframe for demonstration purposes
df = pd.DataFrame(np.arange(5*9).reshape((-1, 5)))
df.to_csv('file.csv', index=False)

# number of rows we want to chunk by
n = 3

# iterate chunks, output to pickle files
for idx, chunk in enumerate(pd.read_csv('file.csv', chunksize=n)):
    chunk.iloc[::-1].to_pickle(f'file_pkl_{idx:03}.pkl.gzip', compression='gzip')

# open file in amend mode and write chunks in reverse
# idx stores the index of the last pickle file written
with open('out.csv', 'a') as fout:
    for i in range(idx, -1, -1):
        chunk_pkl = pd.read_pickle(f'file_pkl_{i:03}.pkl.gzip', compression='gzip')
        chunk_pkl.to_csv(fout, index=False, header=False if i!=idx else True)

# read new file to check results
df_new = pd.read_csv('out.csv')

print(df_new)

    0   1   2   3   4
0  40  41  42  43  44
1  35  36  37  38  39
2  30  31  32  33  34
3  25  26  27  28  29
4  20  21  22  23  24
5  15  16  17  18  19
6  10  11  12  13  14
7   5   6   7   8   9
8   0   1   2   3   4

答案 2 :(得分:0)

我不建议您使用pandas来解析或流式传输任何文件,因为您只是引入了额外的开销。最好的方法是从下至上读取文件。好吧,这段代码的大部分实际上来自here,在这里它接收一个文件并在生成器中返回相反的值,我相信这就是您想要的。

我所做的只是使用您提供的链接中的文件train.csv对它进行了测试,并将结果输出到新文件中。

import os

def reverse_readline(filename, buf_size=8192):
    """a generator that returns the lines of a file in reverse order"""
    with open(filename) as fh:
        segment = None
        offset = 0
        fh.seek(0, os.SEEK_END)
        file_size = remaining_size = fh.tell()
        while remaining_size > 0:
            offset = min(file_size, offset + buf_size)
            fh.seek(file_size - offset)
            buffer = fh.read(min(remaining_size, buf_size))
            remaining_size -= buf_size
            lines = buffer.split('\n')
            # the first line of the buffer is probably not a complete line so
            # we'll save it and append it to the last line of the next buffer
            # we read
            if segment is not None:
                # if the previous chunk starts right from the beginning of line
                # do not concact the segment to the last line of new chunk
                # instead, yield the segment first 
                if buffer[-1] != '\n':
                    lines[-1] += segment
                else:
                    yield segment
            segment = lines[0]
            for index in range(len(lines) - 1, 0, -1):
                if lines[index]:
                    yield lines[index]
        # Don't yield None if the file was empty
        if segment is not None:
            yield segment

reverse_gen = reverse_readline('train.csv')

with open('rev_train.csv','w') as f:
    for row in reverse_gen:
        f.write('{}\n'.format(row))

它基本上是反向读取,直到找到换行符,然后从下到上从文件中产生line。一种非常有趣的方式。

答案 3 :(得分:0)

这完全符合您的要求,但没有熊猫。它逐行读取intest.csv(与将整个文件读取到RAM相对)。它使用文件系统来执行大部分处理,该文件系统使用一系列块文件,这些块文件最后汇总到outtest.csv文件中。如果更改maxLines,则可以优化生成的块文件数与消耗的RAM(数量越大,消耗的RAM越多,但生成的块文件越少)。如果要保留CSV标头的第一行,请将keepHeader设置为True;如果设置为False,它将反转整个文件,包括第一行。

为了踢一下,我在6MB csv测试文件上使用128GB闪存驱动器的旧Raspberry Pi上运行了该程序,我认为出了点问题,因为它几乎立即返回,因此即使在较慢的硬件上也很快。它仅导入一个标准的python库函数(删除),因此非常易于移植。此代码的优点之一是它不会重新放置任何文件指针。一个限制是它不适用于数据中包含换行符的CSV文件。对于这种用例,熊猫将是读取块的最佳解决方案。

from os import remove

def writechunk(fileCounter, reverseString):
    outFile = 'tmpfile' + str(fileCounter) + '.csv'
    with open(outFile, 'w') as outfp:
        outfp.write(reverseString)
    return

def main():
    inFile = 'intest.csv'
    outFile = 'outtest.csv'
    # This is our chunk expressed in lines
    maxLines = 10
    # Is there a header line we want to keep at the top of the output file?
    keepHeader = True

    fileCounter = 0
    lineCounter = 0
    with open(inFile) as infp:
        reverseString = ''
        line = infp.readline()
        if (line and keepHeader):
            headerLine = line
            line = infp.readline()
        while (line):
            lineCounter += 1
            reverseString = line + reverseString
            if (lineCounter == maxLines):
                fileCounter += 1
                lineCounter = 0
                writechunk(fileCounter, reverseString)
                reverseString = ''
            line = infp.readline()
    # Write any leftovers to a chunk file
    if (lineCounter != 0):
        fileCounter += 1
        writechunk(fileCounter,reverseString)
    # Read the chunk files backwards and append each to the outFile
    with open(outFile, 'w') as outfp:
        if (keepHeader):
            outfp.write(headerLine)
        while (fileCounter > 0):
            chunkFile = 'tmpfile' + str(fileCounter) + '.csv'
            with open(chunkFile, 'r') as infp:
                outfp.write(infp.read())
            remove(chunkFile)
            fileCounter -= 1

if __name__ == '__main__':
    main()

答案 4 :(得分:-3)

您重复了代码块,并且根本没有利用熊猫。

@sujay kumar指出的是非常正确的,我会仔细阅读。

文件根本不大。我使用的GB的OHLCV刻度数据没有问题。如果您使用pandas.read_csv(),则不必进行分块传输。当然,这将需要一些时间,但效果会很好。除非您要使用Terrabytes。我尚未对此进行测试。

read_csv()时,您不指定任何索引。如果您这样做了,则可以致电sort_index(),有无ascending=False,具体取决于顺序。

熊猫也可以编写CSV,请改用CSV。我正在粘贴一些示例代码供您组合。

df_temp = pd.read_csv(file_path, parse_dates=True, index_col="Date", usecols=["Date", "Adj Close"], na_values=["nan"])

对系列进行排序

s = pd.Series(list('abcde'), index=[0,3,2,5,4]) s.sort_index()

注意:如果您坚持使用Pandas及其功能,则将运行已经优化的代码,不需要将整个文件加载到内存中。非常容易,几乎就像作弊:)