Pandas:从DataFrame列中生成字典的最有效方法为使用字典嵌套。

3

import pandas as pd
import numpy as np
import random

labels = ["c1","c2","c3"]
c1 = ["one","one","one","two","two","three","three","three","three"]
c2 = [random.random() for i in range(len(c1))]
c3 = ["alpha","beta","gamma","alpha","gamma","alpha","beta","gamma","zeta"]
DF = pd.DataFrame(np.array([c1,c2,c3])).T
DF.columns = labels

数据框如下:

      c1               c2     c3
0    one   0.440958516531  alpha
1    one   0.476439953723   beta
2    one   0.254235673552  gamma
3    two   0.882724336464  alpha
4    two    0.79817899139  gamma
5  three   0.677464637887  alpha
6  three   0.292927670096   beta
7  three  0.0971956881825  gamma
8  three   0.993934915508   zeta

我唯一能想到制作字典的方法是:
D_greek_value = {}

for greek in set(DF["c3"]):
    D_c1_c2 = {}
    for i in range(DF.shape[0]):
        row = DF.iloc[i,:]
        if row[2] == greek:
            D_c1_c2[row[0]] = row[1]
    D_greek_value[greek] = D_c1_c2
D_greek_value

生成的字典看起来像这样:
{'alpha': {'one': '0.67919712421',
  'three': '0.67171020684',
  'two': '0.571150669821'},
 'beta': {'one': '0.895090207979', 'three': '0.489490074662'},
 'gamma': {'one': '0.964777504708',
  'three': '0.134397632659',
  'two': '0.10302290374'},
 'zeta': {'three': '0.0204226923557'}}

我不想假设c1会成块出现(“one”每次都在一起)。我正在处理几百MB的csv文件,感觉自己完全错了。如果您有任何想法,请帮忙!

2个回答

4

如果我理解正确的话,您可以利用groupby来处理大部分的工作:

>>> result = df.groupby("c3")[["c1","c2"]].apply(lambda x: dict(x.values)).to_dict()
>>> pprint.pprint(result)
{'alpha': {'one': 0.440958516531,
           'three': 0.677464637887,
           'two': 0.8827243364640001},
 'beta': {'one': 0.47643995372299996, 'three': 0.29292767009599996},
 'gamma': {'one': 0.254235673552,
           'three': 0.0971956881825,
           'two': 0.79817899139},
 'zeta': {'three': 0.993934915508}}

一些解释:首先我们按照 c3 进行分组,然后选择列 c1 和 c2。这样就得到了我们想要转换为字典的分组结果。
>>> grouped = df.groupby("c3")[["c1", "c2"]]
>>> grouped.apply(lambda x: print(x,"\n","--")) # just for display purposes
      c1                   c2
0    one    0.679926178687387
3    two  0.11495090934413166
5  three   0.7458197179794177 
 --
      c1                   c2
0    one    0.679926178687387
3    two  0.11495090934413166
5  three   0.7458197179794177 
 --
      c1                   c2
1    one  0.12943266757277916
6  three  0.28944292691097817 
 --
      c1                   c2
2    one  0.36642834809341274
4    two   0.5690944224514624
7  three   0.7018221838129789 
 --
      c1                  c2
8  three  0.7195852795555373 
 --

给定其中任何一个子框架,比如倒数第二个,我们需要想出一种方法将其转换为字典。例如:

>>> d3
      c1        c2
2    one  0.366428
4    two  0.569094
7  three  0.701822

如果我们尝试使用dictto_dict,我们得不到想要的结果,因为索引和列标签会妨碍我们:

>>> dict(d3)
{'c1': 2      one
4      two
7    three
Name: c1, dtype: object, 'c2': 2    0.366428
4    0.569094
7    0.701822
Name: c2, dtype: float64}
>>> d3.to_dict()
{'c1': {2: 'one', 4: 'two', 7: 'three'}, 'c2': {2: 0.36642834809341279, 4: 0.56909442245146236, 7: 0.70182218381297889}}

但是我们可以通过使用.values来忽略这个问题,然后将其传递给dict

>>> d3.values
array([['one', 0.3664283480934128],
       ['two', 0.5690944224514624],
       ['three', 0.7018221838129789]], dtype=object)
>>> dict(d3.values)
{'three': 0.7018221838129789, 'one': 0.3664283480934128, 'two': 0.5690944224514624}

因此,如果应用此方法,我们将获得一个系列,其中索引为所需的c3键,并将值作为字典,我们可以使用.to_dict()将其转换为字典:

>>> result = df.groupby("c3")[["c1", "c2"]].apply(lambda x: dict(x.values))
>>> result
c3
alpha    {'three': '0.7458197179794177', 'one': '0.6799...
beta     {'one': '0.12943266757277916', 'three': '0.289...
gamma    {'three': '0.7018221838129789', 'one': '0.3664...
zeta                       {'three': '0.7195852795555373'}
dtype: object
>>> result.to_dict()
{'zeta': {'three': '0.7195852795555373'}, 'gamma': {'three': '0.7018221838129789', 'one': '0.36642834809341274', 'two': '0.5690944224514624'}, 'beta': {'one': '0.12943266757277916', 'three': '0.28944292691097817'}, 'alpha': {'three': '0.7458197179794177', 'one': '0.679926178687387', 'two': '0.11495090934413166'}}

1
非常好。我想知道这是否比我发布的更快。我期望groupby非常快,但是Lambda可能会拖慢它的速度。不过我太懒了,不想去计时。 - Steven Rumbalski
2
@StevenRumbalski:我也是。 :-)我试着看能否仅使用向量化运算获得相同的结果,但失败了;其他人可能会有更聪明的方法。但我认为你已经找到了最大的问题(太多次迭代),在此之外的都相对较小。 - DSM
@DSM 我知道如何使用lambda函数进行排序,但是从".apply"到".to_dict()"究竟发生了什么? - O.rka
1
@O.rka:我添加了一些解释,分步骤进行了分解。 - DSM

3
你正在为每个唯一的希腊字母多次迭代数据框,最好只迭代一次。
由于你需要一个字典中的字典,因此可以使用`collections.defaultdict`,并将嵌套字典的默认构造函数设置为`dict`。
from collections import defaultdict

result = defaultdict(dict)
for dx, num_word, val, greek in DF.itertuples():
    result[greek][num_word] = val

或者使用常规字典和调用setdefault来创建嵌套字典。
result = {}
for dx, num_word, val, greek in DF.itertuples():
    result.setdefault(greek, {})[num_word] = val

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