Black Lives Matter. Please consider donating to Black Girls Code today.

Callback error updating datatable-upload-graph.figure after edit datatable

I change the code of subsection uploading data of editable datatable provided in the user guide to let it show a data table that can be edit when the user uploads a file. I also add pandas groupby and sum function in the display graph callback. But I get an error after I change the value of the datatable. Can anyone teach me how to fix it?

The error is:

Callback error updating datatable-upload-graph.figure
Traceback (most recent call last):
  File "C:\Users\Jason\PycharmProjects\tutorial\venv\lib\site-packages\pandas\core\indexes\base.py", line 2657, in get_loc
    return self._engine.get_loc(key)
  File "pandas\_libs\index.pyx", line 108, in pandas._libs.index.IndexEngine.get_loc

  File "pandas\_libs\index.pyx", line 132, in pandas._libs.index.IndexEngine.get_loc

  File "pandas\_libs\hashtable_class_helper.pxi", line 1601, in pandas._libs.hashtable.PyObjectHashTable.get_item

  File "pandas\_libs\hashtable_class_helper.pxi", line 1608, in pandas._libs.hashtable.PyObjectHashTable.get_item

KeyError: 'Population'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "pandas\_libs\hashtable_class_helper.pxi", line 1608, in pandas._libs.hashtable.PyObjectHashTable.get_item

KeyError: 'Population'

Below is the code:

import base64
import io
import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import dash_table
import pandas as pd
from dash.exceptions import PreventUpdate
import plotly.graph_objs as go

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.config.suppress_callback_exceptions = True
app.layout = html.Div([
    dcc.Upload(
        id='datatable-upload',
        children=html.Div([
            'Drag and Drop or ',
            html.A('Select Files')
        ]),
        style={
            'width': '100%', 'height': '60px', 'lineHeight': '60px',
            'borderWidth': '1px', 'borderStyle': 'dashed',
            'borderRadius': '5px', 'textAlign': 'center', 'margin': '10px'
        },
    ),
    html.Div(id='output-data-upload'),
    dcc.Graph(id='datatable-upload-graph'),
    dcc.Store(id='local'),
])


def parse_contents(contents, filename):
    content_type, content_string = contents.split(',')
    decoded = base64.b64decode(content_string)
    if 'csv' in filename:
        # Assume that the user uploaded a CSV file
        return pd.read_csv(
            io.StringIO(decoded.decode('utf-8')))
    elif 'xls' in filename:
        # Assume that the user uploaded an excel file
        return pd.read_excel(io.BytesIO(decoded))


@app.callback(Output('output-data-upload', 'children'),
               [Input('datatable-upload', 'contents')],
               [State('datatable-upload', 'filename')])
def update_output(contents, filename):
    if contents is None:
        return []
    df = parse_contents(contents, filename)
    return html.Div([
        dash_table.DataTable(
            id='table',
            style_data={
                'whiteSpace': 'normal',
                'height': 'auto'
            },
            style_table={'overflowX': 'scroll',
                         'maxHeight': '300',
                         'overflowY': 'scroll'},
            style_cell={
                'minWidth': '150px', 'maxWidth': '180px',
                'whiteSpace': 'normal',
                'textAlign': 'left'
            },
            style_header={
                'fontWeight': 'bold',
            },
            fixed_rows={'headers': True, 'data': 0},
            columns=[{"name": i, "id": i, 'deletable': True, 'renamable': True} for i in df.columns],
            data=df.to_dict("records"),
            row_deletable=True,
            filter_action="native",
            sort_action="native",
            sort_mode='multi',
            editable=True,

        )
    ])

@app.callback(Output('local', 'data'),
              [Input('table', 'data')])
def savedata(data):
    if data is None:
        raise PreventUpdate
    return data


@app.callback(Output('datatable-upload-graph', 'figure'),
              [Input('local', 'data')])
def display_graph(rows):
    df = pd.DataFrame(rows)

    if (df.empty or len(df.columns) < 1):
        return {
            'data': [{
                'x': [],
                'y': [],
                'type': 'bar'
            }]
        }
    df1 = df.groupby(df.columns[3])
    df2 = df1.sum()
    df3 = df2[df.columns[0]]
    trace = go.Bar(x=df3.index, y=df3)
    return {
        'data': [trace],
        'layout': go.Layout(xaxis={'title': df.columns[3], 'titlefont': {'color': 'black', 'size': 14},
                                   'tickfont': {'size': 9, 'color': 'black'}},
                            yaxis={'title': df.columns[0],
                                   'titlefont': {'color': 'black', 'size': 14, },
                                   'tickfont': {'color': 'black'}})
    }



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

The dataset I use is: