Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
341 views
in Technique[技术] by (71.8m points)

vega lite - Select Visible Data

From this GitHub issue:

I solved this getting the start date that is showing in the visualization and the end date, then i calculated the days between that dates. Depending of the amount of days between, i change the time format for the axis label.

This is a lot of useful for me because i'm zoom-in and zoom-out through this chart and when i zoom-in i need to know more info about the date and when i zoom-out i need to know less info about the date.

I hope that this will be useful for other people.

How can this be done? Specifically, I don't know how to get the start date and end date from an interactive plot.

question from:https://stackoverflow.com/questions/65866212/select-visible-data

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Doing this from JupyterLab can be a little tricky, working around some issues, but here is the solution in several cells:

First the example data:

import pandas as pd
import numpy as np

c1 = np.random.randint(1,6, size=15)
c2 = pd.date_range(start="2021-01-01",end="2021-01-15")
df = pd.DataFrame({"day": c2, "value": c1})
df = df.drop([2, 5,6,7,13])
df

Then Jinja for JupyterLab so you can substitute the Vega-Lite schema into Vega-Embed via Python.

import jinja2
import IPython
import IPython.core.magic as ipymagic

@ipymagic.magics_class
class JinjaMagics(ipymagic.Magics):

    @ipymagic.cell_magic
    def jinja(self, line, cell):
        t = jinja2.Template(cell)
        r = t.render({k:v for k,v in self.shell.user_ns.items()
                                  if k not in self.shell.user_ns_hidden})
        d = getattr(IPython.display, line.strip(), IPython.display.display)
        return d(r)

IPython.get_ipython().register_magics(JinjaMagics)

Then the example schema, a little more complicated to demonstrate some pitfalls.

import altair

s =
  { "mark": "bar"
  , "encoding":
    { "x":
      { "type": "temporal"
      , "bin": "binned"
      , "field": "start"
      , "axis":
        { "tickCount": "day"
          # For whatever reason a custom format function won't work
          # without a transform array.
        , "formatType": "interactiveFormatDate"
        , "format": "test"
        }
      }
    , "x2": {"field": "end"}
    , "y": {"type": "quantitative", "field": "value"}
    }
  , "selection":
    { "interactive":
      { "type": "interval"
      , "bind": "scales"
      , "encodings": ["x"]
      }
    }
  , "transform":
    [ # Convert 'day' from 'string' to timestamp ('number')
      {"calculate": "toDate(datum.day)", "as": "day"}
      # Provide "start" and "end" as Date objects to match the
      # type of temporal domain objects
    , {"calculate": "timeOffset('hours', datum.day, -12)", "as": "start"}
    , {"calculate": "timeOffset('hours', datum.day, 12)", "as": "end"}
    ]
  , "height": 250
  , "width": "container"
  , "$schema": "https://vega.github.io/schema/vega-lite/v4.json"
  , "config": {"customFormatTypes": "True"}
  }

s["data"] = altair.utils.data.to_values(df)
# While a data array can be named, transform array results can't.
# Look at the Vega source to get the resulting name.
s["data"]["name"] = "exp"
s

Some imports for using Vega-Embed.

# Tag Vega-Embed div's with UUIDs ensuring the correct div is targeted.
import uuid
import json

And finally, Vega-Embed. In all fairness, I'm not sure if this is fetching the scripts or using those packaged with JupyterLab.

%%jinja HTML
{% set visid = uuid.uuid4() %}
<html>
<head>
    <style>.vega-embed.has-actions {width:90%}</style>
    <!-- Import Vega & Vega-Lite (does not have to be from CDN) -->
    <script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
    <script src="https://cdn.jsdelivr.net/npm/vega-lite@4"></script>
    <!-- Import vega-embed -->
    <script src="https://cdn.jsdelivr.net/npm/vega-embed@6"></script>
</head>
<body>

<div id="vis-{{visid}}"></div>

<script type="text/javascript">
    vega.expressionFunction('interactiveFormatDate', function(datum, params) {
        // Data points come from both the data fields backing the current channel, 
        // and from the axis domain. Temporal domain axis points are 'Date' objects
        // and are a subset of the visible domain. Data field points are always
        // included even when offscreen and are of whatever type provided to vega.
        var view = this.context.dataflow;
        // These are hacks because you have to inspect the generated vega to
        // determine the dataset names.
        var selection = view.data("interactive_store");
        var data = view.data("data_0");
        // Unless the view has been shifted the selection will be empty
        if (selection.length == 0){
            var d1 = data[0]["start"];
            var d2 = data[data.length-1]["start"];
        }
        else if (selection.length != 0){
            var d1 = selection[0]["values"][0][0];
            var d2 = selection[0]["values"][0][1];
        }
        var dd = (d2 - d1)/1000/60/60/24;
        var tf = vega.timeFormatLocale().timeFormat;
        var tl = ["%d %b", "%Y"];
        return tl.map(x => tf(x)(datum));
    });
    var spec = {{ json.dumps(s)}}
    vegaEmbed('#vis-{{visid}}', spec).then(function(result) {
        // Access the Vega view instance
        // (https://vega.github.io/vega/docs/api/view/) as result.view
    }).catch(console.error);
</script>
</body>
</html>

The most annoying aspect is the lack of proper syntax highlighting for the HTML/JavaScript cells.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...