1

In this VegaLite spec the y-axis order of the bottom-most barplot is updated as the data of that plot is filtered based on the selection in the scatter plot. How can I achieve the same resorting behavior for both the blue and orange bars in the top-most bar plot where I have layered the same barplot together with another chart?

I have tried toggling the axis between shared and independent and switching the order of the layer, but that didn't do it. Conceptually I can imagine using a calculate transform to define a new field that is based on the selection and used as the sort order key, but I can't figure out how to write this vega expression string.

enter image description here

Here is that Altair code if anyone prefers to solve it that way:

import altair as alt
import pandas as pd


data={
 'Term': ['algorithm','learning','learning','algorithm','algorithm','learning'],
 'Freq_x': [1330,1153,504.42,296.69,177.59,140.35],
 'Total': [1330, 1353,1353.7,1330.47,1330.47,1353.7],
 'Category': ['Default', 'Default', 'Topic1', 'Topic1', 'Topic2', 'Topic2'],
 'logprob': [30.0, 27.0, -5.116, -5.1418, -5.4112, -5.5271],
 'loglift': [30.0, 27.0, 0.0975, 0.0891, -0.1803, -0.3135],
 'saliency_ind': [0, 3, 76, 77, 181, 186],
 'x': [None,None,-0.0080,-0.0080,-0.0053,-0.0053],
 'y': [None,None,-0.0056,-0.0056, 0.0003,0.0003],
 'topics': [None,None, 1.0, 1.0, 2.0, 2.0],
 'cluster': [None,None, 1.0, 1.0, 1.0, 1.0],
 'Freq_y': [None,None,20.39,20.39,14.18,14.18]}

df=pd.DataFrame(data)

pts = alt.selection(type="single", fields=['Category'], empty='none')

points = alt.Chart(df).mark_circle().encode(
    x='mean(x)',
    y='mean(y)',
    size='Freq_y',
    detail='Category',
    color=alt.condition(pts, alt.value('#F28E2B'), alt.value('#4E79A7'))
).add_selection(pts)

bars = alt.Chart(df).mark_bar().encode(
    x='Freq_x',
    y=alt.Y('Term'),
)

bars2 = alt.Chart(df).mark_bar(color='#F28E2B').encode(
    x='Freq_x',
    y=alt.Y('Term', sort='-x'),
).transform_filter(
    pts
)

(points | (bars + bars2) & bars2)
joelostblom
  • 43,590
  • 17
  • 150
  • 159

1 Answers1

1

The issue with your spec was that in layers you performed filter transform which created a separate data for each layers. Sorting was working at each level of layer but since both the layers data were separate so each layer was getting sorted inpendently.

So instead of having a filter transform, I tried to manual filter using calculate transform and created a filtered_freq_x field which is later used on 2nd layer and performed sorting using this on window. So with this my data becomes same for both layers, just few fields were added and used.

Let me know if this works for you not. Below is the spec config and editor:

