def test_add_data(self):
        df = pd.DataFrame({
            'Time': [1, 2, 3, 5, 6],
            'Incidence Number': [10, 3, 4, 6, 9]
        })
        my_plot = bp.IncidenceNumberPlot()
        my_plot.add_data(df)

        npt.assert_array_equal(np.array([my_plot.figure['data'][0]['x']]),
                               np.array([np.array([1, 2, 3, 5, 6])]))

        npt.assert_array_equal(np.array([my_plot.figure['data'][0]['y']]),
                               np.array([np.array([10, 3, 4, 6, 9])]))

        with self.assertRaises(TypeError):
            bp.IncidenceNumberPlot().add_data(0)

        with self.assertWarns(UserWarning):
            df = pd.DataFrame({
                't': [1, 2, 3, 5, 6],
                'Incidence Number': [10, 3, 4, 6, 9]
            })
            my_plot.add_data(df, time_key='t')

        with self.assertWarns(UserWarning):
            df = pd.DataFrame({'Time': [1, 2, 4, 5, 6], 'i': [2, 3, 8, 10, 5]})
            my_plot.add_data(df, inc_key='i')
    def test_show_figure(self):
        with patch('plotly.graph_objs.Figure.show') as show_patch:
            df = pd.DataFrame({
                'Time': [1, 2, 3, 5, 6],
                'Incidence Number': [10, 3, 4, 6, 9]
            })
            my_plot = bp.IncidenceNumberPlot()
            my_plot.add_data(df)
            my_plot.show_figure()

        # Assert show_figure is called once
        assert show_patch.called
    def test_update_labels(self):
        df = pd.DataFrame({
            'Time': [1, 2, 3, 5, 6],
            'Incidence Number': [10, 3, 4, 6, 9]
        })
        my_plot = bp.IncidenceNumberPlot()
        my_plot.add_data(df)

        new_time_label = 'Week'
        new_inc_label = 'Inc'

        my_plot.update_labels(time_label=new_time_label)
        self.assertEqual(my_plot.figure['layout']['xaxis']['title']['text'],
                         'Week')

        my_plot.update_labels(inc_label=new_inc_label)
        self.assertEqual(my_plot.figure['layout']['yaxis']['title']['text'],
                         'Inc')
예제 #4
0
    def update_data_figure(self):
        """Update the data figure based on currently stored information.

        Returns
        -------
        plotly.Figure
            Figure with updated data
        """
        data = self.session_data.get('data_storage')

        if data is None:
            raise dash.exceptions.PreventUpdate()

        time_label, inc_label = data.columns[:2]

        plot = bp.IncidenceNumberPlot()

        if 'Imported Cases' in data.columns:
            # Separate data into local and imported cases
            imported_data = pd.DataFrame({
                time_label: data[time_label],
                inc_label: data['Imported Cases']
            })

            # Bar plot of local cases
            plot.add_data(data,
                          time_key=time_label,
                          inc_key=inc_label,
                          name='Local Cases')

            # Bar plot of imported cases
            plot.add_data(imported_data,
                          time_key=time_label,
                          inc_key=inc_label,
                          name='Imported Cases')

        else:
            # If no imported cases are present
            plot.add_data(data, time_key=time_label, inc_key=inc_label)

        # Keeps traces visibility states fixed when changing sliders
        plot.figure['layout']['legend']['uirevision'] = True

        return plot.figure
 def test__init__(self):
     bp.IncidenceNumberPlot()
