将dict的dict转换为pandas中的数据框

Posted

技术标签:

【中文标题】将dict的dict转换为pandas中的数据框【英文标题】:convert dict of dict to dataframe in pandas 【发布时间】:2018-09-23 20:40:44 【问题描述】:

我有一个这样的dict

data = '1':'a':10, 'b':30, '2':'a':20, 'b':60

我想转换成这样的数据框:

x   y   z
1   a   10
1   b   30
2   a   20
2   b   60

有人知道吗?

【问题讨论】:

如果我或其他答案有帮助,请不要忘记accept。谢谢。 【参考方案1】:

dictionary comprehensionconcat 一起使用:

df = pd.concat(k: pd.Series(v) for k, v in data.items()).reset_index()
df.columns = list('xyz')

print (df)
   x  y   z
0  1  a  10
1  1  b  30
2  2  a  20
3  2  b  60

为了获得更好的性能,请使用 list compehension 和 sorting:

L = sorted([(k,k1,v1) for k,v in data.items() for k1,v1 in v.items()], 
            key=lambda x: (x[0], x[1]))
print (L)
[('1', 'a', 10), ('1', 'b', 30), ('2', 'a', 20), ('2', 'b', 60)]

df = pd.DataFrame(L, columns=list('xyz'))
print (df)
   x  y   z
0  1  a  10
1  1  b  30
2  2  a  20
3  2  b  60

时间安排

In [34]: %timeit jez1(data)
16.8 ms ± 403 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [35]: %timeit jez(data)
1.96 s ± 90.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [37]: %timeit jp(data)
43 ms ± 353 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

与@jp 相同的代码:

data = str(k): 'a': 10, 'b': 30 for k in range(10000)

def jp(data):
    return pd.melt(pd.DataFrame.from_dict(data, orient='index').reset_index().rename(columns='index': 'x'),
                   id_vars=['x'], value_vars=['a', 'b'], var_name='y', value_name='z')\
             .sort_values(['x', 'y']).reset_index(drop=True)

def jez(data):
    df = pd.concat(k: pd.Series(v) for k, v in data.items()).reset_index()
    df.columns = list('xyz')
    return df

def jez1(data):
    L = sorted([(k,k1,v1) for k,v in data.items() for k1,v1 in v.items()], key=lambda x: (x[0], x[1]))
    df = pd.DataFrame(L, columns=list('xyz'))
    return df

assert (jez1(data).values == jez(data).values).all()

【讨论】:

刚刚看到您的编辑 - 不错。我仍然想了解为什么concat 慢的内部原因。也许我们需要开始一个pandas-internal标签!? @jpp 在我看来原因是小Series的concat的多次重复。 如果假设我的 dic 是 "data = '1':'a':10, 'b':30, '2':'a':20, 'b': 60, 'c':30,'d' = 10" 相同的代码可以工作吗?? 我的 dic 长度是 2376。但是当我将它转换为 df 时,df 的 len 是 2320。但在实际情况下,它必须是 >2376。不知道为什么它缺少一些行.. @amrutha - 您可以先使用from collections import Counter,然后再使用data = k:Counter(v) for k, v in data.items(),最后应用我的解决方案。【参考方案2】:

这是使用pandas.melt 的一种方式。

d = '1':'a':10, 'b':30, '2':'a':20, 'b':60

res = pd.melt(pd.DataFrame.from_dict(d, orient='index'),
              value_vars=['a', 'b'], var_name='y', value_name='z')

print(res)

#    y   z
# 0  a  10
# 1  a  20
# 2  b  30
# 3  b  60

性能基准测试

我预计pandas.melt 效率低下,但在大量字典上应用pandas.concat 可能会更多昂贵。

data = str(k): 'a': 10, 'b': 30 for k in range(10000)

def jp(data):
    return pd.melt(pd.DataFrame.from_dict(data, orient='index').reset_index().rename(columns='index': 'x'),
                   id_vars=['x'], value_vars=['a', 'b'], var_name='y', value_name='z')\
             .sort_values(['x', 'y']).reset_index(drop=True)

def jez(data):
    df = pd.concat(k: pd.Series(v) for k, v in data.items()).reset_index()
    df.columns = list('xyz')
    return df

assert (jp(data).values == jez(data).values).all()

%timeit jp(data)   # 51.8 ms per loop
%timeit jez(data)  # 2.62 s per loop

【讨论】:

【参考方案3】:

使用Series

pd.Series(d).apply(pd.Series).stack().reset_index()
Out[359]: 
  level_0 level_1   0
0       1       a  10
1       1       b  30
2       2       a  20
3       2       b  60

【讨论】:

以上是关于将dict的dict转换为pandas中的数据框的主要内容,如果未能解决你的问题,请参考以下文章

将dict列表转换为pandas中的行[重复]

将json dict转换为pandas df中的行

将dict的dict转换为pandas DataFrame - 内存问题

python pandas数据框列转换为dict键和值

pandas:如何将字典转换为转置数据框? [复制]

将 pandas.groupby 转换为 dict