Chuyển đổi danh sách từ điển sang DataFrame của gấu trúc


657

Tôi có một danh sách các từ điển như thế này:

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

Và tôi muốn biến nó thành một con gấu trúc DataFramenhư thế này:

      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

Lưu ý: Thứ tự của các cột không quan trọng.

Làm cách nào tôi có thể biến danh sách từ điển thành DataFrame của gấu trúc như hình trên?

Câu trả lời:


951

Giả sử dlà danh sách của bạn, chỉ đơn giản là:

pd.DataFrame(d)

3
Làm thế nào một người có thể sử dụng một trong các cặp khóa / giá trị làm chỉ mục (ví dụ: thời gian)?
MèoLoveJazz

6
@CatsLoveJazz Bạn chỉ có thể làm df = df.set_index('time')sau đó
joris

1
@CatsLoveJazz Không, điều đó là không thể khi chuyển đổi từ một lệnh.
joris

6
Kể từ Pandas 0.19.2, không có đề cập nào về tài liệu này, ít nhất là không có trong các tài liệu chopandas.DataFrame
Leo Alekseyev

1
Lưu ý rằng đối với một từ điển lồng nhau, '{"":{"...bạn sử dụng phương pháp json_n normalize, xem câu trả lời chi tiết của @ cs95
Lorenz

136

Làm cách nào để chuyển đổi danh sách từ điển sang DataFrame của gấu trúc?

Các câu trả lời khác là chính xác, nhưng không có nhiều giải thích về ưu điểm và hạn chế của các phương pháp này. Mục đích của bài đăng này sẽ là để hiển thị các ví dụ về các phương pháp này trong các tình huống khác nhau, thảo luận khi nào nên sử dụng (và khi nào không sử dụng) và đề xuất các phương án thay thế.


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

Tùy thuộc vào cấu trúc và định dạng của dữ liệu của bạn, có những tình huống trong đó cả ba phương pháp đều hoạt động hoặc một số phương pháp hoạt động tốt hơn các phương pháp khác hoặc một số không hoạt động.

Hãy xem xét một ví dụ rất giả tạo.

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

Danh sách này bao gồm "hồ sơ" với mỗi khóa có mặt. Đây là trường hợp đơn giản nhất bạn có thể gặp phải.

# 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

Từ về định hướng từ điển: orient='index'/'columns'

Trước khi tiếp tục, điều quan trọng là phải phân biệt giữa các loại định hướng từ điển khác nhau và hỗ trợ với gấu trúc. Có hai loại chính: "cột" và "chỉ mục".

orient='columns'
Từ điển có hướng "cột" sẽ có các khóa tương ứng với các cột trong DataFrame tương đương.

Ví dụ, dataở trên là trong định hướng "cột".

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

Lưu ý: Nếu bạn đang sử dụng pd.DataFrame.from_records, hướng được coi là "cột" (bạn không thể chỉ định khác) và từ điển sẽ được tải tương ứng.

orient='index'
Với định hướng này, các khóa được giả sử tương ứng với các giá trị chỉ mục. Loại dữ liệu này là phù hợp nhất cho 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

Trường hợp này không được xem xét trong OP, nhưng vẫn hữu ích để biết.

Đặt chỉ mục tùy chỉnh

Nếu bạn cần một chỉ mục tùy chỉnh trên DataFrame kết quả, bạn có thể đặt nó bằng cách sử dụng index=...đối số.

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

Điều này không được hỗ trợ bởi pd.DataFrame.from_dict.

Xử lý các phím / cột bị thiếu

Tất cả các phương thức đều hoạt động vượt trội khi xử lý từ điển với các giá trị khóa / cột bị thiếu. Ví dụ,

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

Đọc tập hợp con của cột

"Điều gì sẽ xảy ra nếu tôi không muốn đọc trong mỗi cột"? Bạn có thể dễ dàng xác định điều này bằng cách sử dụng columns=...tham số.

Ví dụ: từ từ điển mẫu data2ở trên, nếu bạn chỉ muốn đọc các cột "A ',' D 'và' F ', bạn có thể làm như vậy bằng cách chuyển danh sách:

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

Điều này không được hỗ trợ bởi pd.DataFrame.from_dictcác "cột" định hướng mặc định.

pd.DataFrame.from_dict(data2, orient='columns', columns=['A', 'B'])

ValueError: cannot use columns parameter with orient='columns'

Đọc tập hợp con của hàng

Không được hỗ trợ bởi bất kỳ phương pháp nào trực tiếp . Bạn sẽ phải lặp lại dữ liệu của mình và thực hiện xóa ngược tại chỗ khi bạn lặp lại. Ví dụ, để trích xuất chỉ 0 thứ và 2 nd hàng từ data2trên, bạn có thể sử dụng:

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

Thuốc chữa bách bệnh: json_normalizecho dữ liệu lồng nhau

Một thay thế mạnh mẽ, mạnh mẽ cho các phương pháp được nêu ở trên là json_normalizechức năng hoạt động với danh sách từ điển (bản ghi) và ngoài ra còn có thể xử lý các từ điển lồng nhau.

pd.io.json.json_normalize(data)

   A  B  C  D
0  5  0  3  3
1  7  9  3  5
2  2  4  7  6

pd.io.json.json_normalize(data2)

     A    B  C    D    E
0  5.0  NaN  3  3.0  NaN
1  NaN  4.0  7  NaN  6.0

Một lần nữa, hãy nhớ rằng dữ liệu được chuyển đến json_normalizecần phải ở định dạng danh sách từ điển (bản ghi).

Như đã đề cập, json_normalizecũng có thể xử lý từ điển lồng nhau. Đây là một ví dụ được lấy từ tài liệu.

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.io.json.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

Để biết thêm thông tin về metavà các record_pathđối số, kiểm tra tài liệu.


Tóm tắt

Đây là bảng gồm tất cả các phương thức được thảo luận ở trên, cùng với các tính năng / chức năng được hỗ trợ.

nhập mô tả hình ảnh ở đây

* Sử dụng orient='columns'và sau đó hoán vị để có được hiệu quả tương tự như orient='index'.


8
Ái chà! Được rồi, điều này cùng với bài viết Sáp nhập SO thuộc về API. Bạn nên đóng góp vào tài liệu về gấu trúc nếu bạn chưa làm như vậy. Ted Petrou vừa đăng một bài viết trên LinkedIn về sự phổ biến của gấu trúc trên Stack Overflow và đề cập rằng việc thiếu tài liệu tốt sẽ góp phần vào khối lượng câu hỏi ở đây.
Scott Boston

2
@ScottBoston Bạn hoàn toàn đúng, tôi đã nghe nói rằng đủ thời gian bây giờ tôi biết đó là điều tôi nên suy nghĩ nghiêm túc hơn. Tôi nghĩ rằng tài liệu này có thể là một cách tuyệt vời để giúp người dùng, hơn cả việc đăng lên các câu hỏi chỉ đạt được một phần nhỏ của cùng một đối tượng.
cs95

1
đó là câu trả lời hay, tôi nghĩ rằng đã đến lúc chúng ta nên đi lại trong những câu hỏi phổ biến dưới phiên bản gấu trúc mới nhất :-)
YOBEN_S

3
@ely: dù sao đó không bao giờ là lý do để không viết câu trả lời ở đây . Bất kỳ câu trả lời nào cũng có thể trở nên lỗi thời, đó là những gì chúng ta đã bỏ phiếu, và những quan điểm khác nhau và các mục tiêu khác nhau tồn tại ở đây, và luôn có giá trị khi có những cách khác nhau để giải thích cùng một điều.
Martijn Pieters

1
@MartijnPieters Tôi nghi ngờ và không đồng ý với khẳng định cuối cùng của bạn nhưng nhìn chung tôi đồng ý với bạn. Không phải lúc nào cũng coi trọng phụ gia để đối chiếu các câu trả lời khác nhau cho cùng một câu hỏi, đặc biệt nếu một số câu trả lời là cập nhật hoặc khác biệt có điều kiện dựa trên các câu trả lời khác. Trong trường hợp xấu nhất, những câu trả lời đó có thể có giá trị phá hủy khi được đối chiếu với nhau (trái ngược với việc sử dụng câu trả lời cập nhật hơn để đơn giản chỉnh sửa câu trả lời cũ thành trạng thái chính xác hơn). Nhưng một lần nữa, tôi phần lớn đồng ý với bạn.
ely

83

Trong gấu trúc 16.2, tôi đã phải làm pd.DataFrame.from_records(d)để có được điều này để làm việc.


1
điểm hay của phương pháp này là nó cũng hoạt động vớideque
MBZ

3
hoạt động tốt với gấu trúc 0.17.1với giải pháp @joris
Anton Protopopov

2
Giải pháp Usinig 0.14.1 và @joris 'không hoạt động nhưng điều này đã làm
mchen

13
Trong 0.18.1, người ta phải sử dụng from_recordsnếu từ điển không phải tất cả đều có cùng khóa.
fredcallaway

23

Bạn cũng có thể sử dụng pd.DataFrame.from_dict(d)như:

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

Câu hỏi đặt ra là về xây dựng một khung dữ liệu từ một danh sách các dicts, không phải từ một đĩa đơn dictnhư bạn giả định trong câu trả lời của bạn.
a_guest

@a_guest kiểm tra câu trả lời cập nhật. Tôi không giả định.
shivsn

2

Tôi biết một vài người sẽ bắt gặp điều này và không tìm thấy gì ở đây. Cách dễ nhất tôi đã tìm thấy để làm điều đó là như thế này:

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)

Hy vọng điều này sẽ giúp được ai đó!


1
list=[{'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'}]

và cuộc gọi đơn giản:

pd=DataFrame.from_dict(list, orient='columns', dtype=None)

print(pd)

0

Pyhton3: Hầu hết các giải pháp được liệt kê trước đây hoạt động. Tuy nhiên, có những trường hợp khi row_number của dataframe không được yêu cầu và mỗi hàng (bản ghi) phải được viết riêng lẻ.

Phương pháp sau đây rất hữu ích trong trường hợp đó.

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

Để chuyển đổi danh sách từ điển sang DataFrame của gấu trúc, bạn có thể sử dụng "chắp thêm":

Chúng tôi có một từ điển được gọi dicvà dic có 30 mục danh sách ( list1,, Sự list2, list30)

  1. bước 1: xác định một biến để giữ kết quả của bạn (ví dụ: total_df :)
  2. bước 2: khởi tạo total_dfvớilist1
  3. bước 3: sử dụng "vòng lặp" để nối thêm tất cả các danh sách vào total_df
total_df=list1
nums=Series(np.arange(start=2, stop=31))
for num in nums:
    total_df=total_df.append(dic['list'+str(num)])

Các lợi ích cho phương pháp này là gì so với cách tiếp cận vạch ra bởi @ cs95 trong câu trả lời cũ hai năm chi tiết của họ về DataFrame(), DataFrame.from_records().from_dict()?
Jeremy Caney

Tôi đã thử nghiệm tất cả các phương pháp trên cho một từ điển có 30 danh sách, tôi chỉ nhận được câu trả lời bằng cách sử dụng chức năng chắp thêm.
Armin Ahmadi Nasab
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.