Python - 读取 csv 并按列对数据进行分组
Posted
技术标签:
【中文标题】Python - 读取 csv 并按列对数据进行分组【英文标题】:Python - reading a csv and grouping data by a column 【发布时间】:2013-04-19 02:56:47 【问题描述】:我正在处理一个包含 3 列的 csv 文件,如下所示:
timeStamp, value, label
15:22:57, 849, CPU pid=26298:percent
15:22:57, 461000, JMX MB
15:22:58, 28683, Disks I/O
15:22:58, 3369078, Memory pid=26298:unit=mb:resident
15:22:58, 0, JMX 31690:gc-time
15:22:58, 0, CPU pid=26298:percent
15:22:58, 503000, JMX MB
label
列包含不同的值(比如总共 5 个),其中包括空格、冒号和其他特殊字符。
我想要实现的是针对每个指标绘制时间(在同一个图上或在不同的图上)。我可以用matplotlib
做到这一点,但我首先需要根据“标签”对[timeStamps, value]
对进行分组。
我查看了csv.DictReader
以获取标签,并查看itertools.groupby
按“标签”分组,但我正在努力以适当的“pythonic”方式做到这一点。
有什么建议吗?
【问题讨论】:
您的示例的预期输出是什么? 好吧,鉴于我的输入样本都是混合的,我可能不得不将每个 [标签] 的 [时间] 和 [值] 分开。我最初的方法是读取标签,然后读取每一行并根据标签存储每个 [time,value] 对。但这似乎是一种非常低效的方法,这就是我想研究字典和迭代器的原因。 【参考方案1】:你不需要groupby
;你想用collections.defaultdict
来收集一系列[timestamp, value]
标签键控:
from collections import defaultdict
import csv
per_label = defaultdict(list)
with open(inputfilename, 'rb') as inputfile:
reader = csv.reader(inputfile)
next(reader, None) # skip the header row
for timestamp, value, label in reader:
per_label[label.strip()].append([timestamp.strip(), float(value)])
现在per_label
是一个字典,标签作为键,[timestamp, value]
对的列表作为值;我已经去掉了空格(你的输入样本有很多额外的空格)并将value
列变成了浮点数。
对于您的(有限的)输入样本,结果是:
'CPU pid=26298:percent': [['15:22:57', 849.0], ['15:22:58', 0.0]],
'Disks I/O': [['15:22:58', 28683.0]],
'JMX 31690:gc-time': [['15:22:58', 0.0]],
'JMX MB': [['15:22:57', 461000.0], ['15:22:58', 503000.0]],
'Memory pid=26298:unit=mb:resident': [['15:22:58', 3369078.0]]
【讨论】:
这就像一个魅力!谢谢 我的文件可能最终有超过 3 列,但我想我可以处理这个。我假设我只需要在阅读器中添加:时间戳、值、标签、for row in reader
,然后使用索引、row[1]
、row[5]
等。csv.DictReader()
将用 dict 替换列表,因此您可以执行 @987654334 @ 而不是 row[0]
;如果您切换到reader.DictReader
,请忽略next()
调用。
谢谢 Martijn。在一个相关主题上,是否有一种聪明的方法可以根据键拆分我的字典,而无需进行硬编码?例如,我可以这样做 l1, l2, l3 = per_label.values() 但如果我有超过 3 个键怎么办?
@ArgyriosTzakas:为什么需要解压这些值?通过解压缩到 3 个变量,您已经在硬编码变量名称。只需遍历字典:for key in per_label:
或 for value in per_label.values():
等,或将所有值存储在列表中:values_list = per_label.values()
然后在其他地方循环。【参考方案2】:
你可以试试pandas,它提供了一个很好的数据处理结构。
将csv读取到DataFrame
In [123]: import pandas as pd
In [124]: df = pd.read_csv('test.csv', skipinitialspace=True)
In [125]: df
Out[125]:
timeStamp value label
0 15:22:57 849 CPU pid=26298:percent
1 15:22:57 461000 JMX MB
2 15:22:58 28683 Disks I/O
3 15:22:58 3369078 Memory pid=26298:unit=mb:resident
4 15:22:58 0 JMX 31690:gc-time
5 15:22:58 0 CPU pid=26298:percent
6 15:22:58 503000 JMX MB
将DataFrame
按label
分组
In [154]: g = df.groupby('label')
现在你可以得到你想要的
In [155]: g.get_group('JMX MB')
Out[155]:
timeStamp value label
1 15:22:57 461000 JMX MB
6 15:22:58 503000 JMX MB
【讨论】:
非常有趣。我将研究 Pandas,因为我将进行许多类似的数据操作。谢谢! 你能添加如何将分组的 CSV 写入文件吗?不是很简单:***.com/q/10373660/1333493【参考方案3】:你可以使用numpy.loadtxt
:
import numpy as np
result = np.loadtxt('MYFILE', usecols=(0, 1, 2),
dtype=[('time', 'S8'), ('values', np.uint), ('label', 'S33')],
delimiter=', ')
这会将您的表格加载到结构化数组中,其中时间保存为 8 个字符的字符串('S8'),值保存为无符号整数,标签保存为最多 33 个字符的字符串('S33',您可以必须调整这个大小)。然后你可以按类型索引你的值:
>>> print result['values']
[ 849 461000 28683 3369078 0 0 503000]
如果需要,可以根据标签进行过滤:
>>> print result['values'][result['label'] == 'JMX MB']
[461000 503000]
要将时间从字符串转换为浮点数,您可以使用pylab
的日期datestr2num
并将其作为转换器提供给loadtxt
:
import pylab
result = np.loadtxt('MYFILE', usecols=(0, 1, 2),
dtype=[('time', np.float), ('values', np.uint), ('label', 'S33')],
delimiter=', ', converters=0: pylab.datestr2num)
【讨论】:
以上是关于Python - 读取 csv 并按列对数据进行分组的主要内容,如果未能解决你的问题,请参考以下文章