Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/318.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用NumPy数据类型的Python字典查找速度_Python_Python 2.7_Numpy - Fatal编程技术网

使用NumPy数据类型的Python字典查找速度

使用NumPy数据类型的Python字典查找速度,python,python-2.7,numpy,Python,Python 2.7,Numpy,背景 我在NumPy数组中有很多数字消息代码,我需要快速将它们转换为字符串。我在表现上遇到了一些问题,我想了解为什么以及如何快速完成 一些基准 I-琐碎的方法 import numpy as np # dictionary to use as the lookup dictionary lookupdict = { 1: "val1", 2: "val2", 27: "val3", 35: "val4", 59: "val5" } # some t

背景

我在NumPy数组中有很多数字消息代码,我需要快速将它们转换为字符串。我在表现上遇到了一些问题,我想了解为什么以及如何快速完成

一些基准

I-琐碎的方法

import numpy as np

# dictionary to use as the lookup dictionary
lookupdict = {
     1: "val1",
     2: "val2",
    27: "val3",
    35: "val4",
    59: "val5" }

# some test data
arr = np.random.choice(lookupdict.keys(), 1000000)

# create a list of words looked up
res = [ lookupdict[k] for k in arr ]
查字典占了我休息时间的大部分,758毫秒。(我也试过
res=map(lookupdict.get,arr)
,但那更糟。)

II-不带NumPy

import random

# dictionary to use as the lookup dictionary
lookupdict = {
     1: "val1",
     2: "val2",
    27: "val3",
    35: "val4",
    59: "val5" }

# some test data
arr = [ random.choice(lookupdict.keys()) for _ in range(1000000) ]

# create a list of words looked up
res = [ lookupdict[k] for k in arr ]
计时结果变化相当大,达到76毫秒

应该注意的是,我对查找时间感兴趣。随机生成只是为了创建一些测试数据。花不花很多时间都不有趣。这里给出的所有基准测试结果仅用于一百万次查找

III-将NumPy数组转换为列表

我的第一个猜测是,这与列表与数组问题有关。但是,通过修改NumPy版本以使用列表:

res = [ lookupdict[k] for k in list(arr) ]
给我778毫秒,其中大约110毫秒用于转换列表,570毫秒用于查找。因此,查找速度要快一点,但总时间是相同的

IV-从
np.int32
int
的类型转换

由于唯一的区别似乎是数据类型(
np.int32
vs.
int
),我尝试动态转换类型。这有点愚蠢,因为dict可能也这么做:

res = [ lookupdict[int(k)] for k in arr ]
然而,这似乎做了一些有趣的事情,因为时间降到了266毫秒。似乎几乎但不完全相同的数据类型在字典查找方面玩了一些令人讨厌的把戏,并且dict代码在转换方面不是非常有效

V-字典键转换为
np.int32

为了测试这一点,我修改了NumPy版本,在dict键和查找中使用完全相同的数据类型:

import numpy as np

# dictionary to use as the lookup dictionary
lookupdict = {
     np.int32(1): "val1",
     np.int32(2): "val2",
    np.int32(27): "val3",
    np.int32(35): "val4",
    np.int32(59): "val5" }

# some test data
arr = np.random.choice(lookupdict.keys(), 1000000)

# create a list of words looked up
res = [ lookupdict[k] for k in arr ]
这改善到177毫秒。这不是一个微不足道的改善,但与76毫秒相去甚远

VI-使用
int
对象的数组转换

import numpy as np

# dictionary to use as the lookup dictionary
lookupdict = {
     1: "val1",
     2: "val2",
    27: "val3",
    35: "val4",
    59: "val5" }

# some test data
arr = np.array([ random.choice(lookupdict.keys()) for _ in range(1000000) ], 
               dtype='object')

# create a list of words looked up
res = [ lookupdict[k] for k in arr ]
这就得到了86ms,这已经非常接近本机python76ms

结果摘要

import random

# dictionary to use as the lookup dictionary
lookupdict = {
     1: "val1",
     2: "val2",
    27: "val3",
    35: "val4",
    59: "val5" }

# some test data
arr = [ random.choice(lookupdict.keys()) for _ in range(1000000) ]

