尝试将txt转换为csv并连接到一个文件时发生内存错误

xqnpmsa8  于 2023-01-06  发布在  其他
关注(0)|答案(1)|浏览(174)

我有大约400个txt文件,每个文件的大小在几MB到1 GB之间。这些txt文件包含长度分隔的数据。作为一个例子,考虑这两个txt文件(源文件中没有头):
File1.txt:

AB;12345A;400E;500C
CD;12345B;400E;500C

File2.txt:

EC;12345E;400E;500C
EF;12345E;400D;500E

(Once再次:虽然有分号,但这些文件不是“;“-separated它们是按长度分隔的,我用panda read_fwf读入的。)

我的第一个方法是读取每个txt文件,将其存储在 Dataframe 中,并将其附加/连接到一个大 Dataframe :

import pandas as pd
import glob
import csv

path = r'C:\folder\Test'
all_files = glob.glob(path + "\*.txt")

for filename in all_files:
    print(filename)
    col_lengths = {'Column1': range(0, 2), 
                   'Column2': range(3, 9), 
                   'Column3': range(10, 14),
                   'Column4': range(15, 19),
                  }
    col_lengths = {k: set(v) for k, v in col_lengths.items()}

    df = pd.read_fwf(filename, colspecs=[(min(x), max(x)+1) for x in col_lengths.values()], encoding='cp1252', header=None, names=col_lengths.keys(), 
               converters={'Column1':lambda x : str(x),
                          'Column2':lambda x : str(x),
                          'Column3':lambda x : str(x),
                          'Column4':lambda x : str(x),
                          }
               )
   
    li.append(df)

frame = pd.concat(li, axis=0, ignore_index=True)

frame.to_csv(r'C:\folder\Python test.csv', encoding='utf-8', index=False, sep=";", decimal=",", quoting=csv.QUOTE_NONE)

这段代码适用于示例文件,但不适用于我的原始数据,在那里我得到一个内存错误(“无法分配......用于具有形状......和数据类型对象的数组")

我的第二种方法是读入每个txt文件,将每个文件存储为csv文件,然后再次读取每个文件,并将其连接到一个大的 Dataframe 中:

import pandas as pd
import glob
import csv
import os

path = r'C:\folder\Test'
all_files = glob.glob(path + "\*.txt")

for filename in all_files:
    col_lengths = {'Column1': range(0, 2), 
                   'Column2': range(3, 9), 
                   'Column3': range(10, 14),
                   'Column4': range(15, 19),
                  }
    col_lengths = {k: set(v) for k, v in col_lengths.items()}

    df = pd.read_fwf(filename, colspecs=[(min(x), max(x)+1) for x in col_lengths.values()], encoding='cp1252', header=None, names=col_lengths.keys(), 
               converters={'Column1':lambda x : str(x),
                          'Column2':lambda x : str(x),
                          'Column3':lambda x : str(x),
                          'Column4':lambda x : str(x),
                          }
               )
    # convert each txt file to a csv file
    df.to_csv(os.path.join(path, os.path.splitext(os.path.basename(filename))[0] + '.' + "csv"), encoding='utf-8', index=False, sep=";", decimal=",", date_format='%d.%m.%Y', quoting=csv.QUOTE_MINIMAL)

# read in csv files and concatenate
path = r'C:\folder\Test'

all_files = glob.glob(os.path.join(path, "*.csv"))

dtypes= {"Column1": str, "Column2": str, "Column3": str, "Column4": str}

df = pd.concat((pd.read_csv(f, sep=";", encoding='utf-8', dtype=dtypes, decimal=",") for f in all_files), ignore_index=True)

df.to_csv(os.path.join(path,"File.csv"), encoding='utf-8', index=False, sep=";", decimal=",", quoting=csv.QUOTE_MINIMAL)

不幸的是,同样的问题再次出现:使用示例文件,但使用原始数据时再次出现内存错误。
问题总是出在pd.concat上。我原以为第二种方法可能更好,但结果内存错误出现得更早。

作为第三种方法我尝试读入txt并导出为pickle文件,而不是csv,然后尝试连接这些pickle文件:

import pandas as pd
import glob
import csv

path = r'C:\folder\Test'
all_files = glob.glob(path + "\*.pkl")

li = []

for filename in all_files:
    print(filename)
    
    df = pd.read_pickle(filename)
   
    li.append(df)

frame = pd.concat(li, axis=0, ignore_index=True)

frame.to_csv(r'C:\folder\Test\Test.csv', encoding='utf-8', index=False, sep=";", decimal=",", quoting=csv.QUOTE_MINIMAL)

它似乎运行得更快了,但很快又出现了内存错误。
在尝试其他数据格式如parquet,hdf 5或其他技术如dask之前,我的问题是:有没有一种更有效的方法让它运行,以某种方式让我绕过内存错误?
不幸的是,我的RAM大小被限制在16 GB。Windows 64 X和Python 64 X。当然操作系统和其他后台进程会占用一些可用的RAM。也许有一个选项可以改变系统参数并利用更多的RAM,因为Python Pandas没有使用全部的可能性,只是抛出了这个错误,但我并不是一个玩这样的系统参数的忠实粉丝,正如我认为根本原因可能是我如何处理这个问题的方式,我不能肯定地说,更多的RAM,甚至增加物理RAM大小到32 GB将解决这个问题。
(关于我上面的代码:我知道lambda x : str(x)可以被简化为string,但无论如何,这并没有改变我的问题,仍然是内存错误问题。)

qyyhg6bp

qyyhg6bp1#

很有可能你根本不需要Pandas;只需读取每个文件并将它们流输出到一个文件:

import glob
import csv
import os

path = r"C:\folder\Test"
all_files = glob.glob(os.path.join(path, "*.txt"))

with open(os.path.join(path, "output.csv"), "w", encoding="utf-8") as outf:
    cw = csv.writer(outf, delimiter=";", quoting=csv.QUOTE_MINIMAL)
    cw.writerow(["Column1", "Column2", "Column3", "Column4"])
    for i, filename in enumerate(all_files):
        with open(filename, "r", encoding="cp1252") as inf:
            for line in inf:
                col1 = line[0:2]
                col2 = line[3:9]
                col3 = line[10:14]
                col4 = line[15:19]
                cw.writerow([col1, col2, col3, col4])

然而,如果你想使用Pandas,你同样可以将 Dataframe 流传输到你事先打开的单个文件句柄中:

import pandas as pd
import glob
import csv
import os

col_lengths = {
    "Column1": range(0, 2),
    "Column2": range(3, 9),
    "Column3": range(10, 14),
    "Column4": range(15, 19),
}
converters = {col: str for col in col_lengths}
colspecs = [(x.start, x.end + 1) for x in col_lengths.values()]

path = r"C:\folder\Test"
all_files = glob.glob(os.path.join(path, "*.txt"))

with open(os.path.join(path, "output.csv"), "wb") as f:
    for i, filename in enumerate(all_files):
        df = pd.read_fwf(
            filename,
            colspecs=colspecs,
            encoding="cp1252",
            header=None,
            names=col_lengths,
            converters=converters,
        )
        df.to_csv(
            f,
            header=(i == 0),  # write header only for first file
            encoding="utf-8",
            index=False,
            sep=";",
            decimal=",",
            date_format="%d.%m.%Y",
            mode="wb",
            quoting=csv.QUOTE_MINIMAL,
        )

相关问题