有效地在OrderedDictionary

时间:2018-09-27 11:21:13

标签: python python-3.x

我有一个包含比率值的OrderedDictionary。每个条目都有一个密钥的日期(每个日期恰好是一个年度季度的开始),并且值是一个数字。日期按从最早到最新的顺序插入。

{
    date(2017, 1, 1): 95,
    date(2018, 1, 1): 100,
    date(2018, 6, 1): 110,
    date(2018, 9, 1): 112,
}

我的利率词典比这大得多,但这是总的想法。给定一个任意日期,我想在之前的字典中找到该值。例如,查找日期date(2018, 8, 1)应该返回值110,因为条目date(2018, 6, 1)是我查找日期之前的最近键。同样,日期date(2017, 12, 1)应该返回95,因为最接近的前一个键恰好是date(2017, 1, 1)

我可以通过浏览字典中的项目轻松地做到这一点:

def find_nearest(lookup):
    nearest = None
    for d, value in rates.items():
        if(d > lookup):
            break
        nearest = value
    return nearest

这对我来说感觉效率低下,因为在最坏的情况下,我必须扫描整个字典(我之前提到的字典可能很大)。我将进行成千上万的此类查找,因此我希望它表现出色。

解决性能问题的另一种方法是创建一个我所看到的缓存,这也是可行的,尽管我想知道内存限制(我不完全确定缓存会增长多大)。

在这里可以使用任何聪明的方法或Python核心模块吗?

5 个答案:

答案 0 :(得分:2)

由于您要按顺序在dict中插入日期,并且大概使用的是Python 3.7(这使dict的顺序有意义),因此您可以使用递归函数来划分和征服以在O中找到键列表的所需索引(log n)时间复杂度:

def find_nearest(l, lookup):
    if len(l) == 1:
        return l[0]
    mid = len(l) // 2
    if l[mid] > lookup:
        return find_nearest(l[:mid], lookup)
    return find_nearest(l[mid:], lookup)

这样:

from datetime import date
d = {
    date(2017, 1, 1): 95,
    date(2018, 1, 1): 100,
    date(2018, 6, 1): 110,
    date(2018, 9, 1): 112,
}
d[find_nearest(list(d), date(2018, 8, 1))]

返回:110

答案 1 :(得分:2)

sortedcontainers可能就是您想要的。

它将保持键的排序顺序而不是插入顺序,这不同于$_GET['uuid']

安装

if(isset($_GET['uuid'])){
    $uuid = $_GET['uuid'];
    include('database.php');
    $sql = "SELECT * FROM `ips`"; 
    $sql2 =  "SELECT * FROM `ips` WHERE `uuid` =  '". $uuid ."'";
    foreach($mysqli->query($sql) as $row){
        $ipss = $row;
    }
    foreach($mysqli->query($sql2) as $ipsrrr){
        $ipsr = $ipsrrr;
    }
    if($ipss['uuid'] == $uuid && $ipsr['ip'] == $gip){
            $allowed = 1;
    }else{
        header('Location: index.php');
        exit(0);
    }
}else{
    header('Location: index.php');
    exit(0);
}

实现您想要的

collections.OrderedDict

此方法的时间复杂度为O(log n)

答案 2 :(得分:0)

修改 我刚刚意识到您需要一个核心模块-我的答案是使用熊猫!

如果您具有唯一的日期值,则可以使用熊猫创建一个使用日期作为索引的数据框:

df = pd.DataFrame.from_dict(rates, orient='index', columns=['value'])
# Convert index to pandas datetime
df.index = pd.to_datetime(df.index)

这将返回:

              value
2017-01-01     95
2018-01-01    100
2018-06-01    110
2018-09-01    112

然后:

def lookup(date, df):
    # Convert input to datetime
    date = pd.to_datetime(date)
    # Get closest date in index
    closest_date = min(df.index, key=lambda x: abs(x - date))
    # Find corresponding index of closest date
    index = np.where(df.index == closest_date)[0][0]
    # If the date found if greater than the input, then get the date at the index before
    if closest_date > date:
        index -= 1

    return df.iloc[index].value

>> lookup('2018-06-02', df)
Out: 110

>> lookup('2018-05-01', df)
Out: 100

答案 3 :(得分:0)

由于device.writeCharacteristicWithResponseForService()是通过链表实现的,因此尽管可以利用对键进行排序以减少到的次数,但您无法在不到O( n )的时间内按位置直接检索值。 O(log n )。另请参阅:Accessing dictionary items by position in Python 3.6+ efficiently

为了提高效率,我建议您使用第三方库(例如Pandas),该库使用保存在连续内存块中的NumPy数组。时间复杂度为O( n ),但是对于较大的输入字典,您应该会看到性能得到改善。

OrderedDict

一种更详细的替代方法:

import pandas as pd
from datetime import date

d = {date(2017, 1, 1): 95, date(2018, 1, 1): 100,
     date(2018, 6, 1): 110, date(2018, 9, 1): 112}

df = pd.DataFrame.from_dict(d, orient='index')
df.index = pd.to_datetime(df.index)

my_date = pd.to_datetime(date(2018, 8, 1))
res = df[0].iat[df.index.get_loc(my_date, method='ffill')]  # 110

答案 4 :(得分:-1)

您可以尝试.get()方法,该方法仅在存在时返回一个值,否则返回None

import datetime
from datetime import date

def findNearest(somedate, dictionary):
    while dictionary.get(somedate) is None:
        somedate=somedate-datetime.timedelta(1)

    return dictionary.get(somedate)


result=findNearest(date(2017, 1, 3), yourDictionary)

当您打印结果时,它将打印'95',即date(2017,1,1)的值