# create a list of words looked up
res = [ lookupdict[k] for k in arr ]
  • dict键
    int
    ,使用
    int
    索引(本机Python):76毫秒
  • dict键
    int
    ,使用
    int
    对象(NumPy)进行索引:86毫秒
  • dict键
    np.int32
    ,索引为
    np.int32
    :177毫秒
  • dict键
    int
    ,用
    np.int32
    索引:758毫秒
  • 问题


    为什么??我能做些什么使字典查找尽可能快?我的输入数据是一个NumPy数组,因此目前最好(最快但最难看)的方法是将dict键转换为
    np.int32
    。(不幸的是,dict键可能分布在广泛的数字范围内,因此逐数组索引不是一个可行的选择。尽管速度很快,10毫秒)。

    正如您所怀疑的,它是
    int32。\uuuuu hash\uuuu
    的错,它的速度是x11,与
    int一样慢。\uuuu hash\uuu

    %timeit hash(5)
    10000000 loops, best of 3: 39.2 ns per loop
    %timeit hash(np.int32(5))
    1000000 loops, best of 3: 444 ns per loop
    
    (int32的
    int32
    类型是用C实现的。如果你真的很好奇,你可以深入源代码,找出它在那里做什么,这需要很长时间)


    编辑:

    第二个减慢速度的部分是dict查找的隐式比较:

    a = np.int32(5)
    b = np.int32(5)
    %timeit a == b  # comparing two int32's
    10000000 loops, best of 3: 61.9 ns per loop
    %timeit a == 5  # comparing int32 against int -- much slower
    100000 loops, best of 3: 2.62 us per loop
    
    这就解释了为什么V比I和IV快得多。当然,坚持使用all-
    int
    解决方案会更快


    在我看来,你有两个选择:

  • 坚持使用纯
    int
    类型,或在dict查找之前转换为int
  • 如果最大的代码值不是太大,并且/或者内存不是问题,那么可以用dict查找来交换列表索引,而不需要
    散列
    ing
  • 例如:


    (编辑:您可能还想在这里进行试验)

    在我的计时中,您的
    II-不带NumPy
    I
    慢很多

    In [11]: timeit [lookupdict[k] for k in np.random.choice(lookupdict.keys(),1000000)]
    1 loops, best of 3: 658 ms per loop
    
    In [12]: timeit [lookupdict[k] for k in [np.random.choice(lookupdict.keys()) for _ in range(1000000)]]
    1 loops, best of 3: 8.04 s per loop
    
    但是如果通过对值进行
    选择来跳过查找,您将获得更多的时间

    In [34]: timeit np.random.choice(lookupdict.values(),1000000)
    10 loops, best of 3: 85.3 ms per loop
    

    好的,让我们关注查找:

    In [26]: arr =np.random.choice(lookupdict.keys(),1000000)
    
    In [27]: arrlist=arr.tolist()
    
    In [28]: timeit res = [lookupdict[k] for k in arr]
    1 loops, best of 3: 583 ms per loop
    
    In [29]: timeit res = [lookupdict[k] for k in arrlist]
    10 loops, best of 3: 120 ms per loop
    
    In [30]: timeit res = [lookupdict[k] for k in list(arr)]
    1 loops, best of 3: 675 ms per loop
    
    In [31]: timeit res = [lookupdict[k] for k in arr.tolist()]
    10 loops, best of 3: 156 ms per loop
    
    In [32]: timeit res = [k for k in arr]
    1 loops, best of 3: 215 ms per loop
    
    In [33]: timeit res = [k for k in arrlist]
    10 loops, best of 3: 51.4 ms per loop
    
    In [42]: timeit arr.tolist()
    10 loops, best of 3: 33.6 ms per loop
    
    In [43]: timeit list(arr)
    1 loops, best of 3: 264 ms per loop
    
    第一次观察-在
    np.数组上的迭代比在等效列表上的迭代慢


    第二个-
    list(arr)
    arr.tolist()慢。
    <代码>列表()
    似乎有两个问题。就其本身而言,速度较慢,项目为
    np.int32

    这很有趣,我可能已经找到了我问题的答案

    备选方案三是将数组转换为列表。如果采用正确的方法,这似乎可以提供非常好的结果。这:

    时钟778毫秒

    但这是:

    res = [ lookupdict[k] for k in arr.tolist() ]
    
    时钟86毫秒


    这背后的技术解释是,
    arr.tolist
    将数组转换为
    int
    对象,而
    list(arr)
    创建了
    np.int32
    对象的列表。

    您在问题中没有考虑的一个选项,尽管在某些情况下是不可行的公认的有限选项,是将lookupdict转换为数组。在我的机器上,用一个像你这样的小dict,速度非常快

    import numpy as np
    
    # dictionary to use as the lookup dictionary
    lookupdict = {
         1: "val1",
         2: "val2",
        27: "val3",
        35: "val4",
        59: "val5" }
    
    # some test data
    arr = np.random.choice(lookupdict.keys(), 1000000)
    
    table = np.empty(max(lookupdict.keys()) + 1, dtype='S4')
    for key, value in lookupdict.items():
        table[key] = value
    
    res = table[arr]
    

    这里有一个使用熊猫的解决方案,它有五个方面的改进:

    import numpy as np
    import pandas as pd
    
    # dictionary to use as the lookup dictionary
    lookupdict = {
     1: "val1",
     2: "val2",
    27: "val3",
    35: "val4",
    59: "val5" }
    
    # some test data
    arr = np.random.choice(lookupdict.keys(), 1000000)
    
    # create a list of words looked up
    %timeit res = [ lookupdict[k] for k in arr ]
    %timeit res_pd = pd.Series(lookupdict).reindex(arr).values
    print all(res == res_pd)
    
    10 loops, best of 3: 192 ms per loop
    10 loops, best of 3: 35.3 ms per loop
    True
    
    这是每个元素平均35纳秒,因此在本机Python中不可能击败它。如果您不熟悉Pandas,则Series对象类似于OrderedDict或索引数组,可以从标准Python dict构建。方法
    reindex
    提供非常快速的查找;我不知道该怎么做,因为我真的不知道在幕后发生了什么(我不是一个非常有经验的程序员),但它可能是用C或Cython编写的。也许你可以查一下来源,为你的问题想出一个更快的定制解决方案。最后,values属性只返回
    import numpy as np
    import pandas as pd
    
    # dictionary to use as the lookup dictionary
    lookupdict = {
     1: "val1",
     2: "val2",
    27: "val3",
    35: "val4",
    59: "val5" }
    
    # some test data
    arr = np.random.choice(lookupdict.keys(), 1000000)
    
    # create a list of words looked up
    %timeit res = [ lookupdict[k] for k in arr ]
    %timeit res_pd = pd.Series(lookupdict).reindex(arr).values
    print all(res == res_pd)
    
    10 loops, best of 3: 192 ms per loop
    10 loops, best of 3: 35.3 ms per loop
    True
    
    keys = np.array(lookupdict.keys())
    strings = np.array(lookupdict.values())
    %timeit res_np = strings[(np.atleast_2d(arr).T == keys).argmax(axis=1)]
    10 loops, best of 3: 44.6 ms per loop
    
    print all(res == res_np)
    True