简体   繁体   中英

Customize tooltip in Altair chart

I'm using Google Colab for learning Python. In this Google Colab notebook I've built for demostration , I was able to get some JSON data and visualize it with Altair .

This is the chart image I got so far - you can interact with the data in the linked Google Colab notebook :

到目前为止的图表结果:

Below is the explanation of the data I have and what I want to show:

In Yu-Gi-Oh! TCG, cards came from a certain card set. In each card set are Monster, Spell and Trap cards - as well another type of cards like: Link Monsters, XYZ monsters, etc.

A normal JSON structure of a monster card ( - for this purpose - has ATK and DEF ) has the following JSON structure:

{
   "id":89631139,
   "name":"Blue-Eyes White Dragon",
   "type":"Normal Monster",
   "atk":3000,
   "def":2500,
   "level":8,
   "race":"Dragon",
   "attribute":"LIGHT",
   "archetype":"Blue-Eyes"
}

But, a non-monster card ( ie a spell card ) has no values neither in ATK and DEF attributes:

Example:

{
   "id":35261759,
   "name":"Pot of Desires",
   "type":"Spell Card",
   "race":"Normal",
   "archetype":"Greed"
}

I want to visualize in the chart each card that comes in the card set and - while the cursor is on a point ( hover ), it will show the details of the card like, ( Name, ATK, DEF (if any) and the Type of card ).

Example of desired tooltip:

所需的工具提示

After some trial and error, research and read the documentation, I'm facing the following issues and wondering if the results I'm looking are possible:

  • When you hover on a point that does not have ATK and DEF - ( see image 1 ) = how to modify the settings of Altair chart for condition the build of the tooltip? - see image 1:

Image 1 :

图 1

In "image 1", the card called "Pot of Desires" is a Spell Card = it doesn't have ATK or DEF, I've configured the chart with the invalid property for shown Nulll / None / NaN values) and Altair sets 0 as default to those missing values .

The expected output in ( for a non-monster card ) would be:

Name: Pot of Desires

Type: Spell Card

The expected output in ( for a monster card ) would be:

Name: Blue-Eyes White Dragon

ATK: 3000 - DEF: 2500

Type: Normal Monster

The expected output in ( for a link monster card = has ATK , but no DEF ) would be:

Name: Proxy Dragon

ATK: 3000

Type: Link Monster

I want to condition how to build the tooltip in these scenarios, it is possible?

I don't think this is possible, see https://github.com/vega/vega-tooltip/issues/251 . In summary it seems like Vega and VegaTooltip does remove fields with Undefined form the tooltip, but VegaLite forces these to be shown in the tooltip (compare this VegaLite spec with this Vega one ). It seems like they are looking for help to implement the feature you want.

Here is an example with your data that illustrates how both null and Undefined values show up in the tooltip:

df = pd.DataFrame([{
   "id":89631139,
   "name":"Blue-Eyes White Dragon",
   "type":"Normal Monster",
   "atk":3000,
   "def":2500,
   "level":8,
   "race":"Dragon",
   "attribute":"LIGHT",
   "archetype":"Blue-Eyes"
},
{
   "id":35261759,
   "name":"Pot of Desires",
   "type":"Spell Card",
   "race":"Normal",
   "archetype":"Greed"
}])

alt.Chart(df).mark_point(tooltip={'content': 'data'}).encode(
    x='name',
    y='type'
).transform_calculate(
   A = "isValid(datum.atk) ? datum.atk : 'Undefined'",
   D = "isValid(datum.def) ? datum.def : 'Undefined'"
)

Thanks to @joelostblom's answer , my case scenario is not possible - yet - let's hope this scenario could be more common in order to raise more attention of the pull request/feature I've created - https://github.com/vega/vega-lite/issues/7811 .

Meanwhile, the accepted answer is: is not possible , but, I'll checking for new answers and possible solutions.


That being said, I decide to check further and share my findings here:

  • Using Vega Expressions , I tried to customize the tooltip data and I got pretty good results, IMHO, but, not as close as I intended.

Here are the results I mentioned:

使用 transform.calculate 的结果

The Python code of the previous chart is as follows:

# V.2 of the chart/graph:
alt.Chart(df).mark_point(size=200, filled=True, invalid=None, tooltip={'content': 'data'}).encode(
          x={"field": "def", "type": "quantitative", "title": "DEF"}, 
          y={"field": "atk", "type": "quantitative", "title": "ATK"}, 
          color={"field": "type", "type": "nominal", "title": "Types of cards"}, 
          shape="type"
      ).properties(
          title={
              "text": ["Cardset: " + cardSetName], 
              "subtitle": ["Here is shown the " + str(len(df.index)) + " card" + ('s' if len(df.index) > 1 or str(len(df.index)) == 0 else '') + " contained in the cardset."]
              }
          ).transform_calculate(
          Name = "datum.name",
          Type = "datum.race + ' ' + ((substring(datum.type, datum.type.length-1, datum.type.length) == 's') ? substring(datum.type, 2, datum.type.length-1) : substring(datum.type, 2, datum.type.length))",
          ATK_DEF = "isValid(datum.atk) ? '' + datum.atk + (isValid(datum.def) ? '/' + datum.def : '') : 'N/A'"
          ).interactive()
  • The rest of the data is added thanks to tooltip={'content': 'data'} configuration. If there would be a way to declare the fields to use only in the tooltip , this scenario would be possible with Vega/Altair.

Here, I share, the code you can copy/paste in the Vega Editor for try it yourself.

NB This is the direct vega-editor link with the sample data .

VEGA-LITE Code:

