【问题标题】:Convert list of dictionaries to a pandas DataFrame将字典列表转换为 pandas DataFrame
【发布时间】:2014-01-05 10:37:18
【问题描述】:

我有一个这样的字典列表:

[{'points': 50, 'time': '5:00', 'year': 2010}, 
{'points': 25, 'time': '6:00', 'month': "february"}, 
{'points':90, 'time': '9:00', 'month': 'january'}, 
{'points_h1':20, 'month': 'june'}]

我想把它变成一个熊猫DataFrame,像这样:

      month  points  points_h1  time  year
0       NaN      50        NaN  5:00  2010
1  february      25        NaN  6:00   NaN
2   january      90        NaN  9:00   NaN
3      june     NaN         20   NaN   NaN

注意:列的顺序无关紧要。

如何将字典列表转换为如上所示的 pandas DataFrame?

【问题讨论】:

    标签: python dictionary pandas dataframe


    【解决方案1】:

    假设 d 是你的字典列表,简单地说:

    df = pd.DataFrame(d)
    

    注意:这不适用于嵌套数据。

    【讨论】:

    • 如何使用其中一个键/值对作为索引(例如时间)?
    • @CatsLoveJazz 你可以在之后做df = df.set_index('time')
    • @CatsLoveJazz 不,从字典转换时这是不可能的。
    • 从 Pandas 0.19.2 开始,文档中没有提及这一点,至少在 pandas.DataFrame 的文档中没有提及
    • 请注意,对于嵌套字典'{"":{"...,您使用 json_normalize 方法,请参阅@cs95 的详细答案
    【解决方案2】:

    在 pandas 16.2 中,我必须执行 pd.DataFrame.from_records(d) 才能使其正常工作。

    【讨论】:

    • 这种方法的好处是它也适用于deque
    • 与 pandas 0.17.1 和 @joris 解决方案一起工作
    • Usinig 0.14.1 和 @joris 的解决方案没有用,但确实如此
    • 0.18.1 中,如果字典的键不同,则必须使用from_records
    【解决方案3】:

    您也可以将pd.DataFrame.from_dict(d) 用作:

    In [8]: d = [{'points': 50, 'time': '5:00', 'year': 2010}, 
       ...: {'points': 25, 'time': '6:00', 'month': "february"}, 
       ...: {'points':90, 'time': '9:00', 'month': 'january'}, 
       ...: {'points_h1':20, 'month': 'june'}]
    
    In [12]: pd.DataFrame.from_dict(d)
    Out[12]: 
          month  points  points_h1  time    year
    0       NaN    50.0        NaN  5:00  2010.0
    1  february    25.0        NaN  6:00     NaN
    2   january    90.0        NaN  9:00     NaN
    3      june     NaN       20.0   NaN     NaN
    

    【讨论】:

    • 问题是关于从dicts 的列表 构造数据框,而不是像您在答案中假设的那样从单个dict 构造数据框。
    • @a_guest 检查更新的答案。我不是假设。
    【解决方案4】:

    如何将字典列表转换为 pandas DataFrame?

    其他答案是正确的,但就这些方法的优点和局限性而言,没有太多解释。这篇文章的目的是展示这些方法在不同情况下的示例,讨论何时使用(以及何时不使用),并提出替代方案。


    DataFrame()DataFrame.from_records().from_dict()

    根据数据的结构和格式,在某些情况下,三种方法都有效,或者某些方法比其他方法更好,或者有些根本不起作用。

    考虑一个非常人为的例子。

    np.random.seed(0)
    data = pd.DataFrame(
        np.random.choice(10, (3, 4)), columns=list('ABCD')).to_dict('r')
    
    print(data)
    [{'A': 5, 'B': 0, 'C': 3, 'D': 3},
     {'A': 7, 'B': 9, 'C': 3, 'D': 5},
     {'A': 2, 'B': 4, 'C': 7, 'D': 6}]
    

    此列表由“记录”组成,每个键都存在。这是您可能遇到的最简单的情况。

    # The following methods all produce the same output.
    pd.DataFrame(data)
    pd.DataFrame.from_dict(data)
    pd.DataFrame.from_records(data)
    
       A  B  C  D
    0  5  0  3  3
    1  7  9  3  5
    2  2  4  7  6
    

    关于字典方向的单词:orient='index'/'columns'

    在继续之前,重要的是要区分不同类型的字典方向,并支持 pandas。有两种主要类型:“列”和“索引”。

    orient='columns'
    具有“列”方向的字典将使其键对应于等效 DataFrame 中的列。

    例如,上面的data 位于“列”方向。

    data_c = [
     {'A': 5, 'B': 0, 'C': 3, 'D': 3},
     {'A': 7, 'B': 9, 'C': 3, 'D': 5},
     {'A': 2, 'B': 4, 'C': 7, 'D': 6}]
    
    pd.DataFrame.from_dict(data_c, orient='columns')
    
       A  B  C  D
    0  5  0  3  3
    1  7  9  3  5
    2  2  4  7  6
    

    注意:如果您使用pd.DataFrame.from_records,则方向假定为“列”(您不能另外指定),并且将相应地加载字典。

    orient='index'
    使用这个方向,假设键对应于索引值。这种数据最适合pd.DataFrame.from_dict

    data_i ={
     0: {'A': 5, 'B': 0, 'C': 3, 'D': 3},
     1: {'A': 7, 'B': 9, 'C': 3, 'D': 5},
     2: {'A': 2, 'B': 4, 'C': 7, 'D': 6}}
    
    pd.DataFrame.from_dict(data_i, orient='index')
    
       A  B  C  D
    0  5  0  3  3
    1  7  9  3  5
    2  2  4  7  6
    

    OP 中没有考虑这种情况,但了解仍然有用。

    设置自定义索引

    如果您需要在生成的 DataFrame 上自定义索引,可以使用 index=... 参数进行设置。

    pd.DataFrame(data, index=['a', 'b', 'c'])
    # pd.DataFrame.from_records(data, index=['a', 'b', 'c'])
    
       A  B  C  D
    a  5  0  3  3
    b  7  9  3  5
    c  2  4  7  6
    

    pd.DataFrame.from_dict 不支持此功能。

    处理缺失的键/列

    在处理缺少键/列值的字典时,所有方法都是开箱即用的。例如,

    data2 = [
         {'A': 5, 'C': 3, 'D': 3},
         {'A': 7, 'B': 9, 'F': 5},
         {'B': 4, 'C': 7, 'E': 6}]
    
    # The methods below all produce the same output.
    pd.DataFrame(data2)
    pd.DataFrame.from_dict(data2)
    pd.DataFrame.from_records(data2)
    
         A    B    C    D    E    F
    0  5.0  NaN  3.0  3.0  NaN  NaN
    1  7.0  9.0  NaN  NaN  NaN  5.0
    2  NaN  4.0  7.0  NaN  6.0  NaN
    

    读取列的子集

    “如果我不想阅读每一列怎么办”?您可以使用columns=... 参数轻松地指定它。

    例如,在上面的示例字典data2 中,如果您只想读取“A”、“D”和“F”列,可以通过传递一个列表来实现:

    pd.DataFrame(data2, columns=['A', 'D', 'F'])
    # pd.DataFrame.from_records(data2, columns=['A', 'D', 'F'])
    
         A    D    F
    0  5.0  3.0  NaN
    1  7.0  NaN  5.0
    2  NaN  NaN  NaN
    

    pd.DataFrame.from_dict 不支持默认方向“列”。

    pd.DataFrame.from_dict(data2, orient='columns', columns=['A', 'B'])
    
    ValueError: cannot use columns parameter with orient='columns'
    

    读取行的子集

    这些方法直接都不支持。您将不得不迭代您的数据并在迭代时就地执行reverse delete。例如,要从上面的data2 中仅提取第 0th 和 2nd 行,您可以使用:

    rows_to_select = {0, 2}
    for i in reversed(range(len(data2))):
        if i not in rows_to_select:
            del data2[i]
    
    pd.DataFrame(data2)
    # pd.DataFrame.from_dict(data2)
    # pd.DataFrame.from_records(data2)
    
         A    B  C    D    E
    0  5.0  NaN  3  3.0  NaN
    1  NaN  4.0  7  NaN  6.0
    

    万能药:json_normalize 用于嵌套数据

    上述方法的强大替代方案是json_normalize 函数,它适用于字典列表(记录),此外还可以处理嵌套字典。

    pd.json_normalize(data)
    
       A  B  C  D
    0  5  0  3  3
    1  7  9  3  5
    2  2  4  7  6
    
    pd.json_normalize(data2)
    
         A    B  C    D    E
    0  5.0  NaN  3  3.0  NaN
    1  NaN  4.0  7  NaN  6.0
    

    再次提醒,传递给json_normalize 的数据需要采用字典列表(记录)格式。

    如前所述,json_normalize 也可以处理嵌套字典。这是从文档中摘录的示例。

    data_nested = [
      {'counties': [{'name': 'Dade', 'population': 12345},
                    {'name': 'Broward', 'population': 40000},
                    {'name': 'Palm Beach', 'population': 60000}],
       'info': {'governor': 'Rick Scott'},
       'shortname': 'FL',
       'state': 'Florida'},
      {'counties': [{'name': 'Summit', 'population': 1234},
                    {'name': 'Cuyahoga', 'population': 1337}],
       'info': {'governor': 'John Kasich'},
       'shortname': 'OH',
       'state': 'Ohio'}
    ]
    
    pd.json_normalize(data_nested, 
                              record_path='counties', 
                              meta=['state', 'shortname', ['info', 'governor']])
    
             name  population    state shortname info.governor
    0        Dade       12345  Florida        FL    Rick Scott
    1     Broward       40000  Florida        FL    Rick Scott
    2  Palm Beach       60000  Florida        FL    Rick Scott
    3      Summit        1234     Ohio        OH   John Kasich
    4    Cuyahoga        1337     Ohio        OH   John Kasich
    

    有关metarecord_path 参数的更多信息,请查看文档。


    总结

    这是上面讨论的所有方法的表格,以及支持的特性/功能。

    * 使用orient='columns',然后转置得到和orient='index'一样的效果。

    【讨论】:

    • 哇!好的,这与 Merging SO post 一起属于 API。如果您还没有这样做,您应该为 pandas 文档做出贡献。 Ted Petrou 刚刚在 Stack Overflow 上发布了一个 LinkedIn article 关于 pandas 受欢迎程度的帖子,并提到缺乏良好的文档会导致这里出现大量问题。
    • @ScottBoston 你说得完全正确,我已经听过很多次了,现在我知道这是我应该更加认真考虑的事情。我认为文档可以成为帮助用户的好方法,而不是发布只能触及同一受众的一小部分的问题。
    • 这尤其成问题,因为哪些方法适用于哪些情况经常变化的详细信息,因此对 SO 进行非常冗长、深入的回答不仅不如 pandas 官方文档中的有用,但通常甚至有害或误导,因为对函数内部的一些更改可能会突然使答案不正确或事实上错误,并且它没有明确链接到实际源代码库以标记文档更新。
    • 这是一个很好的答案,我认为是时候让我们在最新的 pandas 版本下重新讨论这些常见问题了 :-)
    • @ely:这绝不是不写答案的理由无论如何任何答案都可能过时,这就是我们投票支持的内容,这里存在不同的观点和不同的目标,用不同的方式来解释同一件事总是很有价值的。
    【解决方案5】:

    我发现的最简单的方法是这样的:

    dict_count = len(dict_list)
    df = pd.DataFrame(dict_list[0], index=[0])
    for i in range(1,dict_count-1):
        df = df.append(dict_list[i], ignore_index=True)
    

    【讨论】:

    • 使用pandas时避免循环,循环kill是pandas的全部目的
    • 我没有投反对票,但虽然这在技术上可行,但它的性能很差。请参阅this 了解更多信息。
    【解决方案6】:

    Pyhton3: 之前列出的大多数解决方案都有效。但是,有些情况下不需要数据帧的 row_number 并且必须单独写入每一行(记录)。

    在这种情况下,下面的方法很有用。

    import csv
    
    my file= 'C:\Users\John\Desktop\export_dataframe.csv'
    
    records_to_save = data2 #used as in the thread. 
    
    
    colnames = list[records_to_save[0].keys()] 
    # remember colnames is a list of all keys. All values are written corresponding
    # to the keys and "None" is specified in case of missing value 
    
    with open(myfile, 'w', newline="",encoding="utf-8") as f:
        writer = csv.writer(f)
        writer.writerow(colnames)
        for d in records_to_save:
            writer.writerow([d.get(r, "None") for r in colnames])
    

    【讨论】:

      猜你喜欢
      • 2021-11-28
      • 1970-01-01
      • 1970-01-01
      • 2022-12-01
      • 2018-10-26
      • 2021-11-10
      相关资源
      最近更新 更多