使用Python在一个目录中连接10,000个CSV文件- Pandas太慢

x6492ojm  于 2023-03-21  发布在  Python
关注(0)|答案(6)|浏览(146)

我想将10,000个单行CSV文件连接成一个10,000行CSV文件,最好使用Python。目录中只包含CSV文件。我曾尝试用Pandas这样做,但速度很慢,似乎需要大约一个小时。有没有更快的方法?我目前的代码是:

files = glob.glob("PATH/*.csv")
df = pd.concat(map(pd.read_csv, files))
uinbv5nw

uinbv5nw1#

下面是一个简单的普通运行的解决方案,它一次将一行读入内存。

import glob

inputs = glob.glob("*.csv")
with open("output.csv", "w") as output:
  first = True
  for file in inputs:
    with open(file, "r") as inputfile:
      for no, line in enumerate(inputfile, 1):
        if no == 1 and not first:
          continue
        first = False
        output.write(line)

这需要注意删除每个输入文件的第一行(第一行除外),假设是CSV文件的标题只有一行。
(Do帮你自己一个忙,停止在CSV文件中使用标题。这样代码就可以简化一些。)
这不会尝试验证CSV文件是否具有相同的结构。您应该确保它们具有相同的列数以及相同列中的相同数据。
如果你的数据混合了Windows和Unix的换行符,也会有问题。一个简单的调整是以二进制模式打开文件,即使它们在技术上是文本文件。

iyfjxgzm

iyfjxgzm2#

使用csv模块跳过标题并创建行列表。

import csv
import glob
import pandas as pd

rows = []
for filename in glob.glob("*.csv"):
   with open(filename, newline="") as f:
      reader = csv.reader(f)
      columns = next(reader)
      rows.extend(reader)

df = pd.DataFrame(rows, columns=columns)
798qvoo8

798qvoo83#

您可以使用multiprocessing

import pandas as pd
import pathlib
import multiprocessing as mp

def read_csv(files):
    return pd.concat([pd.read_csv(filename) for filename in files], axis=1)

if __name__ == '__main__':
    batch = 10
    files = sorted(pathlib.Path('PATH').glob('*.csv'))
    with mp.Pool(mp.cpu_count()) as pool:
        dfs = pool.map(read_csv, (files[i:i+batch]
                                  for i in range(0, len(files), batch)))
    df = pd.concat(dfs)

注意:如果使用Python>=3.10,则可以将(files[i:i+batch] for i in range(0, len(files), batch))替换为itertools.pairwise(files, batch)

bjg7j2ky

bjg7j2ky4#

这是假设没有头文件,csv文件看起来像这样:

<start of file>
1,test,12.5,hello there,0
<end of file>

如果你真的需要一个python解决方案,这里有一个纯文本版本。

import glob

files = glob.glob('*.csv')

with open('output.csv', 'a') as output:

    for fname in files:
        with open(fname, 'r') as input:
            output.write(input.readline())

请记住,这只适用于单行csv文件,因为我们只在每个输入文件中调用readline()一次,即只阅读第一行。此外,如果没有换行符,readline会自动在行尾添加换行符。

ztmd8pv5

ztmd8pv55#

试着使用两极,正如作者自我推销的那样:“ lightning 般快速的DataFrame库for Rust and Python”。对于我的用例,我有110个文件~ 1 mb,polars是21倍快:

%%timeit
#Polars
files = glob.glob(r"U:\Negocios CETIP\*.CSV")
df_pl = pl.read_csv(files[0], sep=";", skip_rows=1, infer_schema_length=0)
for file in files[1:]:
    df_pl = pl.concat([df_pl, pl.read_csv(file, sep=";", skip_rows=1, 
                       infer_schema_length=0)], how="diagonal")

3.2 s ± 57.5 ms/循环(7次运行的平均值±标准差,每次运行1个循环)

%%timeit 
#Pandas
files = glob.glob(r"U:\Negocios CETIP\*.CSV")
df_pd = pd.read_csv(files[0], delimiter=";")
for file in files[1:]:
    df_pd = pd.concat([df_pd, pd.read_csv(file, delimiter=";")])

1分钟8 s ± 784 ms/循环(7次运行的平均值±标准差,每次运行1个循环)
Polars甚至还有其他我没有使用过的优化方法,例如,lazy函数(它允许从库中并行化)和正确推断数据类型。
另外,尝试调用polars(或panda)concatening的次数要少一些。最好将所有读取的文件放在一个列表中,然后将它们全部进行concatening。如果你的内存无法处理这个问题,可以尝试在读取几个文件后使用concatening。在我的测试中,它快了9倍:

%%timeit
#Polars adv
files = glob.glob(r"U:\Negocios CETIP\*.CSV")
df_pl2 = []
for file in files:
    df_pl2.append(pl.read_csv(file, sep=";", skip_rows=1, infer_schema_length=0))
df_pl2 = pl.concat(df_pl2, how="diagonal")

362 ms ± 8.6 ms/循环(7次运行的平均值±标准差,每次运行1个循环)
只是对代码的一点解释:网址:pl.read_csv(文件,分隔符=";“,跳过行=1,推断架构长度=0)
1.我跳过了第一行,因为在我的文件中,第一行是无用的,标题在第二行。

  1. inferschema_length=0是一个不好的实践,它强制polars将所有列标识为string。我只是懒得给予正确的dtype。您可能不需要它
    pl.concat(df_pl2,how=“diagonal”)当 Dataframe 没有完全相同的列时(我的一些文件缺少一些列),diagonal在polar中很重要
arknldoa

arknldoa6#

编辑:我第一次用Dask发布了一个答案。但后来我看到了this answer
我觉得你应该去看看。

相关问题