예제 #6
0
    def __init__(self):
        super().__init__()

        self.session_data = {'data_storage': None, 'interval_storage': None}

        self.app = dash.Dash(__name__, external_stylesheets=self.css)
        self.app.title = 'BranchproSim'

        button_style = {
            'width': '100%',
            'height': '60px',
            'lineHeight': '60px',
            'borderWidth': '1px',
            'borderStyle': 'dashed',
            'borderRadius': '5px',
            'textAlign': 'center',
            'margin': '10px'
        }

        self.app.layout = \
            html.Div([
                dbc.Container([
                    html.H1('Branching Processes'),
                    html.Div([]),  # Empty div for top explanation texts
                    dbc.Row([
                        dbc.Col([
                            html.Button(
                                'Add new simulation',
                                id='sim-button',
                                n_clicks=0),
                            dcc.Graph(
                                figure=bp.IncidenceNumberPlot().figure,
                                id='myfig')
                        ]),
                        dbc.Col(
                            self.update_sliders(), id='all-sliders')
                    ], align='center'),
                    dbc.Row(
                        [
                            dbc.Col(
                                children=[
                                    html.H4([
                                        'You can upload your own ',
                                        html.Span(
                                            'incidence data',
                                            id='inc-tooltip',
                                            style={
                                                'textDecoration':
                                                    'underline',
                                                'cursor':
                                                    'pointer'},
                                        ),
                                        ' here.'
                                    ]),
                                    dbc.Modal(
                                        self._inc_modal,
                                        id='inc_modal',
                                        size='xl',
                                    ),
                                    html.Div([
                                        'It will appear as bars, while'
                                        ' the simulation will be a line.'
                                        ' You can upload both local and '
                                        '/ or imported incidence data.'
                                    ]),
                                    dcc.Upload(
                                        id='upload-data',
                                        children=html.Div([
                                            'Drag and Drop or ',
                                            html.A(
                                                'Select Files',
                                                style={
                                                    'text-decoration':
                                                    'underline'}),
                                            ' to upload your Incidence Number '
                                            'data.'
                                        ]),
                                        style=button_style,
                                        # Allow multiple files to be uploaded
                                        multiple=True
                                    ),
                                    html.Div(id='incidence-data-upload')]),
                            dbc.Col(
                                children=[
                                    html.H4([
                                        'You can upload your own ',
                                        html.Span(
                                            'serial interval',
                                            id='si-tooltip',
                                            style={
                                                'textDecoration':
                                                    'underline',
                                                'cursor':
                                                    'pointer'}
                                        ),
                                        ' here.'
                                    ]),
                                    dbc.Modal(
                                        self._si_modal,
                                        id='si_modal',
                                        size='lg',
                                    ),
                                    html.Div([
                                        'Data must contain one serial '
                                        'interval to be used for simulation'
                                        ' displayed as a column. If multiple '
                                        'serial intervals are uploaded, the '
                                        'first one will be used.']),
                                    dcc.Upload(
                                        id='upload-interval',
                                        children=html.Div(
                                            [
                                                'Drag and Drop or ',
                                                html.A(
                                                    'Select Files',
                                                    style={
                                                        'text-decoration': '\
                                                            underline'                                                                      }),
                                                ' to upload your Serial \
                                                    Interval.'
                                            ]),
                                        style=button_style,
                                        # Allow multiple files to be uploaded
                                        multiple=True
                                    ),
                                    html.Div(id='ser-interval-upload')])
                        ],
                        align='center',
                    ),
                    html.Div([]),  # Empty div for bottom text
                    html.Div(id='data_storage', style={'display': 'none'}),
                    html.Div(id='interval_storage', style={'display': 'none'}),
                    dcc.ConfirmDialog(
                        id='confirm',
                        message='Simulation failed due to overflow!',
                    ),
                    ], fluid=True),
                self.mathjax_script
                ])

        # Set the app index string for mathjax
        self.app.index_string = self.mathjax_html

        # Save the locations of texts from the layout
        self.main_text = self.app.layout.children[0].children[1].children
        self.collapsed_text = self.app.layout.children[0].children[-4].children
