Python最有效的方式来保留排序的数据

如何解决Python最有效的方式来保留排序的数据

最有效的方式来跟踪数据的升序/降序。假设我有一个数据流,假设这是非常大的。样本流:

key,mod,value
5,add,1
2,3
4,2
2,rem,5

在阅读流时,我将其放在字典中以跟踪内容。例如,在上述迷你流的结尾,我将有一个带有{5:1,4:2}的字典。其中add表示该值增加了该值,而rem表示您从该键中删除了很多。如果该值变为0,则从字典中删除该键。但是我还希望能够按顺序打印数据(但不必一直打印)。我确实想跟踪最高/最低键,以便知道最高/最低值何时更改。更改密钥或更改其值。

我现在的操作方式是相应地从字典中填充/删除键。这应该是常数O(1)。跟踪sorted_keys列表,其中每个流都会检查新数字是否在词典中,如果不在列表中,则将执行bisect.insort_right(sorted_keys,key)。因此,sorted_keys始终都在排序。假设在排序列表中添加1个值很快,尽管它确实需要扩展大小,因此这可能会使O(n)保持不变。并且我跟踪prev_highestprev_lowest,并分别针对sorted_keys [0]或sorted_keys [-1]进行检查。

我尝试将双端队列与bisect.insort_right,sortedcontainers中的SortedDict,链接列表,OrderedDict一起使用,但似乎上述方法似乎效果最好。还有另一种可能的实现方式可以进一步优化吗?还是我应该按顺序跟踪某个级别,例如说10个项目。并相应地进行更新。但是,这样做的问题是,如果有新密钥,我如何知道它是否是新密钥之一?似乎拥有一个heapq会有所帮助,但是直到弹出它们,我才能获得排序后的值。而且,如果我需要按顺序打印整个内容,则只需对整个字典的键进行排序。

编辑: 使用下面的bisect和SortedDict添加我的测试:

import timeit
import bisect
import random
from sortedcontainers import SortedDict

NUM_ITERATION_TEST = 10
TOTAL_NUM_DATA = 1000000
MODS = ['add','rem']
QUANTITY = [1,5,10,20,100,200,300,500,1000]

DATA = [{'mod': random.choice(MODS),'key': random.randint(0,1000),'val': random.choice(QUANTITY)} for x in range(TOTAL_NUM_DATA)]


def method1(DATA):
    d = {}
    sorted_keys = []

    for data in DATA:
        if data['mod'] == 'add':
            key = data['key']
            if key in d.keys():
                d[key] += data['val']
            else:
                d[key] = data['val']
                bisect.insort_right(sorted_keys,key)
        elif data['mod'] == 'rem':
            key = data['key']
            if key in d.keys():
                if d[key] <= data['val']:
                    del d[key]
                    sorted_keys.remove(key)           
                else:
                    d[key] -= data['val']
            else:
                pass # Deleting something not there yet

def method2(DATA):
    d = SortedDict()

    for data in DATA:
        if data['mod'] == 'add':
            key = data['key']
            if key in d.keys():
                d[key] += data['val']
            else:
                d[key] = data['val']
        elif data['mod'] == 'rem':
            key = data['key']
            if key in d.keys():
                if d[key] <= data['val']:
                    del d[key]
                else:
                    d[key] -= data['val']
            else:
                pass  # Deleting something not there yet


if __name__ == "__main__":
    # METHOD 1
    print("Method 1 Execution Time:")
    print(timeit.timeit("test_timeit.method1(test_timeit.DATA)",number=NUM_ITERATION_TEST,setup="import test_timeit"))

    # METHOD 2
    print("Method 2 Execution Time:")
    print(timeit.timeit("test_timeit.method2(test_timeit.DATA)",setup="import test_timeit"))

以上结果为:

Method 1 Execution Time:
4.427699800000001
Method 2 Execution Time:
12.7445671

解决方法

对于适合内存的数据,“ SortedDict from sortedcontainers”(您已经尝试过)通常和将此类dict保持排序顺序一样好。但是查找时间大约是O(log N)(请参阅最后的编辑-看来是错误的!)。

假定在排序列表中添加1个值很快速,尽管它确实需要扩展大小,所以这可能需要O(n)。

在Python列表L中,在索引i处插入元素必须(至少)物理移动len(L) - i指针,这意味着64位指针的字节数是其的8倍。位盒。这就是当数据“大”时sortedcontainer获得巨大成功的地方:它需要物理移动的最坏情况的指针数量受一个独立于len(L)的常数的限制。在len(L)进入成千上万之前,很难注意到其中的区别。但是,当len(L)达到数百万美元时,两者之间的差异就很大。

