Labelling on each candle high with value from other column - plotly-dash

I would like to add value from column perDel.
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objects as go
import pandas as pd
from datetime import datetime
df = pd.read_csv(r'..\CandlesPlotPython\APOLLOALLN.csv')
app = dash.Dash(__name__)
app.layout = html.Div([
dcc.Checklist(
id='toggle-rangeslider',
options=[{'label': 'Include Rangeslider',
'value': 'slider'}],
value=['slider']
),
dcc.Graph(id="graph"),
])
#app.callback(
Output("graph", "figure"),
[Input("toggle-rangeslider", "value")])
def display_candlestick(value):
fig = go.Figure(go.Candlestick(
x=df['Date'],
open=df['Open Price'],
high=df['High Price'],
low=df['Low Price'],
close=df['Close Price'],
text=df['perDel']
))
fig.update_layout(
xaxis_rangeslider_visible='slider' in value,
annotations=[dict(
x=df['Date'], y=df['High Price'], xref='x', yref='paper',
showarrow=False, xanchor='left', text=df['perDel'].astype(str))]
)
return fig
app.run_server(debug=True)
Error -
ValueError:
Invalid value of type 'pandas.core.series.Series' received for the 'text' property of layout.annotation
Received value: 0 52.5 1 35.54 2 42.56 3 39.88 4 49.67
... 246 59.7 247 47.73 248 59.1 249 55.53 250 42.03 Name: perDel, Length: 251, dtype: object
The 'text' property is a string and must be specified as:
- A string
- A number that will be converted to a string
Any solution?

Like the error is saying the problem is that you're setting a pandas Series as the value of the text property of the Candlestick component.
Either pass a number or a string.
It is not entirely clear to me what you want to show using the text property, but you could convert the df['perDel'] data to a string and pass that to text:
text=",".join(df["perDel"].astype(str).values)
The above would show all numbers in the perDel Series separated by comma's.

Related

How to reduce Geojson size for repeated geometries (like timestamped data) in Pandas

I have a geopandas data frame which contain respective geometries as follows:
Date , value, Region Name, Geometry
2022-01-01 10 , ABC , Point((194 34),(121,23))
2022-02-01, 12 , ABC , Point((194 34),(121,23))
2022-02-01, 13 , DEF , Point((195 35),(123,24))
Almost equivalent Py code
import pandas as pd
import geopandas
import matplotlib.pyplot as plt
from shapely.geometry import Point
import geopandas
d = pd.DataFrame({'RegionName': ['ABC', 'ABC','DEF'],'Date': ['2021-01-01', '2021-02-01','2021-01-01'], 'Values': [10,11,12], 'Latitude': [-34.58, -34.58, -33.45], 'Longitude': [-58.66, -58.66, -70.66]})
gdf = geopandas.GeoDataFrame(d, geometry=geopandas.points_from_xy(d.Longitude, d.Latitude))
gdf = geopandas.GeoDataFrame(d, crs="EPSG:4326")
How can I save this data into a json/geojson file by reducing the size of the file and appending non-repetitive data (e.g. date and value) to the repetitive value (e.g. geometry)
Sth like this:
[
---Region name:
-----ABC
-----Date:
--------2022-01-01
--------2022-02-01
-----Value:
--------10
--------12
-----Geometry
--------Polygon((194 34),(121,23))
---Region name:
-----DEF
-----Date:
--------2022-02-01
-----Value:
--------13
-----Geometry
--------Polygon((194 34),(121,23))
]
Requirement:
This file needs to be consumed by mapbox/leaflet/or any other similar tool
Was able to solve this, first we need the distinct of repetitive columns (lets call it A,e.g. geometry), then form a list of non repetetive ones (lets call this B, e.g. date and value) and then merge B and A and then do the Json conversion.
Py code:
import pandas as pd
import geopandas
import matplotlib.pyplot as plt
from shapely.geometry import Point
import geopandas
d = pd.DataFrame({'RegionName': ['ABC', 'ABC','DEF'],'Date': ['2021-01-01', '2021-02-01','2021-01-01'], 'Values': [10,11,12], 'Latitude': [-34.58, -34.58, -33.45], 'Longitude': [-58.66, -58.66, -70.66]})
gdf = geopandas.GeoDataFrame(d, geometry=geopandas.points_from_xy(d.Longitude, d.Latitude))
gdf = geopandas.GeoDataFrame(d, crs="EPSG:4326")
#create a unique list of static data
df_dis_test= pd.DataFrame({'RegionName': ['ABC', 'DEF'],'Latitude': [-34.58, -33.45], 'Longitude': [-58.66, -70.66]})
gdfdf_dis_test = geopandas.GeoDataFrame(df_dis_test, geometry=geopandas.points_from_xy(df_dis_test['Longitude'], df_dis_test['Latitude']))
gdfdf_dis_test = geopandas.GeoDataFrame(df_dis_test, crs="EPSG:4326")
dgrp=d.groupby(['RegionName']). agg({ 'Date': lambda x: ','.join(x) } )
result = dgrp.merge( gdfdf_dis_test, how="inner", on="RegionName")
dgrpval=d.groupby(['RegionName']). agg({ 'Values': lambda x: list(x) } )
result2 = result.merge( dgrpval, how="inner", on="RegionName")
result2=result2.rename(columns={'geometry_x':'geometry'})
result2Gpd = geopandas.GeoDataFrame(result2, crs="EPSG:4326")#.drop(['geometry_y'],axis=1)
with open('Result2.geojson', 'w') as f:
f.write(result2Gpd.to_json (sort_keys=True, default=str))
and the output

