Noble Joseph
Noble Joseph

Reputation: 1

how to fill colors on a plotly chart based on Y axis values?

I have a plotly chart that tracks the live sentiment for various keywords.

enter image description here

I want the graph to show green when the sentiment is positive and red when it is negative.

Code:

app = dash.Dash(__name__)
app.layout = html.Div(
[   html.Div(className='container-fluid', children=[html.H2('Live Market Sentiment', style={'color':"#0C0F0A", 'text-align': 'center'}),
                                                    html.H5('Search Ticker/Stock:', style={'color':app_colors['text']}),
                                              dcc.Dropdown(id='sentiment_term', options = [{'label':s,'value':s} for s in data_dict.keys()],value =['Google-GOOGL'], multi = False),
                                              ],
             style={'width':'98%','margin-left':10,'margin-right':10,'max-width':50000})


@app.callback(Output('live-graph', 'figure'),
          [Input(component_id='sentiment_term', component_property='value')],
          events=[Event('graph-update', 'interval')])
def update_graph_scatter(sentiment_term):

var1 = str(data_dict[sentiment_term][0])
var2 = str(data_dict[sentiment_term][1])

try:
    if sentiment_term:
        df1 = pd.read_sql("SELECT sentiment.* FROM sentiment_fts fts LEFT JOIN sentiment ON fts.rowid = sentiment.id WHERE fts.sentiment_fts MATCH ? ORDER BY fts.rowid DESC LIMIT 1000", conn, params=(var1+'*',))
        df2 = pd.read_sql("SELECT sentiment.* FROM sentiment_fts fts LEFT JOIN sentiment ON fts.rowid = sentiment.id WHERE fts.sentiment_fts MATCH ? ORDER BY fts.rowid DESC LIMIT 1000", conn, params=(var2+'*',))
        df = df1.append(df2)
    else:
        df = pd.read_sql("SELECT * FROM sentiment ORDER BY id DESC, unix DESC LIMIT 1000", conn)
    df.sort_values('unix', inplace=True)
    df['date'] = pd.to_datetime(df['unix'], unit='ms')
    df.set_index('date', inplace=True)
    init_length = len(df)
    df['sentiment_smoothed'] = df['sentiment'].rolling(int(len(df)/5)).mean()
    df = df_resample_sizes(df)
    X = df.index
    Y = df.sentiment_smoothed.values
    Y2 = df.volume.values
    #df_count = pd.read_sql("SELECT * FROM sentiment ORDER BY id DESC LIMIT 1", conn)
    #analyzer_count =df_count.id.max()
    #print(analyzer_count)
    
    data = plotly.graph_objs.Scatter(
            x=X,
            y=Y,
            name='Sentiment',
            mode= 'lines',
            yaxis='y2',                          
            fill="tozeroy",
            fillcolor = "#8bcbfc"                                                     
            )
    

    return {'data': [data],'layout' : go.Layout(xaxis=dict(range=[min(X),max(X)]),
                                                      yaxis2=dict(range=[min(Y),max(Y)], side='left', overlaying='y',title='sentiment'),
                                                      title='Live sentiment for: "{}"'.format(sentiment_term),
                                                      font={'color':app_colors['text']},
                                                      plot_bgcolor = app_colors['background'],
                                                      paper_bgcolor = app_colors['background'],
                                                      showlegend=False)}

except Exception as e:
    with open('errors.txt','a') as f:
        f.write(str(e))
        f.write('\n')

I tried adding if conditions to the charts but it does not seem to help. please help!

Thanks

Upvotes: 1

Views: 1891

Answers (1)

Jacob K
Jacob K

Reputation: 784

I know this doesn't quite exactly answer your question, but here is some starter code I wrote to get you a related plot. I know this is regular Plotly code but you should be able to integrate this with your Dash code fairly easily. I did a lot of digging online and it appears that Plotly doesn't support multiple colors for the fill option as part of the trace. There are some solutions, but they only work if the y values don't change between positive and negative a lot.

Here is some starter code if they the values do not change signs frequently

# Import packages
import numpy as np
import pandas as pd
import plotly.graph_objects as go

# Generate some random data
numPts = 100
xData = pd.date_range(start='1/01/2020', end='12/31/2020', periods=numPts)
yDataPos = np.random.random(numPts//2)*4             # Random data [0, 4)
yDataNeg = (np.random.random(numPts//2) - 1)*2       # Random data [-2, 0)

# Create Plotly Plot
posData = go.Scatter(x=xData[0:numPts//2], y=yDataPos, fill='tonexty', line_color='green', 
                     name='Trace 1', showlegend=True, legendgroup="mytrace")
negData = go.Scatter(x=xData[numPts//2:], y=yDataNeg, fill='tozeroy', line_color='red', 
                     showlegend=False, legendgroup="mytrace")
fig = go.Figure(data=[posData,negData])
fig.update_layout(title='Live Sentiment')
fig.update_xaxes(title_text="Date")
fig.update_yaxes(title_text="Value")

Solution_1 You would just need to add some filtering to find where your data is positive and negative and then plug in those values into the appropriate traces.

If the values do change sign frequently, I'd just recommend using a bar chart

allYData = yDataPos + yDataNeg
colors = ['red' if val < 0 else 'green' for val in allYData]

dataTrace = go.Bar(x=xData, y=allYData, marker=dict(color=colors), name='Data')
fig = go.Figure(data=dataTrace)
fig.update_layout(title='Live Sentiment')
fig.update_xaxes(title_text="Date")
fig.update_yaxes(title_text="Value")

Solution_2

Upvotes: 1

Related Questions