Using width="container" in an altair chart saved as html - altair

I am trying to use the feature width="container" that allows the width of the chart to be the same as parent container in a saved chart. The rendered chart does not use the width defined in the parent div. See the reprex below.
>>> import altair as alt
>>> import pandas as pd
>>> from altair_saver import save
>>> df = pd.DataFrame(
... {
... "x": range(6),
... "y": range(6, 0, -1),
... "category": ["A", "B", "A", "B", "A", "B"],
... }
... )
...
>>> chart = alt.Chart(df, width="container").mark_line().encode(x="x:Q", y="y:Q", color="category:N")
>>> chart_html = save(chart, fmt="html", method="html", standalone=False)
>>> html = f"<html><head></head><body><div style='width: 500px'>{chart_html}</div></body></html>"
>>> with open("test_save.html", "w") as f:
... f.write(html)
...
>>> alt.__version__
'4.1.0'
>>> from altair_saver import __version__
>>> __version__
'0.5.0'
The rendered chart is very small, and if I change the size of the window in any way, the chart grows.
Maybe there is something I do not understand or use incorrectly. I want the chart rendered as HTML as I am not in a notebook environment.

Related

Print table in plotly dash with multiple lines in one cell

Currently I have a pandas dataframe :
df = pd.DataFrame({
"date": ["20210613", "20210614", "20210615"],
"user": ["A\nB", "C", "D"],
"machine" : [1, 0, 3]
})
I wonder if there is any way to print this table to my dash app like this:
no matter using pure text print into dcc.Textarea or dash_table.DataTable are fine.
Currently I still can not figure out a good way to achieve this, many thanks.
You can do it in a DataTable by using the style_cell property on the DataTable like this:
import dash
import dash_table
import pandas as pd
df = pd.DataFrame(
{
"date": ["20210613", "20210614", "20210615"],
"user": ["A\nB", "C", "D"],
"machine": [1, 0, 3],
}
)
app = dash.Dash(__name__)
app.layout = dash_table.DataTable(
id="table",
columns=[{"name": i, "id": i} for i in df.columns],
data=df.to_dict("records"),
style_cell={"whiteSpace": "pre-line"},
)
if __name__ == "__main__":
app.run_server(debug=True)
You can make the datatable cells break when they encounter the \n character by setting the white-space CSS attribute for the cells in the table
I found this answer on this thread

How to convert a pandas DataFrame to YAML in python

