将dict的dict转换为pandas DataFrame - 内存问题

Posted

技术标签:

【中文标题】将dict的dict转换为pandas DataFrame - 内存问题【英文标题】:Converting dict of dicts into pandas DataFrame - memory issues 【发布时间】:2013-12-28 04:08:03 【问题描述】:

我有一个数据结构,它由一个三层嵌套的字典组成,它记录了三部分对象的出现次数。我想用它构建一个具有特定形状的 DataFrame,但我想不出一种不涉及消耗大量工作内存的方法——因为表很大(几个GBs)。

基本功能如下所示:

class SparseCubeTable:
    def __init__(self):
        self.table = 
        self.dim1 = []
        self.dim2 = []
        self.dim3 = []

    def increment(self, dim1, dim2, dim3):
        if dim1 in self.table:
            if dim2 in self.table[dim1]:
                if dim3 in self.table[dim1][dim2]:
                    self.table[dim1][dim2][dim3] += 1
                else:
                    self.dim3.append(dim3)
                    self.table[dim1][dim2][dim3] = 1
            else:
                self.dim2.append(dim2)
                self.dim3.append(dim3)
                self.table[dim1][dim2] = dim3:1
        else:
            self.dim1.append(dim1)
            self.dim2.append(dim2)
            self.dim3.append(dim3)
            self.table[dim1] = dim2:dim3:1

这是为了使键的求和更容易,除其他外。 SparseCubeTable 的使用方式如下:

In [23]: example = SparseCubeTable()

In [24]: example.increment("thing1", "thing2", "thing3")

In [25]: example.increment("thing1", "thing2", "thing3")

In [26]: example.increment("thing4", "thing5", "thing6")

In [27]: example.increment("thing1", "thing3", "thing5")

你可以这样获取数据:

In [29]: example.table['thing1']['thing2']['thing3']
Out[29]: 2

我想要的 DataFrame 是这样的:

1 2 3 4
thing1 thing2 thing3 2
thing1 thing3 thing5 1
thing4 thing5 thing6 1

DataFrame 将保存为 HDF5 db,其中第 1-3 列被索引并在第 4 列进行统计转换(这需要整个表暂时在内存中)。

据我了解,问题在于pandas.DataFrame.from_dict 函数构建了一种完全不同的结构,其中的键用作行标签。但是,尝试使用 from_records 会迫使我将整个数据结构复制到一个列表中,这意味着我现在需要担心双倍的内存大小。

我尝试在以下位置实施解决方案:

Create a pandas DataFrame from generator?

但在 0.12.0 中,它最终要做的是首先构建一个巨大的字符串列表,这更糟。我假设将结构写出到 csv 并将其读回在内存上也会很糟糕。

有没有更好的方法来做到这一点?还是我应该尝试以某种方式在SparseCubeTable 中进一步压缩内存?必须构建一个中间列表数据结构才能使用from_records,这似乎很浪费。

【问题讨论】:

我只是花时间浏览了一下,但我认为from_dict 上的orient 关键字可能会为您工作。 您或许可以直接构建一个 Panel,Panel(example.table),这可能对您有用 @DanAllan 试过了。它只需要一级键,然后将它们设为列或行索引。我有这个三方结构。 @Jeff 我正在摆弄您的 Panel 解决方案,但我需要沿所有三列(我的 DataFrame 版本)进行分组,并且 Panel.group_by 不存在并且 Panel.to_frame () 创建一个空的 DataFrame。 我正要建议,你根本不要走这条路。而是先直接构建一个 HDF 表。分块读取,执行 value_counts,写入新表并重复,请参阅此解决方案:***.com/questions/15798209/…。 【参考方案1】:

这是一个有效解决方案的代码。

创建一些看起来像你的数据。这是 1000 个 3 元组的列表

In [1]: import random

In [2]: tags = [ 'thing0'.format(i) for i in xrange(100) ]

In [3]: data = [ (random.choice(tags),random.choice(tags),random.choice(tags)) for i in range(1000) ]

我们的写入函数,确保我们在写入时索引是全局唯一的(实际上并不是必需的,但由于索引实际上是写得“更好”)

In [4]: def write(store,c):
   ...:     df = DataFrame(c,columns=['dim1','dim2','dim3'])
   ...:     try:
   ...:         nrows = store.get_storer('df').nrows
   ...:     except:
   ...:         nrows = 0
   ...:     df.index += nrows
   ...:     store.append('df',df,data_columns=True)
   ...:     return []
   ...: 

In [5]: collector = []

In [6]: store = pd.HDFStore('data.h5',mode='w')

