Pandas DataFrame.from_dict()从冗长的dicts字典生成时表现不佳

时间:2018-03-26 12:28:41

标签: python pandas dictionary dataframe sparse-matrix

在我的Python应用程序中,我发现使用词典字典作为构建稀疏pandas DataFrame的源数据很方便,然后我用它来训练sklearn中的模型。

字典的结构如下:

data = {"X": {'a': 1, 'b': 2, 'c': 3}, "Y": {'d': 4, 'e': 5, 'f': 6}, "Z": {'g': 7, 'h': 8, 'i': 9}}

理想情况下,我想把它变成这样的数据框:

df = pandas.DataFrame.from_dict(data, orient="index").fillna(0).astype(int)

产生这个:

e d f a c b i h g X 0 0 0 1 3 2 0 0 0 Y 5 4 6 0 0 0 0 0 0 Z 0 0 0 0 0 0 9 8 7

现在,这是我的问题。我的数据有数十万行(即外部字典中的键数)。这些中的每一个都只有少数与之关联的列(即每个内部字典中的键数),但列数总数为千。我发现使用from_dict的DataFrame生成非常慢,大约2.5-3分钟,200,000行和6,000列。

此外,在行索引是MultiIndex的情况下(即,外部方向的键是X,Y和Z而不是元组),from_dict甚至更慢,大约为20,000行的7+分钟。我发现如果不使用字典字典,而是使用字典列表,然后使用set_index将MultiIndex添加回生成的DataFrame,则可以避免这种开销。

总之,您如何建议我处理此问题?库开发人员可以清楚地改进MultiIndex的性能,但是我在这里使用错误的工具吗?如果写入磁盘,DataFrame的大小约为2.5GB。在大约2分钟左右从磁盘读取2.5GB文件似乎是正确的,但理论上我的数据在内存中的稀疏性应该会更快。

3 个答案:

答案 0 :(得分:2)

事实证明,sklearn有一个完全符合我需要的课程。

sklearn.feature_extraction.DictVectorizer

我将数据生成为字典列表,将行标签放在一边。然后:

vectorizer = sklearn.feature_extraction.DictVectorizer(dtype=numpy.uint8, 
sparse=False)

matrix = vectorizer.fit_transform(data)
column_labels = vectorizer.get_feature_names()

df = pandas.DataFrame(matrix, index=row_labels, columns=column_labels)

在一分钟左右完成,这对我来说足够快。也许有人可以进一步改进它。

答案 1 :(得分:1)

我的建议是使用稀疏矩阵并用数字(行/列)标识符替换字母。

以下是基于最小范例的基准测试示例。

import pandas as pd, numpy as np
from scipy.sparse import coo_matrix

def original(data):
    df = pd.DataFrame.from_dict(data, orient="index").fillna(0).astype(int)
    return df

def jp(data):
    res = {(ord(k), ord(i)): j for k, v in data.items() for i, j in v.items()}

    n = len(res)

    rows = np.array(pd.factorize(list(zip(*res.keys()))[0])[0])
    cols = np.array(pd.factorize(list(zip(*res.keys()))[1])[0])
    values = np.array(list(res.values()))

    return pd.DataFrame(coo_matrix((values, (rows, cols)),
                        shape=(len(np.unique(rows)), n)).toarray())

%timeit original(data)  # 1.45 ms
%timeit jp(data)        # 488 µs

如果您愿意,可以将索引/列重命名为单独的步骤。我没有对此进行过测试,但我的直觉是这个步骤的方法仍然相当快。

<强>结果

   0  1  2  3  4  5  6  7  8
0  1  2  3  0  0  0  0  0  0
1  0  0  0  4  5  6  0  0  0
2  0  0  0  0  0  0  7  8  9

答案 2 :(得分:0)

OP 的答案仍然无法用于非常大的字典(或具有更多内存限制)。最好使用 sklearn 的稀疏特性,让生活更轻松:

data = {"X": {'a': 1, 'b': 2, 'c': 3}, "Y": {'d': 4, 'e': 5, 'f': 6}, "Z": {'g': 7, 'h': 8, 'i': 9}}
vectorizer = sklearn.feature_extraction.DictVectorizer(dtype=numpy.uint8, 
    sparse=True) # <------ Here

row_labels = list(data) 
matrix = vectorizer.fit_transform([data[i] for i in row_labels]) 
column_labels = vectorizer.get_feature_names()

df = pandas.DataFrame.sparse.from_spmatrix(matrix,    # <----- and Here
 index=row_labels, columns=column_labels)