Python:如何高效地搜索大型数组?

Python:如何高效地搜索大型数组?,python,python-2.7,list,pickle,Python,Python 2.7,List,Pickle,问题: 我正在做一个数据分析项目,该项目要求我将一个未知单词的子串与一组好单词和坏单词进行比较 我最初生成了4个列表,并将它们以pickle格式存储在磁盘中 -rw-rw-r-- 1 malware_corpus malware_corpus 189M May 4 13:11 clean_a.pkl -rw-rw-r-- 1 malware_corpus malware_corpus 183M May 4 13:12 clean_b.pkl -rw-rw-r-- 1 malware_corp

问题:

我正在做一个数据分析项目,该项目要求我将一个未知单词的子串与一组好单词和坏单词进行比较

我最初生成了4个列表,并将它们以pickle格式存储在磁盘中

-rw-rw-r-- 1 malware_corpus malware_corpus 189M May  4 13:11 clean_a.pkl
-rw-rw-r-- 1 malware_corpus malware_corpus 183M May  4 13:12 clean_b.pkl
-rw-rw-r-- 1 malware_corpus malware_corpus 1.7M Apr 30 11:12 data_backup.csv
-rw-rw-r-- 1 malware_corpus malware_corpus 2.9M May  4 13:13 data.csv
-rw-rw-r-- 1 malware_corpus malware_corpus 231M May  4 13:12 mal_a.pkl
-rw-rw-r-- 1 malware_corpus malware_corpus 232M May  4 13:13 mal_b.pkl

因此,在我的代码中,每当出现一个新字符串时,我都会将这4个列表中的子字符串与这4个列表进行比较,并计算分数。由于这4个列表都存储在内存中,所以程序运行缓慢

此外,每个列表都有数百万个单词,如果我想进行搜索,我会花费更长的时间,因为这需要时间

所需解决方案:

任何有效的方法来存储4个列表,这样它们就不会占用我的内存。 在4个列表中搜索字符串的更好方法。 如何在python中访问大型列表。 代码部分:

    def create_corpus(self):
    """corpus

    :param domain: Doamin passed will be split and words are stored in
    corpus.
    """
    with open(os.path.join(os.path.dirname(os.path.realpath(__file__)),'utils/x.txt'),'r') as f:
        for line in f:
            line = line.rstrip()

            self.line_x = self.calculate_xs(line)
            for i in self.line_x:
                self.clean_xs.append(i)
            self.line_y = self.calculate_ys(line)
            for i in self.line_y:
                self.clean_ys.append(i)
    with open(os.path.join(os.path.dirname(os.path.realpath(__file__)),'utils/y.txt'),'r') as f:
        for line in f:
            line = line.rstrip()
            self.line_x = self.calculate_x(line)
            for i in self.line_x:
                self.mal_xs.append(i)
            self.line_y = self.calculate_y(line)
            for i in self.line_y:
                self.mal_ys.append(i)

    # Store the Datasets in pickle Formats
    with open(os.path.join(os.path.dirname(os.path.realpath(__file__)),\
                           'utils/clean_x.pkl'),'wb') as f:
        pickle.dump(self.clean_xs , f)

    with open(os.path.join(os.path.dirname(os.path.realpath(__file__)),\
                           'utils/clean_ys.pkl'),'wb') as f:
        pickle.dump(self.clean_ys , f)
    with open(os.path.join(os.path.dirname(os.path.realpath(__file__)),\
                           'utils/mal_xs.pkl'),'wb') as f:
        pickle.dump(self.mal_xs , f)
    with open(os.path.join(os.path.dirname(os.path.realpath(__file__)),\
                           'utils/mal_ys.pkl'),'wb') as f:
        pickle.dump(self.mal_ys, f)
    return 1


def compare_score_function(self,domain):
    self.domain = domain
    self.g_freq = {}
    self.b_score = 0.0
    from collections import Counter
    for x in self.substrings_of_domain:
        self.g_freq[x] = {}
        self.g_freq[x]['occur'] = self.clean_x.count(x)
        self.g_freq[x]['freq']  = self.clean_x.count(x)/len(self.clean_x)
    for key,value in self.g_freq.iteritems():
        self.b_score += value['freq']
    return self.b_score

def calculate_x(self,domain):
    self.domain = self.clean_url(domain)
    self.bgrm = list(ngrams(self.domain,2))
    self.bgrm = [''.join(a) for a in self.bgrm ]
    return self.bgrm

def calculate_y(self,domain):
    self.domain = self.clean_url(domain)
    self.tgrm = list(ngrams(self.domain,3))
    self.tgrm = [''.join(a) for a in self.tgrm]
    return self.tgrm
实例说明

清洁列表=['ap'、'pp'、'pl'、'le'、'bo'、'xl'、'ap'] 清洁列表=['apa'、'ppa'、'fpl'、'lef'、'bfo'、'xdl'、'mpd'] 坏的清单=['ti','qw','zx','qa','qa','qa','qa','uy'] 坏的列表=['zzx'、'zxx'、'qww'、'qww'、'qww'、'uyx'、'uyx'] 这里假设以下是我的4个列表:

我的新绳子来了——假设是苹果 -现在我将计算苹果的x个单词=>['ap','pp','pl','le'] -现在我将计算苹果的y个单词=>['app','ppl','ple','lea']

