Оптимальный / самый быстрый способ подсчета дат в списке python

У меня есть список дат, и цель состоит в том, чтобы подсчитывать появления каждой даты , сохраняя при этом порядок, в котором они отображаются в исходном списке. Рассмотрим следующий пример:

Список only_dates выглядит так:

 [datetime.date(2017, 3, 9), datetime.date(2017, 3, 10), datetime.date(2017, 3, 10), datetime.date(2017, 3, 11)] 

Я пытаюсь использовать groupby :

 import itertools day_wise_counts = [(k, len(list(g))) for k, g in itertools.groupby(only_dates)] print(str(day_wise_counts)) 

Это печатает

 [(datetime.date(2017, 3, 10), 1), (datetime.date(2017, 3, 9), 1), (datetime.date(2017, 3, 10), 1), (datetime.date(2017, 3, 11), 1)] 

Я понимаю, что это происходит потому, что в конечном счете каждый объект даты рассматривается как другой при группировке.

Я ожидал, что выход будет следующим:

 [(datetime.date(2017, 3, 9), 1), (datetime.date(2017, 3, 10), 2), (datetime.date(2017, 3, 11), 1)] 

Я не обязательно ищу список кортежей. Выход словаря также будет достаточным, если сохранится первоначальный порядок дат. ( OrderedDict ).

Как я могу это достичь?

Обновление. Предлагаются различные подходы, которые все работают хорошо. Но я должен был упомянуть, что я буду делать эту операцию для большого количества данных. Так что было бы здорово, если бы ваше решение было оптимальным с точки зрения времени работы. Если хотите, отредактируйте свой ответ / комментарий.

Обновление 2: размер данных может составлять до 1 миллиона строк.

2 Solutions collect form web for “Оптимальный / самый быстрый способ подсчета дат в списке python”

Действительно, вы можете использовать OrderedDict :

 from collections import OrderedDict import datetime inp = [datetime.date(2017, 3, 9), datetime.date(2017, 3, 10), datetime.date(2017, 3, 10), datetime.date(2017, 3, 11)] odct = OrderedDict() for item in inp: try: odct[item] += 1 except KeyError: odct[item] = 1 print(odct) 

который печатает:

 OrderedDict([(datetime.date(2017, 3, 9), 1), (datetime.date(2017, 3, 10), 2), (datetime.date(2017, 3, 11), 1)]) 

Вы также запросили тайминги, так что вот они:

 from collections import OrderedDict, Counter import datetime import random # Functions def ordereddict(inp): odct = OrderedDict() for item in inp: try: odct[item] += 1 except KeyError: odct[item] = 1 return odct def dawg(inp): cnts=Counter(inp) seen=set() return [(e, cnts[e]) for e in inp if not (e in seen or seen.add(e))] def chris1(inp): return [(item, inp.count(item)) for item in list(OrderedDict.fromkeys(inp))] def chris2(inp): c = Counter(inp) return [(item,c[item]) for item in list(OrderedDict.fromkeys(inp))] # Taken from answer: https://stackoverflow.com/a/23747652/5393381 class OrderedCounter(Counter, OrderedDict): 'Counter that remembers the order elements are first encountered' def __repr__(self): return '%s(%r)' % (self.__class__.__name__, OrderedDict(self)) def __reduce__(self): return self.__class__, (OrderedDict(self),) # Timing setup timings = {ordereddict: [], dawg: [], chris1: [], chris2: [], OrderedCounter: []} sizes = [2**i for i in range(1, 20)] # Timing for size in sizes: func_input = [datetime.date(2017, random.randint(1, 12), random.randint(1, 28)) for _ in range(size)] for func in timings: res = %timeit -o func(func_input) # if you use IPython, otherwise use the "timeit" module timings[func].append(res) 

и построены:

 %matplotlib notebook import matplotlib.pyplot as plt import numpy as np fig = plt.figure(1) ax = plt.subplot(111) for func in timings: ax.plot([2**i for i in range(1, 20)], [time.best for time in timings[func]], label=str(func.__name__)) ax.set_xscale('log') ax.set_yscale('log') ax.set_xlabel('size') ax.set_ylabel('time [seconds]') ax.grid(which='both') ax.legend() plt.tight_layout() 

введите описание изображения здесь

Я назначил его на Python-3.5. Подходы с использованием Counter , вероятно, будут немного медленнее на python-2.x ( Counter был оптимизирован для python-3.x). Также chris2 и dawg перекрывают друг друга (потому что между ними практически нет разницы во времени).

Таким образом, за исключением первого подхода @Chris_Rands и OrderedCounter – подходы выполняют очень схожие и в основном зависят от количества дубликатов в вашем списке.

Это в основном коэффициент разницы в 1,5-2. Я не мог найти разницу в реальном времени для 1 миллиона элементов, а также 3 «быстрых» подхода.

Вы можете использовать list.count() с list.count() списка а-списка по списку, полученному из OrderedDict уникальных упорядоченных дат:

 import datetime from collections import OrderedDict lst = [datetime.date(2017, 3, 9), datetime.date(2017, 3, 10), datetime.date(2017, 3, 10), datetime.date(2017, 3, 11)] [(item,lst.count(item)) for item in list(OrderedDict.fromkeys(lst))] # [(datetime.date(2017, 3, 9), 1), (datetime.date(2017, 3, 10), 2), (datetime.date(2017, 3, 11), 1)] 

Или аналогичным образом с помощью collections.Counter вместо list.count :

 from collections import Counter c = Counter(lst) [(item,c[item]) for item in list(OrderedDict.fromkeys(lst))] # [(datetime.date(2017, 3, 9), 1), (datetime.date(2017, 3, 10), 2), (datetime.date(2017, 3, 11), 1)] 

Или используйте OrderedCounter .

EDIT: см. Отличный тест от @MSeifert .

  • Есть ли причины не использовать упорядоченный словарь?
  • Python 3 диапазон поворота к списку
  • sort () возвращает None
  • string.lower в Python 3
  • Как программа может выбирать между двумя функциями в python?
  • Почему ... == True возвращает False в Python 3?
  • Должен ли я использовать «in» или «или» в инструкции if в Python 3.x для проверки переменной на несколько значений?
  • Наиболее распространенный символ в строке
  •  
    Interesting Posts for Van-Lav

    Установить Popping (Python)

    Как воспроизводить звук в Python БЕЗ прерывания музыки / других звуков от воспроизведения

    Python: как управлять пространством имен после необработанного исключения?

    Найти группы захвата в пользовательском регулярном выражении

    Запрос на фляжку Python dataurl из url – для декодирования base64 image

    Ошибка gcc-4.2 с статусом выхода 1

    python + matplotlib: как вставить больше пространства между осью и метки тика в полярную диаграмму?

    Matplotlib subplots_adjust hspace, чтобы заголовки и xlabels не перекрывались?

    Почему я не могу изменить атрибут __metaclass__ класса?

    ipython-ноутбук НЕ печатает до завершения всей программы

    Как динамически вставлять функции C из Python в Linux (без LD_PRELOAD)?

    Регулярные выражения Python для фильтрации списка строк

    Pandas: Pivot to True / False, drop column

    изменение диапазона x по умолчанию в гистограмме matplotlib

    Каково намеренное использование раздела DEFAULT в конфигурационных файлах, используемых ConfigParser?

    Python - лучший язык программирования в мире.