我有一个包含以下内容的 CSV:
ID Name Series Value
250 A 3 20
250 A 3 40
250 A 3 60
251 B 4 16
251 B 4 18
251 B 4 24
251 B 4 42
Series 列表示有多少元素属于彼此,因此取第一行(不是标题行),Series = 3。所以我需要收集 Series 指定的行数,包括当前行。这样它们就可以像这样分组(按 Value):
[(20, 40, 60), (16, 18, 24, 42)]
本质上,我在 CSV 中按顺序向下移动,而 Series 告诉我接下来要收集多少行(包括当前行)。如果我们再次使用第一行,则值为 3,因此我的分组必须从当前行开始总共有 3 行。
我读入了 CSV 并将其从 Reader 转换为 List,但无法想出一个解决方案来主动更改迭代索引基于系列中找到的值的行。如果我尝试:
for row in rows...
我最终遍历每一行,我将不得不更改 rows 的值并在遍历列表时更改列表是一个坏主意。如果我尝试:
for x in range(1, len(rows)...
我无法设计出一种方法来更改当前 x 的位置。
请您参考如下方法:
如果您不能使用 pandas,只需使用 collections.defaultdict 的典型分组习惯用法:
import csv
import collections
with open("path/to/file.csv") as f:
reader = csv.DictReader(f)
grouped = collections.defaultdict(list)
for row in reader:
grouped[row['Series']].append(int(row['Value']))
这会给你一个从系列到值的方便的字典:
In [26]: grouped
Out[26]: defaultdict(list, {'3': [20, 40, 60], '4': [16, 18, 24, 42]})
如果您必须有一个元组列表:
In [28]: list(map(tuple, grouped.values()))
Out[28]: [(20, 40, 60), (16, 18, 24, 42)]
如果您要使用pandas.DataFrame,我会使用:
In [35]: [tuple(g.Value) for _,g in df.groupby('Series')]
Out[35]: [(20, 40, 60), (16, 18, 24, 42)]
评论后编辑
因此,在详细阐述您的问题之后,有几种方法。这是一个丑陋的,使用 itertools.islice 推进迭代器:
import csv
from itertools import islice
with io.StringIO(csvstring) as f:
reader = csv.DictReader(f)
grouped = []
for row in reader:
n = int(row['Series']) - 1
val = row['Value']
next_vals = (int(r['Value']) for r in islice(reader, n))
grouped.append((val,)+ tuple(next_vals))
你也可以使用 itertools.groupby:
import itertools
import operator
import csv
with open('path/to/file.csv') as f:
reader = csv.DictReader(f)
grouped = itertools.groupby(reader, operator.itemgetter('Series'))
result = []
for _, g in grouped:
result.append(tuple(int(r['Value']) for r in g))
结果:
In [48]: result
Out[48]: [(20, 40, 60), (16, 18, 24, 42)]
请注意,仅出于说明的目的,您不需要 itertools 来执行此操作,您可以按以下方式进行循环:
import csv
with open('path/to/file.csv') as f:
reader = csv.DictReader(f)
grouped = []
for row in reader:
n = int(row['Series']) - 1
val = row['Value']
sub = [val]
for _ in range(n):
sub.append(int(next(reader)['Value'])) #advance the iterator using next
grouped.append(tuple(sub))