遍历您的数据(或来自流或其他),然后写入。

In [7]: for i, d in enumerate(data):
   ...:     collector.append(d)
   ...:     if i % 100 == 0 and i:
   ...:         collector = write(store,collector)
   ...:         

In [8]: write(store,collector)
Out[8]: []

商店

In [9]: store
Out[9]: 
<class 'pandas.io.pytables.HDFStore'>
File path: data.h5
/df            frame_table  (typ->appendable,nrows->1000,ncols->3,indexers->[index],dc->[dim1,dim2,dim3])
In [9]: store
Out[9]: 
<class 'pandas.io.pytables.HDFStore'>
File path: data.h5
/df            frame_table  (typ->appendable,nrows->1000,ncols->3,indexers->[index],dc->[dim1,dim2,dim3])

In [10]: store.select('df')
Out[10]: 
       dim1     dim2     dim3
0   thing28  thing87  thing29
1   thing62  thing70  thing50
2   thing64  thing12  thing98
3   thing33  thing98  thing46
4   thing46   thing5  thing76
5    thing2   thing9  thing21
6    thing1  thing63  thing68
7   thing42  thing30  thing45
8   thing56  thing71  thing77
9   thing99  thing10  thing91
10  thing40   thing9  thing10
11  thing70  thing54  thing59
12  thing94  thing65   thing3
13  thing93  thing24  thing25
14  thing95  thing94  thing86
15  thing41  thing55   thing3
16  thing88  thing10  thing47
17  thing89  thing58  thing33
18  thing16  thing66  thing55
19  thing68  thing20  thing99
20  thing34  thing71  thing28
21  thing67  thing87  thing97
22  thing77  thing74   thing6
23  thing63  thing41  thing30
24  thing14  thing62  thing66
25  thing20  thing36  thing67
26  thing33  thing19  thing58
27   thing0  thing71  thing24
28   thing1  thing48  thing42
29  thing18  thing12   thing4
30  thing85  thing97  thing20
31  thing73  thing71  thing70
32  thing91  thing43  thing48
33  thing45   thing6  thing87
34   thing0  thing28   thing8
35  thing56  thing38  thing61
36  thing39  thing92  thing35
37  thing69  thing26  thing22
38  thing16  thing16  thing79
39   thing4  thing16  thing12
40  thing81  thing79   thing1
41  thing77  thing90  thing83
42  thing53  thing17  thing89
43  thing53  thing15  thing37
44  thing25   thing7  thing20
45  thing44  thing14  thing25
46  thing62  thing84  thing23
47  thing83  thing50  thing60
48  thing68  thing64  thing24
49  thing73  thing53  thing43
50  thing86  thing67  thing31
51  thing75  thing63  thing82
52   thing8  thing10  thing90
53  thing34  thing23  thing12
54  thing66  thing97  thing26
55  thing66  thing53  thing27
56  thing79  thing22  thing37
57  thing43  thing82  thing66
58  thing87  thing53  thing92
59  thing33  thing71  thing97
        ...      ...      ...

[1000 rows x 3 columns]

In [11]: store.close()

然后你可以做一些有趣的事情。如果您没有阅读整个集合,则可能需要将其分块(如果您正在计算事物,这会涉及更多内容)。

In [56]: pd.read_hdf('data.h5','df').apply(lambda x: x.value_counts())
Out[56]: 
         dim1  dim2  dim3
thing0     12     6     8
thing1     14     7     8
thing10    10    10     7
thing11     8    10    14
thing12    11    14    11
thing13    11    12     7
thing14     8    14     3
thing15    12    11    11
thing16     7    10    11
thing17    16     9    13
thing18    13     8    10
thing19    11     7     8
thing2      9     5    17
thing20     6     7    11
thing21     7     8     8
thing22     4    17    14
thing23    14    11     7
thing24    10     5    14
thing25    11    11    12
thing26    13    10    15
thing27    12    15    16
thing28    11    10     8
thing29     7     7     8
thing3     11    14    14
thing30    11    16     8
thing31     7     6    12
thing32     8    12     9
thing33    13    12    12
thing34    12     8     5
thing35     6    10     8
thing36     6     9    13
thing37     8    10    12
thing38     7    10     4
thing39    14    11     7
thing4      9     7    10
thing40    12     8     9
thing41     8    16    11
thing42     9    11    13
thing43     8     6    13
thing44     9    13    11
thing45     7    13     7
thing46    12     8    13
thing47     9    10     9
thing48     8     9     9
thing49     4     8     7
thing5     13     7     7
thing50    14    12     9
thing51     5     7    11
thing52     9    11    12
thing53     9    15    15
thing54     7     9    13
thing55     6    10    10
thing56    12    11    11
thing57    12     9    11
thing58    12    12    10
thing59     6    13    10
thing6      8     5     7
thing60    12     9     6
thing61     5     9     9
thing62     8    10     8
          ...   ...   ...

