2011-11-06 2 views
2

У меня возникли проблемы с выяснением способа сортировки большого набора данных в более полезные данные.Как отсортировать несколько столбцов файла CSV с помощью python?

Исходный файл в формате 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, но мне не повезло. Любые советы о том, как подойти к этому, будут высоко оценены.

ответ

2

Это делает то, что вы просите в своем комментарии к ответу Реймонда - возвращает только строки с наименьшим z для каждой x, y пары:

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]] 
+0

Это, кажется, отлично работает, две проблемы, которые я реализую, - это то, что z должна быть минимальной энергией, и в настоящее время вы получаете максимум. – Daniel

+0

Не уверен, что вы имеете в виду .. он группируется по столбцам 1 и 2 и берет минимальную сумму столбца 3 для каждой группы. Вы хотите группировать столбец 4, а не 3? – agf

+0

Я немного изменил его. Если вы хотите сортировать по разным столбцам, просто измените 'x, y, z' на' x, y, e' или что угодно. – agf

0

Я думаю, что numpy's lexsort рассмотрит ваши потребности в сортировке.

В общем, я думаю, что ваши шаги:

  1. Чтение CSV в Numpy массив - вы пробовали питона csv package или Numpy-х genfromtext() function?

  2. Сортировка с помощью lexsort

  3. Обрежьте ненужные строки

EDIT: См this related SO question.

+0

это выглядит очень перспективным кажется более простым методом, то, что я пытался. Там может быть творческий способ сортировки, чтобы все нежелательные строки были внизу. – Daniel

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') 
+0

Когда я сделал этот метод, я не смог понять, как вырезать образцы другими энергиями. Поэтому в выводе у меня есть желание сохранить 1, 2 и 4-, но удалить строку 3. – Daniel

+0

Только что отредактировал ответ, чтобы показать использование списка, чтобы отфильтровать ненужные образцы. –

+0

Правильно, но это не всегда так, что каждая конкретная координата x, y нуждается в наименьшей энергии, выбранной из возможных координат z, и это не всегда положительно или есть другие отрицательные значения.В настоящее время я пытаюсь разделить строки на группы x, y, которые являются одинаковыми, а затем удаляют все, кроме самой низкой энергии. – Daniel