{
  "config": {"view": {"continuousWidth": 200, "continuousHeight": 300}},
  "hconcat": [
    {
      "mark": "circle",
      "encoding": {
        "color": {
          "condition": {"value": "#F28E2B", "selection": "selector046"},
          "value": "#4E79A7"
        },
        "detail": {"type": "nominal", "field": "Category"},
        "size": {"type": "quantitative", "field": "Freq_y"},
        "x": {"type": "quantitative", "aggregate": "mean", "field": "x"},
        "y": {"type": "quantitative", "aggregate": "mean", "field": "y"}
      },
      "selection": {
        "selector046": {
          "type": "single",
          "fields": ["Category"],
          "empty": "none"
        }
      }
    },
    {
      "vconcat": [
        {
          "transform": [
            {
              "joinaggregate": [
                {"field": "Freq_x", "op": "max", "as": "max_Fx"}
              ]
            },
            {
              "calculate": "selector046['Category'] ? selector046['Category'] : []",
              "as": "filterCategory"
            },
            {
              "calculate": "indexof(datum.filterCategory,datum['Category']) > -1 ? datum['Freq_x'] : null",
              "as": "filtered_Freq_x"
            },
            {
              "sort": [{"field": "filtered_Freq_x", "order": "descending"}],
              "window": [{"op": "rank", "as": "Sorted"}]
            }
          ],
          "height": 50,
          "layer": [
            {
              "mark": "bar",
              "encoding": {
                "x": {"type": "quantitative", "field": "Freq_x"},
                "y": {
                  "type": "nominal",
                  "field": "Term",
                  "sort": {"field": "Sorted"}
                }
              }
            },
            {
              "mark": {"type": "bar", "color": "#F28E2B"},
              "encoding": {
                "x": {"type": "quantitative", "field": "filtered_Freq_x"},
                "y": {
                  "type": "nominal",
                  "field": "Term",
                  "sort": {"field": "Sorted"}
                }
              }
            }
          ]
        },
        {
          "mark": {"type": "bar", "color": "#F28E2B"},
          "encoding": {
            "x": {"type": "quantitative", "field": "Freq_x"},
            "y": {"type": "nominal", "field": "Term", "sort": "-x"}
          },
          "transform": [{"filter": {"selection": "selector046"}}]
        }
      ]
    }
  ],
  "data": {"name": "data-d807cd22b94d04d6f1543201cfe5f45e"},
  "$schema": "https://vega.github.io/schema/vega-lite/v4.8.1.json",
  "datasets": {
    "data-d807cd22b94d04d6f1543201cfe5f45e": [
      {
        "Term": "algorithm",
        "Freq_x": 1330,
        "Total": 1330,
        "Category": "Default",
        "logprob": 30,
        "loglift": 30,
        "saliency_ind": 0,
        "x": null,
        "y": null,
        "topics": null,
        "cluster": null,
        "Freq_y": null
      },
      {
        "Term": "learning",
        "Freq_x": 1153,
        "Total": 1353,
        "Category": "Default",
        "logprob": 27,
        "loglift": 27,
        "saliency_ind": 3,
        "x": null,
        "y": null,
        "topics": null,
        "cluster": null,
        "Freq_y": null
      },
      {
        "Term": "learning",
        "Freq_x": 504.42,
        "Total": 1353.7,
        "Category": "Topic1",
        "logprob": -5.116,
        "loglift": 0.0975,
        "saliency_ind": 76,
        "x": -0.008,
        "y": -0.0056,
        "topics": 1,
        "cluster": 1,
        "Freq_y": 20.39
      },
      {
        "Term": "algorithm",
        "Freq_x": 296.69,
        "Total": 1330.47,
        "Category": "Topic1",
        "logprob": -5.1418,
        "loglift": 0.0891,
        "saliency_ind": 77,
        "x": -0.008,
        "y": -0.0056,
        "topics": 1,
        "cluster": 1,
        "Freq_y": 20.39
      },
      {
        "Term": "algorithm",
        "Freq_x": 177.59,
        "Total": 1330.47,
        "Category": "Topic2",
        "logprob": -5.4112,
        "loglift": -0.1803,
        "saliency_ind": 181,
        "x": -0.0053,
        "y": 0.0003,
        "topics": 2,
        "cluster": 1,
        "Freq_y": 14.18
      },
      {
        "Term": "learning",
        "Freq_x": 140.35,
        "Total": 1353.7,
        "Category": "Topic2",
        "logprob": -5.5271,
        "loglift": -0.3135,
        "saliency_ind": 186,
        "x": -0.0053,
        "y": 0.0003,
        "topics": 2,
        "cluster": 1,
        "Freq_y": 14.18
      }
    ]
  }
}
wahab memon
  • 2,193
  • 2
  • 10
  • 23
  • Thank you so much for this! So much to learn from here. I also posted this: https://stackoverflow.com/questions/67978915/vega-lite-to-altair-sorting-a-chart-based-on-the-dynamically-updated-axis as I would like to convert the spec to Altair if possible. – campo Jun 15 '21 at 01:33
  • @campo I have not worked with altair so I won't be able to answer that. – wahab memon Jun 15 '21 at 04:20