{
  "config": {"view": {"continuousWidth": 400, "continuousHeight": 300}},
  "data": {"name": "data-5583486ec9c6448394a7b9390873045c"},
  "mark": {
    "type": "point",
    "filled": true,
    "invalid": null,
    "size": 200,
    "tooltip": {"content": "data"}
  },
  "encoding": {
    "color": {"type": "nominal", "field": "type", "title": "Types of cards"},
    "shape": {"type": "nominal", "field": "type"},
    "x": {"type": "quantitative", "field": "def", "title": "DEF"},
    "y": {"type": "quantitative", "field": "atk", "title": "ATK"}
  },
  "selection": {
    "selector149": {
      "type": "interval",
      "bind": "scales",
      "encodings": ["x", "y"]
    }
  },
  "title": {
    "text": ["Cardset: 2017 Mega-Tins"],
    "subtitle": ["Here is shown the 8 cards contained in the cardset."]
  },
  "transform": [
    {"calculate": "datum.name", "as": "Name"},
    {
      "calculate": "datum.race + ' ' + ((substring(datum.type, datum.type.length-1, datum.type.length) == 's') ? substring(datum.type, 2, datum.type.length-1) : substring(datum.type, 2, datum.type.length))",
      "as": "Type"
    },
    {
      "calculate": "isValid(datum.atk) ? '' + datum.atk + (isValid(datum.def) ? '/' + datum.def : '') : 'N/A'",
      "as": "ATK_DEF"
    }
  ],
  "$schema": "https://vega.github.io/schema/vega-lite/v4.8.1.json",
  "datasets": {
    "data-5583486ec9c6448394a7b9390873045c": [
      {
        "id": 89631139,
        "name": "Blue-Eyes White Dragon",
        "type": "2 Normal Monsters",
        "desc": "This legendary dragon is a powerful engine of destruction. Virtually invincible, very few have faced this awesome creature and lived to tell the tale.",
        "atk": 3000,
        "def": 2500,
        "level": 8,
        "race": "Dragon",
        "attribute": "LIGHT",
        "archetype": "Blue-Eyes"
      },
      {
        "id": 46986414,
        "name": "Dark Magician",
        "type": "2 Normal Monsters",
        "desc": "The ultimate wizard in terms of attack and defense.",
        "atk": 2500,
        "def": 2100,
        "level": 7,
        "race": "Spellcaster",
        "attribute": "DARK",
        "archetype": "Dark Magician"
      },
      {
        "id": 26920296,
        "name": "Dreamland",
        "type": "2 Spell Cards",
        "desc": "This card can activate these effects depending on the monster card types on the field.\n● Fusion: Once per turn, if a monster(s) is sent from your hand or field to the GY by a card effect (except during the Damage Step): You can draw 1 card.\n● Synchro: When a monster(s) is Normal or Special Summoned (except during the Damage Step): You can increase their Levels by 1.\n● Xyz: Once per turn, during your End Phase: Destroy the monster(s) on the field with the highest Level.\nYou can only activate 1 \"Dreamland\" per turn.",
        "atk": null,
        "def": null,
        "level": null,
        "race": "Field",
        "attribute": null,
        "archetype": null
      },
      {
        "id": 80532587,
        "name": "Elder Entity N'tss",
        "type": "1 Fusion Monster",
        "desc": "1 Synchro Monster + 1 Xyz Monster\nMust be Special Summoned (from your Extra Deck) by sending the above cards you control to the GY. (You do not use \"Polymerization\".) Once per turn: You can Special Summon 1 Level 4 monster from your hand. If this card is sent to the GY: You can target 1 card on the field; destroy it. You can only Special Summon \"Elder Entity N'tss(s)\" once per turn.",
        "atk": 2500,
        "def": 1200,
        "level": 4,
        "race": "Fairy",
        "attribute": "LIGHT",
        "archetype": null
      },
      {
        "id": 23085002,
        "name": "Number 68: Sanaphond the Sky Prison",
        "type": "2 XYZ Monsters",
        "desc": "2 Level 8 monsters\nGains 100 ATK and DEF for each monster in the GYs. Once per turn: You can detach 1 material from this card; until the end of your opponent's next turn, this card cannot be destroyed by card effects, also neither player can Special Summon monsters from the GYs.",
        "atk": 2100,
        "def": 2700,
        "level": 8,
        "race": "Rock",
        "attribute": "DARK",
        "archetype": null
      },
      {
        "id": 59479050,
        "name": "Number 71: Rebarian Shark",
        "type": "2 XYZ Monsters",
        "desc": "2 Level 3 monsters\nOnce per turn, if this card has material: You can target 1 \"Number\" Xyz Monster in your GY, except \"Number 71: Rebarian Shark\"; Special Summon it, and if you do, attach 1 material from this card to it. If this card is sent to the GY: You can choose 1 \"Rank-Up-Magic\" Spell from your Deck and place it on top of your Deck.",
        "atk": 0,
        "def": 2000,
        "level": 3,
        "race": "Dragon",
        "attribute": "WATER",
        "archetype": null
      },
      {
        "id": 35261759,
        "name": "Pot of Desires",
        "type": "2 Spell Cards",
        "desc": "Banish 10 cards from the top of your Deck, face-down; draw 2 cards. You can only activate 1 \"Pot of Desires\" per turn.",
        "atk": null,
        "def": null,
        "level": null,
        "race": "Normal",
        "attribute": null,
        "archetype": "Greed"
      },
      {
        "id": 22862454,
        "name": "Proxy Dragon",
        "type": "1 Link Monster",
        "desc": "2 monsters\r\nIf a card(s) you control would be destroyed by battle or card effect, you can destroy 1 of your monsters this card points to, instead.",
        "atk": 1400,
        "def": null,
        "level": null,
        "race": "Cyberse",
        "attribute": "LIGHT",
        "archetype": null
      }
    ]
  }
}

CONFIG:

{}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM