6

简单字典:

d = {'a': set([1,2,3]), 'b': set([3, 4])}

(如果重要,这些集合可以变成列表)

如何将其转换为长/整齐DataFrame,其中每一列都是一个变量,每个观察都是一行,即:

  letter  value
0      a      1
1      a      2
2      a      3
3      b      3
4      b      4

以下工作,但它有点麻烦:

id = 0
tidy_d = {}
for l, vs in d.items():
    for v in vs:
        tidy_d[id] = {'letter': l, 'value': v}
        id += 1
pd.DataFrame.from_dict(tidy_d, orient = 'index')

有什么pandas魔法可以做到这一点吗?就像是:

pd.DataFrame([d]).T.reset_index(level=0).unnest()

显然不存在unnest并且来自R。

4

5 回答 5

3

您可以将理解与itertools.chainand一起使用zip

from itertools import chain

keys, values = map(chain.from_iterable, zip(*((k*len(v), v) for k, v in d.items())))

df = pd.DataFrame({'letter': list(keys), 'value': list(values)})

print(df)

  letter  value
0      a      1
1      a      2
2      a      3
3      b      3
4      b      4

这可以以更易读的方式重写:

zipper = zip(*((k*len(v), v) for k, v in d.items()))
values = map(list, map(chain.from_iterable, zipper))

df = pd.DataFrame(list(values), columns=['letter', 'value'])
于 2018-10-03T12:12:36.107 回答
3

numpy.repeat与 一起使用chain.from_iterable

from itertools import chain

df = pd.DataFrame({
    'letter' : np.repeat(list(d.keys()), [len(v) for k, v in d.items()]),
    'value' : list(chain.from_iterable(d.values())), 
})
print (df)
  letter  value
0      a      1
1      a      2
2      a      3
3      b      3
4      b      4
于 2018-10-03T12:08:05.763 回答
1

一些融化的时间和稍微修改的答案:

import random
import timeit
from itertools import chain
import pandas as pd
print(pd.__version__)

dict_size = 1000000
randoms = [random.randint(0, 100) for __ in range(10000)]
max_list_size = 1000
d = {k: random.sample(randoms, random.randint(1, max_list_size)) for k in
     range(dict_size)}

def chain_():
    keys, values = map(chain.from_iterable,
                       zip(*(([k] * len(v), v) for k, v in d.items())))
    pd.DataFrame({'letter': list(keys), 'value': list(values)})

def melt_():
    pd.DataFrame.from_dict(d, orient='index'
        ).rename_axis('letter').reset_index(
        ).melt(id_vars=['letter'], value_name='value'
        ).drop('variable', axis=1).dropna()

setup ="""from __main__ import chain_, melt_"""
repeat = 3
numbers = 10
def timer(statement, _setup=''):
  print(min(
    timeit.Timer(statement, setup=_setup or setup).repeat(repeat, numbers)))

print('timing')
timer('chain_()')
timer('melt_()')

似乎 max_list_size 100 的熔化速度更快:

1.0.3
timing
246.71311019999996
204.33705529999997

max_list_size 1000 更慢:

2675.8446872
4565.838648400002

可能是因为分配内存的 df 比需要的大得多

链式答案的变体:

def chain_2():
    keys, values = map(chain.from_iterable,
                       zip(*((itertools.repeat(k, len(v)), v) for k, v in d.items())))
    pd.DataFrame({'letter': list(keys), 'value': list(values)})

似乎没有更快

(蟒蛇3.7.6)

于 2020-04-11T16:53:53.897 回答
1

受这篇文章的启发,有点“pandaic” :

pd.DataFrame.from_dict(d, orient = 'index') \
  .rename_axis('letter').reset_index() \
  .melt(id_vars = ['letter'], value_name = 'value') \
  .drop('variable', axis = 1) \
  .dropna()
于 2019-06-18T06:33:43.303 回答
0

只是另一个,

from collections import defaultdict
e = defaultdict(list)
for key, val in d.items():
    e["letter"] += [key] * len(val)
    e["value"] += list(val)
df = pd.DataFrame(e)
于 2018-10-03T12:55:30.123 回答