В нашей повседневной жизни как специалисты по обработке данных мы постоянно работаем с различными структурами данных Python, такими как списки, наборы или словари, или, в более общем плане, мы работаем с итерациями и сопоставлениями . Иногда преобразование этих структур данных или манипулирование ими может стать интенсивным кодом выхода. Это может привести к нечитаемому коду и увеличить вероятность появления ошибок. К счастью, есть изящный модуль Python под названием funcy, который помогает нам облегчить эти задачи. В этой короткой статье я покажу вам как простой код Python, так и соответствующие функциональные функции, которые позволяют более эффективно решать пять различных задач с помощью более читаемого кода. Я уверен, что прочитав этот пост, вы станете еще веселее :).
Пропустить / Проект
Иногда вам дают словарь, и вы хотите продолжить работу только с его подмножеством. Например, создавая образ, вы создаете конечную точку API отдыха и хотите вернуть только подмножество атрибутов вашей модели. С этой целью funcy предоставляет две функции, а именно omit и project.
from funcy import project, omit data = {"this": 1, "is": 2, "the": 3, "sample": 4, "dict": 5} # FUNCY omitted_f = omit(data, ("is", "dict")) # PLAIN PYTHON omitted_p = {k: data[k] for k in set(data.keys()).difference({"is", "dict"}) } # FUNCY projected_f = project(data, ("this", "is")) # PLAIN PYTHON projected_p = {k: data[k] for k in ("this", "is")}
С funcy вам не только нужно вводить меньше символов, но и получить более читаемый и менее подверженный ошибкам код. Но зачем нам две функции? Если количество ключей, которые вы хотите сохранить, меньше, чем количество ключей, которые вы хотите удалить, выберите проект, в противном случае выберите опустить.
Сглаживание вложенных структур данных
Предположим, у вас есть вложенная структура данных, такая как список списков и списков, и вы хотите свести ее в единый список.
from funcy import lflatten data = [1, 2, [3, 4, [5, 6]], 7, [8, 9]] # FUNCY flattened_f = lflatten(data) # PLAIN PYTHON def flatter(in_): for e in in_: if isinstance(e, list): yield from flatter(e) else: yield e flattend_p = [e for e in flatter(data)]
Как видите, функциональная версия - это всего лишь одна строка, в то время как простая версия Python выглядит довольно сложной. Мне также потребовалось некоторое время, чтобы придумать решение, и я до сих пор не уверен в нем на 100%. Итак, я бы остановился на функции funcy :) Помимо версии списка l flatten, funcy также предлагает более общую версию для итераций, которая называется flatten, без префикса l. Вы найдете это для различных функций.
Разделение на куски
Предположим, у вас есть итерация из n элементов, и вы хотите разделить ее на блоки, содержащие k ‹n элементов. Последний кусок может быть меньше k, если n не делится на k. Это похоже на обучающий набор из n образцов, которые вы хотите разделить на пакеты размером k для выполнения пакетной обработки.
from funcy import lchunks data = list(range(10100)) # FUNCY for batch in lchunks(64, data): # process the batch pass # PLAIN PYTHON from typing import Iterable, Any, List def my_chunks(batch_size:int, data:Iterable[Any])->List[Any]: res = [] for i, e in enumerate(data): res.append(e) if (i + 1) % batch_size == 0: yield res res = [] if res: yield res for batch in my_chunks(64, data): # process the batch pass
Обратите внимание, что я использовал версию lchunks для разделения списка, а не более общую версию chunks для разделения итераций. Все, что вам нужно сделать, это передать желаемый размер пакета / фрагмента и итерацию, которую вы хотите разделить. Существует еще одна функциональная функция, называемая partition, которая возвращает только те фрагменты, которые содержат ровно k элементов. Следовательно, он пропустит последний, если n не делится на k.
Объединение нескольких словарей
Предположим, у вас есть несколько словарей, в которых хранятся данные за разные даты, но один и тот же объект. Ваша цель - объединить все эти словари в один и объединить данные из одинаковых ключей с помощью определенной функции. Здесь пригодится функция merge_with из funcy. Вам просто нужно передать функцию слияния и все словари, которые вы хотите объединить.
from funcy import merge_with, lcat d1 = {1: [1, 2], 2: [4, 5, 6]} # FUNCY VERSION merged_f = merge_with(lcat, d1,d2) # PYTHON VERSION from itertools import chain from typing import Callable, Iterable, Any, Dict def _merge(func: Callable[[Iterable], Any], *dics:List[Dict])->Dict: # Get unique keys keys = {k for d in dics for k in d.keys()} return {k: func((d[k] for d in dics if k in d)) for k in keys} merged_p = _merge(lambda l: list(chain(*l)), d1, d2)
Также существует функция join_with, которая похожа на merge_with, но вместо передачи каждого словаря в качестве одного аргумента вы передаете итерацию словарей. Да, и я «случайно» внедрил другую функциональную функцию lcat,, которая объединяет различные списки в один.
Кэшированное свойство
И последнее, но не менее важное: что-то совершенно другое, но очень полезное; декоратор cached_prope rty. Как следует из названия, он позволяет создавать свойства, которые выполняются только один раз, а затем кэшировать результат этого выполнения и возвращать этот результат во всех последующих вызовах. Я часто использую это при создании классов наборов данных, так как это дает мне очень чистый и читаемый интерфейс, сокращая время загрузки.
from funcy import cached_property import pandas as pd # Funcy Version class DatasetsF: @cached_property def movies(self) -> pd.Dataframe: return pd.read_csv("the_biggest_movie_file.csv") # PYTHON VERSION class DatasetsP: def __init__(self): self._movies = None @property def movies(self) -> pd.Dataframe: if self._movies is None: self._movies = pd.read_csv("the_biggest_movie_file.csv") return self._movies
Заключение
В этом посте я познакомил вас с funcy, показав очень небольшое, но все же удобное подмножество предлагаемых им функций. Чтобы получить краткий обзор всех функций, ознакомьтесь с этой шпаргалкой. Я надеюсь, что эта статья побудит вас изучить несколько забавных приемов. Он может предложить гораздо больше, чем то, что я вам здесь показал. Благодарим вас за то, что вы следите за мной, и не стесняйтесь обращаться ко мне с вопросами, комментариями или предложениями.