예제 #7
0
    def update_figure(self, fig=None, simulations=None, source=None):
        """Generate a plotly figure of incidence numbers and simulated cases.

        By default, this method uses the information saved in self.session_data
        to populate the figure with data. If a current figure and dash callback
        source are passed, it will try to just update the existing figure for
        speed improvements.

        Parameters
        ----------
        fig : dict
            Current copy of the figure
        simulations : pd.DataFrame
            Simulation trajectories to add to the figure.
        source : str
            Dash callback source

        Returns
        -------
        plotly.Figure
            Figure with updated data and simulations
        """
        data = self.session_data.get('data_storage')

        if data is None:
            raise dash.exceptions.PreventUpdate()

        if fig is not None and simulations is not None:
            # Check if there is a faster way to update the figure
            if len(fig['data']) > 0 and source in [
                    'epsilon', 'init_cond', 'r0', 'r1', 't1'
            ]:
                # Clear all traces except one simulation and the data
                if ('Imported Cases' in data.columns) and ('Incidence Number'
                                                           in data.columns):
                    fig['data'] = [
                        fig['data'][0], fig['data'][1], fig['data'][-1]
                    ]
                else:
                    fig['data'] = [fig['data'][0], fig['data'][-1]]

                # Set the y values of that trace equal to an updated simulation
                fig['data'][-1]['y'] = simulations.iloc[:, -1]

                return fig

            elif len(fig['data']) > 0 and source == 'sim-button':
                # Add one extra simulation, and set its y values
                fig['data'].append(copy.deepcopy(fig['data'][-1]))
                fig['data'][-1]['y'] = simulations.iloc[:, -1]

                if ('Imported Cases' in data.columns) and ('Incidence Number'
                                                           in data.columns):
                    sim_tuple = range(1, len(fig['data']) - 2)
                else:
                    sim_tuple = range(len(fig['data']) - 2)

                for i in sim_tuple:
                    # Change opacity of all traces in the figure but for the
                    # first - the barplot of incidences
                    # last - the latest simulation
                    fig['data'][i + 1]['line']['color'] = 'rgba(255,0,0,0.25)'
                    fig['data'][i + 1]['showlegend'] = False

                return fig

        time_label, inc_label = (data.columns[0], 'Incidence Number')
        num_simulations = len(simulations.columns) - 1

        # Make a new figure
        plot = bp.IncidenceNumberPlot()
        if 'Imported Cases' in data.columns:
            # Separate data into local and imported cases
            imported_data = pd.DataFrame({
                time_label: data[time_label],
                inc_label: data['Imported Cases']
            })

            if 'Incidence Number' in data.columns:
                # Bar plot of local cases
                plot.add_data(data.iloc[:, :2],
                              time_key=time_label,
                              inc_key=inc_label,
                              name='Local Cases')

            # Bar plot of imported cases
            plot.add_data(imported_data,
                          time_key=time_label,
                          inc_key=inc_label,
                          name='Imported Cases')

        else:
            # If no imported cases are present
            plot.add_data(data, time_key=time_label, inc_key=inc_label)

        # Keeps traces visibility states fixed when changing sliders
        plot.figure['layout']['legend']['uirevision'] = True

        for sim in range(num_simulations):
            df = simulations.iloc[:, [0, sim + 1]]
            df.columns = [time_label, inc_label]
            plot.add_simulation(df, time_key=time_label, inc_key=inc_label)

            # Unless it is the most recent simulation, decrease the opacity to
            # 25% and remove it from the legend
            if sim < num_simulations - 1:
                plot.figure['data'][-1]['line'].color = 'rgba(255,0,0,0.25)'
                plot.figure['data'][-1]['showlegend'] = False

        return plot.figure
