将字典列表转换为pandas DataFrame

1162

如何将一个字典列表转换成 DataFrame? 给定:

[{'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

注意:列的顺序不重要。

8个回答

1628

如果ds是一个dict的列表:

df = pd.DataFrame(ds)

注意:这无法处理嵌套数据。


4
如何使用键/值对中的一个作为索引(例如时间)? - CatsLoveJazz
12
дҪ еҸҜд»ҘеңЁеҗҺйқўеҠ дёҠdf = df.set_index('time')жқҘи®ҫзҪ®зҙўеј•дёәж—¶й—ҙгҖӮ - joris
2
@CatsLoveJazz 不,从字典转换时不可能实现该操作。 - joris
7
截至 Pandas 0.19.2 版本,文档中没有提到这一点,至少在 pandas.DataFrame 的文档中没有。 - Leo Alekseyev
3
请注意,对于嵌套字典'{"":{"...,您需要使用json_normalize方法进行规范化处理,详见@cs95的详细回答。 - questionto42
显示剩余5条评论

414

如何将一个字典列表转换为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

虽然这种情况在原始问题中没有被考虑,但了解它仍然很有用。

设置自定义索引

如果您需要在结果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

使用默认的 orient 参数 "columns",pd.DataFrame.from_dict 不支持此操作。

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

读取部分行

这些方法中没有一个可以直接支持 读取部分行。您需要遍历数据并在遍历时执行原地反向删除。例如,要从上面的data2中仅提取第0行和第2行,可以使用:

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 参数的更多信息,请查阅文档。


总结

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

enter image description here

* 使用 orient='columns' 然后转置即可获得与 orient='index' 相同的效果。


36
哇! 好的,这个和Merging SO post一起应该归入API。如果您还没有参与过pandas文档编写,则应该为其做出贡献。Ted Petrou刚刚发布了一篇关于Stack Overflow上pandas流行度的LinkedIn文章,并提到缺乏良好的文档是导致这里问题数量增加的原因之一。 - Scott Boston
4
@ScottBoston 你说得非常正确,我已经听到足够多次了,我知道这是我应该认真考虑的事情。我认为文档可以是帮助用户的好方法,比发帖回答问题能够触达更多受众。 - cs95
1
这是一个特别棘手的问题,因为哪些方法适用于哪些情况的细节经常发生变化,因此在SO上有非常详尽的答案不仅没有在pandas官方文档中那么有用,而且通常甚至是有害或误导性的,因为某些函数内部的更改可能会突然使答案不正确或事实上错误,并且它没有明确链接到实际源代码库以进行文档更新。 - ely
1
这是一个很好的答案,我认为现在是时候重新使用最新的pandas版本来回顾那些常见问题了 :-) - BENY
4
@ely:这从来不是不在这里回答问题的理由。无论如何,任何答案都可能过时,这就是我们投票的目的,而且这里存在不同的观点和不同的目标,拥有不同的解释方式总是很有价值的。 - Martijn Pieters
显示剩余6条评论

107
在 Pandas 16.2 中,我需要执行 pd.DataFrame.from_records(d) 才能使其工作。

1
这种方法的好处是它也适用于deque - MBZ
3
使用@joris的解决方案,可与pandas 0.17.1良好地配合使用。 - Anton Protopopov
2
使用0.14.1版本和@joris的解决方案无效,但这个有效。 - mchen
14
在版本0.18.1中,如果字典的键不全相同时,必须使用from_records方法。 - fredcallaway

35

你还可以使用 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

这个问题是关于从一个字典列表构建数据框,而不是像你在回答中假设的那样从单个字典构建的。 - a_guest
@a_guest,请查看更新后的答案。我不是在假设。 - shivsn

2

Python3: 之前列出的大多数解决方案都有效。但是,有时不需要数据框的行号,而是需要逐个写入每一行(记录)。

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

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])

0
如果字典中缺少键,简单的`pd.DataFrame()`构造函数将通过将NaN值分配给缺少的键来处理它。这会“搞乱”数据类型并将整数转换为浮点数。例如,使用OP中的示例数据,`'year'`列有缺失值,这些缺失值会被转换为浮点数,这可能不是现在我们拥有可空整数数据类型所期望的结果。解决此问题的一种方法是无论如何构造数据框,并在稍后使用`astype()`处理数据类型。
lst = [{'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'}]

dtypes = {'points': 'Int32', 'time': 'string', 'year': 'Int32', 'month': 'string', 'points_h1': 'Int32'}
df = pd.DataFrame(lst).astype(dtypes)

然而,如果有很多键,它的扩展性就不好。一个简单的开箱即用的方法是将列表转换为json数组,并使用pd.read_json作为json读取。它的好处是你可以在构建过程中设置dtype,将整数转换为Int类型,但保留其他所有内容(例如字符串、浮点数等)。
import json, io  # both of these are in the standard library
df = pd.read_json(io.StringIO(json.dumps(lst)), dtype='Int32')

result


0
我发现最简单的方法是这样的:
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时要避免循环,循环会破坏pandas的整个目的。 - sushanth
我没有点踩,但是虽然这个方法在技术上可以实现,但它的性能相当差。请参见此链接获取更多信息。 - EJoshuaS - Stand with Ukraine

0

我有一个包含日期时间键和整数值的字典列表:

list = [{datetime.date(2022, 2, 10): 7}, {datetime.date(2022, 2, 11): 1}, {datetime.date(2022, 2, 11): 1}]

我遇到了一个问题,使用上述方法将其转换为Dataframe时,它创建了包含日期的列...

我的解决方案:

df = pd.DataFrame()
for i in list:
    temp_df = pd.DataFrame.from_dict(i, orient='index')
    df = df.append(temp_df)

您正在更改“数据框架”的方向。所选答案还将使您的“数据框架”以列/垂直方向呈现。 - GodWin1100

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