Использование Numpy массива как если бы это были электронные таблицы — какой подход лучше?

Вопрос или проблема

Работаю над моделированием на python, которое рассчитывает доходность портфеля акций, облигаций и/или недвижимости на протяжении определенного срока, учитывая два возможных сценария налогообложения. Модель на python основана на таблице, содержащей 18 столбцов для каждого из этих двух сценариев налогообложения, и была валидирована с её помощью. Модель предоставляет результаты для приложения Streamlit, чтобы пользователи могли рассмотреть свою ситуацию и смоделировать свои цифры. Входные данные Streamlit сохраняются в словаре, который передается в класс с моделью.

Для первой версии я использовал массив Numpy размером N X M, где N – это количество лет для моделирования (переменная ‘term’), а M – количество столбцов. Для года 0, то есть первой ‘строки’ матрицы, я вычисляю соответствующие значения. Затем, используя цикл для N – 1 лет, модель вычисляет остальные строки. Она выглядит следующим образом.


config = {'акции' : 1000000,
        'облигации' : 1000000,
        'недвижимость' : 1000000,
        'доходность_акций' : 0.062,
        'доходность_облигаций' : 0.035,
        'доходность_недвижимости' : 0.06,
        'срок' : 20
        }

class boxer:

    def __init__(self, **parameters):

        for k, v in parameters.items():
            setattr(self, k, v)

    def return_calc(self):

        matrix = np.zeros((self.term, 18), dtype=float)

        '''инициализация первой строки матрицы'''

        ## ИНВЕСТИЦИИ И ДОХОДЫ
        matrix[0][0] = self.equity        # акции
        matrix[0][1] = self.bonds         # облигации
        matrix[0][2] = self.property      # недвижимость
        matrix[0][3] = matrix[0][0] * self.equity_return    # доходность по акциям
        matrix[0][4] = matrix[0][1] * self.bonds_return     # доходность по облигациям
        matrix[0][5] = matrix[0][2] * self.property_return  # доходность по недвижимости
        ## и так далее, определяя еще 13 'столбцов'

        '''инициализация оставшейся строки матрицы'''      
        for i in range(matrix.shape[0]):

            if i > 0:

                ## ИНВЕСТИЦИИ И ДОХОДЫ
                matrix[i][0] = matrix[i-1][9]         # акции
                matrix[i][1] = matrix[i-1][10]        # облигации
                matrix[i][2] = matrix[i-1][11]        # недвижимость
                matrix[i][3] = matrix[i][0] * self.equity_return    # доходность по акциям
                matrix[i][4] = matrix[i][1] * self.bonds_return     # доходность по облигациям
                matrix[i][5] = matrix[i][2] * self.property_return  # доходность по недвижимости (чистая аренда)

        output = pd.DataFrame(matrix, columns=
                              ['Акции','Облигации','Недвижимость','Доходность_Акций','Доходность_Облигаций','Доходность_Недвижимости'])

        return output

Это работает безупречно – dataframe красиво отображается в Streamlit, а графики также очень просты – но теперь, когда я собираюсь работать над улучшенной версией 2, через неделю, я уже ломаю голову над тем, для чего были все эти столбцы. Я чувствую, что ‘злоупотребил’ массивом Numpy, чтобы создать свою модель. Есть ли у кого-то советы / отзывы о том, как лучше это сделать?

Ответ или решение

Ваша задача заключается в разработке модели для расчета доходности портфеля акций, облигаций и недвижимости с учётом налоговых сценариев. Вы, очевидно, использовали массив NumPy для представления данных в виде таблицы, но столкнулись с проблемами в управлении и понимании кода. Позвольте предложить альтернативный подход, который упростит вашу работу и даст более структурированное представление о данных.

Подход, основанный на использовании DataFrame

1. Использование Pandas DataFrame вместо NumPy массива

Pandas предоставляет структуру данных, известную как DataFrame, которая лучше подходит для работы с табличными данными. Нет необходимости в ручном управлении индексами и столбцами, когда вы можете сразу использовать имена столбцов для доступа к данным. Вот пример, как можно переписать ваш код с использованием DataFrame:

import pandas as pd

class Portfolio:

    def __init__(self, **parameters):
        self.data = pd.DataFrame(columns=['Year', 'Equity', 'Bonds', 'Property', 'Return_Equity', 
                                          'Return_Bonds', 'Return_Property'])

        self.config = parameters
        self.data['Year'] = range(self.config['term'])

        # Задаем начальные значения
        self.data.loc[0, 'Equity'] = self.config['equity']
        self.data.loc[0, 'Bonds'] = self.config['bonds']
        self.data.loc[0, 'Property'] = self.config['property']
        self.data.loc[0, 'Return_Equity'] = self.data.loc[0, 'Equity'] * self.config['equity_return']
        self.data.loc[0, 'Return_Bonds'] = self.data.loc[0, 'Bonds'] * self.config['bonds_return']
        self.data.loc[0, 'Return_Property'] = self.data.loc[0, 'Property'] * self.config['property_return']

    def calculate_returns(self):
        for i in range(1, self.config['term']):
            self.data.loc[i, 'Equity'] = self.data.loc[i-1, 'Return_Equity']
            self.data.loc[i, 'Bonds'] = self.data.loc[i-1, 'Return_Bonds']
            self.data.loc[i, 'Property'] = self.data.loc[i-1, 'Return_Property']
            self.data.loc[i, 'Return_Equity'] = self.data.loc[i, 'Equity'] * self.config['equity_return']
            self.data.loc[i, 'Return_Bonds'] = self.data.loc[i, 'Bonds'] * self.config['bonds_return']
            self.data.loc[i, 'Return_Property'] = self.data.loc[i, 'Property'] * self.config['property_return']

        return self.data

2. Преимущества использования Pandas DataFrame

  • Читаемость: Код становится более понятным благодаря использованию имен столбцов вместо индексов массива. Вы можете легко понять, что каждый столбец означает, не запоминая номера.

  • Гибкость: DataFrame обеспечивает встроенные функции для манипуляции и анализа данных, такие как фильтрация, агрегация и группировка. Это значительно упрощает работу с данными.

  • Упрощение визуализации: При использовании таких библиотек, как Matplotlib и Seaborn, интеграция с DataFrame позволяет легко строить графики и диаграммы, что делает ваши результаты более наглядными.

3. Следующие шаги

Теперь, когда у вас есть более чистая и структурированная реализация, вы можете продолжать расширять функциональность вашего приложения, добавляя новые метрики, например, расчет среднего дохода, стандартного отклонения, анализ исторической доходности и другие финансовые показатели. Кроме того, использование DataFrame облегчит сохранение данных в CSV или Excel форматы, что может быть полезным для вашей аудитории.

4. Заключение

Переход от использования массива NumPy к Pandas DataFrame позволит вам управлять своими данными более эффективно. Это не только упростит ваш код, но и повысит его устойчивость к изменениям. Инвестируйте время в изучение и применение Pandas, и это обязательно окупится в дальнейшем.

Оцените материал
Добавить комментарий

Капча загружается...