【问题标题】:Python dictionary from two lists来自两个列表的 Python 字典
【发布时间】:2015-08-05 09:22:30
【问题描述】:

我有两个列表,一个是值列表,另一个是日期列表。

我想创建一个以值和日期为键的字典。但是许多值具有相同的“键”(日期)。在制作字典之前,我需要将具有相同日期(相同键)的值添加在一起。

两个列表具有相同数量的元素,但日期列表的某些值重复(因为每个日期都有多个值)。

根据键(日期)对值进行分组(将它们加在一起)的最佳方法是什么?

列表示例

dates = [datetime(2014, 2, 1, 0, 0),datetime(2014, 2, 1, 0, 0),datetime(2014, 2, 1, 0, 0),datetime(2014, 3, 1, 0, 0),datetime(2014, 3, 1, 0, 0)]

values = [2,7,4,8,4]

I want my dictionary to look like this:
dict = [datetime(2014, 2, 1, 0, 0):13,datetime(2014, 3, 1, 0, 0):8,datetime(2014, 3, 1, 0, 0):4]

【问题讨论】:

  • 添加值是什么意思?将它们添加到列表中,还是简单的算术加法?
  • 使用代码帮助您描述问题,例如a = [1,2,3]b = ['foo', 'bar']
  • 你能展示一下你的字典是什么样子的吗?

标签: python dictionary


【解决方案1】:

如果您有重复的日期并且想要对重复键的值进行分组,请使用defaultdict

from collections import defaultdict
d = defaultdict(int)
for dte, val in zip(dates, values):
    d[dte] += val

输出:

defaultdict(<class 'int'>, {datetime.datetime(2014, 2, 1, 0, 0): 13, datetime.datetime(2014, 3, 1, 0, 0): 12})

或者使用普通的字典和dict.setdefault:

d = {}
for dte, val in zip(dates,values):
    d.setdefault(dte,0)
    d[dte] += val

最后你可以使用默认值为 0 的 dict.get:

d = {}
for dte, val in zip(dates,values):
    d[dte] = d.get(dte, 0) + val

defaultdict 将是最快的方法,因为它正是为此目的而设计的。

【讨论】:

  • 一点小提示:defaultdict 是一个类字典对象,如果访问的键没有值,它会设置默认值。
  • 很好,这行得通,我只需要将每个键的值加在一起就完成了:) 谢谢
  • @LukaPerović,我为您的编辑进行了编辑,您可以 += 您实际想要添加的值,此外,我认为您的输出应该只有两个键
【解决方案2】:

假设这是您的输入,

>>> dates = ['2015-01-01', '2015-01-01', '2015-01-02', '2015-01-03']
>>> values = [10, 15, 10, 10]

组合值,

>>> data = zip(dates, values)
[('2015-01-01', 10), ('2015-01-01', 15), ('2015-01-02', 10), ('2015-01-03', 10)]

聚合相同日期的值,

>>> import itertools
>>> new_data = []
>>> for key, group in itertools.groupby(data, lambda x: x[0]):
        tmp = [key, 0]    #: '0' is the default value
        for thing in group:
            tmp[1] += thing[1]
    new_data.append(tmp)

打印new_data

>>> new_data
[['2015-01-01', 25], ['2015-01-02', 10], ['2015-01-03', 10]]

现在构建最终字典,

>>> dict(new_data)
{'2015-01-03': 10, '2015-01-02': 10, '2015-01-01': 25}

【讨论】:

    【解决方案3】:

    itertoolsdefaultdict 对此完全没有必要。我认为这更简单,更容易阅读。

    dates = [datetime(2014, 2, 1, 0, 0),datetime(2014, 2, 1, 0, 0),datetime(2014, 2, 1, 0, 0),datetime(2014, 3, 1, 0, 0),datetime(2014, 3, 1, 0, 0)]
    values = [2,7,4,8,4]
    
    combined = {}
    for (date,value) in zip(dates,values):
      if date in combined:
        combined[date] += value
      else:
        combined[date] = value
    

    性能分析

    我并不是说defaultdict 是一个糟糕的解决方案,我只是指出它需要更多的隐性知识才能在没有陷阱的情况下使用。

    但这并不是最快的解决方案。

    from collections import defaultdict
    from datetime import datetime
    import timeit
    
    dates = [datetime(2014, 2, 1, 0, 0),datetime(2014, 2, 1, 0, 0),datetime(2014, 2, 1, 0, 0),datetime(2014, 3, 1, 0, 0),datetime(2014, 3, 1, 0, 0)]
    values = [2,7,4,8,4]
    
    def combine_default_dict(dates=dates,values=values):
      d = defaultdict(int)
      for dte, val in zip(dates, values):
          d[dte] += val
      return d
    
    def combine_setdefault(dates=dates,values=values):
      d = {}
      for dte, val in zip(dates,values):
          d.setdefault(dte,0)
          d[dte] += val
      return d
    
    def combine_get(dates=dates,values=values):
      d = {}
      for dte, val in zip(dates,values):
          d[dte] = d.get(dte, 0) + val
      return d
    
    def combine_contains(dates=dates,values=values):
      d = {}
      for (date,value) in zip(dates,values):
        if date in d:
          d[date] += value
        else:
          d[date] = value
      return d
    
    def time_them(number=100000):
      for func_name in [k for k in sorted(globals().keys()) if k.startswith('combine_')]:
        timer = timeit.Timer("{0}()".format(func_name),"from __main__ import {0}".format(func_name))
        time_taken = timer.timeit(number=number)
        print "{0} - {1}".format(time_taken,func_name)
    

    产量:

    >>> time_them()
    0.388070106506 - combine_contains
    0.485766887665 - combine_default_dict
    0.415601968765 - combine_get
    0.472551822662 - combine_setdefault
    

    我已经在几台不同的机器和 python 版本上进行了尝试。 combine_default_dictcombine_setdefault 竞争最慢。 combine_contains 一直是最快的。

    【讨论】:

    • 使用 defaultdict 来准确理解其设计目的是什么难以阅读?
    • @PadraicCunningham:我并不是说它很难阅读,只是比较难。 defaultdict 的行为需要对其实现的隐含理解。您必须隐含地理解,如果您向它询问键的值,您将取回默认值并同时使用默认值实例化该键。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-21
    • 1970-01-01
    • 2015-09-09
    • 1970-01-01
    • 2019-03-26
    • 2011-07-30
    相关资源
    最近更新 更多