2

I need to change stacked barchart width to be overlapped. I found this question and solution How to plot a superimposed bar chart using matplotlib in python? and I would like to reproduce the same chart on DASH Plotly python framework.

The code is as below:

import matplotlib.pyplot as plt
import numpy as np

width = 0.8

highPower   = [1184.53,1523.48,1521.05,1517.88,1519.88,1414.98,
               1419.34,1415.13,1182.70,1165.17]
lowPower    = [1000.95,1233.37, 1198.97,1198.01,1214.29,1130.86,
               1138.70,1104.12,1012.95,1000.36]

indices = np.arange(len(highPower))

plt.bar(indices, highPower, width=width, 
        color='b', label='Max Power in mW')
plt.bar([i+0.25*width for i in indices], lowPower, 
        width=0.5*width, color='r', alpha=0.5, label='Min Power in mW')

plt.xticks(indices+width/2., 
           ['T{}'.format(i) for i in range(len(highPower))] )

plt.legend()
plt.show()

enter image description here

Question: How to edit to accomodate DASH principles? For instance, on Dash, bar doesn't accept width=0.5*width adn alpha=0.5 Thanks.

My own code is as below:

from plotly.offline import init_notebook_mode, iplot
from plotly import graph_objs as go
init_notebook_mode(connected = True)
import pandas as pd
import numpy as np


dfb=pd.read_csv('https://www.dropbox.com/s/90y07129zn351z9/test_data.csv?dl=1', encoding="latin-1", infer_datetime_format=True, parse_dates=['date'], skipinitialspace=True)
dfb["date"]=pd.to_datetime(dfb['date']) 
dfb["site"]=dfb["site"].astype("category")
cm_inc=dfb[dfb.site == 5].pivot_table(index='date', values = 'site', aggfunc = {  'site' : 'count' }  )
dfb['cm_target'] = [40]*len(dfb)  
dfb.to_csv('test_data.csv', index=False)

data = [
    go.Bar(x=cm_inc.index, y=cm_inc['site'], name='Enroll Site A',  
            #base=0
           ),
    go.Bar(x=cm_inc.index, y=dfb['cm_target'], name='Target Site A', 
           #base=0,
           #width=0.5
          )]

layout = go.Layout(
    barmode='stack',
)

fig = dict(data = data, layout = layout)
iplot(fig, show_link=False)

enter image description here

The proposed solution by @Teoretic to use base=0 on both traces and to use barmode='stack' is not working. Thanks.

MGB.py
  • 461
  • 2
  • 9
  • 25

2 Answers2

1

EDIT edited answer to use new data that was added to the question

You can do overlapped barchart in Plotly by doing these 2 steps:
1) setting barmode in layout to 'stack'
2) setting base of every barchart to 0
3) small numeric value to set to X values

enter image description here

Also you might want to play around with:
1) Setting "width" parameter of the second barchart to the value that suits you
2) Making labels of "X" axis data more suitable to you

Sample code (run in Jupyter Notebook):

from plotly.offline import init_notebook_mode, iplot
from plotly import graph_objs as go
init_notebook_mode(connected = True)
import pandas as pd
import numpy as np


dfb=pd.read_csv('https://www.dropbox.com/s/90y07129zn351z9/test_data.csv?dl=1', encoding="latin-1", infer_datetime_format=True, parse_dates=['date'], skipinitialspace=True)
dfb["date"]=pd.to_datetime(dfb['date']) 
dfb["site"]=dfb["site"].astype("category")
cm_inc=dfb[dfb.site == 5].pivot_table(index='date', values = 'site', aggfunc = {  'site' : 'count' }  )
dfb['cm_target'] = [40]*len(dfb)  
dfb.to_csv('test_data.csv', index=False)

# You need small int indexes for "width" and "base" = 0 trick to work
indexes = [int(i.timestamp()) / 10000 for i in cm_inc.index] 
# For string dates labels
dates_indexes = [str(i) for i in cm_inc.index]

data = [
    go.Bar(x=indexes, 
           y=dfb['cm_target'], 
           name='Target Site A', 
           base=0
          ),
    go.Bar(x=indexes, 
           y=cm_inc['site'],
           name='Enroll Site A', 
           base=0,
           width=5  # Width value varies depending on number of samples in data
           )
]

