altair 中的多项选择

Multiple selections in altair

我想要两个交互式图例,类似于中的第二个例子 this section Altair 文档,但没有矩形图例。 一个图例应通过一个分类变量 ("First name") 控制颜色,而第二个图例应通过另一个分类变量 ("Last name") 控制标记。虽然我能够获得正确的美学效果(见下图),但第二个图例的交互性会导致意外行为。

我生成上图的代码如下:

import pandas as pd
import altair as alt

# Create dataframe
data = [[7, 10, 'Alex', 'Smith'],
        [12, 20, 'Bob', 'Jones'],
        [10, 30, 'Clive', 'Smith'],
        [42, 40,  'Alex', 'Johnson']]
df = pd.DataFrame(data,columns=['Favorite number', 'Age', 'First name', 'Last name'])

# Create selections
selection_first_name = alt.selection_multi(fields=['First name'])
selection_last_name = alt.selection_multi(fields=['Last name'])

# Create first name conditions
color_first_name = alt.condition(selection_first_name,
                                 alt.Color('First name:N', legend=None),
                                 alt.value('lightgray'))

# Create last name conditions
shape_last_name = alt.condition(selection_last_name,
                                    alt.Shape('Last name:N', legend=None),
                                    alt.value('lightgray'))

# Create interactive scatter plot
scatter = alt.Chart(df).mark_point(size=100).encode(
    x='Favorite number:Q',
    y='Age:Q',
    color=color_first_name,
    shape=shape_last_name,
    tooltip=['First name', 'Last name']
).add_selection(
    selection_first_name
).add_selection(
    selection_last_name
).add_selection(
    alt.selection_interval(bind='scales')
)

# Create interactive model name legend
legend_first_name = alt.Chart(df).mark_point(size=100).encode(
    y=alt.Y('First name:N', axis=alt.Axis(orient='right')),
    color=color_first_name
).add_selection(
    selection_first_name
)

# Create interactive model name legend
legend_last_name = alt.Chart(df).mark_point(size=100).encode(
    y=alt.Y('Last name:N', axis=alt.Axis(orient='right')),
    shape=shape_last_name
).add_selection(
    selection_last_name
)

# Combine plotting elements
chart = scatter | legend_first_name | legend_last_name

生成的 HTML 图表在此处:

<!DOCTYPE html>
<html>
<head>
  <style>
    .vega-actions a {
        margin-right: 12px;
        color: #757575;
        font-weight: normal;
        font-size: 13px;
    }
    .error {
        color: red;
    }
  </style>
  <script type="text/javascript" src="https://cdn.jsdelivr.net/npm//vega@5"></script>
  <script type="text/javascript" src="https://cdn.jsdelivr.net/npm//vega-lite@3.4.0"></script>
  <script type="text/javascript" src="https://cdn.jsdelivr.net/npm//vega-embed@4"></script>
