2010-10-27 2 views
8

Я обрабатываю CSV-файл и подсчитываю уникальные значения столбца 4. До сих пор я кодировал эти три способа. Один использует «если ключ в словаре», второй ловушки KeyError, а третий использует «DefaultDictionary». Например (где х [3] представляет собой значение из файла и "" представляет собой словарь):Добавить новые ключи в словарь при добавлении существующих значений

Первый способ:

if x[3] in a: 
    a[x[3]] += 1 
else: 
    a[x[3]] = 1 

Второй способ:

try: 
    b[x[3]] += 1 
except KeyError: 
    b[x[3]] = 1 

Третий путь:

from collections import defaultdict 
c = defaultdict(int) 
c[x[3]] += 1 

Мой вопрос: какой путь является более эффективным ... чище ... лучше ... и т.д. Или есть лучший путь. Оба способа работают и дают один и тот же ответ, но я думал, что я буду использовать ум улья в качестве учебного примера.

Спасибо -

+0

Дополнительная редакция: Я запускаю версию 2.7. Должен добавить это раньше! –

+0

'Counter' ** медленнее ** однако он имеет гораздо больше функциональности, чем' defaultdict (int) '- см. Мой ответ. –

+0

Вы можете увеличить несколько значений? – Mohsin

ответ

6

Использование collections.Counter. Counter является синтаксически defaultdict(int), но то, что это круто о нем, что он принимает итератор в конструктор, тем самым экономя дополнительный шаг (я предполагаю, что все ваши примеры выше, завернутых в течение петли.)

from collections import Counter 
count = Counter(x[3] for x in my_csv_reader) 

До введения collections.Counter, collections.defaultdict был наиболее идиоматический для этой задачи, поэтому для пользователей < 2.7 используйте defaultdict.

from collections import defaultdict 
count = defaultdict(int) 
for x in my_csv_reader: 
    count[x[3]] += 1 
+0

Я собираюсь заявить о статусе новичков в этом пояснении. «X» в разделе «для x» относится к отдельной строке, возвращаемой csv.reader? «Я думаю, что я реализовал csv reader: my_csv_reader = csv.reader (open ('c: /Test.csv', 'rb'), delimiter = ',', quotechar = '|') d = Счетчик (x [3] для x в my_csv_reader) , но d более чем в два раза больше, чем три других метода. –

+0

Да, 'x' относится к строке, возвращаемой считывателем csv. Что вы имеете в виду, будучи более чем вдвое большим? Является ли это неправильным подсчетом? Просьба уточнить. –

+0

Уточнение: мой код неправильный, и мой вопрос был неполным! Существует оператор if, окружающий первые три метода, которые удаляют повторяющиеся значения, сопоставляя текущую строку с предыдущей строкой, прежде чем значение будет добавлено в словарь. Новый метод не в этом состоянии, поэтому я получил в общей сложности все строки, а не уникальные! –

1
from collections import Counter 
Counter(a) 
+0

«a» - это мой словарь. Является ли это «Счетчик (x [3])? –

+0

Нет« счетчика »нового в 3.1? Я запускаю 2.7. –

+0

