Plotly - clicking on legend items - how to make an initial setting? - python-3.x

When someone clicks on a legend item, it becomes grey, and the data disappears, for instance, here. It is possible to set that an item from a legend will be grey after opening the .HTML output and will appear after clicking of that? Thank you

You can do that using the visible property on the trace, just set visible='legendonly'.
visible
Type: enumerated , one of ( True | False | "legendonly" )
Default: True
Determines whether or not this trace is visible. If "legendonly", the
trace is not drawn, but can appear as a legend item (provided that the
legend itself is visible).
A common use case is when one has a lot of traces and wants to show only a few of them initially, eg. with plotly.express :
import plotly.express as px
df = px.data.gapminder().query("continent == 'Europe'")
# Nb. This creates one trace per country (color='country'), with each trace `name`
# inheriting the value of its respective country.
fig = px.line(df, x='year', y='gdpPercap', color='country', symbol="country")
# Arbitrary selection
sel = ['Norway', 'Ireland', 'France', 'Switzerland']
# Disable the traces that are not in the selection
fig.update_traces(selector=lambda t: t.name not in sel, visible='legendonly')
fig.show()

Related

Displaying multiple values in Altair/Streamlit tooltips on a bar chart

My DataFrame looks similar to this:
name
reached points
Jose Laderman
13
William Kane
13
I am currently displaying the aggregated count of students reached points of an assignment on an Altair bar chart within Streamlit like this:
brush = alt.selection(type='interval', encodings=['x'])
interactive_test = alt.Chart(df_display_all).mark_bar(opacity=1, width=5).encode(
x= alt.X('reached points', scale=alt.Scale(domain=[0, maxPoints])),
y=alt.Y('count()', type='quantitative', axis=alt.Axis(tickMinStep=1), title='student count'),
).properties(width=1200)
upper = interactive_test.encode(
alt.X('reached points', sort=alt.EncodingSortField(op='count', order='ascending'), scale=alt.Scale(domain=brush, domainMin=-0.5))
)
lower = interactive_test.properties(
height=60
).add_selection(brush)
concat_distribution_interactive = alt.vconcat(upper, lower)
Which produces this output and everything looks fine
The information I want my tooltip to show is a list of students that reached the specific amounts of reached points I'm hovering over. When adding something like:
tooltip='name'
the way my bar chart seems to display values has now been altered to this
When adding something like
tooltip='reached points'
The data seems to be displayed normally but without a tooltip that gives me the necessary information. Is it possible to display tooltip data that isn't used in my x or y axis but still part of the DataFrame I'm putting into the chart?

Is there a way to display the value of a mark next to the mark in Altair

I was playing around with the following example from the Altair Gallery:
https://altair-viz.github.io/gallery/airports_count.html
As of right now, the only way to display the actual count appears to be via the tooltip, as the example shows. However, I am trying to code a static visualization for which it would be very helpful if the exact value was displayed right next to the mark itself, without the user having to hover or interact in any way. Is there a way to achieve this?
You can do this by manually calculating offsets for text labels, though this is admittedly difficult when the points become crowded:
import altair as alt
from vega_datasets import data
airports = data.airports.url
states = alt.topo_feature(data.us_10m.url, feature='states')
# US states background
background = alt.Chart(states).mark_geoshape(
fill='lightgray',
stroke='white'
).properties(
width=500,
height=300
).project('albersUsa')
# airport positions on background
base = alt.Chart(airports).transform_aggregate(
latitude='mean(latitude)',
longitude='mean(longitude)',
count='count()',
groupby=['state']
).encode(
longitude='longitude:Q',
latitude='latitude:Q',
)
points = base.mark_circle().encode(
size=alt.Size('count:Q', title='Number of Airports'),
color=alt.value('steelblue'),
tooltip=['state:N','count:Q']
).properties(
title='Number of airports in US'
)
text = base.mark_text(
dx=15, dy=10
).encode(
text='count:Q'
)
background + points + text
Long-term, a better solution will be to use vega-label, which will be able to do this automatically once it's part of the Vega-Lite package. For Altair, this feature is tracked in this bug: https://github.com/altair-viz/altair/issues/1731

How to add text on interactive Scatter on Altair?

