Уведомления

Группа в Telegram: @pythonsu

#1 Авг. 11, 2022 01:51:13

diagon
Зарегистрирован: 2022-08-11
Сообщения: 2
Репутация: +  0  -
Профиль   Отправить e-mail  

Как свернуть список словарей

Есть структура данных:
[
{’name’: ‘Foo’, ‘size’: 2, ‘count’: 1}
{’name’: ‘Bar’, ‘size’: 1, ‘count’: 3}
{’name’: ‘Bar’, ‘size’: 1, ‘count’: 2}
]

На выходе должно получиться:
[
{’name’: ‘Foo’, ‘size’: 2, ‘count’: 1}
{’name’: ‘Bar’, ‘size’: 1, ‘count’: 5}
]

Как сделать это на Python?
Понимаю, что в Django ORM это сделать просто, на у меня сложная вычисляемая структура данных и, кажется, проще это обработать оперативке, тем более список не особо большой. В голову приходит reduce, но не понимаю как там можно использовать составные ключи, также на уме библиотека для работы со структурами данных, но особо с ней не работал. Ключей в реальной задаче больше, чем 2, и суммируемых полей тоже.

Офлайн

#2 Авг. 11, 2022 02:44:33

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 9867
Репутация: +  853  -
Профиль   Отправить e-mail  

Как свернуть список словарей

Добавил ещё поле одно.

  
>>> def get_uniq_fields(lst, field_name):
...     return tuple({i[field_name]:None for i in lst})
... 
>>> def combine_fields(lst, field_name):
...     return sum(i[field_name] for i in lst)
... 
>>> def join_objects(lst):
...     out = []
...     names = get_uniq_fields(lst, 'name')
...     for name in names:
...         filtered_by_name = tuple(i for i in lst if i['name'] == name)
...         template = filtered_by_name[0]
...         count_combined = combine_fields(filtered_by_name, 'count')
...         some_combined = combine_fields(filtered_by_name, 'some')
...         template['count'] = count_combined
...         template['some'] = some_combined
...         out.append(template)
...     return out
... 
>>> lst = [{'name': 'Foo', 'size': 2, 'count': 1, 'some': 10},
...        {'name': 'Bar', 'size': 1, 'count': 3, 'some': 20},
...        {'name': 'Bar', 'size': 1, 'count': 2, 'some': 30}]
>>> 
>>> out = join_objects(lst)
>>> out
[{'name': 'Foo', 'size': 2, 'count': 1, 'some': 10}, {'name': 'Bar', 'size': 1, 'count': 5, 'some': 50}]
>>>



Отредактировано py.user.next (Авг. 11, 2022 02:48:15)

Офлайн

#3 Авг. 11, 2022 11:08:21

FishHook
От:
Зарегистрирован: 2011-01-08
Сообщения: 8312
Репутация: +  568  -
Профиль   Отправить e-mail  

Как свернуть список словарей

   
from typing import Dict, Tuple, Generator
  
class Summarizer:
  
    fields_to_sum = ["count"]
    unique_fields = ["name", "size"]
  
    def __init__(self):
        self._result: Dict[Tuple[str, ...], Tuple[int, ...]] = {}
  
    def add(self, record: Dict):
  
        uniques = tuple(record[i] for i in self.unique_fields)
        sums = tuple(record[i] for i in self.fields_to_sum)
        if sub_sums := self._result.get(uniques):
            sums = tuple(sum(i) for i in zip(sums, sub_sums))
        self._result[uniques] = sums
  
    @property
    def result(self) -> Generator[Dict, None, None]:
        for uniques, sums in self._result.items():
            _u = {k: v for k, v in zip(self.unique_fields, uniques)}
            _s = {k: v for k, v in zip(self.fields_to_sum, sums)}
            yield {**_u, **_s}
  
summarizer = Summarizer()
l = [{"name": "Foo", "size": 2, "count": 1},
     {"name": "Bar", "size": 1, "count": 3},
     {"name": "Bar", "size": 1, "count": 2}
     ]
  
for i in l:
    summarizer.add(i)
  
print(list(summarizer.result))



Отредактировано FishHook (Авг. 11, 2022 11:09:31)

Офлайн

#4 Авг. 11, 2022 12:05:06

