Инициализация и перерасчет в редактируемой графически графической таблице - PullRequest
0 голосов
/ 24 октября 2019

В настоящее время я работаю над проектом, позволяющим проводить интерактивную оценку запасов. Это требует, чтобы пользователь выбрал акцию, введя тикер акций, и затем этот пользователь должен иметь возможность настроить драйверы акций и наблюдать за влиянием этих изменений на оценку компании.

Для достижения этого,сначала необходимо заполнить таблицу информацией о запасах, а затем обновить вычисления на основе измененных пользователем переменных. В идеале можно было бы обновить вычисления внутри самой таблицы, как показано в примере «Обновление столбцов той же таблицы» здесь . Однако в моем случае я изо всех сил пытаюсь объединить инициализацию таблицы с выходом из выбора запаса с обновлением вычислений таблицы после пользовательского изменения содержимого таблицы.

Я собрал оченьпростой, сокращенный, пример ниже. В этом случае я подумал, что может быть проще разделить данные о запасах и результаты расчета в две отдельные таблицы. Это то, что я тоже хочу сделать, и представляет собой более широкий вариант использования, чем все в одной таблице.

Я немного аннотировал свой код, так что моя логика, надеюсь, достаточно ясна. Но основной обзор кода выглядит следующим образом:

(i) Запрос пользовательского ввода тикера (в данном примере stock1 или stock2).

(ii) Отображение тикера или сообщения об ошибке, еслиневерный тикер.

(iii) Сохранение данных запаса в промежуточном кадре данных (в соответствии с примером 1 здесь .)

(iv) Заполнение таблицы FCFf выбранными данными запаса длявыбранный в данный момент запас.

(v) Рассчитать текущую стоимость, используя данные из данных FCFf

Именно в (v) моя проблема возникает: мой подход заключается в загрузке данных запаса из (iii в stock_df), загружая данные fcff из (iv) в fcff_df и перезаписывая поля fcff в stock_df полями в fcff_df. К сожалению, после изменения вручную любого из значений FCFf (например, в столбце fcff_fy3) таблица оценки не обновляется: вместо этого я получаю ошибку

TypeError: ufunc 'true_divide' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''

Меня поразило, что, возможно, пользовательский ввод не интерпретируетсяс тем же типом, что и ячейка, и вместо этого считается строкой?

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

Обратите внимание, что я также хотел бы иметь возможность рассчитывать, когда вносятся измененияк одному столу. Например, если бы я объединил таблицу оценки и таблицы FCFf в одну, а также добавил ставку дисконтирования 'r', используемую в do_pv, в качестве редактируемого поля, как мне это сделать? Спасибо за любую помощь.

Пример данных и код:

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import dash_table
import pandas as pd

from dash_table.Format import Format, Scheme, Sign, Symbol

df = pd.DataFrame(data = { 'ticker' :  ['stock1', 'stock2'], 'r' : [0.1, 0.2], 'fcff_fy1' : [7902, 9409] , 'fcff_fy2' : [13912, 68969], 'fcff_fy3' : [11309, 7154], 'fcff_fy4' : [13912, 68969], 'fcff_fy5' : [76158,   84090]   })
fcff_cols = ['fcff_fy1', 'fcff_fy2', 'fcff_fy3', 'fcff_fy4', 'fcff_fy5']
valuation_cols = ['Item', 'Valuation']

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.config.suppress_callback_exceptions = True
app.css.append_css({
    "external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"
})

app.layout = html.Div([
    html.H1('Stock Valuation'),
    html.H5('Choose stock from stock1, stock2'),
    dcc.Input(id='stock-id', value='stock1', type='text'),  #human text input of stock ticker
    html.Div(id='my-tick'), # Output to identify and inform user whether ticker selection is vald
    html.Hr(),
    html.Div(id='stock-data', style={'display': 'none'}), #intermediate storage of stock data dataframe.
    html.H5('FCFf drivers'),
    dash_table.DataTable(id='fcff-table', # Free cashflow table for selected stock
                         columns = [{"name": i, "id": i, 'format': Format(precision=2)} for i in (['Item'] + fcff_cols)],
                         data = ['FCFf'] + [0 for x in fcff_cols], editable = True),
    html.Hr(),
    html.H5('PV'),
    html.Div(dash_table.DataTable(id='valn-table', columns=[{"name": i, "id": i} for i in valuation_cols], data=[0 for x in ['Valuation']], editable = True)), # Valuation table, fed from FCff table
])