现在我将在clean_x_列表和bad_x_列表中搜索apple的每个x字,即['ap'、'pp'、'pl'、'le'] 然后我将计算频率和发生次数

在清除列表中出现ap=2

清洁列表中ap的频率=2/7 在错误列表中出现ap=0 在错误列表中发生ap=0/7
类似地,我计算其他单词的出现频率和频率,最后求和,考虑对列表进行排序,并使用搜索列表。在这种情况下,最糟糕的查找时间是Olog n。

节省空间的一个选项是以压缩方式存储word文件,但也不能将整个word文件读取到内存中。为此,一个简单的选项是允许您像操作常规文件一样操作gzip存档:

import gzip

with gzip.open('input.gz','rt') as text_f:
    for line in text_f:
        line = line.strip()
        print(line)
通过这种方式,您可以将文件中的每一行视为列表中的一项,并相应地进行处理


请注意rt或wt open方法,它会将其处理为文本,而不是二进制-这将取决于您是否只是存储纯文本/json,或者对数据(如pickle)使用二进制格式。

基本上有三个选项:对中的列表进行线性扫描

>>> lst = random.sample(range(1, 1000000), 100000)
>>> x = lst[50000]
>>> %timeit x in lst
100 loops, best of 3: 2.12 ms per loop
。。。在Ologn中的排序列表中进行二进制搜索,使用模块

>>> srt = sorted(lst)
>>> srt[bisect.bisect_left(srt, x)] == x
True
>>> %timeit srt[bisect.bisect_left(srt, x)] == x
1000000 loops, best of 3: 444 ns per loop
。。。以及O1中哈希集中的查找:

显然,集合是目前最快的,但它也比基于列表的方法占用更多的内存。对分法可能是一种很好的折衷方法,它比本例中的线性扫描快5000倍,并且只需要对列表进行排序

>>> sys.getsizeof(lst)
800064
>>> sys.getsizeof(srt)
900112
>>> sys.getsizeof(st)
4194528
但是,除非您的计算机内存非常有限,否则这应该不是问题。特别是,它不会使代码变慢。要么所有的东西都放在内存中,要么一切正常,要么不正常,你的程序就会停下来

如果您的好/坏单词列表可以包含重复的单词,那么set不是一个选项,而bisect也不能很好地工作。在这种情况下,为每个列表创建一个。然后,您可以获得文本中每个子字符串的出现次数和频率。作为一种哈希映射/字典,计数器中的查找也将是O1

>>> clean_x_list = ['ap','pp','pl','le','bo','xl','ap']
>>> w = "apple"
>>> wx = [w[i:i+2] for i in range(len(w)-1)]
>>> ccx = collections.Counter(clean_x_list)

>>> occ_wx = {x: ccx[x] for x in wx}
>>> occ_wx
{'ap': 2, 'pp': 1, 'pl': 1, 'le': 1}

>>> freq_wx = {x: ccx[x] / len(clean_x_list) for x in wx}
>>> freq_wx
{'ap': 0.2857142857142857,
 'pp': 0.14285714285714285,
 'pl': 0.14285714285714285,
 'le': 0.14285714285714285}

类似于clean_y_list、bad_x_list等等。

您发布的代码似乎是类的一部分,但缺少很多成员函数,例如calculate_xs。所以很难知道你在做什么。您是否尝试将列表存储为字典以加快搜索速度?不确定占用的内存部分是否较少,但您可以将它们存储在一个集合中。该集合将提供O1查找,但占用的内存比列表多。使用trie树是否有帮助?由于所有这4个列表都存储在内存中,程序运行缓慢将列表存储在内存中不会使程序运行缓慢,除非您的内存太少,这将导致程序将内存交换到硬盘驱动器,在这种情况下,速度会非常慢。但如果不是这样,而且在大多数现代计算机上,内存消耗不应该是一个问题,您可以使用集合进行快速查找。您的解释有一个问题:如果您在搜索时看到我的代码,我甚至会计算子字符串出现的次数。@BackdoorCipher老实说,我不能真正理解你的代码,以及它是如何与实际问题联系在一起的。你到底在计算什么,一个好词或坏词在文本中出现的频率有多高?在这种情况下,用另一种方法从文本中创建一个计数器,然后迭代好/坏单词并在计数器中查找计数。或者,如果您只需要累计计数,请在textIam中为x使用集合和集合中的sumx,计算clean和malicio中每个子字符串的出现次数
us字符串列表。@BackdoorCipher那么子字符串到底是什么呢?我希望好/坏列表最多包含一个单词,那么怎么会有大于1的计数呢?或者你真的在计算单词的部分,例如前缀或后缀?好的和坏的列表是我的单词库中所有子字符串的列表,它可以有重复。
>>> clean_x_list = ['ap','pp','pl','le','bo','xl','ap']
>>> w = "apple"
>>> wx = [w[i:i+2] for i in range(len(w)-1)]
>>> ccx = collections.Counter(clean_x_list)

>>> occ_wx = {x: ccx[x] for x in wx}
>>> occ_wx
{'ap': 2, 'pp': 1, 'pl': 1, 'le': 1}

>>> freq_wx = {x: ccx[x] / len(clean_x_list) for x in wx}
>>> freq_wx
{'ap': 0.2857142857142857,
 'pp': 0.14285714285714285,
 'pl': 0.14285714285714285,
 'le': 0.14285714285714285}