You can use the vega expression function indexof(array, value),
together with accessing the values to highlight from the clicked selection via selection_name.column_name (this is click.highlight in my example below).
Unfortunately there are a few gotchas that makes it a bit difficult to figure out the exact syntax.
Most notably, it seems like the click selection sends its values as a list by default, but as "Undefined" if nothing is selected.
indexof does not work for an undefined value/empty selection (for either multi or single select),
and will return an empty plot with nothing to click
so we need to make sure that we provide one or multiple init value(s) as a list of dictionaries when creating the selection
(you could build this up programmatically as long as it follow the format in my example below).
indexof returns a -1 if no match is found, so we can use this in our comparison.
Altogether,
an example could look something like what I have below
where the outlined point(s) are the currently selected one(s)
and the ones with full opacity are the ones that are highlighted based on another column in the dataframe
(neighboring countries in your example).
import altair as alt
import pandas as pd
df = pd.DataFrame({
'x': [0.5, 1, 1.5],
'y': [0.5, 1, 1.5],
'country': ['Canada', 'Mexico', 'USA'],
'highlight': [['Canada'], ['USA'], ['USA', 'Canada']]
})
click = alt.selection_multi(
fields=['highlight', 'country'],
name='click',
empty='none',
init=[{'highlight': 'Canada'}, {'highlight': 'USA'}, {'country': 'USA'}]
)
alt.Chart(df).mark_circle(
size=200,
strokeWidth=3
).encode(
x='x',
y='y',
opacity=alt.condition('indexof(click.highlight, datum.country) != -1', alt.value(1), alt.value(0.4)),
stroke=alt.condition('indexof(click.country, datum.country) != -1', alt.value('#ff7f0e'), alt.value('#1f77b4')),
tooltip='country'
).add_selection(
click
)

To understand exactly what is going on,
we can construct a more intricate example that computes as few different help columns
and displays them on the side of the chart as a table.
Note that in the example below I have also included a check for whether click.country is an array
and wrap it into an array if needed,
which allows us to start without an init in the selection (you can use this logic in the example above as well).
click = alt.selection_multi(
fields=['highlight', 'country'],
name='click',
empty='none',
)
scatter = alt.Chart(df).mark_circle(
size=200,
strokeWidth=3
).transform_calculate(
clicked = 'click.country',
indexof = 'indexof(datum.country, datum.highlight)',
clicked_indexof = 'indexof(isArray(click.highlight) ? click.highlight : [click.highlight], datum.country)',
).encode(
x='x',
y='y',
opacity=alt.condition('datum.clicked_indexof != -1', alt.value(1), alt.value(0.4)),
stroke=alt.condition('indexof(isArray(click.country) ? click.country : [click.country], datum.country) != -1', alt.value('#ff7f0e'), alt.value('#1f77b4')),
tooltip='country'
).add_selection(
click
)
# Base chart for data tables
ranked_text = scatter.mark_text(
align='right',
fontSize=14,
fontWeight=800
).encode(
x=alt.X(),
y=alt.Y('row_number:O', axis=None),
stroke=alt.Stroke(), # A stroke makes the text hard to read
color=alt.condition('indexof(isArray(click.country) ? click.country : [click.country], datum.country) != -1', alt.value('#ff7f0e'), alt.value('#1f77b4')),
).transform_window(
row_number='row_number()'
).transform_window(
rank='rank(row_number)'
).transform_filter(
alt.datum.rank<16
)
# Create a table text column for each desired field in the data source
table_columns = []
for column in ['country', 'highlight', 'indexof', 'clicked', 'clicked_indexof']:
table_columns.append(
ranked_text.encode(
text=f'{column}:N'
).properties(
title=alt.TitleParams(
text=column,
align='right'
)
)
)
(scatter | alt.hconcat(*table_columns)).configure_view(strokeWidth=0)
