如何根据第一列的内容拆分一个巨大的CSV文件?

11
  • 我有一个250MB以上的巨大csv文件需要上传
  • 文件格式为group_id, application_id, reading,数据可能如下所示
1, a1, 0.1
1, a1, 0.2
1, a1, 0.4
1, a1, 0.3
1, a1, 0.0
1, a1, 0.9
2, b1, 0.1
2, b1, 0.2
2, b1, 0.4
2, b1, 0.3
2, b1, 0.0
2, b1, 0.9
.....
n, x, 0.3(lets say)  
  • 我想根据 group_id 来分割文件,所以输出应该是 n 个文件,其中 n=group_id

输出

File 1

1, a1, 0.1
1, a1, 0.2
1, a1, 0.4
1, a1, 0.3
1, a1, 0.0
1, a1, 0.9
并且
File2
2, b1, 0.1
2, b1, 0.2
2, b1, 0.4
2, b1, 0.3
2, b1, 0.0
2, b1, 0.9
.....
并且
File n
n, x, 0.3(lets say)  
如何有效地做到这一点?

这些行按group_id排序了吗? - senderle
预期组ID已经排序了吗? - aweis
8个回答

19

awk有能力:

 awk -F "," '{print $0 >> ("FILE" $1)}' HUGE.csv

哦,是的。那比我的方法好。不过,你在那个命令中漏掉了第一个单引号。我认为白日梦想家想要 ("File" $1) - Mike
当我使用这种方法生成了前17个文件后,我会收到“awk:[ONE_OUTPUT_FILENAME] makes too many open files”的错误提示。将“; close(“FILE”$1)”添加到awk命令中可以解决这个问题。 - Karl Bartel

10

如果文件已经按group_id排序,你可以这样做:

import csv
from itertools import groupby

for key, rows in groupby(csv.reader(open("foo.csv")),
                         lambda row: row[0]):
    with open("%s.txt" % key, "w") as output:
        for row in rows:
            output.write(",".join(row) + "\n")

2
你可以使用 operator.itemgetter(0) 代替丑陋的 lambda。 - John La Rooy
1
operator.itemgetter(0) 是否比 lambda row: row[0] 更加简洁易懂? - Steven Rumbalski
@StevenRumbalski:无论如何,这比“lambda”更快。我没有包括它是因为我担心会让人感到困惑。 - Fred Foo
不幸的是,groupby 对空行感到困惑。例如,当它看到空行[0]时停止,然后再次开始。 - Omar
@Pavlos:不,只有在数据已经排序的情况下,groupby才能按预期工作。Unix命令"sort"应该是您需要的所有内容,以便按第一列对简单的CSV文件进行排序。 - Karl Bartel
你有什么想法可以阻止密钥被写入输出文件吗? - Nick Duddy

4
sed 命令的一行式用法:
sed -e '/^1,/wFile1' -e '/^2,/wFile2' -e '/^3,/wFile3' ... OriginalFile 

唯一的缺点是您需要输入n-e语句(由省略号表示,最终版本中不应出现)。所以这个一行代码可能会很长。
然而,优点是它只通过文件进行一次操作,不需要假设排序,也不需要使用Python。此外,它还是一个仅有一行的代码!

2
如果按照group_id排序,那么itertools.groupby会很有用。因为它是一个迭代器,所以你不需要把整个文件加载到内存中;你仍然可以逐行写入每个文件。使用csv来加载文件(如果你还不知道的话)。

1
如果它们按组ID排序,您可以使用csv模块迭代文件中的行并输出它。您可以在此处找到有关该模块的信息。

1
如何:
  • 逐行读取输入文件
  • 每行使用 , 进行 split() 操作以获取 group_id
  • 对于每个新的 group_id,打开一个输出文件
    • 在找到时将每个 groupid 添加到 set/dict 中以便跟踪
  • 将该行写入相应的文件中
  • 完成!

0

这里有一些值得你思考的东西:

import csv
from collections import namedtuple

csvfile = namedtuple('scvfile',('file','writer'))

class CSVFileCollections(object):

    def __init__(self,prefix,postfix):
        self.prefix = prefix
        self.files = {}

    def __getitem__(self,item):
        if item not in self.files:
            file = open(self.prefix+str(item)+self.postfix,'wb')
            writer = csv.writer(file,delimiter = ',', quotechar = "'",quoting=csv.QUOTE_MINIMAL)
            self.files[item] = csvfile(file,writer) 
        return self.files[item].writer

    def __enter__(self): pass

    def __exit__(self, exc_type, exc_value, traceback):
        for csvfile in self.files.values() : csvfile.file.close()


with open('huge.csv') as readFile, CSVFileCollections('output','.csv') as output:
    reader = csv.reader(readFile, delimiter=",", quotechar="'")
    for row in reader:
        writer = output[row[0]]
        writer.writerow(row)

这在我的Python 3.8.9中出现了问题:writer = output[row[0]]; TypeError: 'NoneType' object is not subscriptable - Zach Young

0

这里有一个适用于ID排序或未排序的解决方案。未排序版本唯一的开销是多次打开目标(组ID)CSV文件:

import csv

reader = csv.reader(open("test.csv", newline=""))

prev_id = None
out_file = None
writer = None

for row in reader:
    this_id = row[0]

    if this_id != prev_id:
        if out_file is not None:
            out_file.close()

        fname = f"file_{this_id}.csv"
        out_file = open(fname, "a", newline="")
        writer = csv.writer(out_file)
        prev_id = this_id

    writer.writerow(row)

这是测试输入,但现在1和2交错:

1, a1, 0.1
2, b1, 0.1
1, a1, 0.2
2, b1, 0.2
1, a1, 0.4
2, b1, 0.4
1, a1, 0.3
2, b1, 0.3
1, a1, 0.0
2, b1, 0.0
1, a1, 0.9
2, b1, 0.9

当我运行它时,我看到:

./main.py
opening file_1.csv for appending...
opening file_2.csv for appending...
opening file_1.csv for appending...
opening file_2.csv for appending...
opening file_1.csv for appending...
opening file_2.csv for appending...
opening file_1.csv for appending...
opening file_2.csv for appending...
opening file_1.csv for appending...
opening file_2.csv for appending...
opening file_1.csv for appending...
opening file_2.csv for appending...

我的输出文件看起来像:

1, a1, 0.1
1, a1, 0.2
1, a1, 0.4
1, a1, 0.3
1, a1, 0.0
1, a1, 0.9

并且

2, b1, 0.1
2, b1, 0.2
2, b1, 0.4
2, b1, 0.3
2, b1, 0.0
2, b1, 0.9

我还创建了一个假的大文件,大小为289MB,有100个ID组(每个ID有250,000行),我的解决方案大约在12秒内运行。相比之下,使用groupby()的被接受答案在大型CSV上运行约10秒;高评分的awk脚本则需要约1分钟。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接