【问题标题】:Nested dictionary to multiindex dataframe where dictionary keys are column labels嵌套字典到多索引数据帧,其中字典键是列标签
【发布时间】:2024-01-16 03:02:01
【问题描述】:

假设我有一本看起来像这样的字典:

dictionary = {'A' : {'a': [1,2,3,4,5],
                     'b': [6,7,8,9,1]},

              'B' : {'a': [2,3,4,5,6],
                     'b': [7,8,9,1,2]}}

我想要一个看起来像这样的数据框:

     A   B
     a b a b
  0  1 6 2 7
  1  2 7 3 8
  2  3 8 4 9
  3  4 9 5 1
  4  5 1 6 2

有没有方便的方法来做到这一点?如果我尝试:

In [99]:

DataFrame(dictionary)

Out[99]:
     A               B
a   [1, 2, 3, 4, 5] [2, 3, 4, 5, 6]
b   [6, 7, 8, 9, 1] [7, 8, 9, 1, 2]

我得到一个数据框,其中每个元素都是一个列表。我需要的是一个多索引,其中每个级别对应于嵌套字典中的键和对应于列表中每个元素的行,如上所示。我想我可以提出一个非常粗略的解决方案,但我希望可能会有一些更简单的解决方案。

【问题讨论】:

    标签: python dictionary pandas dataframe multi-index


    【解决方案1】:

    Pandas 想要将 MultiIndex 值作为元组,而不是嵌套字典。最简单的方法是在尝试将字典传递给 DataFrame 之前将其转换为正确的格式:

    >>> reform = {(outerKey, innerKey): values for outerKey, innerDict in dictionary.iteritems() for innerKey, values in innerDict.iteritems()}
    >>> reform
    {('A', 'a'): [1, 2, 3, 4, 5],
     ('A', 'b'): [6, 7, 8, 9, 1],
     ('B', 'a'): [2, 3, 4, 5, 6],
     ('B', 'b'): [7, 8, 9, 1, 2]}
    >>> pandas.DataFrame(reform)
       A     B   
       a  b  a  b
    0  1  6  2  7
    1  2  7  3  8
    2  3  8  4  9
    3  4  9  5  1
    4  5  1  6  2
    
    [5 rows x 4 columns]
    

    【讨论】:

    • +1,但是如果字典值列表的长度,例如,这不起作用5 在这个具体的例子中,不匹配。有什么想法可以解决这个问题吗?例如如果reform= ('A', 'a'): [1, 2, 3, 4, 5], ('A', 'b'): [6, 7, 8, 9,]
    • @Zhubarb:您希望生成的 DataFrame 是什么样的? DataFrame 必须是矩形的;它不能有不同长度的列。
    • 列在数据框中随机排序,因为字典是无序的。如果不使用 OrderedDict,如何执行所需的顺序?
    • 如果.iteritems().items()替换,上述解决方案仅适用于python 3.5及更高版本
    • 这很棒。仅供参考,如果values 是“记录”的形式,也可以使用pd.DataFrame.from_dict 完成:[{'a': 1, 'b': 2}, {'a': 3, 'b': 4}, ...]
    【解决方案2】:
    dict_of_df = {k: pd.DataFrame(v) for k,v in dictionary.items()}
    df = pd.concat(dict_of_df, axis=1)
    

    请注意,python 的列顺序丢失了

    【讨论】:

    • 我在使用这种方法时遇到了另一个问题。从 yahoofinancials 导入 YahooFinancials 股票代码 = ['AAPL', 'WFC', 'F', 'FB', 'DELL', 'SNE'] yahoo_financials = YahooFinancials(tickers) BB=yahoo_financials.get_key_statistics_data() dict_of_df = {k: pd .DataFrame(v) for k,v in BB.items()} df = pd.concat(dict_of_df, axis=1) ValueError: If using all scalar values, you must pass an index
    【解决方案3】:

    这个答案有点晚了,但是......

    您正在寻找.stack 中的功能:

    df = pandas.DataFrame.from_dict(dictionary, orient="index").stack().to_frame()
    # to break out the lists into columns
    df = pd.DataFrame(df[0].values.tolist(), index=df.index)
    

    【讨论】:

    • 谢谢!此答案不需要将嵌套字典重新格式化为 {(key0, key1): [data0, data_n...]} 也不会在 pandas v1.x 上失败。
    • 这很棒。也适用于缺少键的数据。
    【解决方案4】:

    这个递归函数应该可以工作:

    def reform_dict(dictionary, t=tuple(), reform={}):
        for key, val in dictionary.items():
            t = t + (key,)
            if isinstance(val, dict):
                reform_dict(val, t, reform)
            else:
                reform.update({t: val})
            t = t[:-1]
        return reform
    

    【讨论】:

      【解决方案5】:

      如果字典中的列表长度不同,可以改用BrenBarn的方法。

      >>> dictionary = {'A' : {'a': [1,2,3,4,5],
                               'b': [6,7,8,9,1]},
                       'B' : {'a': [2,3,4,5,6],
                              'b': [7,8,9,1]}}
      
      >>> reform = {(outerKey, innerKey): values for outerKey, innerDict in dictionary.items() for innerKey, values in innerDict.items()}
      >>> reform
       {('A', 'a'): [1, 2, 3, 4, 5],
        ('A', 'b'): [6, 7, 8, 9, 1],
        ('B', 'a'): [2, 3, 4, 5, 6],
        ('B', 'b'): [7, 8, 9, 1]}
      
      >>> pandas.DataFrame.from_dict(reform, orient='index').transpose()
      >>> df.columns = pd.MultiIndex.from_tuples(df.columns)
         A     B   
         a  b  a  b
      0  1  6  2  7
      1  2  7  3  8
      2  3  8  4  9
      3  4  9  5  1
      4  5  1  6  NaN
      [5 rows x 4 columns]
      

      【讨论】: