在python列表中计算日期的最佳/最快方法

Pal*_*Dot 6 python datetime list counting python-3.x

我有一个日期列表,目标是计算每个日期的出现次数,同时保持它们在原始列表中的显示顺序.请考虑以下示例:

该列表only_dates如下所示:

[datetime.date(2017, 3, 9), datetime.date(2017, 3, 10), datetime.date(2017, 3, 10), datetime.date(2017, 3, 11)]
Run Code Online (Sandbox Code Playgroud)

我正在尝试使用groupby:

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

这打印

[(datetime.date(2017, 3, 10), 1), (datetime.date(2017, 3, 9), 1), (datetime.date(2017, 3, 10), 1), (datetime.date(2017, 3, 11), 1)]
Run Code Online (Sandbox Code Playgroud)

我理解这种情况正在发生,因为最终每个日期对象在分组时被视为不同的日期对象.

我期待输出为:

[(datetime.date(2017, 3, 9), 1), (datetime.date(2017, 3, 10), 2), (datetime.date(2017, 3, 11), 1)]
Run Code Online (Sandbox Code Playgroud)

我不一定在寻找元组列表.只要保持原始日期顺序,字典输出也就足够了.(OrderedDict也许).

我怎样才能做到这一点?

更新:有可能建议多种方法都能正常运行.但我应该提到我将为大量数据执行此操作.因此,如果您的解决方案在运行时间方面是最佳的,那就太好了.如果可以,请相应地编辑您的答案/评论.

更新2:数据大小可以达到100万行.

MSe*_*ert 4

事实上,你可以使用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)
Run Code Online (Sandbox Code Playgroud)

打印:

OrderedDict([(datetime.date(2017, 3, 9), 1),
             (datetime.date(2017, 3, 10), 2),
             (datetime.date(2017, 3, 11), 1)])
Run Code Online (Sandbox Code Playgroud)

您还询问了时间安排,所以它们是:

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: /sf/answers/1662335671/
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)
Run Code Online (Sandbox Code Playgroud)

并绘制:

%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()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

我在 Python-3.5 上计时。使用的方法Counter在 python-2.x 上可能会慢一些(Counter针对 python-3.x 进行了优化)。而且chris2dawg方法彼此重叠(因为它们之间几乎没有时间差)。

因此,除了@Chris_Rands的第一种方法和OrderedCounter- 这些方法的执行非常相似,并且主要取决于列表中重复项的数量。

主要相差 1.5-2 倍。对于 100 万个项目,我在 3 种“快速”方法之间找不到任何实时差异。