# Fill FCFf table with selected stock data
@ app.callback(
    [Output('fcff-table', 'columns'),
     Output('fcff-table', 'data')],
    [Input('stock-data', 'children')])
def display_output(df):
    stock_df = pd.read_json(df)
    fcff_table = pd.DataFrame(columns = fcff_cols, data = stock_df[fcff_cols].values.round(2), index=['FCFf'])
    fcff_table.reset_index(inplace=True)
    fcff_table.rename(columns={'index': 'Item'}, inplace=True)
    fcff_col_param = []
    for col in ['Item'] + fcff_cols:
        fcff_col_param.append({"name": str(col), "id": str(col)})
    return [fcff_col_param, fcff_table.to_dict('records')]


# Read data in from editable FaCFf table, and use to calculate and populate valn-table
@app.callback(
    [Output('valn-table', 'columns'),
     Output('valn-table', 'data')],
    [Input('fcff-table', 'data'),
     Input('stock-data', 'children')])
def display_valn(f_data, df):
    stock_df = pd.read_json(df) # Read current stock data from intermediate div in html.
    fcff_df = pd.DataFrame(f_data) # Convert FCFf table data to dataframe
    fcff_df = fcff_df[fcff_df.Item == 'FCFf'].drop('Item', axis=1)
    fcff_df.columns = fcff_cols

    stock_df[fcff_cols] = fcff_df[fcff_cols].values # overwrite fcff data with fcff data from the table, allowing users to change fcff assumptions.
    stock_df = do_pv(stock_df) # do PV of cashflows using stock's discount rate, r

    # construct valuation table output for display in valn-table
    data = [['Present Value of forecast FCFf (m)', stock_df['pv'].values[0].round(2)], ['Discount rate (r)', stock_df['r'].values[0].round(3)]],

table = pd.DataFrame(data, columns = ['Item', 'Valuation'])

    dt_col_param = [{"name": 'Item', "id": 'Item'}, {"name": 'Valuation', "id": 'Valuation'}]
    return [dt_col_param, table.to_dict('records')]

# Select stock for consideration via manual entry of ticker.
@app.callback(
    Output('my-tick', 'children'),
    [Input('stock-id', 'value')]
)
def update_output_ticker(input_value):
    if not input_value in df.ticker.unique():
        return 'Incorrect ticker'
    else:
        return df[df.ticker == input_value].ticker

# Store selected stock data dataframe in intermediate step.
@app.callback(Output('stock-data', 'children'),
              [Input('stock-id', 'value')])
def do_stock_df(selected_ticker):
    stock_df = df[df.ticker == selected_ticker]
    return stock_df.to_json()

def do_pv(df):
    df['pv'] = 0
    # pv of 5-year forecasts
    for i in range(1, 5):
        df.pv += df['fcff_fy' + str(i)].values[0] / ((1 + df.r.values[0]) ** i)
    return df

if __name__ == '__main__':
    app.run_server(debug=True)

1 Ответ

1 голос
/ 27 октября 2019

Попробуйте изменить модуль do_pv на приведенный ниже. Похоже, что после редактирования таблицы значение выбирается в виде строки, которая вызывает эту ошибку.

def do_pv(df):
    df['pv'] = 0
    # pv of 5-year forecasts
    for i in range(1, 5):
        #its string for the edited columns
        print(type(df['fcff_fy' + str(i)].values[0])) 
        df['pv'] +=  float(df['fcff_fy' + str(i)].values[0]) / float((1 + df.r.values[0]) ** i)
    return df
...