命名元组的嵌套字典转换为Pandas数据框。

5

我定义了如下的namedtuple:

In[37]: from collections import namedtuple
        Point = namedtuple('Point', 'x y')

嵌套字典的格式如下:
In[38]: d
Out[38]: 
{1: {None: {1: Point(x=1.0, y=5.0), 2: Point(x=4.0, y=8.0)}},
2: {None: {1: Point(x=45324.0, y=24338.0), 2: Point(x=45.0, y=38.0)}}}

我正在尝试从字典d中创建一个pandas dataframe,而无需使用for循环。

我已经通过以下方式成功地从字典的子集中创建了dataframe:

In[40]: df=pd.DataFrame(d[1][None].values())

In[41]: df

Out[41]: 
   x  y
0  1  5
1  4  8

但是我希望能够从整个字典中创建数据框。

我想要数据框输出以下内容(我使用的是多索引符号):

In[42]: df
Out[42]:
Subcase Step ID  x       y
1       None 1   1.0     5.0
             2   4.0     8.0
2       None 1   45324.0 24338.0
             2   45.0    38.0

DataFrame的from_dict方法只支持最多两层嵌套,所以我无法使用它。我还考虑修改d字典的结构来实现我的目标。此外,也许它不必是一个字典。
谢谢。

你说它不一定要是一个字典 - 那字典中的数据来源是什么?或者你指的是在将字典转换为DataFrame之前,将其转换为中间结构吗? - Jeff
数据的来源是二进制文件。它被转换为字典以便于访问和快速查询。最好仍然保持为字典。我的意思是,我可以修改将二进制文件转换为字典的代码,并使用更适合pandas的东西。转换字典似乎效率低下。 - snowleopard
2个回答

2

在SO上已经有几个类似问题的答案(这里这里这里)。这些解决方案也可以适用于此问题。但是,它们中没有一个真正通用,可以在任意字典上运行。因此,我决定编写更通用的内容。

这是一个可以在任何字典上运行的函数。该字典的每个元素必须具有相同的级别(深度),否则可能会引发错误。

def frame_from_dict(dic, depth=None, **kwargs):
    def get_dict_depth(dic):
        if not isinstance(dic, dict):
            return 0
        for v in dic.values():
            return get_dict_depth(v) + 1

    if depth is None:
        depth = get_dict_depth(dic)

    if depth == 0:
        return pd.Series(dic)
    elif depth > 0:
        keys = []
        vals = []
        for k, v in dic.items():
            keys.append(k)
            vals.append(frame_from_dict(v, depth - 1))
        try:
            keys = sorted(keys)
        except TypeError:
            # unorderable types
            pass
        return pd.concat(vals, axis=1, keys=keys, **kwargs)

    raise ValueError("depth should be a nonnegative integer or None")

我为了通用性而放弃了这个问题中的一个命名元组案例。但如果需要,它可以进行调整。
在这个特定的情况下,它可以按如下方式应用:
df = frame_from_dict(d, names=['Subcase', 'Step', 'ID']).T
df.columns = ['x', 'y']
df
Out[115]: 
                       x        y
Subcase Step ID                  
1       NaN  1       1.0      5.0
             2       4.0      8.0
2       NaN  1   45324.0  24338.0
             2      45.0     38.0

谢谢,这个方法非常好用。我之前也知道这个解决方案,但是我尽量避免使用 for 循环,因为我可以控制字典的定义。我决定将键铺平成一个元组。请参见下面的解决方案。 - snowleopard
@snowleopard 我明白了。你有将嵌套字典的键扁平化为元组的通用方法吗?我认为这是问题的关键所在。 - ptrj
是的,你说得对,但我正在从二进制文件创建字典,这样我就可以控制字典的定义方式。 - snowleopard
好的,直接创建元组是更好的方法。 - ptrj

0

我决定将键值扁平化为元组(在使用 pandas 0.18.1 进行测试):

In [5]: from collections import namedtuple

In [6]: Point = namedtuple('Point', 'x y')

In [11]: from collections import OrderedDict

In [14]: d=OrderedDict()

In [15]: d[(1,None,1)]=Point(x=1.0, y=5.0)

In [16]: d[(1,None,2)]=Point(x=4.0, y=8.0)

In [17]: d[(2,None,1)]=Point(x=45324.0, y=24338.0)

In [18]: d[(2,None,2)]=Point(x=45.0, y=38.0)

最后,

In [7]: import pandas as pd

In [8]: df=pd.DataFrame(d.values(),  index=pd.MultiIndex.from_tuples(d.keys(), names=['Subcase','Step','ID']))


In [9]:df
Out[9]: 
                       x        y
Subcase Step ID                  
1       NaN  1       1.0      5.0
             2       4.0      8.0
2       NaN  1   45324.0  24338.0
             2      45.0     38.0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接