InformationPane

Sometimes there is complicated data associated with a point in a data map, and a short bit of text suitable for a tooltip hover just isn’t going to suffice. The InformationPane offers a means to pack complex information, including formatted text, images and links, coming from multiple different fields of a dataframe, into a single view, allowing a richer representation of data associated to a selected point in a data map. We will outline the core functionality of the InformationPane, how to link it to a plot, and some of the optional customization available for an InformationPane.

The first step is to load thisnotthat and panel, as well asd pandas (for dataframes).

[1]:
import thisnotthat as tnt
import panel as pn
import pandas as pd

To make Panel based objects interactive within a notebook we need to load the panel extension.

[2]:
pn.extension()

Now to demonstrate the InformationPane we will need some data that has interesting associated information. To keep things simple we will make a very simple dataframe about animals, with title information, an associated image, and some body text (the latter two taken from Wikipedia).

[3]:
data = pd.DataFrame({
    "title": ["Penguin", "Snake", "Octopus", "Echidna"],
    "image": [
        "https://upload.wikimedia.org/wikipedia/commons/thumb/0/07/Emperor_Penguin_Manchot_empereur.jpg/222px-Emperor_Penguin_Manchot_empereur.jpg",
        "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a6/Emerald_tree_boa444.jpg/320px-Emerald_tree_boa444.jpg",
        "https://upload.wikimedia.org/wikipedia/commons/thumb/5/57/Octopus2.jpg/315px-Octopus2.jpg",
        "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e9/Short-beaked_echidna_in_ANBG.jpg/320px-Short-beaked_echidna_in_ANBG.jpg"
    ],
    "text_body": [
        "Penguins (order Sphenisciformes /sfɪˈnɪsɪfɔːrmiːz/, family Spheniscidae /sfɪˈnɪsɪdiː/) are a group of aquatic flightless birds. They live almost exclusively in the Southern Hemisphere: only one species, the Galápagos penguin, is found north of the Equator. Highly adapted for life in the water, penguins have countershaded dark and white plumage and flippers for swimming. Most penguins feed on krill, fish, squid and other forms of sea life which they catch with their bills and swallow it whole while swimming. A penguin has a spiny tongue and powerful jaws to grip slippery prey.",
        "Snakes are elongated, limbless, carnivorous reptiles of the suborder Serpentes /sɜːrˈpɛntiːz/.[2] Like all other squamates, snakes are ectothermic, amniote vertebrates covered in overlapping scales. Many species of snakes have skulls with several more joints than their lizard ancestors, enabling them to swallow prey much larger than their heads (cranial kinesis). To accommodate their narrow bodies, snakes' paired organs (such as kidneys) appear one in front of the other instead of side by side, and most have only one functional lung. Some species retain a pelvic girdle with a pair of vestigial claws on either side of the cloaca. Lizards have evolved elongate bodies without limbs or with greatly reduced limbs about twenty-five times independently via convergent evolution, leading to many lineages of legless lizards.[3] These resemble snakes, but several common groups of legless lizards have eyelids and external ears, which snakes lack, although this rule is not universal (see Amphisbaenia, Dibamidae, and Pygopodidae).",
        "An octopus (pl: octopuses or octopodes, see below for variants) is a soft-bodied, eight-limbed mollusc of the order Octopoda (/ɒkˈtɒpədə/, ok-TOP-ə-də[3]). The order consists of some 300 species and is grouped within the class Cephalopoda with squids, cuttlefish, and nautiloids. Like other cephalopods, an octopus is bilaterally symmetric with two eyes and a beaked mouth at the center point of the eight limbs.[a] The soft body can radically alter its shape, enabling octopuses to squeeze through small gaps. They trail their eight appendages behind them as they swim. The siphon is used both for respiration and for locomotion, by expelling a jet of water. Octopuses have a complex nervous system and excellent sight, and are among the most intelligent and behaviourally diverse of all invertebrates.",
        "Echidnas (/ɪˈkɪdnəz/), sometimes known as spiny anteaters,[1] are quill-covered[2] monotremes (egg-laying mammals) belonging to the family Tachyglossidae /tækiˈɡlɒsɪdiː/. The four extant species of echidnas and the platypus are the only living mammals that lay eggs and the only surviving members of the order Monotremata.[3] The diet of some species consists of ants and termites, but they are not closely related to the true anteaters of the Americas, which (along with sloths and armadillos) are xenarthrans. Echidnas live in Australia and New Guinea.",
    ]
})
data
[3]:
title image text_body
0 Penguin https://upload.wikimedia.org/wikipedia/commons... Penguins (order Sphenisciformes /sfɪˈnɪsɪfɔːrm...
1 Snake https://upload.wikimedia.org/wikipedia/commons... Snakes are elongated, limbless, carnivorous re...
2 Octopus https://upload.wikimedia.org/wikipedia/commons... An octopus (pl: octopuses or octopodes, see be...
3 Echidna https://upload.wikimedia.org/wikipedia/commons... Echidnas (/ɪˈkɪdnəz/), sometimes known as spin...