[100 rows x 3 columns]

然后您可以像这样执行“groupby”:

In [69]: store = pd.HDFStore('data.h5')

In [61]: dim1 = Index(store.select_column('df','dim1').unique())
In [66]: store.close()

In [67]: groups = dim1[0:10]

In [68]: groups
Out[68]: Index([u'thing28', u'thing62', u'thing64', u'thing33', u'thing46', u'thing2', u'thing1', u'thing42', u'thing56', u'thing99'], dtype='object')

In [70]: pd.read_hdf('data.h5','df',where='dim1=groups').apply(lambda x: x.value_counts())
Out[70]: 
         dim1  dim2  dim3
thing1     14     2     1
thing10   NaN     1     1
thing11   NaN     1     2
thing12   NaN     5   NaN
thing13   NaN     1   NaN
thing14   NaN     1     1
thing15   NaN     1     1
thing16   NaN     1     3
thing17   NaN   NaN     2
thing18   NaN     1     1
thing19   NaN     1     2
thing2      9     1     1
thing20   NaN     2   NaN
thing21   NaN   NaN     1
thing22   NaN     2     2
thing23   NaN     2     3
thing24   NaN     2     1
thing25   NaN     3     2
thing26   NaN     2     2
thing27   NaN     3     1
thing28    11   NaN   NaN
thing29   NaN     1     2
thing30   NaN     2   NaN
thing31   NaN     1     1
thing32   NaN     1     1
thing33    13     1     2
thing34   NaN     1   NaN
thing35   NaN     1   NaN
thing36   NaN     1     1
thing37   NaN     1     2
thing38   NaN     3   NaN
thing39   NaN     3     1
thing4    NaN     2   NaN
thing41   NaN   NaN     1
thing42     9     1     1
thing43   NaN   NaN     1
thing44   NaN     1     2
thing45   NaN   NaN     2
thing46    12   NaN     1
thing47   NaN     1     1
thing48   NaN     1   NaN
thing49   NaN     1   NaN
thing5    NaN     2     2
thing50   NaN   NaN     3
thing51   NaN     2     2
thing52   NaN     1     3
thing53   NaN     2     4
thing55   NaN   NaN     2
thing56    12     1     1
thing57   NaN   NaN     3
thing58   NaN     1     2
thing6    NaN   NaN     1
thing60   NaN     1     1
thing61   NaN     1     4
thing62     8     2     1
thing63   NaN     1     1
thing64    15   NaN     1
thing66   NaN     1     2
thing67   NaN     2   NaN
thing68   NaN     1     1
          ...   ...   ...

[90 rows x 3 columns]

【讨论】:

啊,这真是太好了。我不确定我是否完全理解最后一点:“如果你没有阅读整个系列,你可能想要分块。”我最终确实需要一起计算“thing2 thing9 thing21”,因为练习的重点是计算三元组的某些类型的统计信息,尽管我当然也需要其他 value_counts。 我的意思是这个。如果您可以在内存中读取存储 HDF5 表,那么做事情就变得很容易(例如,就像上面的值一样,顺便说一句,我不确定这是否正是您想要的,但是一种开始的方式)。如果您无法读取内存中的 HDF5 表,您可能需要分块处理它,例如基本上对您选择的部分进行操作,例如通过组的子集。 (您可以像上面那样选择)。 啊,我明白你的意思了。我怀疑该表太大而无法保存在内存中。分块构建意味着我必须构建类似于原始示例中的 SparseCubeTable 大小的东西,这样我不仅可以获得单个每个令牌的 value_counts,还可以获得“thing1 thing2 thing3”的实际次数" 在原始 XML 文件中一起发生,与数据中的所有其他“thingA thingB thingC”成比例。在这种情况下,我会回到我原来的问题。顺便说一句,并非所有事情都可以在每个维度上发生,这就是为什么事情如此复杂。

以上是关于将dict的dict转换为pandas DataFrame - 内存问题的主要内容,如果未能解决你的问题,请参考以下文章

将dict的dict转换为pandas DataFrame - 内存问题

将 dict 构造函数转换为 Pandas MultiIndex 数据帧

将 pandas.groupby 转换为 dict

将dict列表转换为pandas中的行[重复]

python pandas数据框列转换为dict键和值

将json dict转换为pandas df中的行