</head>
<body>
  <div id="vis"></div>
  <script>
    (function(vegaEmbed) {
      var spec = {"config": {"view": {"width": 400, "height": 300}, "mark": {"tooltip": null}}, "hconcat": [{"mark": {"type": "point", "size": 100}, "encoding": {"color": {"condition": {"type": "nominal", "field": "First name", "legend": null, "selection": "selector001"}, "value": "lightgray"}, "shape": {"condition": {"type": "nominal", "field": "Last name", "legend": null, "selection": "selector002"}, "value": "lightgray"}, "tooltip": [{"type": "nominal", "field": "First name"}, {"type": "nominal", "field": "Last name"}], "x": {"type": "quantitative", "field": "Favorite number"}, "y": {"type": "quantitative", "field": "Age"}}, "selection": {"selector001": {"type": "multi", "fields": ["First name"]}, "selector002": {"type": "multi", "fields": ["Last name"]}, "selector003": {"type": "interval", "bind": "scales"}}}, {"mark": {"type": "point", "size": 100}, "encoding": {"color": {"condition": {"type": "nominal", "field": "First name", "legend": null, "selection": "selector001"}, "value": "lightgray"}, "y": {"type": "nominal", "axis": {"orient": "right"}, "field": "First name"}}, "selection": {"selector001": {"type": "multi", "fields": ["First name"]}}}, {"mark": {"type": "point", "size": 100}, "encoding": {"shape": {"condition": {"type": "nominal", "field": "Last name", "legend": null, "selection": "selector002"}, "value": "lightgray"}, "y": {"type": "nominal", "axis": {"orient": "right"}, "field": "Last name"}}, "selection": {"selector002": {"type": "multi", "fields": ["Last name"]}}}], "data": {"name": "data-ec767a72044047bc9825da631c9465fc"}, "$schema": "https://vega.github.io/schema/vega-lite/v3.4.0.json", "datasets": {"data-ec767a72044047bc9825da631c9465fc": [{"Favorite number": 7, "Age": 10, "First name": "Alex", "Last name": "Smith"}, {"Favorite number": 12, "Age": 20, "First name": "Bob", "Last name": "Jones"}, {"Favorite number": 10, "Age": 30, "First name": "Clive", "Last name": "Smith"}, {"Favorite number": 42, "Age": 40, "First name": "Alex", "Last name": "Johnson"}]}};
      var embedOpt = {"mode": "vega-lite"};

      function showError(el, error){
          el.innerHTML = ('<div class="error" style="color:red;">'
                          + '<p>JavaScript Error: ' + error.message + '</p>'
                          + "<p>This usually means there's a typo in your chart specification. "
                          + "See the javascript console for the full traceback.</p>"
                          + '</div>');
          throw error;
      }
      const el = document.getElementById('vis');
      vegaEmbed("#vis", spec, embedOpt)
        .catch(error => showError(el, error));
    })(vegaEmbed);

  </script>
</body>
</html>

您的形状编码条件如下所示:

shape_last_name = alt.condition(selection_last_name,
                                    alt.Shape('Last name:N', legend=None),
                                    alt.value('lightgray'))

它指定当一个点未被选中时,它应该具有名为 lightgray 的形状。 lightgray 不是有效的形状,因此未绘制任何点。

如果你想根据形状选择改变颜色,你应该把条件放在颜色编码中。如果要根据形状图例选择更改形状,则应在条件内使用有效的形状值。

我怀疑你想要的是这样的:

import pandas as pd
import altair as alt

# Create dataframe
data = [[7, 10, 'Alex', 'Smith'],
        [12, 20, 'Bob', 'Jones'],
        [10, 30, 'Clive', 'Smith'],
        [42, 40,  'Alex', 'Johnson']]
df = pd.DataFrame(data,columns=['Favorite number', 'Age', 'First name', 'Last name'])

# Create selections
selection_first_name = alt.selection_multi(fields=['First name'], empty='none')
selection_last_name = alt.selection_multi(fields=['Last name'], empty='none')

# Create interactive scatter plot
scatter = alt.Chart(df).mark_point(size=100).encode(
    x='Favorite number:Q',
    y='Age:Q',
    color=alt.condition(selection_first_name & selection_last_name,
                        alt.Color('First name:N', legend=None),
                        alt.value('lightgray') ),
    shape=alt.Shape('Last name:N', legend=None),
    tooltip=['First name', 'Last name']
).add_selection(
    alt.selection_interval(bind='scales')
)

# Create interactive model name legend
legend_first_name = alt.Chart(df).mark_point(size=100).encode(
    y=alt.Y('First name:N', axis=alt.Axis(orient='right')),
    color=alt.condition(selection_first_name,
                        alt.Color('First name:N', legend=None),
                        alt.value('lightgray') ),
).add_selection(
    selection_first_name

)

# Create interactive model name legend
legend_last_name = alt.Chart(df).mark_point(size=100).encode(
    y=alt.Y('Last name:N', axis=alt.Axis(orient='right')),
    shape=alt.Shape('Last name:N', legend=None),
    color=alt.condition(selection_last_name,
                        alt.value('black'),
                        alt.value('lightgray') ),
).add_selection(
    selection_last_name
)

# Combine plotting elements
chart = scatter | legend_first_name | legend_last_name