The InformationPane needs a (markdown) template to use to generate the formatted information. Here is a simple example, giving some idea of what can be done. Within the template we can use curly braces around a column name of the dataframe to denote that the contents of that field of a given record should be substituted in. Since we have markdown at our disposal we can make titles, show images, and style text as we wish, including with breaks. Note that we are not using a full-powered templating engine (such as Jinja) at this time, so things should be relatively simple. None the less this provides a significant amount of flexibility in presenting complex information.

[4]:
template = """
# {title}

---

![Penguin image]({image})

---

**All about the {title}:**

{text_body}

---

(An example of using a template that can contain your own text and formatting as well)
"""

Caution!

Remark how the Markdown text is all flush with the leftmost column of the text editor. This is because the Markdown format is indentation-aware. Markdown header

|template = """\
|# A header!
|"""

is different from

|    template = """\
|    # Not a header
|    """

which is rather understood as verbatim code. Indeed, the string in the second example starts with 4 spaces: indentation within a string body is part of the string. So the rule of thumb is to always layout the Markdown template within a triple-quoted string flush with the editor, regardless of code indentation (within class, function or any other block). One can also use the ``dedent`` function from thetextwrapmodule of Python’s Standard Library.


Now we can create our InformationPane. We need to pass it a dataframe of data, and the markdown template to use for formatting records from that dataframe. When initially displayed the InformationPane doesn’t show anythign interesting – just placeholder text noting that nothing is selected.

[5]:
info = tnt.InformationPane(
    raw_dataframe=data,
    markdown_template=template,
    width=300,
    height=500,
)
info
[5]:

The primary Param of the InformationPane is the selected attribute. Initially it is an empty list, in which case the placeholder text is displayed. However the value of the attribute is dynamic, and can be changed. If used in an interactive notebook session, then setting the selected attribute to a list of numeric indices will result in the last item of the list being used to determine which record to format in the InformationPane. Obviously the InformationPane can only handle one selected item at a time – the requirement of using a list is to ensure the selected Param integrates with all the other methods of generating selections (from a PlotPane, SearchWidget, or DataPane for example) which can select multiple items. By using the last item on the list the InformationPane uses the most recently selected record.

We can set selected and have the InformationPane update accordingly, showing us information about Echidnas.

[6]:
info.selected = [3]

This really comes into its own when linked with a data map. FOr our data map here we will simply use a diamond layout of items, and put those in a BokehPlotPane. By setting the tools to use tap instead of lasso we ensure that single items are selected at a time by clicking on them (which is more in line with how we expect the InformationPane to be used).

[7]:
data_map = [
    [1, 0],
    [0, 1],
    [-1, 0],
    [0, -1],
]
plot = tnt.BokehPlotPane(
    data_map,
    labels=["penguin", "snake", "octopus", "echidna"],
    tools="pan,wheel_zoom,tap",
    show_legend=False,
    width=500,
    height=500,
)

A quick visual check shows that our PlotPane data map looks like the sort of thing we want.

[8]:
plot.pane
[8]:

Let’s leave our original InformationPane looking at an Echidna, and create a new pane that we will link with the plot. We can use the link method and individually specify that we want to link the selected Params from the PlotPane and the InformationPane, but the simpler approach is to use the link_to_plot method which requires us to simply specify the plot to be linked to. With this done we can create a simple Row layout of the PlotPane and our InformationPane.

[9]:
info2 = tnt.InformationPane(
    raw_dataframe=data,
    markdown_template=template,
    width=300,
    height=500,
)
info2.link_to_plot(plot);
pn.Row(plot, info2)
[9]:

Now we can click on points in the data map, and have the InformationPane update to provide the formatted information for that point. Clicking on empty space will nullify the selection and set the InformationPane back to the placeholder text.

The InformationPane provides a number of ways to customise the display, including the placeholder text, the markdown extensions used, and css-style for the markdown pane. An example of using some of these is shown below.

[10]:
test_info = tnt.InformationPane(
    raw_dataframe=data,
    markdown_template=template,
    placeholder_text="Text When Nothing is Selected",
    dedent=True,
    extensions=["admonition", "codehilite"],
    style={"border": "2px solid red", "background": "salmon", "color": "darkred"}
)
test_info
[10]: