从字典列表创建一个 pandas DataFrame,字典键设置为行标签

Create a pandas DataFrame from a list of dictionaries with dictionary keys set as row labels

我有一个字典列表。每个字典都包含一个键值对。我想将这个列表转换成一个 pandas DataFrame,它有一个“时间”列,其中包含每个字典中行中的值,每行的标签是相应字典项中的键。

例如,我将只显示列表中的前两个元素:

list_example = [{'companies_info_5000_5100': 121.20147228240967},\
 {'companies_info_5100_5200': 116.49221062660217}]

从这个 list_example 我想创建一个像这样的 DataFrame:

time
companies_info_5000_5100 121.201472
companies_info_5100_5200 116.492211

我搜索了可能的解决方案并提出了我自己的解决方案,如下所示:

import pandas as pd
df_list = []
for d in list_example:
    d_df = pd.DataFrame.from_dict(d, orient="index", columns=["time"])
    df_list.append(d_df)
df = pd.concat(df_list,axis= 0)

有了这段代码,我得到了我想要的,但是我确信一定有一些函数可以在没有 for 循环的情况下更有效地执行此操作。例如,如果我 运行 df = pd.DataFrame(df_list),那么它会创建一个 DataFrame,但字典键用作列,我在 DataFrame 中得到 NaN。我确信必须对该函数进行一些修改,告诉 pandas 使用键作为行标签。我正在寻找这个更简单、更优雅和 Pythonic 的解决方案。

就我在这里搜索而言,我找不到答案。

试试这个

# build a nested dict from list_example and build df
df = pd.DataFrame.from_dict({k: {'time': v} for d in list_example for k,v in d.items()}, orient='index')
print(df)
                                time
companies_info_5000_5100  121.201472
companies_info_5100_5200  116.492211

您可以使用:

df = (pd.concat(map(pd.Series, list_example))
        .to_frame('time')
      )

输出:

                                time
companies_info_5000_5100  121.201472
companies_info_5100_5200  116.492211

可能的解决方案之一是:

  • 从每个字典创建一个系列
  • 连接它们(到目前为止结果仍然是 Series),
  • 将其转换为 DataFrame,设置(唯一)列的名称。

执行此操作的代码是:

result = pd.concat([ pd.Series(d.values(), index=d.keys())
    for d in list_example ]).to_frame('time')

对于你的示例数据,我得到了:

                                time
companies_info_5000_5100  121.201472
companies_info_5100_5200  116.492211

Pandas接近

pd.DataFrame(list_example).stack().droplevel(0).to_frame('time')

                                time
companies_info_5000_5100  121.201472
companies_info_5100_5200  116.492211

这个问题收到了 4 个有用的答案。

他们都工作并完成工作虽然根据Whosebug 规则,只允许一个接受的答案。所以,我决定检查他们的速度并接受最有效(最快)的答案。

为此,我人工创建了一个长度为 100,000 的字典列表:

check_length = 100000

list_example = []

for i in range(check_length):
    list_example.append({f"companies_info_{i}": i})

然后,我定义了4个方法

Method name Author
Method 1 Me
Method 2 My own suggested solution described in the question
Method 3 @mozway
Method 4 @not a robot
Method 5 @Valdi_Bo

我放弃了@Shubham Sharma 建议的最后一种称为 Pandas 方法 的方法,因为即使是 12 GB 的 RAM 也不够用。所以,显然这是最糟糕的方式。

包含100000个词典的列表迭代100次的结果如下:

Method name Author Results
Method 1 Me 58.829195756912235, 95% CI (58.436393856257794, 59.221997657566675)
Method 2 My own suggested solution described in the question 28.41278486251831, 95% CI (28.330043057325845, 28.495526667710777)
Method 3 @mozway 17.587587616443635, 95% CI (17.526133899890418, 17.649041332996852)
Method 4 @not a robot 0.20350171089172364, 95% CI (0.19587073491102097, 0.2111326868724263)
Method 5 @Valdi_Bo 15.767115621566772, 95% CI (15.721122343444568, 15.813108899688975)

P.S。如果有人对我检查每种情况的速度的代码感兴趣,请看这里:

import pandas as pd
import numpy as np
import time
import math


# Method 1

def get_frame_method_1(l):

    list_example_d = {"time": l}

    df_1 = pd.DataFrame.from_dict(data=list_example_d, orient="columns")

    index_list = []

    for count, d in enumerate(df_1.time):
        index_list.extend(list(d.keys()))
        df_1.time[count]= list(d.values())[0]

    df_1.index= index_list

    return df_1


# Method 2

def get_frame_method_2(l):

    df_list = []

    for d in l:
        d_df = pd.DataFrame.from_dict(data=d, orient="index", columns=["time"])
        df_list.append(d_df)

    df_2 = pd.concat(df_list, axis= 0)

    return df_2


# Method 3

def get_frame_method_3(l):

    df_3 = (pd.concat(map(pd.Series, l))
            .to_frame('time')
        )
    
    return df_3


# Method 4

def get_frame_method_4(l):

    # build a nested dict from list_example and build df
    df_4 = pd.DataFrame.from_dict({k: {'time': v} for d in l for k,v in d.items()}, orient='index')

    return df_4


# Method 5

def get_frame_method_5(l):

    df_5 = pd.concat([ pd.Series(d.values(), index=d.keys())
        for d in l ]).to_frame('time')
    
    return df_4


check_length = 100000

list_example = []

for i in range(check_length):
    list_example.append({f"companies_info_{i}": i})


total_time_1_d = {}

for i in range(100):
    t_0 = time.time()
    df_1 = get_frame_method_1(list_example)
    t_1 = time.time()
    df_2 = get_frame_method_2(list_example)
    t_2 = time.time()
    df_3 = get_frame_method_3(list_example)
    t_3 = time.time()
    df_4 = get_frame_method_4(list_example)
    t_4 = time.time()
    df_5= get_frame_method_5(list_example)
    t_5 = time.time()
    total_time_1_d[f"{i}"] = {"Method 1": (t_1-t_0), "Method 2": (t_2-t_1), "Method 3": (t_3-t_2), "Method 4": (t_4-t_3), "Method 5": (t_5-t_4)}
    print(i)


total_time_df = pd.DataFrame.from_dict(data= total_time_1_d, orient="index")


for i in range(5):
    print(f"Method {i+1}: Mean - {total_time_df.describe().iloc[1, i]}, 95% CI ({total_time_df.describe().iloc[1, i]-1.96*(total_time_df.describe().iloc[2, i])/math.sqrt((total_time_df.describe().iloc[0, i]))}, {total_time_df.describe().iloc[1, i]+1.96*(total_time_df.describe().iloc[2, i])/math.sqrt((total_time_df.describe().iloc[0, i]))})")