2014-08-07 4 views
0

Я немного новичок в python и стараюсь сначала изучить python для анализа данных. У меня есть файл CSV, который выглядит так (ID, категория):Быстрая сортировка файла csv?

67512367,0 
67567,5 
89789789,5 
... 
... 
876289347,10 
638, 10 
... 
... 
98723489,20 
3828909, 20 
... 
...<going upto> 
78789789, 200 
978789, 200 

То, что я хотел бы сделать, это сортировать идентификаторы в соответствии с их категориями. Так, в конце концов, я хотел бы что-то, выглядит следующим образом:

list_5 = [67567, 89789789, .., ] 
list_10 = [876289347, 638, ...] 
list_200 = [78789789, 978789, ...] 

Проблема заключается в том, что файл CSV имеет около 1,5 миллионов словарных статей. Таким образом, я загрузить их в данный момент с помощью izip и читателя Csv так:

data = izip(csv.reader(open("data.csv", "rb"))) 

и у меня есть записи в data, которые я могу перебирать с помощью просто:

for i in data: 
    print i 
    #print i[0][0] # for ids 
    #print i[0][1] # for category 

Теперь, я знаю, что я могу использовать конструкцию if-else/elif, чтобы проверить, есть ли i[0][1]==5, а затем добавить i[0][0] (идентификатор) в список, но это будет выглядеть очень медленно - и мой список большой.

Мне было интересно, есть ли какой-либо другой элегантный способ (может быть, что-то использовать itertools?) До bucketize идентификаторы, основанные на значении второго столбца (категории).

+3

См. Http://stackoverflow.com/q/25184415/3001761 - здесь будет работать то же решение ('defaultdict (list)') '' 5: [67567, 89789789, ...]. ..} '. – jonrsharpe

+0

Это довольно удивительно - никогда не знала о коллекциях раньше. Решила проблему через минуту. Спасибо за тонну за это. Если бы вы могли ответить на этот вопрос, я приму это. Еще раз спасибо! – JohnJ

+0

Просто указывая, что ваш вызов 'izip' на самом деле для вас ничего не делает. Просто удалите его, и он будет работать так же. – Dougal

ответ

1

Поскольку вы говорите, что вы «пытаетесь изучить python для анализа данных в первую очередь», вам обязательно нужно взглянуть на pandas, чтобы у вас был лучший инструментарий для игры. (Это не означает, что, конечно, не полезно знать, как создавать инструменты, похожие на панды, с нуля. Но по моему опыту даже с пандами у вас есть более чем достаточные шансы реализовать свои навыки на Python, и более интересно выяснить как сделать что-то реальное, чем выяснить, как переопределять базовую функциональность)

Вы можете прочитать файл в DataFrame (например, листе Excel) с помощью read_csv:.

>>> import pandas as pd 
>>> df = pd.read_csv("group.csv", names=["ID", "category"]) 
>>> df 
      ID category 
0 67512367   0 
1  67567   5 
2 89789789   5 
3 876289347  10 
4  638  10 
5 98723489  20 
6 3828909  20 
7 78789789  200 
8  978789  200 

А потом строить словарь категорий к использованию с использованием groupby:

>>> {k: v.tolist() for k,v in df.groupby("category")["ID"]} 
{0: [67512367], 200: [78789789, 978789], 10: [876289347, 638], 20: [98723489, 3828909], 5: [67567, 89789789]} 

Хотя вы также можете совершать много операций на объектах groupby (например, статистику вычислений и т. Д.), Поэтому, если честно, мне не нужен фактический список индексов. Чаще всего я просто хочу «делать эту операцию в каждой группе», но YMMV.

+0

Спасибо DSM за это - я посмотрю на панды. Вы посмотрели комментарий johnsharpes? Мне кажется, что это самый быстрый способ сделать сортировку. Мысли? – JohnJ

+0

@JohnJ: немного смущает назвать это своего рода. Я бы не стал беспокоиться о производительности, хотя: 1.5M строк довольно мало, поэтому все, что вы делаете с ним, которое должно быть линейным, должно быть быстрым. Возможно, вы даже доминируете на времени ввода-вывода, хотя это только предположение. – DSM

0

Вы можете использовать itertools.groupby:

# test_big.csv was 1.74GB 
def test(): 
    from itertools import groupby 
    bucketized_grouped_keys = {} 
    with open('test_big','r') as f: 
     for key, group in groupby(f,lambda T: T.split(',')[1].rstrip('\n')): 
       bucketized_grouped_keys.update({key:group}) 
    print(bucketized_grouped_keys.keys()) 

if __name__ == '__main__': 
    import cProfile 
    cProfile.run('test()','test.profile') 
    import pstats 
    stats = pstats.Stats('test.profile') 
    stats.strip_dirs().sort_stats('time').print_stats() 

Выход:

['47', '44', '2', '42', '49'] 
Thu Aug 07 10:55:39 2014 test.profile 

     445620949 function calls in 239.002 seconds 

    Ordered by: internal time 

    ncalls tottime percall cumtime percall filename:lineno(function) 
148540313 95.738 0.000 178.467 0.000 csv_test.py:6(<lambda>) 
     1 60.535 60.535 239.002 239.002 csv_test.py:2(test) 
148540313 55.128 0.000 55.128 0.000 {method 'split' of 'str' objects} 
148540313 27.601 0.000 27.601 0.000 {method 'rstrip' of 'str' objects} 

     1 0.000 0.000 0.000 0.000 {open} 
     1 0.000 0.000 239.002 239.002 <string>:1(<module>) 
     5 0.000 0.000 0.000 0.000 {method 'update' of 'dict' objects 
} 
     1 0.000 0.000 0.000 0.000 {method 'keys' of 'dict' objects} 
     1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Prof 
iler' objects} 

Ключом функция:

lambda T: T.split(',')[1].rstrip('\n') 

, который так же, как:

def T(item): 
    return item.split(',')[1].rstrip('\n') 

Чтобы получить доступ к группам:

category_name = '42' 
bucketized_grouped_keys[category_name] 
+0

Хотя ваше решение работает так, как ожидалось, мне нравится то, что предложил johnrsharpe. В принципе, это всего две строки: 'd = defaultdict (list)', а затем 'for i в данных: d [i [0] [1]]. Append (i [0] [0])' и это он. d ['5'] затем дает мне все значения bucketized в качестве аккуратного списка. Основная информация, кажется, находится здесь: https://docs.python.org/2/library/collections.html#collections.defaultdict – JohnJ

0

вызова сортировать дважды.

sortedCats = sorted(data, key=attrgetter('category_name')) 
sortedIds = sorted(sortedCats, key=attrgetter('id')) 

Это работает, потому что алгоритм сортировки используется в питона timsort, а второй сорт использует группы, созданные первого рода, чтобы сделать меньше работы.

Смежные вопросы