layout = go.Layout(
    barmode='stack',
    xaxis=dict(
        showticklabels=True,
        ticktext=dates_indexes,
        tickvals=[i for i in indexes],
    )
)

fig = dict(data = data, layout = layout)
iplot(fig, show_link=False)
Teoretic
  • 2,483
  • 1
  • 19
  • 28
  • Your code works only on Jupyter notebook but doesnt work on Dash. When I add base=0 to both traces, the actual disappears remaining only the target. Happens also when I add width. I am applying same principle to my time series dash app where x axis is date time index. – MGB.py Aug 05 '18 at 18:03
  • Look my code ive updated. If you remove comment "#" it doesn't work: – MGB.py Aug 05 '18 at 18:31
  • 2
    @MGB.py well, it's not very good that you changed the input data after asking the question and getting the answer to it, but I will take a look – Teoretic Aug 05 '18 at 19:27
  • 1
    @MGB.py I think I found the problem, will update my answer soon – Teoretic Aug 05 '18 at 19:32
  • 1
    @MGB.py edited the answer, hope this will help! Oddly this base=0 trick only works with small integer values in "x" axis. It didn't work for me using strings, dates or larger integer values (e.g. the whole timestamp()) – Teoretic Aug 05 '18 at 20:29
  • Thanks for quick reply. Although, I would be much satisfied if the showticklabels should appear as before without seconds and above picture Ive shared. Otherwise, remove hour:min:seconds. How to remove them and let there just the date. Thanks. – MGB.py Aug 05 '18 at 20:41
  • @MGB.py for proper date to string formatting you can use [`strftime` function](https://docs.python.org/3/library/datetime.html#datetime.date.strftime) (you can find the example [here](https://stackoverflow.com/a/7999977/5299969)) – Teoretic Aug 05 '18 at 20:47
  • it is not an easy stuff. – MGB.py Aug 05 '18 at 21:10
0
from plotly.offline import init_notebook_mode, iplot
from plotly import graph_objs as go
init_notebook_mode(connected = True)
import pandas as pd
import numpy as np
from datetime import timedelta, datetime, tzinfo
import time
from datetime import datetime as dt


dfb=pd.read_csv('https://www.dropbox.com/s/90y07129zn351z9/test_data.csv?dl=1', encoding="latin-1", infer_datetime_format=True, parse_dates=['date'], skipinitialspace=True)
dfb["date"]=pd.to_datetime(dfb['date']) 

dfb["site"]=dfb["site"].astype("category")
cm_inc=dfb[dfb.site == 5].pivot_table(index='date', values = 'site', aggfunc = {  'site' : 'count' }  )
dfb['cm_target'] = [40]*len(dfb)  
dfb.to_csv('test_data.csv', index=False)

# You need small int indexes for "width" and "base" = 0 trick to work
#indexes = [int(i.timestamp()) / 10000 for i in cm_inc.index] 
indexes =pd.to_datetime(cm_inc.index) 
# For string dates labels
#dates_indexes = [str(i) for i in cm_inc.index]
dates_indexes = pd.to_datetime(cm_inc.index) 

data = [
    go.Bar(x=indexes, 
           y=dfb['cm_target'], 
           name='Target Site A', 
           base=0
          ),
    go.Bar(x=indexes, 
           y=cm_inc['site'],
           name='Enroll Site A', 
           base=0,
           #width=2  # Width value varies depending on number of samples in data
           )
]

layout = go.Layout(
    barmode='stack',
    xaxis=dict(
        showticklabels=True,
        ticktext=dates_indexes,
        tickvals=[i for i in indexes],
    )
)

fig = dict(data = data, layout = layout)
iplot(fig, show_link=False)

enter image description here

MGB.py
  • 461
  • 2
  • 9
  • 25
  • @Teoretic, while it is not really overlapped, i'd prefer like this without time, i have changed timestamp to indexes =pd.to_datetime(cm_inc.index) and dates_indexes = pd.to_datetime(cm_inc.index) . Thanks. – MGB.py Aug 05 '18 at 22:39