我有一个大型CSV文件,该文件按其几个列排序,我们称这些列为sorted_columns
。
我想对这些sorted_columns
进行分组,并对每个分组应用一些逻辑。
该文件不完全适合内存,因此我想分块读取它并在每个块上执行groupby
。
我注意到的是,即使文件已按这些列排序,也不会保留组的顺序。
最终,这就是我想要做的:
import pandas as pd
def run_logic(key, group):
# some logic
pass
last_group = pd.DataFrame()
last_key = None
for chunk_df in df:
grouped_by_df = chunk_df.groupby(sorted_columns, sort=True)
for key, group in grouped_by_df:
if last_key is None or last_key == key:
last_key = key
last_group = pd.concat([last_group, group])
else: # last_key != key
run_logic(last_key, last_group)
last_key = key
last_group = group.copy()
run_logic(last_key, last_group)
但这是行不通的,因为groupby
不能保证保留组的顺序。如果两个连续的块中都存在相同的key
,则不能保证在第一个块中它将是最后一个组,在下一个块中它将是第一个。
我尝试将groupby
更改为使用sort=False
,还尝试更改列的顺序,但这没有帮助。
如果密钥已经在原始文件中排序,那么有人对如何保留组的顺序有任何想法吗?
还有其他方法可以一次从文件中读取完整的组吗?
参考方案
itertools.groupby
将返回键和该键分组的所有值的迭代器。
如果您的文件已经按照所需的键进行排序,那么就可以了。
groupby函数将为您处理几乎所有事情。
从documentation:
groupby()
的操作类似于Unix中的uniq
过滤器。每当键函数的值更改时,它都会生成一个中断或新组(这就是为什么通常需要使用相同的键函数对数据进行排序的原因)。这种行为与SQL的GROUP BY有所不同,后者会按通用元素的输入顺序来聚合它们。
run_logic是要应用于记录组的任何业务逻辑。这个例子只是简单地计算迭代器中的观察次数。
data_iter只需为每个CSV发出1行。只要您的文件按所需的字段排序,就不需要将整个文件读入内存。
块使用groupby通过输入行的前3个字段对输入迭代器进行分组。它产生键和与该键关联的值的相应迭代器。
#!/usr/bin/env python3
import csv
from itertools import groupby
def run_logic(key, group):
cntr = 0
for rec in group:
cntr = cntr + 1
return (key, cntr)
def data_iter(filename):
with open(filename, "r") as fin:
csvin = csv.reader(fin)
for row in csvin:
yield row
def chunks(diter):
for chunk, iter_ in groupby(diter, key=lambda x: x[0:3]):
yield (chunk, iter_)
if __name__ == "__main__":
csviter = data_iter("test.csv")
chunk_iter = chunks(csviter)
for chunk, iter_ in chunk_iter:
print(run_logic(chunk, iter_))
输入数据
['1', '1', '1', 'a', 'a', 'a', 'a']
['1', '1', '1', 'b', 'b', 'b', 'b']
['1', '1', '1', 'c', 'c', 'c', 'c']
['1', '1', '1', 'd', 'd', 'd', 'd']
['1', '1', '1', 'e', 'e', 'e', 'e']
['2', '1', '1', 'a', 'a', 'a', 'a']
['2', '1', '1', 'd', 'd', 'd', 'd']
['2', '1', '1', 'e', 'e', 'e', 'e']
['2', '1', '1', 'b', 'b', 'b', 'b']
['2', '1', '1', 'c', 'c', 'c', 'c']
['3', '1', '1', 'e', 'e', 'e', 'e']
['3', '1', '1', 'b', 'b', 'b', 'b']
['3', '1', '1', 'c', 'c', 'c', 'c']
['3', '1', '1', 'a', 'a', 'a', 'a']
['3', '1', '1', 'd', 'd', 'd', 'd']
分组数据
组:['1','1','1']
['1', '1', '1', 'a', 'a', 'a', 'a']
['1', '1', '1', 'b', 'b', 'b', 'b']
['1', '1', '1', 'c', 'c', 'c', 'c']
['1', '1', '1', 'd', 'd', 'd', 'd']
['1', '1', '1', 'e', 'e', 'e', 'e']
组:['2','1','1']
['2', '1', '1', 'a', 'a', 'a', 'a']
['2', '1', '1', 'd', 'd', 'd', 'd']
['2', '1', '1', 'e', 'e', 'e', 'e']
['2', '1', '1', 'b', 'b', 'b', 'b']
['2', '1', '1', 'c', 'c', 'c', 'c']
组:['3','1','1']
['3', '1', '1', 'e', 'e', 'e', 'e']
['3', '1', '1', 'b', 'b', 'b', 'b']
['3', '1', '1', 'c', 'c', 'c', 'c']
['3', '1', '1', 'a', 'a', 'a', 'a']
['3', '1', '1', 'd', 'd', 'd', 'd']
应用业务逻辑
组:['1','1','1']
(['1', '1', '1'], 5)
组:['2','1','1']
(['2', '1', '1'], 5)
组:['3','1','1']
(['3', '1', '1'], 5)
pandas DataFrame:根据另一列中的布尔值计算总和 - python我对Python相当陌生,我尝试在pandas中模拟以下逻辑我目前正在循环抛出行,并希望对前几行的AMOUNT列中的值求和,但只求和最后一次看到的“ TRUE”值。实际数据似乎效率低下(我的数据框大约有500万行)?想知道用Python处理这种逻辑的有效方法是什么?逻辑:逻辑是,如果FLAG为TRUE,我想对前几行的AMOUNT列中的值求和,但只求和最后一次…
在返回'Response'(Python)中传递多个参数 - python我在Angular工作,正在使用Http请求和响应。是否可以在“响应”中发送多个参数。角度文件:this.http.get("api/agent/applicationaware").subscribe((data:any)... python文件:def get(request): ... return Response(seriali…
Python exchangelib在子文件夹中读取邮件 - python我想从Outlook邮箱的子文件夹中读取邮件。Inbox ├──myfolder 我可以使用account.inbox.all()阅读收件箱,但我想阅读myfolder中的邮件我尝试了此页面folder部分中的内容,但无法正确完成https://pypi.python.org/pypi/exchangelib/ 参考方案 您需要首先掌握Folder的myfo…
python pandas:按行对条件进行分组 - python我有一个大的pandas数据框,试图从中形成一些行的对。我的df如下所示:object_id increment location event 0 1 d A 0 2 d B 0 3 z C 0 4 g A 0 5 g B 0 6 i C 1 1 k A 1 2 k B ... ... ... ... 对象ID描述特定的对象。增量是每次发生某事(跟踪订单)时…
Python-Excel导出 - python我有以下代码:import pandas as pd import requests from bs4 import BeautifulSoup res = requests.get("https://www.bankier.pl/gielda/notowania/akcje") soup = BeautifulSoup(res.cont…