I try to adapt the Selection Detail Example from altair doc (https://altair-viz.github.io/gallery/select_detail.html#selection-detail-example).
I won't detailed my Dataframe structure which is identical with the one from the example (included variable names).
The native code is working well :
# Data is prepared, now make a chart
selector = alt.selection_single(empty='all', fields=['id'])
base = alt.Chart(data).properties(
width=250,
height=250
).add_selection(selector)
points = base.mark_point(filled=True, size=200,opacity=0.9).encode(
x=alt.X('mean(y)',title='Durée de perception',scale=alt.Scale(domain=(11, 23))),
y=alt.Y('mean(x)',title='Taux de marge (%PM)'),
color=alt.condition(selector, 'id:O', alt.value('lightgray')),
tooltip = ['mean(y)','mean(x)']
)
timeseries = base.mark_bar(opacity=1).encode(
x=alt.X('time', title='Items'),
y=alt.Y('value', scale=alt.Scale(domain=(-1, 1)),stack=None),
color=alt.Color('id:O',scale=alt.Scale(domain=domain, range=range_))
#, legend=None)
).transform_filter(
selector
)
points | timeseries
No problem at this stage even if it could be useful to hide all the bars on right chart when no selection is made on the right chart (don't know if it's possible ?)
After that I try to add text to the scatter plot adding this at the end of the code :
text = points.mark_text(dy=-5).encode(
x=alt.X('mean(y)',title='Durée de perception',scale=alt.Scale(domain=(11, 23))),
y=alt.Y('mean(x)',title='NBV (%CA)'),
text='id:O'
)
(points + text) | timeseries
which leads to the following error message :
Javascript Error: Duplicate signal name: "selector094_tuple"
This usually means there's a typo in your chart specification. See the javascript console for the full traceback.
If you have any idea on how to do, i would be grateful
Thanks
The issue is that you cannot add the same selection to two different layers, which you do implicitly by deriving text from points. Try this instead:
text = alt.Chart(data).mark_text(dy=-5).encode(
x=alt.X('mean(y)',title='Durée de perception',scale=alt.Scale(domain=(11, 23))),
y=alt.Y('mean(x)',title='NBV (%CA)'),
text='id:O'
)
(points + text) | timeseries

Interactive labeling of images in jupyter notebook

I have a list of pictures:
pictures = {im1,im2,im3,im4,im5,im6}
Where
im1:
im2:
im3:
im4:
im5:
im6:
I want to assign the pictures to labels (1,2,3,4 etc.)
For instance, here pictures 1 to 3 belong to label 1, picture 4 belongs to label 2, picture 5 to label 3, and picture 6 to label 4.
-> label = {1,1,1,2,3,4}
Since I need to see the images when I label them, I need a method to do that while labeling them. I was thinking of creating an array of images:
And then I define the ranges by clicking on the first and last picture belonging to the same labels, so for example:
What do you think ? Is this somehow possible ?
I would like to assign different labels to different ranges of pictures.
For instance: When one has finished selecting the first label one could indicate it by a Double-click and then do the selection of the second label range, then Double-click, then do the selection of the third label range, then Double-click, then do the selection of the fourth label range, etc.
It does not have to be Double-clicking to change the selection of the labels, it could also just be a buttom or any other idea that you might have.
In the end one should have the list of labels.
Essentially, most of the interaction you are looking for boils down to being able to display images, and detect clicks on them in real time. As that is the case, you can use the jupyter widgets (aka ipywidgets) module to achieve most (if not all) of what you are looking for.
Take a look at the button widget which is described here with explanation on how to register to its click event. The problem - we can't display an image on a button, and I didn't find any way to do this within the ipywidgets documentation. There is an image widget, but it does not provide an on_click event. So construct a custom layout, with a button underneath each image:
COLS = 4
ROWS = 2
IMAGES = ...
IMG_WIDTH = 200
IMG_HEIGHT = 200
def on_click(index):
print('Image %d clicked' % index)
import ipywidgets as widgets
import functools
rows = []
for row in range(ROWS):
cols = []
for col in range(COLS):
index = row * COLS + col
image = widgets.Image(
value=IMAGES[index], width=IMG_WIDTH, height=IMG_HEIGHT
)
button = widgets.Button(description='Image %d' % index)
# Bind the click event to the on_click function, with our index as argument
button.on_click(functools.partial(on_click, index))
# Create a vertical layout box, image above the button
box = widgets.VBox([image, button])
cols.append(box)
# Create a horizontal layout box, grouping all the columns together
rows.append(widgets.HBox(cols))
# Create a vertical layout box, grouping all the rows together
result = widgets.VBox(rows)
You can technically also write a custom widget to display an image and listen for a click, but I simply don't believe it's worth your time and effort.
Good luck!
The qsl package provides widgets that do this. For your case, the following code would allow you to label images in batches. Full disclosure, qsl is a project I started because I, like you, wanted to label images from inside Jupyter notebooks.
import qsl
from IPython.display import display
labeler = qsl.MediaLabeler(
items=[
{"target": "https://i.stack.imgur.com/cML6z.jpg"},
{"target": "https://i.stack.imgur.com/6EVAP.jpg"},
{"target": "https://i.stack.imgur.com/CAxUw.jpg"},
{"target": "https://i.stack.imgur.com/8fhan.jpg"},
{"target": "https://i.stack.imgur.com/eMXn5.jpg"},
{"target": "https://i.stack.imgur.com/YFBfM.jpg"}
],
# Optional, you can also configure the labeler from
# the UI.
config={
"image": [
{
"name": "Type",
"options": [
{"name": "Foo"},
{"name": "Bar"}
]
}
]
},
# Optional, set to 1 if you want to label
# one image at a time.
batch_size=4,
# Optionally, save labels to JSON. You
# can also get the labels using `labeler.items`.
jsonpath="labels.json"
)
display(labeler)
This generates a UI that looks like this.
Here is a Google Colab notebook that shows how to do this in Google Colab.

How to specify dates on the x-axis to make a bar graph using Python 3

I want to make a bar graph just like the one below. How do I specify the dates on the x-axis using Python 3.5?
So I tried to replicate the axis values as much as possible in the below example.
Some properties you may need to know when setting the x-axis are:
type ( enumerated : "-" | "linear" | "log" | "date" | "category" )
default: "-" Sets the axis type. By default, plotly attempts to determined the axis type by looking into the data of the traces
that referenced the axis in question.
nticks (integer greater than or equal to 0)
default: 0 Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen
automatically to be less than or equal to nticks. Has an effect only
if tickmode is set to "auto".
tick0 (number or categorical coordinate string) Sets the placement of the first tick on this axis. Use with dtick. If the
axis type is "log", then you must take the log of your starting tick
(e.g. to set the starting tick to 100, set the tick0 to 2) except
when dtick="L" (see dtick for more info). If the axis type is
"date", it should be a date string, like date data. If the axis type
is "category", it should be a number, using the scale where each
category is assigned a serial number from zero in the order it
appears.
tickformat (string)
default: "" Sets the tick label formatting rule using d3 formatting mini-languages which are very similar to those in Python.
For numbers, see:
https://github.com/d3/d3-format/blob/master/README.md#locale_format
And for dates see:
https://github.com/d3/d3-time-format/blob/master/README.md#locale_format
We add one item to d3's date formatter: "%{n}f" for fractional seconds
with n digits. For example, "2016-10-13 09:15:23.456" with tickformat
"%H~%M~%S.%2f" would display "09~15~23.46"
tickangle (angle)
default: auto Sets the angle of the tick labels with respect to the horizontal. For example, a tickangle of -90 draws the tick
labels vertically.
So these properties are set to the below values.
type='date',
nticks=40,
tick0=86400000.0,
tickformat="%Y%m%d",
tickangle=-45,
Please checkout the below example and let me know if your issue is resolved!
import pandas as pd
import numpy as np
import plotly.offline as py_offline
import plotly.graph_objs as go
py_offline.init_notebook_mode()
df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv")
trace_high = go.Bar(
x=df.Date,
y=df['AAPL.High'],
name = "AAPL High",
opacity = 0.8)
data = [trace_high]
layout = dict(
title = "Manually Set Date Range",
xaxis = dict(
type='date',
nticks=40,
tick0=86400000.0,
tickformat="%Y%m%d",
tickangle=-45,
range = ['2016-07-01','2016-07-31'])
)
fig = dict(data=data, layout=layout)
py_offline.iplot(fig, filename = "Manually Set Range")
If your data are in a dataframe accompanied with a date column ('dat') you first need to make that column a date object and then make it an index.
df['dat'] = pd.to_datetime(df.['dat'])
df.index = pd['dat']
df['column name'].plot()
you can also sort your values before plotting
df.sort_value('dat')

Resources