Equivalent function for px.bar() in go.bar()?

I’m brand new to the Dash framework.

My (toy) data is as follows:

df = pd.DataFrame({'Make':['Ford', 'Ford', 'Ford', 'BMW', 'BMW', 'BMW', Mercedes', 'Mercedes', 'Mercedes'],
                          'Score':['88.6', '76.6', '100', '79.1', '86.8', '96.4', '97.3', '98.7', '98.5'],
                          'Dimension':['Speed', 'MPG', 'Styling', 'Speed', 'MPG', 'Styling', 'Speed', 'MPG', 'Styling'],
                          'Month':['Apr-19', 'Apr-19', 'Apr-19', 'Apr-19', 'Apr-19', 'Apr-19', 'Apr-19', 'Apr-19', 'Apr-19']})

I have the following function:

px.bar(df, x='Make', y='Score', color='Dimension', barmode='group')

How would I map the px.bar() function arguments to the go.bar() function arguments?

Can go.Bar() take a data frame as the first positional argument and allow me to simply pass in the column names of the x and y values?

Also, can I facet by a particular ‘color’ using go.Bar()?

Thanks!

Well, px.bar exists to do this for your by assembling a number of go.Bar objects into a figure… what are you trying to do with go.Bar that you can’t do with px.bar?

@nicolaskruchten – thank you for responding.

Firstly, here is my (toy) data set:

df = pd.DataFrame({'Make':['Ford', 'Ford', 'Ford', 'BMW', 'BMW', 'BMW', Mercedes', 'Mercedes', 'Mercedes'],
                          'Score':['88.6', '76.6', '100', '79.1', '86.8', '96.4', '97.3', '98.7', '98.5'],
                          'Dimension':['Speed', 'MPG', 'Styling', 'Speed', 'MPG', 'Styling', 'Speed', 'MPG', 'Styling'],
                          'Month':['Apr-19', 'Apr-19', 'Apr-19', 'Apr-19', 'Apr-19', 'Apr-19', 'Apr-19', 'Apr-19', 'Apr-19']})

Originally, I used go.Bar(), but couldn’t figure out how to dynamically add n number of bars to the plot. So, I added them manually (knowing in advance that this particular data set contains 3 unique values for Make). Here is that code:

fig_01 = go.Figure(data=[
    go.Bar(name='Ford', x=df['Make'], y=df['Score'], text=df['Score'], textposition='auto'),
    go.Bar(name='BMW', x=df['Make'], y=df['Score'], text=df['Score'], textposition='auto'),
    go.Bar(name='Mercedes', x=df['Make']y=df['Score'], text=df['Score'], textposition='auto')
    ])

The problem with this approach is that it breaks once the number of unique values in Make is anything other than 3.

Then, I stumbled upon px.bar(), which seems to dynamically add the required number of bars for you. This is great!

Using px.bar(), I’m tried to add some data labels to the bars using the text and textposition arguments that the go.Bar() function offers.

And, here is full code:

import base64
import datetime
import io
import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
import plotly.graph_objects as go
import dash_table
import pandas as pd


app = dash.Dash()

app.layout = html.Div([
dcc.Upload(
        id='upload-data',
        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'
         },
        # Allow multiple files to be uploaded
        multiple=True
),

html.Div(id='output-data-upload'),
])

def parse_contents(contents, filename, date):
    content_type, content_string = contents.split(',')

    decoded = base64.b64decode(content_string)
    try:
        if 'csv' in filename:
        # Assume that the user uploaded a CSV file
            df = pd.read_csv(
                io.StringIO(decoded.decode('utf-8')))
        elif 'xls' in filename:
        # Assume that the user uploaded an excel file
            df = pd.read_excel(io.BytesIO(decoded))
    except Exception as e:
        print(e)
        return html.Div([
            'There was an error processing this file.'
        ])

    return html.Div([
        html.H5(filename),
        html.H6(datetime.datetime.fromtimestamp(date)),

        dash_table.DataTable(
            data=df.to_dict('records'),
            columns=[{'name': i, 'id': i} for i in df.columns]
        ),

        html.Hr(),  # horizontal line
        


        dcc.Graph(
            figure = 
            px.bar(df, x='Make', y='Score', color='Dimension', barmode='group', text='Score', textposition='auto'),
            ),        


        html.Hr(),

        # For debugging, display the raw contents provided by the web browser
        html.Div('Raw Content'),
        html.Pre(contents[0:200] + '...', style={
            'whiteSpace': 'pre-wrap',
            'wordBreak': 'break-all'
        })
    ])

@app.callback(Output('output-data-upload', 'children'),
              [Input('upload-data', 'contents')],
              [State('upload-data', 'filename'),
               State('upload-data', 'last_modified')])
def update_output(list_of_contents, list_of_names, list_of_dates):
    if list_of_contents is not None:
        children = [
            parse_contents(c, n, d) for c, n, d in
            zip(list_of_contents, list_of_names, list_of_dates)]
        return children

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

As you can see in bold text above, I tried:

px.bar(df, x='Make', y='Score', color='Dimension', barmode='group', text='Score', textposition='auto'),

But, that results in this error message:

TypeError: bar() got an unexpected keyword argument 'textposition'

Questions:

  1. Is there a one-to-one mapping between the px.Bar() arguments and the go.Bar() arguments? Is it possible to control textposition in px.Bar()?

  2. Why would one use go.Bar() instead of px.Bar()? (px.Bar seems like it’s easier to use, but perhaps a bit less flexible)

  3. Have I structured the code above in such a way that it conforms to “best practices” when using Plotly Dash? (or, as a complete newbie to Python, Plotly and Dash, have I completely “hacked” it together?) :slight_smile:

Thanks in advance!

1 Like

There is no simple mapping between the arguments to px.bar() and go.Bar as the former creates a whole figure and the latter defines only part of a figure. That said, in this case the answer is pretty easy: textposition is not a supported argument for px.bar() because it’s set to "auto" on the resulting go.Bar objects automatically if you set text:

If you want to modify the trace objects that px.bar() creates, you can easily do that, for example if you want the text on the outside, you can use .update_traces(textposition="outside"). See the Creating and Updating Figures documentation for more details.

Thank you, @nicolaskruchten.