Привет, ребята. Сегодня я хотел бы поговорить об реализации объектно-ориентированного (OO) дизайна для создания модуля бизнес-аналитики с Python и Pandas.
Панды широко используются для анализа данных, его легко использовать, можно легко создать сценарий из сотен строк кодов, используя панды для обработки сложных данных. Тем не менее, работая со многими аналитиками данных, я обнаружил, что большинство сценариев, созданных аналитиками данных, должны попасть в категорию «процедурного кода», который не используется повторно. Немного изменение контекста часто приводит к полному повторному записи кода. Хорошо, если вы находитесь в области «исследования», где ваш код используется для «разведки» или «экспериментальных» целей, но если вы хотите писать код, который способствует более крупной системе или коду, который будет выполняться многократно, вам нужно Для реализации некоторой архитектуры.
Мой фон
Я работаю в области финансов, где мне нужно ежедневно обрабатывать данные со сложными отношениями. Необработанные данные, которые я получил, обычно слишком «необработанные», так что извлечение из любой полезной информации, часто требует много шагов. Код может легко стать невозможным для поддержания из -за несоответствия необработанной структуры данных и бизнес -логики.
Решение
Вскоре я понял, что мне нужен слой OO поверх необработанных данных, чтобы справиться со всей сложностью. Причина, по которой я использую термин Oo слой Вот что OO – это по сути слой абстракции. Это позволяет вам сосредоточиться только на бизнес -логике, вместо того, чтобы беспокоиться о том, как внедрить бизнес из необработанных данных. Архитектура – это снижение вашей когнитивной нагрузки, когда вы пытаетесь отредактировать кодовую базу.
Мы постараемся создать модель OO для некоторых торговых данных:
20190105 | 1001 | B | 8 | 2 | 1 |
20190105 | 1001 | B | 4 | 3 | 2 |
20190106 | 1001 | S | 3 | 1 | 2 |
20190106 | 1001 | B | 6 | 5 | 3 |
20190106 | 1001 | S | 4 | 6 | 3 |
20190107 | 1001 | S | 6 | 3 | 1 |
20190105 | 1002 | B | 6 | 2 | 1 |
20190105 | 1002 | B | 8 | 3 | 2 |
20190106 | 1002 | S | 3 | 1 | 2 |
20190106 | 1002 | B | 5 | 5 | 3 |
20190106 | 1002 | S | 4 | 6 | 3 |
20190107 | 1002 | S | 6 | 3 | 1 |
Некоторая информация об учетной записи:
Дэйвид | 1001 |
Том | 1002 |
и некоторые данные о закрытии:
20190105 | 1 | 2 |
20190106 | 1 | 3 |
20190107 | 1 | 2 |
20190105 | 2 | 2 |
20190106 | 2 | 3 |
20190107 | 2 | 5 |
20190105 | 3 | 5 |
20190106 | 3 | 6 |
20190107 | 3 | 7 |
Во -первых, у нас будет «уровень данных» для обработки ввода/вывода исходных данных. Так как у нас нет фактического источника данных, мы издеваемся над этим:
import pandas as pd from io import StringIO class Data(): def trade_df(self): # This method should handle the import of source data data = StringIO() s = """ Account|Stock_Code|BuySell|Date|Quantity|Unit_Price 1001|001|B|20190105|8|2 1001|002|B|20190105|4|3 1001|002|S|20190106|3|1 1001|003|B|20190106|6|5 1001|003|S|20190106|4|6 1001|001|S|20190107|6|3 1002|001|B|20190105|6|2 1002|002|B|20190105|8|3 1002|002|S|20190106|3|1 1002|003|B|20190106|5|5 1002|003|S|20190106|4|6 1002|001|S|20190107|6|3 """ data.write(s.replace(' ','')) data.seek(0) df = pd.read_csv(data,sep='|',dtype={'Account':str, 'Date':str, 'Stock_Code':str, "Date":str}) df.Date = pd.to_datetime(df.Date,format='%Y%m%d') return df def account_df(self): data = StringIO() s = """ Account|Name 1001|David 1002|Tom """ data.write(s.replace(' ','')) data.seek(0) df = pd.read_csv(data, sep='|', dtype={'Account': str, 'Name': str}) return df def stock_prices(self): data = StringIO() s = """ Stock_Code|Closing_Price|Date 001|2|20190105 001|3|20190106 001|2|20190107 002|2|20190105 002|3|20190106 002|5|20190107 003|5|20190105 003|6|20190106 003|7|20190107 """ data.write(s.replace(' ','')) data.seek(0) df = pd.read_csv(data, sep='|', dtype={'Stock_Code': str, 'Date': str}) df.Date = pd.to_datetime(df.Date,format='%Y%m%d') return df
Данные
Объект должен обрабатывать только импорт и валидацию данных только, и он не должен реализовать и бизнес -логику.
Тогда на вершине Данные
Объект, мы построим наш первый слой OO:
class Book(): def __init__(self): self._data = Data() @property def trade_book_df(self): if not hasattr(self,'_trade_book_df'): df = self._data.trade_df().join(self._data.account_df().set_index('Account'),on='Account') df['Trade_Amount'] = df.Quantity * df.Unit_Price self._trade_book_df = df return self._trade_book_df def stock_prices(self,date=None): df = self._data.stock_prices() if date: df = df[df.Date == date] return df @property def holdings(self): return Holdings(self) @property def accounts(self): return Accounts(self.holdings) Book().trade_book_df | | Account | Stock_Code | BuySell | Date | Quantity | Unit_Price | Name | Trade_Amount | |---:|----------:|-------------:|:----------|:--------------------|-----------:|-------------:|:-------|---------------:| | 0 | 1001 | 001 | B | 2019-01-05 00:00:00 | 8 | 2 | David | 16 | | 1 | 1001 | 002 | B | 2019-01-05 00:00:00 | 4 | 3 | David | 12 | | 2 | 1001 | 002 | S | 2019-01-06 00:00:00 | 3 | 1 | David | 3 | | 3 | 1001 | 003 | B | 2019-01-06 00:00:00 | 6 | 5 | David | 30 | | 4 | 1001 | 003 | S | 2019-01-06 00:00:00 | 4 | 6 | David | 24 | | 5 | 1001 | 001 | S | 2019-01-07 00:00:00 | 6 | 3 | David | 18 | | 6 | 1002 | 001 | B | 2019-01-05 00:00:00 | 6 | 2 | Tom | 12 | | 7 | 1002 | 002 | B | 2019-01-05 00:00:00 | 8 | 3 | Tom | 24 | | 8 | 1002 | 002 | S | 2019-01-06 00:00:00 | 3 | 1 | Tom | 3 | | 9 | 1002 | 003 | B | 2019-01-06 00:00:00 | 5 | 5 | Tom | 25 | | 10 | 1002 | 003 | S | 2019-01-06 00:00:00 | 4 | 6 | Tom | 24 | | 11 | 1002 | 001 | S | 2019-01-07 00:00:00 | 6 | 3 | Tom | 18 |
-
Книга
Объект имеет_data
Атрибут, который владеетДанные
объект. trade_book_df
недвижимость внедрена две бизнес -логика:- Присоединение
trade_df
иaccount_df
Анкет - Определение
Trade_amount
->Количество
*UNIT_PRICE
Анкет
- Присоединение
-
stock_prices
Метод извлекает цены акций определенной даты. - Это обеспечивает шлюзы в
Холдинги
иСчета
контекст, о котором мы собираемся поговорить.
Контексты
” Контекст » – это измерение, которое вы хотите просмотреть данные, все представления с одним и тем же измерением должны быть инкапсулированы в одном объекте.
Например, чтобы просмотреть данные в контексте «удержания», нам нужно:
- Совокучить торговые данные в течение определенного времени.
- Умножьте количество удержания количества и цены закрытия акций, чтобы получить рыночную стоимость.
class Holdings(): def __init__(self,book): self._book = book def holdings_of(self,date): trades_df = self._book.trade_book_df date_hld_df = trades_df[trades_df.Date <= date] date_hld_df['qnt_change'] = date_hld_df['BuySell'].map({'B':1,'S':-1}) * date_hld_df.Quantity hld_df = date_hld_df.groupby(['Account','Stock_Code'],as_index=False)\ .agg({'qnt_change':sum})\ .rename(columns={'qnt_change':'Holdings'}) hld_df = hld_df.join(self._book.stock_prices(date).set_index('Stock_Code'),on='Stock_Code') hld_df['Market_Value'] = hld_df.Closing_Price * hld_df.Holdings return hld_df from datetime import date Book().holdings.holdings_of(date(2019,1,6)) | | Account | Stock_Code | Holdings | Closing_Price | Date | Market_Value | |---:|----------:|-------------:|-----------:|----------------:|:--------------------|---------------:| | 0 | 1001 | 001 | 8 | 3 | 2019-01-06 00:00:00 | 24 | | 1 | 1001 | 002 | 1 | 3 | 2019-01-06 00:00:00 | 3 | | 2 | 1001 | 003 | 2 | 6 | 2019-01-06 00:00:00 | 12 | | 3 | 1002 | 001 | 6 | 3 | 2019-01-06 00:00:00 | 18 | | 4 | 1002 | 002 | 5 | 3 | 2019-01-06 00:00:00 | 15 | | 5 | 1002 | 003 | 1 | 6 | 2019-01-06 00:00:00 | 6 |
Затем вы можете построить контекст в верхней части контекста. Например, у вас может быть Счета
Контекст, построенный на вершине Холдинги
Контекст:
class Accounts(): def __init__(self,holdings): self._holdings = holdings def account_value(self,date): df = self._holdings.holdings_of(date) return df.groupby('Account',as_index=False).agg({'Market_Value':sum, 'Date':'first'}) Book().accounts.account_value(date(2019,1,6)) | | Account | Market_Value | Date | |---:|----------:|---------------:|:--------------------| | 0 | 1001 | 39 | 2019-01-06 00:00:00 | | 1 | 1002 | 39 | 2019-01-06 00:00:00 |
Выше приведено простой пример того, как дизайн OO может быть применен к вашему сценарию Pandas. Архитектура позволяет вам легко масштабироваться по мере увеличения количества источников данных. Это было лишь очень краткое введение, есть еще много методов моделирования, которые вы можете использовать для управления сложными данными. Наконец, я хочу подчеркнуть, что эти методы становятся все более и более важными. Традиционно, такая проблема может быть решена с помощью базы данных и реляционной модели, но, учитывая растущую сложность и рассеянность данных, вы с большей вероятностью столкнетесь с ситуациями, когда ваша база данных не может справиться со всем, и вам понадобится некоторые дополнения от вашего кода, Особенно, когда вам нужны данные из нескольких источников данных.
Оригинал: “https://dev.to/fpim/object-oriented-design-architecture-with-panda-me4”