How to extend ‘text’ field in Graph.ExtendData with Plotly-Dash?

I have an animated graph that I update in a clientside callback. However, I want to update the text as well as the x and y values of the traces in Graph.extendData(), but it seems that that doesn't work. Is there something I'm missing? Alternatively, is there a different method I should be using instead?
Adopting the code from this post (Plotly/Dash display real time data in smooth animation), I'd like something like this, but where updating the text with extendData actually worked:
import dash
import dash_html_components as html
import dash_core_components as dcc
import numpy as np
from dash.dependencies import Input, Output, State
# Example data (a circle).
resolution = 1000
t = np.linspace(0, np.pi * 2, resolution)
x, y = np.cos(t), np.sin(t)
text = str(t)
# Example app.
figure = dict(data=[{'x': [], 'y': []}], text = [], layout=dict(xaxis=dict(range=[-1, 1]), yaxis=dict(range=[-1, 1])))
app = dash.Dash(__name__, update_title=None) # remove "Updating..." from title
app.layout = html.Div([
dcc.Graph(id='graph', figure=dict(figure)), dcc.Interval(id="interval", interval=25),
dcc.Store(id='offset', data=0), dcc.Store(id='store', data=dict(x=x, y=y, text=text, resolution=resolution)),
])
# This makes the graph fail to draw instead of just extending the text as wel!
app.clientside_callback(
"""
function (n_intervals, data, offset) {
offset = offset % data.x.length;
const end = Math.min((offset + 10), data.x.length);
return [[{x: [data.x.slice(offset, end)], y: [data.y.slice(offset, end)], text: [data.text.slice(offset, end)]}, [0], 500], end]
}
""",
[Output('graph', 'extendData'), Output('offset', 'data')],
[Input('interval', 'n_intervals')], [State('store', 'data'), State('offset', 'data')]
)
if __name__ == '__main__':
app.run_server()
Alternatively, is there a different method I should be using instead?

output of a callback as input of another callback, second callback run twice

The dashboard include a dropdown, a table and a graph.
By selecting a item from dropdown, some rows of the table is shown and figure plot the data in the table.
I have two callback,
at the first one:
input : dropdown
output: table
second one:
input: table
output: figure
So the output of first callback is input of the second one.
The following code output look like this:
import dash
import dash_table
import pandas as pd
from copy import deepcopy
import plotly.graph_objects as go
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
data = pd.DataFrame({"a": ["a1", "a2"],
"b": [1, 2]})
app = dash.Dash(
__name__,
external_stylesheets=[dbc.themes.BOOTSTRAP],
prevent_initial_callbacks=True,
)
app.layout = dbc.Container(
[
dbc.Row(dbc.Col([dcc.Dropdown(
id='dropdown1',
options=[
{'label': '1', 'value': 1},
{'label': '2', 'value': 2}
],
value=1)])),
dbc.Row(
dbc.Col([
dash_table.DataTable(
id="datatable",
columns=[dict(id=i,
name=j,
)
for i, j in zip(
["a", "b"],
["a", "b"],
)],
data=data.loc[data['a'] ==
"a1"].to_dict("records"),
sort_mode="single",
)
])),
dbc.Row([dbc.Col([html.Div(
children=dcc.Graph(id="graph"), className="card")
])]),
], fluid=True,
)
########################################################
#app.callback(
Output("datatable", "data"),
[Input("dropdown1", "value")],
prevent_initial_call=True
)
def update_current_table(value):
idx = int(value)
print(idx)
if idx == 1:
df = (data.loc[data["a"] == "a1"])
return df.to_dict("records")
else:
df = deepcopy(data.loc[data["a"] == "a2"])
return df.to_dict("records")
########################################################
# app.callback(
Output('graph', 'figure'),
[Input("datatable", "derived_virtual_data"),
Input("dropdown1", "value")
],
prevent_initial_call=True
)
def update_figure(table, value):
df = pd.DataFrame(table)
print(df)
fig = go.Figure()
return fig
if __name__ == "__main__":
app.run_server(debug=True, port=8000)
The problem here is callback run twice:
a b
0 a1 1
a b
0 a2 2
and for the first time print the previous values of the table
This cause error in the main code that I am working on.
How to prevent dash from runnig callback twice?
I found some other questions complaining about running the callback twic, but I could not found a proper solution for that.
The problem is that you have two inputs for the callback that's running twice:
# app.callback(
Output('graph', 'figure'),
[Input("datatable", "derived_virtual_data"),
Input("dropdown1", "value")
],
prevent_initial_call=True
)
def update_figure(table, value):
The dropdown changes, and triggers this callback, but it also triggers the table to update. The updated table causes the derived_virtual_datato trigger this callback again. You can fix this by making the dropdown value a State instead, like this:
# app.callback(
Output('graph', 'figure'),
[Input("datatable", "derived_virtual_data")],
[State("dropdown1", "value"),]
prevent_initial_call=True
)
def update_figure(table, value):
Edit:
On another read through, you don't even use the value from the dropdown in the second input. Just removing it entirely will also work:
# app.callback(
Output('graph', 'figure'),
[Input("datatable", "derived_virtual_data")],
prevent_initial_call=True
)
def update_figure(table):

