Панды: Группируйте и создавайте диктанты с именами и значениями оставшихся столбцов

#python #python-3.x #pandas #dataframe #dictionary

Вопрос:

Ниже приведен мой df :

 In [425]: df
Out[425]: 
     a  b  c      d
0  abc  1  1   True
1  abd  1  1  False
2  abe  1  2  False
3  abf  1  2   True
4  abg  2  2   True
 

Я хочу сгруппироваться по столбцам b c и создать диктант с оставшимися именами столбцов и их значениями.

Ожидаемый Результат:

 [
    {
        "b": 1,
        "c": 1,
        "attr":[
            {
            "a": "abc",
            "d": True
            },
            {
            "a": "abd",
            "d": False
            }
        ]
    },
    {
        "b": 1,
        "c": 2,
        "attr":[
            {
            "a": "abe",
            "d": False
            },
            {
            "a": "abf",
            "d": True
            }
        ]
    },
    {
        "b": 2,
        "c": 2,
        "attr":[
            {
            "a": "abg",
            "d": True
            }
        ]
    }
]
 

Моя попытка:

 In [423]: df.set_index(['b', 'c']).agg(list, 1).to_dict()
Out[423]: {(1, 1): ['abd', False], (1, 2): ['abf', True], (2, 2): ['abg', True]}
 

Я могу группировать и создавать диктант, но не знаю, как добавить к нему имена столбцов.

Комментарии:

1. Хорошая фотография в профиле 😉

2. Хе-хе. спасибо @jezrael

Ответ №1:

Понимание

 [dict(b=b, c=c, attr=d.to_dict('records'))
 for (b, c), d in df.set_index(['b', 'c']).groupby(['b', 'c'])]

[{'b': 1, 'c': 1, 'attr': [{'a': 'abc', 'd': True}, {'a': 'abd', 'd': False}]},
 {'b': 1, 'c': 2, 'attr': [{'a': 'abe', 'd': False}, {'a': 'abf', 'd': True}]},
 {'b': 2, 'c': 2, 'attr': [{'a': 'abg', 'd': True}]}]
​
 

Комментарии:

1. Это здорово. Спасибо. 1.

Ответ №2:

Используйте пользовательскую лямбда-функцию с DataFrame.to_dict помощью in GroupBy.apply :

 d = (df.groupby(['b','c'])[['a','d']]
       .apply(lambda x: x.to_dict('records'))
       .reset_index(name='attr')
       .to_dict('records'))
print (d)
[{'b': 1, 'c': 1, 'attr': [{'a': 'abc', 'd': True}, 
                           {'a': 'abd', 'd': False}]},
  {'b': 1, 'c': 2, 'attr': [{'a': 'abe', 'd': False},
                            {'a': 'abf', 'd': True}]}, 
  {'b': 2, 'c': 2, 'attr': [{'a': 'abg', 'd': True}]}]
 

Альтернатива, если имеется несколько столбцов:

 d = (df.set_index(['b','c'])
       .groupby(['b','c'])
       .apply(lambda x: x.to_dict('records'))
       .reset_index(name='attr')
       .to_dict('records'))
print (d)
 

Производительность в огромном фрейме данных со многими группами:

 np.random.seed(123)
N = 1000000

L1 = list('abcdefghijklmno')
L = np.random.randint(100,size=N)
df = pd.DataFrame({'a': np.random.choice(L1, N),
                   'b': np.random.choice(L, N),
                   'c':np.random.choice(L, N),
                   'd':np.random.choice([True, False], N),})
print (df)

In [51]: %timeit [dict(b=b, c=c, attr=d.to_dict('records')) for (b, c), d in df.set_index(['b', 'c']).groupby(['b', 'c'])]
6.01 s ± 247 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [53]: %timeit (df.groupby(['b','c'])[['a','d']].apply(lambda x: x.to_dict('records')).reset_index(name='attr').to_dict('records'))
4.79 s ± 137 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)