xam1816
Зарегистрирован: 2020-05-11
Сообщения: 1352
Репутация: +  118  -
Профиль   Отправить e-mail  

Как свернуть список словарей

  
lst = [
    {'name': 'Foo', 'size': 2, 'count': 1},
    {'name': 'Bar', 'size': 1, 'count': 3},
    {'name': 'Bar', 'size': 1, 'count': 2}
]
new_dict = {}
for d in lst:
    k = (d['name'], d['size'])
    new_dict.setdefault(k, 0)
    new_dict[k] += d['count']
out = [{'name': k[0], 'size': k[1], 'count': v} for k, v in new_dict.items()]
print(out)

 
[{'name': 'Foo', 'size': 2, 'count': 1}, {'name': 'Bar', 'size': 1, 'count': 5}]

Офлайн

#5 Авг. 11, 2022 12:32:34

FishHook
От:
Зарегистрирован: 2011-01-08
Сообщения: 8312
Репутация: +  568  -
Профиль   Отправить e-mail  

Как свернуть список словарей

xam1816

diagon
Ключей в реальной задаче больше, чем 2, и суммируемых полей тоже.
Мы не знаем насколько больше, можем предположить, что ключей десятки. Ваше решение имеет право на жизнь, вопросов нет, но мне кажется что в данном случае задачу надо решать в общем виде. Мне кажется хорошим критерием считать, что если мы изменим структуру данных добавив/удалив поле, то лучшее решение то, которое не влечет за собой никаких изменений кроме декларативных, слабее - если нам придется изменять алгоритм. Хотя, конечно, этим можно пожертвовать ради простоты и/или производительности.



Офлайн

#6 Авг. 11, 2022 14:38:56

diagon
Зарегистрирован: 2022-08-11
Сообщения: 2
Репутация: +  0  -
Профиль   Отправить e-mail  

Как свернуть список словарей

Всем спасибо большое за интересные предложения.
Вспомнил еще про pandas

 import pandas as pd
data = [
{'name': 'Foo', 'size': 2, 'count': 1},
{'name': 'Bar', 'size': 1, 'count': 3},
{'name': 'Bar', 'size': 1, 'count': 2}
]
df = pandas.DataFrame(data)
out = df.groupby(['name', 'size'], as_index=False).sum('count').to_dict('records')
print(print)

 [
{'name': 'Bar', 'size': 1, 'count': 5},
{'name': 'Foo', 'size': 2, 'count': 1}
]

Но поразмышляв какое-то время пришел к решению расширить структуру БД, дабы получать необходимые данные одним запросом.
Причиной также послужила следующая задача, для которой пришлось бы группировать структура данных сходным образом. В следствии чего алгоритмы бы усложнились, код стал плохо читаем.

В случае необходимости обрабтки в памяти думаю лучше использовать pandas, код выходит более читабельным.
Конечно можно написать свою функцию “сворачивания” и использовать ее, но что-то мне подсказывает, что в pandas хорошо оптимизирована работа с данными и сильно быстрее собственный код работать не будет.
Интересно услышать рассуждения других по поводу подобных задач.

Офлайн

#7 Авг. 11, 2022 15:18:14

FishHook
От:
Зарегистрирован: 2011-01-08
Сообщения: 8312
Репутация: +  568  -
Профиль   Отправить e-mail  

Как свернуть список словарей

diagon
Интересно услышать рассуждения других по поводу подобных задач.
Мне кажется, тут все очень индивидуально и зависит от конкретной задачи, объема данных, их структуры, структуры команды и т.д. Несколько соображений:
- если данные табличные, данных много и запросы большие и сложные, то есть смысл по полной использовать возможности СУБД, это будет быстрее
- если данных относительно мало, а скорость обработки критична, то pandas не факт что даст преимущества, вы потеряете время на создании датафреймов, они не тривиально внутри устроены.
- нужна ли вам лишняя зависимость (тяжелая зависимость) в проекте ради одной строки кода? Надо ли вам увеличивать порог вхождения членов команды ради этого?
- pandas, насколько я понимаю, это инструмент биг-дата, он хорошо работает в связке с Hadoop и колоночными данными. Мне видится, что постобработка данных извлеченных из реляционной БД пандой, это какой-то странный оверхед и где-то тут есть ошибка архитектуры.



Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version