convert dict of dict into a dataframe

I have a slightly complicated json that I need to convert into a dataframe. This is a standard output json from another API and hence the field names will not change.
I have the below dict which is more complicated than what I have worked with till now
>>> import pandas as pd
>>> data = [{'annotation_spec': {'description': 'Story_Driven',
... 'display_name': 'Story_Driven'},
... 'segments': [{'confidence': 0.52302074,
... 'segment': {'end_time_offset': {'nanos': 973306000, 'seconds': 14},
... 'start_time_offset': {}}}]},
... {'annotation_spec': {'description': 'real', 'display_name': 'real'},
... 'segments': [{'confidence': 0.5244379,
... 'segment': {'end_time_offset': {'nanos': 973306000, 'seconds': 14},
... 'start_time_offset': {}}}]}]
I looked through all related SO posts and the closest I can get this into a dataframe is this
from pandas.io.json import json_normalize
pd.DataFrame.from_dict(json_normalize(data,record_path=
['segments'],meta=[['annotation_spec','description'],
['annotation_spec','display_name']],errors='ignore'))
This gives me an output like this
>>> from pandas.io.json import json_normalize
>>> pd.DataFrame.from_dict(json_normalize(data,record_path=['segments'],meta=[['annotation_spec','description'],['annotation_spec','display_name']],errors='ignore'))
confidence segment annotation_spec.description annotation_spec.display_name
0 0.523021 {u'end_time_offset': {u'nanos': 973306000, u's... Story_Driven Story_Driven
1 0.524438 {u'end_time_offset': {u'nanos': 973306000, u's... real real
>>>
I want to break down the "segment"column above as well into its components. How can I do that?
Basically json_normalize takes care of nested dicts, here we have a problem because of the list in the segements key.
So if the length of the list will always be 1, we can just remove the list and then apply json_normalize
### function to remove the lsit, we basically check if its a list, if so just take the first element
remove_list = lambda dct:{k:(v[0] if type(v)==list else v) for k,v in dct.items()}
data_clean = [remove_list(entry) for entry in data]
json_normalize(data_clean, sep="__")

How to convert this json file to pandas dataframe

The format in the file looks like this
{ 'match' : 'a', 'score' : '2'},{......}
I've tried pd.DataFrame and I've also tried reading it by line but it gives me everything in one cell
I'm new to python
Thanks in advance
Expected result is a pandas dataframe
Try use json_normalize() function
Example:
from pandas.io.json import json_normalize
values = [{'match': 'a', 'score': '2'}, {'match': 'b', 'score': '3'}, {'match': 'c', 'score': '4'}]
df = json_normalize(values)
print(df)
Output:
If one line of your file corresponds to one JSON object, you can do the following:
# import library for working with JSON and pandas
import json
import pandas as pd
# make an empty list
data = []
# open your file and add every row as a dict to the list with data
with open("/path/to/your/file", "r") as file:
for line in file:
data.append(json.loads(line))
# make a pandas data frame
df = pd.DataFrame(data)
If there is more than only one JSON object on one row of your file, then you should find those JSON objects, for example here are two possible options. The solution with the second option would look like this:
# import all you will need
import pandas as pd
import json
from json import JSONDecoder
# define function
def extract_json_objects(text, decoder=JSONDecoder()):
pos = 0
while True:
match = text.find('{', pos)
if match == -1:
break
try:
result, index = decoder.raw_decode(text[match:])
yield result
pos = match + index
except ValueError:
pos = match + 1
# make an empty list
data = []
# open your file and add every JSON object as a dict to the list with data
with open("/path/to/your/file", "r") as file:
for line in file:
for item in extract_json_objects(line):
data.append(item)
# make a pandas data frame
df = pd.DataFrame(data)