可编辑绘图表中的初始化和重新计算

时间:2019-10-24 20:56:33

标签: python plotly-dash plotly-python

我目前正在研究一个启用交互式库存评估的项目。这要求用户通过输入股票行情自动收录器来选择股票,然后该用户应能够调整股票驱动因素并观察这些变化对公司估值的影响。

为实现此目的,必须首先在表格中填充库存信息,然后根据用户编辑的变量更新计算。理想情况下,可以按照示例“更新同一表的列” here来更新表本身内的计算。但是,就我而言,我正在努力将表的初始化与库存选择的输出结合在一起,并在用户修改表的内容时更新表的计算。

我在下面整理了一个非常简单,简短的示例。在这种情况下,我认为将库存数据和计算输出分离到两个单独的表中可能会更简单。这也是我要做的事情,并且比将所有内容都放在一个表中代表了更广泛的用例。

我对代码做了一些注释,以便使我的逻辑很清楚。但是代码的基本概述如下:

(i)请求用户输入代码(在此示例中为stock1或stock2)。

(ii)显示代码,如果代码不正确,则会显示错误消息。

(iii)在中间数据框中存储库存数据(根据示例1 here。)

(iv)使用当前选定股票的选定股票数据填充FCFf表。

(v)使用FCFf数据中的数据计算现值

在(v)中似乎出现了我的问题:我的方法包括将(iii)中的库存数据加载到stock_df中,将(iv)中的fcff数据加载到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表合并为一个,并且还添加了do_pv中使用的折现率'r'作为可编辑字段,我该怎么做?谢谢您的帮助。

示例数据和代码:

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 个答案:

答案 0 :(得分:1)

尝试将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