繁体   English   中英

查找列表中元素索引的最快方法

[英]Fastest way to find index of elements in a list

我有一组具有某些功能(例如Y)的示例(例如X),我需要在上面运行一些机器学习算法(例如PCA)。 一种方法是生成矩阵(样本,特征)。 我构建矩阵的方法涉及2个步骤:

  1. 获取整个数据集的所有特征值。 我们称它为游泳池。
  2. 对于样本,对于样本中的每个值,在池中找到该值的索引。 值存在的索引为1,不存在的索引为0。

例如:考虑以下示例

sample1 = A, B, D
sample2 = A, C, E
sample3 = A, C, D
  1. 生成的池= A,B,C,D,E
  2. 生成矩阵

    • 样本1 => 1,1,0,1,0
    • 样本2 => 1,0,1,0,1
    • 样本3 => 1,0,0,1,0

生成此矩阵的挑战在于数据非常庞大。 行数为465337 ,列数为35526155

生成池大约花费了20分钟,即使这很慢,我也很满意。 但是,当生成矩阵的向量(即行)时,我必须考虑样本中的所有值,以得到池中该值的索引。

这花费了大量时间。 有没有更好的方法来查找元素索引? 如果程序本身不是最佳方法,请让我知道生成矩阵的更好方法。

另外,我只是存储索引并从中构造CSR矩阵,而不是密集矩阵。

您可以使用scikit-learn的Count Vectorizer实现这种类型的编码。 请参阅此处的示例用例。 因为您提到了机器学习并且正在使用Python,所以我认为您通常对sklearn熟悉。

但是,由于CountVectorizer用于文本标记,因此将其用于您的问题有点麻烦。

例如,如果您使用以下格式输入数据:

samples = [['A', 'B', 'D'],
           ['A', 'C', 'E'],
           ['A', 'C', 'D']]

您应该首先将内部列表转换为字符串:

samples_s = ["".join(l) for l in samples]

这使

['ABD', 'ACE', 'ACD']

现在,您可以适合CountVectorizer的实例。 您只需要定义什么构成令牌。 在这里,我使用了一个简单的正则表达式来定义任何单个字符(例如“ A”,“ B”或“ C”)作为标记。 您还可以提供静态词汇表。

vec = CountVectorizer(token_pattern='.')
X = vec.fit_transform(samples_s)

调用X.toarray()返回

array([[1, 1, 0, 1, 0],
       [1, 0, 1, 0, 1],
       [1, 0, 1, 1, 0]], dtype=int64)

使用sklearn的实现应该比自己进行编码要快得多。

正如我在评论中提到的那样,您希望为此使用sklearn.feature_extraction.text.CountVectorizer ,并带有binary=True参数,因为您实际上并不希望计数。 您将创建编码输出稀疏矩阵以启动!

但是,如果您对方法的基本问题感兴趣,从根本上讲,问题是您使用的是序列类型list ,其中.index方法是线性时间操作。 当您尝试使用列表时,您会感到这一事实的痛苦。 以下是仅使用字典即可​​如何更有效地执行此操作的示意图

In [15]: tokens = list('qwertydfgndjfkgnf')

In [16]: pool = {}

In [17]: for t in tokens:
    ...:     pool.setdefault(t, len(pool))
    ...:

In [18]: pool
Out[18]:
{'d': 6,
 'e': 2,
 'f': 7,
 'g': 8,
 'j': 10,
 'k': 11,
 'n': 9,
 'q': 0,
 'r': 3,
 't': 4,
 'w': 1,
 'y': 5}

In [19]: tokens.index('g') # ew, O(n) time complexity
Out[19]: 8

In [20]: pool['g'] # nice! O(1) time complexity
Out[20]: 8

现在,该池包含从标记到索引的编码。 在这里访问索引是一个固定时间的操作。 这将大大提高性能。 事实上,因为我们只是做一个dict ,首先,从一个不转换set到一个list ,这将通过大量的减少您的持续性因素。

请注意,以上内容实质上是sklearn对象正在执行的操作。

https://github.com/scikit-learn/scikit-learn/blob/ab93d65/sklearn/feature_extraction/text.py#L745

但是请注意,它们使用defaultdict ,它通过以下令人愉悦的小方法针对此类情况进行了优化:

In [24]: from collections import defaultdict

In [25]: pool = defaultdict()

In [26]: pool.default_factory = pool.__len__

In [27]: for t in tokens:
    ...:     pool[t]
    ...:

In [28]: pool
Out[28]:
defaultdict(<method-wrapper '__len__' of collections.defaultdict object at 0x1088aa9f8>,
            {'d': 6,
             'e': 2,
             'f': 7,
             'g': 8,
             'j': 10,
             'k': 11,
             'n': 9,
             'q': 0,
             'r': 3,
             't': 4,
             'w': 1,
             'y': 5})

当它们在文档上循环时,它们还会构建稀疏表示,因此它们实际上仅对数据集进行一次传递。 因此,sklearn对象的优化程度与您将要获得的优化程度一样。 sklearn的源代码实际上是很容易上手的,值得检查一下它们仅使用不带Cython扩展或任何内容的纯python完成的工作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM