【问题标题】:How to split a huge csv file based on content of first column?如何根据第一列的内容拆分巨大的 csv 文件?
【发布时间】:2022-03-18 07:13:02
【问题描述】:
  • 我有一个 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排序?
  • 组id是否已经排好序了?

标签: python linux unix ubuntu


【解决方案1】:

awk 有能力:

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

【讨论】:

  • 哦,是的。这比我的方法好。但是,您缺少该命令的第一个单引号。我认为白日梦者想要("File" $1)
  • 在生成前 17 个文件后使用此方法时,我收到“awk:[ONE_OUTPUT_FILENAME] 打开的文件过多”。添加'; close("FILE" $1)' 到 awk 命令解决了这个问题。
【解决方案2】:

如果文件已经按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")

【讨论】:

  • 你可以用operator.itemgetter(0)代替丑陋的lambda
  • operator.itemgetter(0) 真的比lambda row: row[0] 丑吗?
  • @StevenRumbalski:无论如何,它都比lambda 快。我把它省略了,因为我担心它可能会令人困惑。
  • 不幸的是,groupby 被空行弄糊涂了。例如,它在看到空行[0] 时停止,然后重新开始。
  • @Pavlos:不,groupby 只有在数据已经排序的情况下才能按预期工作。 unix 命令“sort”应该是您按第一列对简单 CSV 文件进行排序所需的全部内容。
【解决方案3】:

Sed 单线:

sed -e '/^1,/wFile1' -e '/^2,/wFile2' -e '/^3,/wFile3' ... OriginalFile 

唯一的缺点是您需要放入 n -e 语句(由省略号表示,不应出现在最终版本中)。所以这条单线可能是一条很长的线。

不过,好处是它只通过文件一次,不需要排序,也不需要 python。另外,它是一个可怕的班轮!

【讨论】:

    【解决方案4】:

    如果行按group_id 排序,那么itertools.groupby 在这里会很有用。因为它是一个迭代器,所以您不必将整个文件加载到内存中;您仍然可以逐行编写每个文件。使用csv 加载文件(以防你还不知道)。

    【讨论】:

      【解决方案5】:

      如果它们按组 id 排序,您可以使用 csv 模块遍历文件中的行并输出它。你可以找到关于模块here的信息。

      【讨论】:

        【解决方案6】:

        怎么样:

        • 一次读取一行输入文件
        • split() , 上的每一行以获取 group_id
        • 对于您找到的每个新 group_id,打开一个输出文件
          • 在找到它们时将每个 groupid 添加到 set/dict 中,以便跟踪
        • 将该行写入相应的文件
        • 完成!

        【讨论】:

          【解决方案7】:

          这里有一些食物给你:

          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
          【解决方案8】:

          这是一个适用于已排序或未排序 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 的假 BIG 文件,其中包含 100 个 ID 组(每个 ID 250_000 行),我的解决方案在大约 12 秒内运行。作为比较,使用groupby() 的公认答案在大 CSV 上运行大约 10 秒;高评价的 awk 脚本运行大约一分钟。

          【讨论】:

            猜你喜欢
            • 2015-03-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-10-10
            • 1970-01-01
            • 2016-09-09
            • 2016-06-04
            • 1970-01-01
            相关资源
            最近更新 更多