예제 #8
0
    def __init__(self, long_callback_manager=None):
        """
        Parameters
        ----------
        long_callback_manager
            Optional callback manager for long callbacks.
            See https://dash.plotly.com/long-callbacks
        """
        super(BranchProInferenceApp, self).__init__()

        self.app = dash.Dash(__name__,
                             external_stylesheets=self.css,
                             long_callback_manager=long_callback_manager)
        self.app.title = 'BranchproInf'

        self.session_data = {
            'data_storage': None,
            'interval_storage': None,
            'posterior_storage': None
        }

        button_style = {
            'width': '100%',
            'height': '60px',
            'lineHeight': '60px',
            'borderWidth': '1px',
            'borderStyle': 'dashed',
            'borderRadius': '5px',
            'textAlign': 'center',
            'margin': '10px'
        }

        self.app.layout = html.Div([
            dbc.Container(
                [
                    html.H1('Branching Processes', id='page-title'),
                    html.Div([]),  # Empty div for top explanation texts
                    html.H2('Incidence Data'),
                    dbc.Row(
                        dbc.Col(
                            dcc.Graph(figure=bp.IncidenceNumberPlot().figure,
                                      id='data-fig'))),
                    dbc.Row(
                        [
                            dbc.Col(children=[
                                html.H6([
                                    'You can upload your own ',
                                    html.Span(
                                        'incidence data',
                                        id='inc-tooltip',
                                        style={
                                            'textDecoration': 'underline',
                                            'cursor': 'pointer'
                                        },
                                    ), ' here. It will appear as bars.'
                                ]),
                                dbc.Modal(
                                    self._inc_modal,
                                    id='inc_modal',
                                    size='xl',
                                ),
                                html.Div([
                                    'Data must be in the following column '
                                    'format: `Time`, `Incidence number`, '
                                    '`Imported Cases` (optional), '
                                    '`R_t` (true value of R, optional).'
                                ]),
                                dcc.Upload(
                                    id='upload-data',
                                    children=html.Div([
                                        'Drag and Drop or ',
                                        html.A('Select Files',
                                               style={
                                                   'text-decoration':
                                                   'underline'
                                               }), ' to upload your Incidence \
                                                    Number data.'
                                    ]),
                                    style=button_style,
                                    # Allow multiple files to be uploaded
                                    multiple=True),
                                html.Div(id='incidence-data-upload')
                            ]),
                            dbc.Col(children=[
                                html.H6([
                                    'You can upload your own ',
                                    html.Span('serial interval',
                                              id='si-tooltip',
                                              style={
                                                  'textDecoration':
                                                  'underline',
                                                  'cursor': 'pointer'
                                              }), ' here.'
                                ]),
                                dbc.Modal(
                                    self._si_modal,
                                    id='si_modal',
                                    size='lg',
                                ),
                                html.Div([
                                    'Data must contain one or more serial '
                                    'intervals to be used for constructing'
                                    ' the posterior distributions each '
                                    'included as a column.'
                                ]),
                                dcc.Upload(
                                    id='upload-interval',
                                    children=html.Div([
                                        'Drag and Drop or ',
                                        html.A('Select Files',
                                               style={
                                                   'text-decoration':
                                                   '\
                                                            underline'
                                               }), ' to upload your Serial \
                                                    Interval.'
                                    ]),
                                    style=button_style,
                                    # Allow multiple files to be uploaded
                                    multiple=True),
                                html.Div(id='ser-interval-upload')
                            ])
                        ],
                        align='center',
                    ),
                    html.H2('Plot of R values'),
                    html.Progress(id='progress_bar'),
                    html.Div(
                        id='first_run',  # see flip_first_run() in the app
                        children='True',
                        style={'display': 'none'}),
                    dbc.Row(
                        [
                            dbc.Col(children=dcc.Graph(
                                figure=bp.ReproductionNumberPlot().figure,
                                id='posterior-fig',
                                style={'display': 'block'})),
                            dbc.Col(self.update_sliders(), id='all-sliders')
                        ],
                        align='center',
                    ),
                    html.Div([]),  # Empty div for bottom text
                    html.Div(id='data_storage', style={'display': 'none'}),
                    html.Div(id='interval_storage', style={'display': 'none'}),
                    html.Div(id='posterior_storage', style={'display': 'none'})
                ],
                fluid=True),
            self.mathjax_script
        ])

        # Set the app index string for mathjax
        self.app.index_string = self.mathjax_html

        # Save the locations of texts from the layout
        self.main_text = self.app.layout.children[0].children[1].children
        self.collapsed_text = self.app.layout.children[0].children[-4].children