如何使用Python对CSV文件的多列进行排序?

3

我有困难,不知道如何将大型数据集排序为更有用的数据。

下面显示了CSV格式中的原始文件- 数据表示x、y、z位置和最终能量。x、y、z坐标的范围相当大,下面是一个小片段- 基本上这是在一个体积内进行能量搜索。

-2.800000,-1.000000,5.470000,-0.26488315
-3.000000,1.000000,4.070000,-0.81185718
-2.800000,-1.000000,3.270000,1.29303723
-2.800000,-0.400000,4.870000,-0.51165026

很遗憾,将数据在所需的四个维度上绘制非常困难,因此我需要对这些数据进行裁剪。我希望以这样一种方式来完成,即将体积转化为最低能量z轴上的表面。在较小的数据集上,这很简单,在Excel中按X、Y和能量排序,然后删除所有高于最低能量的能量。对于小数据集来说,这已经足够简单了,但很快就会变得棘手。
我尝试过各种方法来做到这一点,例如拆分CSV并使用sort命令,但是效果不佳。如何解决这个问题的任何建议都将不胜感激。
3个回答

4
这将按每个对返回具有最小z的行,与您在Raymond答案评论中提出的要求一致。
from operator import itemgetter
from itertools import groupby
from csv import reader


def min_z(iterable):
    # the data converted from strings to numbers
    floats = [[float(n) for n in row] for row in iterable]
    # the data sorted by x, y, z
    floats.sort(key=lambda (x, y, z, e): (x, y, z))
    # group the data by x, y
    grouped_floats = groupby(floats, key=itemgetter(slice(0, 2)))
    # return the first item from each group
    # because the data is sorted
    # the first item is the smallest z for the x, y group
    return [next(rowgroup) for xy, rowgroup in grouped_floats]


data = """-2.800000,-1.000000,5.470000,-0.26488315
-3.000000,1.000000,4.070000,-0.81185718
-2.800000,-1.000000,3.270000,1.29303723
-2.800000,-0.400000,4.870000,-0.51165026""".splitlines()


print min_z(reader(data))

输出:

[[-3.0, 1.0, 4.07, -0.81185718], 
 [-2.8, -1.0, 3.27, 1.29303723], 
 [-2.8, -0.4, 4.87, -0.51165026]]

这似乎很好地运作了,我遇到的两个问题是z需要是最小能量,而目前你得到的是最大能量。 - Daniel
不确定您的意思...它按列1和2进行分组,并针对每个组取列3的最小值。您是否想要按列4而不是3进行分组? - agf
我稍微修改了一下。如果您想按不同的列排序,只需将 x,y,z 改为 x,y,e 或其他任何列即可。 - agf
啊,现在结果更有意义了。是的,第四列是我们需要取最小值的那一列。 - Daniel
完美运行,只需要打开一个csv文件!谢谢您的帮助。 - Daniel
@Ophion 在我执行 min_z(reader(data)) 的地方,改为执行 min_z(reader(open('path/file.name', 'r'))). - agf

2
使用csv.reader将数据读入元组列表中,按照(x, y)值对数据进行排序。为了清晰明了,使用named tuples来标识字段。
然后使用itertools.groupby来聚类相关的(x, y)数据点。对于每个组,使用min来隔离能量最低的一个。
>>> import csv, collections, itertools

>>> raw_data = '''\
-2.800000,-1.000000,5.470000,-0.26488315
-3.000000,1.000000,4.070000,-0.81185718
-2.800000,-1.000000,3.270000,1.29303723
-2.800000,-0.400000,4.870000,-0.51165026
'''.splitlines()

>>> Sample = collections.namedtuple('Sample', ['x', 'y', 'z', 'energy'])
>>> data = [Sample(*row) for row in csv.reader(raw_data)]
>>> data.sort(key=lambda s: (s.x, s.y))
>>> for xy, group in itertools.groupby(data, key=lambda s: (s.x, s.y)):
        print min(group, key=lambda s: s.energy)


Sample(x='-2.800000', y='-0.400000', z='4.870000', energy='-0.51165026')
Sample(x='-2.800000', y='-1.000000', z='5.470000', energy='-0.26488315')
Sample(x='-3.000000', y='1.000000', z='4.070000', energy='-0.81185718')

当我使用这种方法时,我无法弄清如何剪切掉其他能量的样本。因此,在输出中,我想保留1、2和4行,但删除第3行。 - Daniel
刚刚编辑了答案,展示了使用列表推导式来过滤掉不需要的样本。 - Raymond Hettinger
正确,但并非总是如此。每个特定的x,y坐标需要从可能的z坐标中选择最低能量,而这并不总是正数或存在其他负值。目前我正在尝试将线条分成相同的x,y组,并删除除最低能量之外的所有内容。 - Daniel
好的,我已经编辑过了,按(x,y)坐标显示分组,并找到每个组的最小值。当按浮点值分组时,请注意,即使是很小的差异也会创建不同的组。 - Raymond Hettinger

0

我认为numpy的lexsort函数可以满足您的排序需求。

一般来说,我认为您需要执行以下步骤:

  1. 将csv文件读入numpy数组--您尝试过Python的csv包或numpy的genfromtext()函数吗?

  2. 使用lexsort进行排序

  3. 去掉不必要的行

编辑:请参阅这个相关的SO问题


看起来非常有前途,似乎比我尝试的方法更简单。可能有一种创造性的方法可以将所有不需要的行排序,使它们位于底部。 - Daniel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接