我会尝试一个折衷方案:使用sortedcontainers SortedList跟踪当前键,并使用Python普通字典作为实际字典。然后:

对于“键添加值”:查看键是否在字典中。非常快。如果是,则无需触摸SortedList。只是改变字典。如果键不在字典中,则需要将其添加到SortedList和字典中。

对于“密钥rem值”:查看密钥是否在字典中。如果不是,我不知道您想做什么,但是您会弄清楚;-)但是,如果它在字典中,请减去该值。如果结果为非零,则操作完成。否则(结果为0),从dict和SortedList中删除键。

注意:不是出于语义原因,我建议使用SortedList而不是SortedSet,而是因为SortedSet需要更多的内存,因此要与有序列表并行维护集合。您没有必要使用它。

除了字典外,您可能真的还需要double-ended ("min max") heap。从您所说的内容中无法猜测-这取决于,例如,您仅想知道“最小和/或最大”的频率,而不是经常要实现整个排序顺序的频率。但是我不知道为速度而构建的Python的最小-最大堆实现-它们是凌乱的代码野兽,很少使用。

编辑

再三考虑,似乎sortedcontainer的SortedDict已经结合了SortedList和普通Python dict(的子类)。例如,在SortedDict中设置值的实现方式如下:

def __setitem__(self,key,value):
    if key not in self:
        self._list_add(key)
    dict.__setitem__(self,value)

因此,只有在字典中没有键时,它才会触摸SortedList。如果您维护自己的对,则没有太多改善机会。

自己动手

这是另一种尝试:

def method3(DATA):
    sorted_keys = SortedList()
    d = {}

    for data in DATA:
        if data['mod'] == 'add':
            key = data['key']
            if key in d:
                d[key] += data['val']
            else:
                d[key] = data['val']
                sorted_keys.add(key)
        elif data['mod'] == 'rem':
            key = data['key']
            if key in d:
                if d[key] <= data['val']:
                    del d[key]
                    sorted_keys.remove(key)
                else:
                    d[key] -= data['val']
            else:
                pass  # Deleting something not there yet

这实现了我最初的建议:使用纯Python字典维护您自己的SortedList对。它具有与使用SortedDict相同的O()行为,但以恒定因子显示则明显更快。看起来部分原因是因为dict操作现在全部用C编码(SortedDict用Python编码),其余的原因是我们仅对每个data项目的dict成员资格进行一次测试。例如,在

if key in d:
    d[key] += data['val']

d是SortedDict时,key in d会对其进行一次显式测试,但是d.__setitem__()的实现必须再次对其进行测试,以便可以向其添加key如果键未知,则为隐藏的SortedList。从更高级别的角度来看,我们已经知道密钥位于if主体中的dict中,因此可以完全忽略此处的显式SortedList。

,

您在Arc<Mutex<Params>>中犯了两个错误:

  • 您选择method1而不是if key in d.keys():。没有必要在此处创建按键视图。

  • 您可以使用if key in d:从列表中删除,而不是使用sorted_keys.remove(key)来查找索引,然后删除该索引。

修复这些问题,将一些方法存储在局部变量中以进行更短/更快的访问,并使用bisect可能找到一个键并获取其值(而不是d.get检查然后查找值) ),我得到这些时间(方法1/2是您的,方法3是Tim的,方法4是我的):

in

Tim要求我将Round 1 method1 7.590627200000004 method2 19.851634099999984 method3 6.093115100000006 method4 5.069753999999989 Round 2 method1 7.857367500000009 method2 19.59779759999998 method3 6.057990299999972 method4 5.0046839999999975 Round 3 method1 7.843560700000012 method2 19.8673627 method3 6.079332300000033 method4 5.073929300000032 更改为randint(0,1000)

randint(0,40_000)

method1 607.2835661000001 method2 26.667593300000135 method3 12.84969140000021 method4 16.68231250000008 (仅较快速的解决方案):

randint(0,400_000)

我的版本:

method3 20.179627500000002
method4 115.39424580000002

完整的基准代码,包括正确性检查:

def method4(DATA):
    d = {}
    sorted_keys = []
    insort = bisect.insort_right
    index = bisect.bisect_left
    get = d.get
    
    for data in DATA:
        if data['mod'] == 'add':
            key = data['key']
            val = get(key)
            if val:
                d[key] = val + data['val']
            else:
                d[key] = data['val']
                insort(sorted_keys,key)
        elif data['mod'] == 'rem':
            key = data['key']
            val = get(key)
            if val:
                if val <= data['val']:
                    del d[key]
                    del sorted_keys[index(sorted_keys,key)]
                else:
                    d[key] = val - data['val']
            else:
                pass # Deleting something not there yet

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-