【问题标题】:Fast way to turn dictionary to (dense) matrix将字典转换为(密集)矩阵的快速方法
【发布时间】:2019-06-03 19:44:25
【问题描述】:

我有一个字典列表,其中的值始终为整数,键为一些字符串。我们可以将其解释为一个矩阵,其中每个字典是一行,每一列对应于至少属于其中一个字典的键。字典表示多项式,其中键是单项式,值是系数。

例如[{'x':1, 'y':1, 'z':2}, {'x': 2}, {'y':1, 'z':3}]对应矩阵:

[ 1 1 2,
  2 0 0,
  0 1 3 ]

我经常做这个操作,我需要一个高性能的解决方案。矩阵不是很大,所以我需要一个最小化开销的解决方案。目前,一些计算将大约三分之一的时间花在字典的向量化上。

这基本对应sklearn.feature_extraction.DictVectorizer。我在 sagemath 工作,它不附带 sci-kit learn,所以使用它并不理想。此外,DictVectorizer 构建了一个稀疏矩阵,然后将其转换为密集矩阵。我自己尝试了这种方法,但由于额外的开销很大,所以速度较慢。

我目前的算法如下:

def dictionary_vectorizer(list_of_dicts):
    # Make a list of all the keys occurring in the dictionaries
    keys = set()
    for dic in list_of_dicts:
        for key in dic.keys():
            keys.add(key)

    # Create a mapping keys -> column_index
    key_map = {key: index for index, key in enumerate(keys)}

    output = np.zeros((len(list_of_dicts), len(keys)))
    for row_number, dic in enumerate(list_of_dicts):
        for key, value in dic.items():
            output[row_number, key_map[key]] = value

    return output

在我的实际代码中,我使用 sagemath 的 matrix 构造函数而不是 np.zeros,但它并没有太大变化。似乎初始化一个零矩阵,然后编辑行并不是最快的方法。逐行计算矩阵,然后将结果连接起来给出相同的速度。

有没有明显的方法可以加快速度(同时保持低开销)?

【问题讨论】:

  • 稀疏矩阵的创建速度并不快。 dok 矩阵是dict 的子类,但没有实现update(因为它想验证值)。创建后,dok 仍需转换为csr 格式,然后再转换为toarray()。鉴于您的映射的非传统性质,我认为您的代码与您将获得的一样好。
  • 您可以使用keys = set(itertools.chain.from_iterable(e.keys() for e in a)) 减少一些开销
  • 如何定义键和列索引之间的映射?是字典顺序吗?
  • 顺序与具体问题无关。无论如何,填充矩阵似乎是最耗时的,而不是找到键集。

标签: python numpy sage


【解决方案1】:

如果,根据这部分问题:

这基本对应sklearn.feature_extraction.DictVectorizer。 我在 sagemath 工作,它不附带 sci-kit learn,所以使用 这并不理想。

希望将 scikit-learn 安装到 SageMath 中,然后取决于 关于 Sage 的安装方式,在终端中运行可能就足够了:

$ sage --pip install scikit-learn

然后可以在 Sage 中执行以下操作:

sage: data = [{'x':1, 'y':1, 'z':2}, {'x':2}, {'y':1, 'z':3}]
sage: data
[{'x': 1, 'y': 1, 'z': 2}, {'x': 2}, {'y': 1, 'z': 3}]

sage: from sklearn.feature_extraction import DictVectorizer
sage: v = DictVectorizer(dtype=int, sparse=False)
sage: X = v.fit_transform(data)
sage: print(X)
[[1 1 2]
 [2 0 0]
 [0 1 3]]

如果目标只是加快问题中的代码速度, 跳过键到列索引映射的创建 可能会使事情变得稍微快一些,但不会大幅提升。

def dictionary_vectorizer(list_of_dicts):
    # List all keys occurring in the dictionaries
    keys = set(key for dic in list_of_dicts for key in dic)
    # Initialize zero array of correct shape
    output = np.zeros((len(list_of_dicts), len(keys)))
    # Set nonzero entries
    for row_number, dic in enumerate(list_of_dicts):
        for col_number, key in enumerate(keys):
            if key in dic:
                output[row_number, col_number] = dic[key]
    return output

如果函数应用于大量数据 共享相同的单项式,这可能是有意义的 分离出对键的检测。

可以考虑重新使用相同的数组,并传递它 作为一个论点,以节省创建新的成本 numpy 数组。不确定这会有多大帮助。

如果使用字典列表有限制,可以 还要考虑改变数据生产步骤。

Cython 或 Pythran 可能有助于进一步加快这一进程。

【讨论】:

  • 查看DictVectorizer的源码,它构建了一个稀疏矩阵,如果sparse=False它调用.toarray()方法将其变成一个密集矩阵。在我的情况下,构建稀疏矩阵的额外开销是不值得的。我主要链接到 DictVectorizer 作为我期望的输出类型的示例。我编辑了这个问题,希望能让我的意图更清楚。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多