@Count: [существует в 2.7] (http://docs.python.org/library /collections.html#collections.Counter) – SilentGhost

0

Поскольку у вас нет доступа к Counter, лучше всего это ваш третий подход. Это намного чище и легче читать. Кроме того, у него нет вечного тестирования (и ветвления), которое имеют первые два подхода, что делает его более эффективным.

6

Вы спрашивали, какая из них была более эффективной. Предполагая, что вы говорите о скорости выполнения: если ваши данные малы, это не имеет значения. Если он большой и типичный, случай «уже существует» будет происходить гораздо чаще, чем случай «не в dict». Это наблюдение объясняет некоторые результаты.

Ниже приведен код, который может использоваться с модулем timeit для изучения скорости без накладных расходов на чтение файлов. Я взял на себя смелость добавить 5-й метод, который не является неконкурентоспособным и будет работать на любом Python по меньшей мере с 1.5.2 [проверено] и далее.

from collections import defaultdict, Counter 

def tally0(iterable): 
    # DOESN'T WORK -- common base case for timing 
    d = {} 
    for item in iterable: 
     d[item] = 1 
    return d 

def tally1(iterable): 
    d = {} 
    for item in iterable: 
     if item in d: 
      d[item] += 1 
     else: 
      d[item] = 1 
    return d 

def tally2(iterable): 
    d = {} 
    for item in iterable: 
     try: 
      d[item] += 1 
     except KeyError: 
      d[item] = 1 
    return d 

def tally3(iterable): 
    d = defaultdict(int) 
    for item in iterable: 
     d[item] += 1 

def tally4(iterable): 
    d = Counter() 
    for item in iterable: 
     d[item] += 1 

def tally5(iterable): 
    d = {} 
    dg = d.get 
    for item in iterable: 
     d[item] = dg(item, 0) + 1 
    return d 

Типичный пробег (в Windows XP "Командная строка" окно):

prompt>\python27\python -mtimeit -s"t=1000*'now is the winter of our discontent made glorious summer by this son of york';import tally_bench as tb" "tb.tally1(t)" 
10 loops, best of 3: 29.5 msec per loop 

Вот результаты (мсек на петле):

0 base case 13.6 
1 if k in d 29.5 
2 try/except 26.1 
3 defaultdict 23.4 
4 Counter  79.4 
5 d.get(k, 0) 29.2 

Другое испытание времени:

prompt>\python27\python -mtimeit -s"from collections import defaultdict;d=defaultdict(int)" "d[1]+=1" 
1000000 loops, best of 3: 0.309 usec per loop 

prompt>\python27\python -mtimeit -s"from collections import Counter;d=Counter()" "d[1]+=1" 
1000000 loops, best of 3: 1.02 usec per loop 

Скорость Counter, возможно, из-за того, что он частично реализован в коде Python, тогда как defaultdict полностью находится в C (по крайней мере, в 2.7).

Обратите внимание, что Counter() это не просто «синтаксический сахар» для defaultdict(int) - он реализует полный bag аки multiset объекта - см документации для деталей; они могут спасти вас от переосмысления колеса, если вам нужна какая-то причудливая постобработка. Если все, что вы хотите сделать, это подсчитать, используйте defaultdict.

Update в ответ на вопрос @Steven Rumbalski: «»»Мне любопытно, что произойдет, если вы переместите итерацию в конструктор счетчика: d = Счетчик (итерацию) (У меня есть Python 2.6 и не может проверить его) «»»

tally6:. просто делает d = Count(iterable); return d, занимает 60,0 мсек

Вы можете посмотреть на источник (collections.py в хранилище SVN) ... вот что мой Python27\Lib\collections.py делает, когда iterable не является экземпляром сопоставления:

  self_get = self.get 
      for elem in iterable: 
       self[elem] = self_get(elem, 0) + 1 

Видел, что код где-нибудь раньше? Существует много операций, чтобы вызвать код, который можно использовать в Python 1.5.2 :-O

+0

Хорошие тайминги.Мне любопытно, что произойдет, если вы переместите iterable в конструктор Counter: 'd = Counter (iterable)'? (У меня есть python 2.6 и я не могу его протестировать.) –

+0

@Steven Rumbalski: см. Мой обновленный ответ. –

0

Использовать setdefault.

a[x[3]] = a.setdefault(x[3], 0) + 1 

setdefault получает значение указанного ключа (x[3] в данном случае), или, если он не существует, то указанное значение (0 в данном случае).

+0

Пожалуйста, объясните, почему вы думаете, что 'd [k] = d.setdefault (k, 0) + 1' было бы лучше, чтобы' d [k] = d.get (k, 0) + 1' –

+0

Да, вы правильно. Я думал об этом, потому что это установило бы ценность, но вы все равно это делаете. – kindall

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