【问题标题】:How to Aggregate records in Python?如何在 Python 中聚合记录?
【发布时间】:2013-05-17 21:46:39
【问题描述】:

我有一个以下 csv 文件(每行是动态字符数,但列是固定的...希望我说得通)

   **001**     Math        **02/20/2013**  A

   **001**     Literature  **03/02/2013**  B

   **002**     Biology     **01/01/2013**  A

   **003**     Biology     **04/08/2013**  A

   **001**     Biology     **05/01/2013**  B

   **002**     Math        **03/10/2013**  C

我正在尝试将结果以以下格式保存到另一个 csv 文件中,该文件按学生 ID 分组并按日期升序排列。

   001,#Math;A;02/20/2013#Biology;B;05/01/2013#Literature;B;03/02/2013

   002,#Biology;A;01/01/2013#Math;C;03/10/2013

   003,#Biology;A;04/08/2013

虽然有一个限制。 输入文件很大,大约有 2 亿行。 我尝试使用 c# 并将其存储在数据库中并编写 sql 查询。它非常缓慢且不被接受。谷歌搜索后,我听说 python 对于这些操作非常强大。我是 Python 新手,开始玩代码。我非常感谢 PYTHON 专家帮助我获得上述结果。

【问题讨论】:

  • 文件中的每一行是否使用固定数量的字符?因为它可能有助于提供更快的解决方案。
  • @Markon hmmmm 我不这么认为
  • 好的。所以基本上每一行都有动态数量的字符。您应该重新编辑您的问题,因为 CSV 似乎具有固定的结构。
  • 已编辑:(每行是动态字符数,但列是固定的......希望我说得通)
  • 我认为您的数据库方法是正确的...使用数据库的批量导入来获取数据,创建一个视图以适当的键顺序返回它,然后使用 itertools.groupby 聚合结果。或者,使用 MapReduce 查看数据库并让它完成繁重的工作

标签: python python-2.7


【解决方案1】:
content='''
   **001**     Math        **02/20/2013**  A

   **001**     Literature  **03/02/2013**  B

   **002**     Biology     **01/01/2013**  A

   **003**     Biology     **04/08/2013**  A

   **001**     Biology     **05/01/2013**  B

   **002**     Math        **03/10/2013**  C
'''

from collections import defaultdict

lines = content.split("\n")
items_iter = (line.split() for line in lines if line.strip())

aggregated = defaultdict(list)

for items in items_iter:
    stud, class_, date, grade = (t.strip('*') for t in items)
    aggregated[stud].append((class_, grade, date))

for stud, data in aggregated.iteritems():
    full_grades = [';'.join(items) for items in data]
    print '{},#{}'.format(stud, '#'.join(full_grades))

输出:

003,#Biology;A;04/08/2013
002,#Biology;A;01/01/2013#Math;C;03/10/2013
001,#Math;A;02/20/2013#Literature;B;03/02/2013#Biology;B;05/01/2013

当然,这是一个丑陋的 hackish 代码,只是为了向您展示如何在 python 中完成它。处理大数据流时,请使用generatorsiterators,不要使用file.readlines()just iterate。迭代器不会一次读取所有数据,而是在您迭代它们时逐块读取,而不是更早。

如果您担心 200m 记录是否适合内存,请执行以下操作:

  1. 按学生 ID 将记录分类到单独的“桶”中(如 bucket sort

    cat all_records.txt | grep 001 > stud_001.txt # do if for other students also

  2. 按桶进行处理

  3. 合并

grep 只是一个例子。制作一个更高级的脚本(awk 或 python),它将按学生 ID 过滤,例如,过滤所有 ID

【讨论】:

  • 如果 2 亿条记录适合内存......(或者至少是非重复部分)......
  • Jakub M.:2 亿 * 1 字节 ~ 200MB。因此,如果您的线路有 10 个字节,则需要约 2GB 的内存。这里的问题不是迭代次数,而是你存储行的“哪里”。
  • @Markon 甚至给定了 1 个字节 - 考虑 Python 的对象开销,而且它变得更小更快......
  • @Jakub M 非常感谢。我会试试你的例子,看看它是如何表现的。最终,此脚本将在生产 Windows 环境中运行。 +1 快速解决方案。
  • 不过,@JonClements 关于数据库的建议是正确的方法,如果你不做一些单一的镜头,扔掉脚本
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-07-23
  • 1970-01-01
  • 2013-12-19
  • 2020-10-13
  • 2011-09-29
  • 1970-01-01
相关资源
最近更新 更多