import yaml
import pandas as pd
data = ['apple','car','smash','market','washing']
bata = ['natural','artificail','intelligence','outstanding','brain']
df = pd.DataFrame(zip(data,bata),columns=['Data','Bata'])
for columns in df:
for list in df[columns]:
text = yaml.dump_all(list)
print(text)
I used above code but I'm getting each letter printed. How to get good YAML format. Thank you.
You can use yaml.dump to get text in yaml format
>>> import yaml
>>> import pandas as pd
>>> data = ['apple','car','smash','market','washing']
>>> bata = ['natural','artificail','intelligence','outstanding','brain']
>>> df = pd.DataFrame(zip(data,bata),columns=['Data','Bata'])
>>> text = yaml.dump(
df.reset_index().to_dict(orient='records'),
sort_keys=False, width=72, indent=4,
default_flow_style=None)
>>> text
'- {index: 0, Data: apple, Bata: natural}\n- {index: 1, Data: car, Bata: artificail}\n- {index: 2, Data: smash, Bata: intelligence}\n- {index: 3, Data: market, Bata: outstanding}\n- {index: 4, Data: washing, Bata: brain}\n'
import yaml
import pandas as pd
data = ['apple','car','smash','market','washing']
bata = ['natural','artificail','intelligence','outstanding','brain']
df = pd.DataFrame(zip(data,bata),columns=['Data','Bata'])
text = yaml.dump(df.to_dict(orient='records'),default_flow_style=None)`
If you want save to file your df:
with open('test_df_to_yaml.yaml', 'w') as file:
documents = yaml.dump({'result': df.to_dict(orient='records')}, file, default_flow_style=False)
If you open after saving as DataFrame:
with open('test_df_to_yaml.yaml', mode="rt", encoding="utf-8") as test_df_to_yaml:
df_merged = pd.DataFrame(yaml.full_load(test_df_to_yaml)['result'])

How to ignore or clip negative values in altair charts from the chart code itself?

I want to NOT show the negative value in the bar chart. The main idea is to NOT have that y-axis offset(in the actual problem its a facet), so any way to achieve this is fine - maybe clipping - just not at data level, preferably from the chart itself.
I thought of using alt.Scale but the domain requires you to specify a max limit and the issue is that I do not know that first hand, and I cannot find a way to programmatically specify max over the values.
You can use the following demo chart -
import pandas as pd
import altair as alt
dd = pd.DataFrame({'a': [0,1,2,3,4,5], 'b': [10,14, -5, 15, 0, 5]})
a = alt.Chart().mark_bar().encode(
x='a',
y=alt.Y('b:Q')
)
b = alt.Chart().mark_line().transform_window(
rolling_mean = 'mean(b)',
frame=[-2, 0]).encode(
x='a',
y='rolling_mean:Q'
)
alt.layer(a, b, data=dd)
There are only two ways I know of to hide data on a chart. First, you can set an explicit scale domain and set clip=True for the relevant marks:
import pandas as pd
import altair as alt
dd = pd.DataFrame({'a': [0,1,2,3,4,5], 'b': [10,14, -5, 15, 0, 5]})
a = alt.Chart().mark_bar(clip=True).encode(
x='a',
y=alt.Y('b:Q', scale=alt.Scale(domain=[0, 16]))
)
b = alt.Chart().mark_line().transform_window(
rolling_mean = 'mean(b)',
frame=[-2, 0]).encode(
x='a',
y='rolling_mean:Q'
)
alt.layer(a, b, data=dd)
Second, you can apply a filter transform to your data to remove rows from your dataset:
import pandas as pd
import altair as alt
dd = pd.DataFrame({'a': [0,1,2,3,4,5], 'b': [10,14, -5, 15, 0, 5]})
a = alt.Chart().mark_bar().encode(
x='a',
y=alt.Y('b:Q', scale=alt.Scale(domain=[0, 16]))
)
b = alt.Chart().mark_line().transform_window(
rolling_mean = 'mean(b)',
frame=[-2, 0]).encode(
x='a',
y='rolling_mean:Q'
)
alt.layer(a, b, data=dd).transform_filter(alt.datum.b > 0)
Note that difference: because this transform was applied at the top level, it removes rows for both sub-panels. If you instead apply the filter for only one of the subcharts, the rows will only be removed from that layer:
import pandas as pd
import altair as alt
dd = pd.DataFrame({'a': [0,1,2,3,4,5], 'b': [10,14, -5, 15, 0, 5]})
a = alt.Chart().transform_filter(
alt.datum.b > 0
).mark_bar().encode(
x='a',
y=alt.Y('b:Q', scale=alt.Scale(domain=[0, 16]))
)
b = alt.Chart().mark_line().transform_window(
rolling_mean = 'mean(b)',
frame=[-2, 0]).encode(
x='a',
y='rolling_mean:Q'
)
alt.layer(a, b, data=dd)
One way to do it seems to use transform_filter as follows -
.transform_filter(alt.datum.b >= 0 )

How to add annotation based on the value in bokeh

I want to be able to display "NO DATA" when there is a value '0' in counts. For example for Strawberries, "NO DATA" should be displayed in the graph.
from bokeh.io import show, output_file
from bokeh.plotting import figure
output_file("bar_basic.html")
fruits = ['Apples', 'Pears', 'Nectarines', 'Plums', 'Grapes', 'Strawberries']
counts = [5, 3, 4, 2, 4, 0]
p = figure(x_range=fruits, plot_height=350, title="Fruit Counts")
p.vbar(x=fruits, top=counts, width=0.9)
p.y_range.start = 0
show(p)
For example, for above data the graph should look like this:example vbar with NO DATA
You can select the data with the count value '0' with Pandas. This new dataframe can be used to create another ColumnDataSource to use for the LabelSet to show the text 'NO DATA' in the figure.
from bokeh.io import show, output_file
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, LabelSet
import pandas as pd
output_file("bar_basic.html")
fruits = ['Apples', 'Pears', 'Nectarines', 'Plums', 'Grapes', 'Strawberries']
counts = [5, 3, 4, 2, 4, 0]
df = pd.DataFrame.from_dict({'fruits': fruits, 'counts': counts})
source = ColumnDataSource(df)
p = figure(x_range=fruits, plot_height=350, title="Fruit Counts")
p.vbar(x='fruits', top='counts', source=source, width=0.9)
df_nodata = df.loc[df['counts'] == 0]
pd.options.mode.chained_assignment = None
df_nodata.loc[:, 'text'] = 'NO DATA'
source_nodata = ColumnDataSource(df_nodata)
labels = LabelSet(x='fruits', y=1, text='text', text_align='center', source=source_nodata)
p.add_layout(labels)
p.y_range.start = 0
show(p)

Remove bars with 0 height in a bar graph in dash

I am trying to make a group bar graph in dash, I am plotting subject codes on the x-axis so they are not continuous numbers and I am getting empty bars for the missing subject codes so is there any way to remove these spaces or invisible bars.
This is the bar graph I am getting.
This is my code.
df = pd.read_csv('sampledata.csv')
a=df['SiteCode'].loc[df['SubjectStatus']=='In Progress'].value_counts()
a.index=a.index.astype(str)
b=df['SiteCode'].loc[df['SubjectStatus']=='Withdrawn'].value_counts()
b.index=b.index.astype(str)
x1=a.index
x2=b.index
trace1=go.Bar(
x=x1,
y=a.values,
name='In Progress',
)
trace2=go.Bar(
x=x2,
y=b.values,
name='Withdrawn',
)
app = dash.Dash()
app.layout = html.Div(
dcc.Graph(id='graph',
figure=go.Figure(data=[trace1,trace2],layout=go.Layout(barmode='group')))
if __name__=='__main__':
app.run_server()
Thanks in advance
PS: I am a noob in dash and python both so go easy on me.
You should try set barmode='stack', because barmode='group' added empty space if your one of your traces have empty values.
import dash
from dash.dependencies import Output, Input
import dash_core_components as dcc
import dash_html_components as html
import plotly
import plotly.graph_objs as go
import pandas as pd
app = dash.Dash(__name__)
df = pd.DataFrame({'x': [100, 100, 105, 110, 110, 115, 120, 125],
'y': [1, 2, 1, 1, 2, 2, 1, 1]})
colors = {
'background': '#111111',
'background2': '#FF0',
'text': '#7FDBFF'
}
df1 = df.loc[df["y"] == 1]
df2 = df.loc[df["y"] == 2]
trace1 = go.Bar(
x=df1["x"],
y=df1["y"],
name='In Progress',
)
trace2 = go.Bar(
x=df2["x"],
y=df2["y"],
name='Withdrawn',
)
app.layout = html.Div(children=[
html.Div([
html.H5('ANNx'),
dcc.Graph(
id='cx1',
figure=go.Figure(data=[trace1, trace2],
layout=go.Layout(barmode='group')))],)])
if __name__ == '__main__':
app.run_server(debug=True)
For example, in this code at value 105, 115 and 120 one trace is empty and this create space in plot:
Using another barmode solved this problem:

Resources