<jupyter_start><jupyter_text>**INTERACTION** *<NAME> i <NAME>**December 2020* **Environment preparation**In order to work with the dataset, create the visualizations and implement different interaction techniques, we need to prepare the environment we will be working on.<jupyter_code># Necessary libraries for code execution.
import pandas as pd
import altair as alt
import datetime
# Google drive loading as work station for local-usage of the files.
from google.colab import drive
drive.mount('/content/gdrive',force_remount= True)<jupyter_output>Mounted at /content/gdrive<jupyter_text>We disabled the maximum number of rows when using Altair to avoid possible errors, even though this dataframe don't have large dimensions.<jupyter_code># Disabling the maximum rows option to avoid problems in Altair utilitzation. 
alt.data_transformers.disable_max_rows()<jupyter_output><empty_output><jupyter_text>**Dataset loading**We now can load the file produced in the *Preprocessing* script.<jupyter_code># Load of the clean data.
df = pd.read_csv('/content/gdrive/My Drive/GCED/Q5/VI/Projecte 2/CleanData.csv')
df.head()<jupyter_output><empty_output><jupyter_text>In this dataset we have information about all *Bicing®* stations. Each row corresponds to the average metrics of a station per October day. Specifically,we have the total number of bikes, both electrical and mechanical and the number of available docks. Moreover, each row includes the geospatial information of the respective station. **1 - OVERVIEW**On this fist view, which consists in a combination of a coropleth and a symbol map, we had to main purposes. On one hand, we wanted to show the number of available bikes per district in a format that allowed the user to extract this information with a glance. On the other hand, we wanted to establish the map as an important element for interaction, allowing to select several districts to further explore their attributes in the following plots.In order to further explain this visualization, we will fractionate its analysis in two parts:* **VISUAL REPRESENTATION**As previously said, this representation consists of a choropleth map. On it, the average number of available bikes (of the selected type) is encoded using the colour chanel and applying it uniformly to the district in which it belongs. We also decided to overlap a symbol map layer to the choropleth.In this, the circles' colours (which was the selected shape for the symbols) encode the districts' names. This layer has the purpose to allow the user to identify the districts without needing to hover over them (as we will explain in *Interaction*). This identification is performed associating the circle colour to its name using the interactive legend provided in the visualization.&nbsp;* **INTERACTION**In this plot, we can distinguish three interactive objects:- The *Kind* selector: Consists in a simple legend that allows the user to select the type of bicycle he wants to retrieve information. - The *district legend*: Squared legend that allows selecting one/multiple districts. - The *Map* itself. It also allows the selection of one/multiple districts.\The *Kind* selector behaves as a non-spatial filter, modifying the values taken into account by the choropleth. The district selection/s imply a highlighting of the regions, increasing the thickness of its border and making unselected regions more transparent.For last, when hovering over a district, its name and the exact value for the average shown is provided. ---The first step to create this visualization consists of loading the map data.<jupyter_code>#Loading the BCN disricts map.
dataGeo = 'https://raw.githubusercontent.com/martgnz/bcn-geodata/master/districtes/districtes.geojson'
map_data = alt.Data(url=dataGeo, format=alt.DataFormat(property='features',type='json'))<jupyter_output><empty_output><jupyter_text>To compute the average availability metrics per district and to allow the user to filter by the bike type, an auxiliary dataframe was created.To do so, a groupby operation with the mean function was performed, as well as a long-form transformation of the data set. We also executed a rename operation in the *Type* values to make them easier to understand.For the symbol placement, another auxiliary dataframe was computed, containing the centroid of the stations per district.\*PD: Although these operations could have been done using Altair aggregate and fold functions, we decided to store the dataframes they will be later used, avoiding code repetition.*<jupyter_code># Auxiliar data frames

# Computing the average metrics
aux_avg_df = df.groupby(["Districte"]).agg('mean')[["available_bikes","mechanical","electrical"]]
aux_avg_df = aux_avg_df.reset_index()

# Long-form conversion
aux_avg_df =  pd.melt(aux_avg_df, id_vars=['Districte'],var_name = 'Type', value_name='Average')
aux_avg_df['Type'].replace({"available_bikes":"Total","mechanical":"Mechanical","electrical":"Electrical"},inplace = True)

# Centroid auxiliar dataframe
centroids = df.groupby(["Districte"]).agg('mean')[["lat","lon"]]
centroids = centroids.reset_index()
aux_avg_df.head()
centroids.head()<jupyter_output><empty_output><jupyter_text>The next step after the data conversion consisted of creating different interactive techniques. For the type of bikes, we made it single selection, including the nearest option, to not allow multiple/empty choices, since conceptually they don't make senseRegarding district selection, it is multiple but does not include the nearest option. This is mainly to allow the user to click off the map and select all districts quickly<jupyter_code># District selection 
map_multi = alt.selection_multi(fields=['Districte'], init = [{'Districte':'Les Corts'}], empty = 'all')

# Kind of bike selection
type_select = alt.selection_single(fields=['Type'], init = {'Type':'Total'}, nearest = True)

# Auxiliar condition for district color encoding
district_color = alt.condition(map_multi,alt.Color('Districte:N',legend = None,scale = alt.Scale(scheme= 'category10')),alt.value('lightgray'))<jupyter_output><empty_output><jupyter_text>Once all the necessary resources for the visualization were properly encoded, we proceeded to elaborate the plot.<jupyter_code># Coropleth
coro = alt.Chart(aux_avg_df).mark_geoshape(stroke = 'black').properties(
    title = "Average number of bikes per district"
).encode(
    color = alt.Color('Average:Q',scale = alt.Scale(scheme= 'yelloworangered')),
    strokeWidth = alt.condition(map_multi,alt.value(3),alt.value(1)),
    opacity = alt.condition(map_multi,alt.value(1.0),alt.value(0.3)),
    tooltip = ['Districte', 'Average']
).transform_lookup(
    lookup = 'Districte',
    from_ = alt.LookupData(map_data,'properties.NOM',fields=["type", "properties", "geometry"])
).transform_filter(
    type_select
).add_selection(
    map_multi
).properties(width = 750, height = 750)


# District symbols
districts = alt.Chart(centroids).mark_circle(size = 300, stroke = "Black").encode(
    opacity = alt.condition(map_multi,alt.value(1),alt.value(0.5)),
    strokeWidth = alt.condition(map_multi,alt.value(3),alt.value(1)),
    longitude="lon:Q",
    latitude="lat:Q",
    #size = 4,
    fill = alt.Color('Districte:N',scale = alt.Scale(scheme= 'category10'), legend = None),
)


# Buttons for type of bike selection
type_butt =  alt.Chart(aux_avg_df).mark_circle().encode(
    y = alt.Y('Type:N',axis = alt.Axis(orient = 'right', title = None),sort = alt.Sort(['Total','Mechanical','Electrical'])),
    color = alt.condition(type_select, alt.value('black'),alt.value('lightgray'))
).add_selection(
    type_select
).properties(title = 'Kind')


# Legend for district selection
legend = alt.Chart(df).mark_rect().encode(
    y = alt.Y('Districte',axis = alt.Axis(orient = 'right', title = None)),
    color = district_color
).add_selection(
    map_multi
).properties(title = 'District')


def_coro = coro + districts

(type_butt | def_coro |legend).resolve_scale(
    x = 'independent',
    y = 'independent'
)<jupyter_output><empty_output><jupyter_text>**2 - BUBBLE CHART**

In this view, the average mechanical/electrical bikes and the number of stations per district is encoded using a bubble chart. 

* **VISUAL REPRESENTATION**

In this plot, the average number of mechanical bikes is encoded using the X-axis, and the average number of electrical bikes is encoded using the Y-axis.
Using the color channel, the district name is encoded as in the previous representations. For last, the number of stations per district is encoded using the size of the marks.

To avoid changes on the scales when performing modifications on the districts/day of the month when using interaction, they have been fixed ( after previously visualizing the maximum respective ranges).

\
* **INTERACTION**

In this plot, there are also three interactive objects:

- The *Day of the Month* selector: Slider that allows the user to select the day of the month (and previous) to be shown on the display.

- The *district legend*.

- The *Bubbles* of the chart. As in the choropleth and the legend, allow multiple district selections by clicking on the desired bubbles.

\
The district selection/s imply applying a lightgray value to the non-selected districts while maintaining the original colour to the selected ones.

The *Day of the Month* slider has two functions. On one hand, it behaves as a non-spatial filter, only showing the bubbles for the days smaller or equal than the introduced value. On the other hand, maintains the opacity of the selected value while making translucid the ones that are smaller, leaving a trail.

For last, an auxiliary tooltip layer was implemented. The main reason for this is to only provide the details-on-demand for the selected values, instead of showing them when hovering any bubble.

This helps the user to visualize only the information of the district/day he is interested in, as well as easing the hovering task for a specific bubble, which becomes difficult in dense regions if this solution is not implemented.<jupyter_code># Tooltip selector
tooltips = alt.selection_single(on = 'mouseover',empty = 'none') 
# Slider/selector for day of the month
day_month_slider = alt.binding_range(max=31, min = 1, step = 1, name = 'Day of the month')

day_month_selection = alt.selection_single(fields=['day_month'], bind = day_month_slider, init = {'day_month':1})
# Bubble chart (splited in two charts to reuse base for tooltip)
base = alt.Chart(df).mark_circle().transform_aggregate(
    avg_mechanical="mean(mechanical)",
    avg_electrical="mean(electrical)",
    Dist_count="distinct(station_id)",
    groupby=['Districte','day_month']
).encode(
    x = alt.X("avg_mechanical:Q", title = "Average Mechanical Bikes",scale=alt.Scale(domain=[0,20])),
    y = alt.Y("avg_electrical:Q", title = "Average Electrical Bikes", scale=alt.Scale(domain=[0,2.5])),
    size = alt.Size("Dist_count:Q",scale=alt.Scale(domain=[0,100]), title = "Number of stations"),
)

bubble = base.encode(
    color = district_color,
    opacity = alt.condition(day_month_selection,alt.value(1.0),alt.value(0.3)),
).add_selection(
    day_month_selection,
    map_multi
).transform_filter(
   alt.datum.day_month <= day_month_selection.day_month)

# Auxiliar layer for tooltip (only hovers on selection)
tooltip_aux = base.encode(
     opacity=alt.value(0),
    tooltip=[alt.Tooltip('Dist_count:Q', title ="Number of stations"),alt.Tooltip('avg_mechanical:Q', title = "Average mechanical"), alt.Tooltip('avg_electrical:Q',title = "Average electrical")]
  ).transform_filter(
  day_month_selection & map_multi
).add_selection(tooltips)

def_bubble = (bubble+tooltip_aux).properties(width = 600, height = 350, title = "Bubble chart")
legend | def_bubble<jupyter_output><empty_output><jupyter_text>**3 - SCATTER PLOT**After generating the previous visualizations, we then proceeded to develop representations that have a higher granularity, selecting the district as an atomic entity.This could allow the user to solve tasks regarding the intra-district and inter-district behaviour comparison more easily. Even though in the previous graph we can compare the difference in the number of stations per district, the user cannot, per example, visualize if all the stations behave similarly or, conversely, several operating patterns are present.In order to provide this knowledge, a scatter plot visualization was performed.* **VISUAL REPRESENTATION**In this plot, the average number of mechanical/electrical bikes is encoded using the 2-D position of the mark. Emphasize that each point of this visualization represents a station.Using the colour channel, the district name is encoded as in the previous representations. To avoid the change of scales on the different working days, we also fixed the X and Y axis domains, allowing direct comparison between the juxtaposed plots.\* **INTERACTION**In this plot, there are two interactive objects:- The *district legend*.- The *points* of the chart. Idem district legend.\The district selection/s imply applying a lightgray value to the non-selected districts while maintaining the original colour to the selected ones.In this plot, we have decided not to include a tooltip. The reasoning behind this decision is that as the stations of the same district show, in general, a similar behaviour, selecting a specefic point is difficult. Moreover, this visualization was designed to provide a general description of the internal district activity and not to evaluate each station specifically.<jupyter_code># Creating an SP with all the values.
sp = alt.Chart(df).mark_circle().transform_aggregate(
    avg_mech = 'mean(mechanical)',
    avg_elec = 'mean(electrical)',
    groupby = ['station_id','weekday','Districte']
).encode(
    x= alt.X('avg_mech:Q', title = "Average Mechanical Bikes", scale=alt.Scale(domain=[0,35])),
    y= alt.Y('avg_elec:Q', title = "Average Electrical Bikes", scale = alt.Scale(domain = [0,6])),
    color=district_color
).add_selection(
    map_multi
).properties(width=300, height=300)

# Obteining the SPs per day and linking them.
sp_1=sp.transform_filter(alt.datum.weekday == 1).properties(title='Monday')
sp_2=sp.transform_filter(alt.datum.weekday == 2).properties(title='Tuesday')
sp_3=sp.transform_filter(alt.datum.weekday == 3).properties(title='Wednesday')
sp_4=sp.transform_filter(alt.datum.weekday == 4).properties(title='Thursday')
sp_5=sp.transform_filter(alt.datum.weekday == 5).properties(title='Friday')

f_sp = sp_1 | sp_2 | sp_3| sp_4 | sp_5

legend | f_sp<jupyter_output><empty_output><jupyter_text>**4 - HISTOGRAM**

To follow this more in-depth analysis of the districts, we considered that having a visualization showing the station distribution conditioned to the number of available bikes could be an interesting field of study, that will allow delving into the information provided by the [scatter plots](3).

To fulfil this task, we considered that a histogram was the best option.

* **VISUAL REPRESENTATION**

In this plot, the X-axis encodes the average number of available bikes (of the selected type), transforming this variable to ordinal applying binning.

The longitude of each bar represents the number of stations that fall into that range of values.

For last, the district name is encoded using the colour channel as in the previous cases. 

\
* **INTERACTION**

In this plot, we can see two interactions:

- The *district legend*.

- The *Kind* selector.

\

The *Kind* selection filters the type of bikes take into account to compute the bins.

The district selection allows visualizing the histogram for the chosen district. As it is a multiple selector, if several districts are picked, a stacked bar chart is visualized.


In this plot, we wanted to apply two different fixed scales, one for the total/mechanical bikes and the other for electrical bikes. Doing this, we could establish the same bin partition for all the districts, making the task of comparing histograms much easier. However, applying a condition in the scale is not currently supported by *Altair*. As consequence, the binning partitions cannot be fixed, because applying criteria that suits the mechanical bikes would imply that compacting the electrical bikes into a single/two bars.
 ---To encode this histogram, we have created an auxiliary dataframe similar to the used in the [overview](1). In this, the groupby operation is performed by station, instead of per district. In this auxiliary data, a long-form format conversion was also applied.<jupyter_code># Auxiliar data frame that computes the average per station.
aux_avg_station_df = df.groupby(["Districte","station_id"]).agg('mean')[["available_bikes","mechanical","electrical"]]
aux_avg_station_df = aux_avg_station_df.reset_index()

# Long-form conversion
aux_avg_station_df =  pd.melt(aux_avg_station_df, id_vars=['Districte','station_id'],var_name = 'Type', value_name='Average')
aux_avg_station_df['Type'].replace({"available_bikes":"Total","mechanical":"Mechanical","electrical":"Electrical"},inplace = True)
# Histogram (stacked if  multiple selections)
hist = alt.Chart(aux_avg_station_df).mark_bar().encode(
    x = alt.X("Average:Q", bin = True),
    y = alt.Y("count():Q",title = "Number of stations"),
    color = district_color
).transform_filter(
    type_select 
).transform_filter(
    map_multi
).properties(width = 600)

type_butt | hist |legend<jupyter_output><empty_output><jupyter_text>**5 - FINAL VISUALIZATION** To arrange properly the charts, a modification of the [bubble chart](2) legend has been made. We had to exactly set the position for this legend to not distort the general view.<jupyter_code>base_mod = alt.Chart(df).mark_circle().transform_aggregate(
    avg_mechanical="mean(mechanical)",
    avg_electrical="mean(electrical)",
    Dist_count="distinct(station_id)",
    groupby=['Districte','day_month']
).encode(
    x = alt.X("avg_mechanical:Q", title = "Average Mechanical Bikes",scale=alt.Scale(domain=[0,20])),
    y = alt.Y("avg_electrical:Q", title = "Average Electrical Bikes", scale=alt.Scale(domain=[0,2.5])),
    size = alt.Size("Dist_count:Q",scale=alt.Scale(domain=[0,100]), title = "Number of stations", legend = alt.Legend(orient = "none",legendX= 2433, legendY= 0)),
)

bubble_mod = base_mod.encode(
    color = district_color,
    opacity = alt.condition(day_month_selection,alt.value(1.0),alt.value(0.3)),
).add_selection(
    day_month_selection,
    map_multi
).transform_filter(
   alt.datum.day_month <= day_month_selection.day_month)

# Auxiliar layer for tooltip (only hovers on selection)
tooltip_aux_mod = base_mod.encode(
     opacity=alt.value(0),
    tooltip=[alt.Tooltip('Dist_count:Q', title ="Number of stations"),alt.Tooltip('avg_mechanical:Q', title = "Average mechanical"), alt.Tooltip('avg_electrical:Q',title = "Average electrical")]
  ).transform_filter(
  day_month_selection & map_multi
).add_selection(tooltips)

def_bubble_mod = (bubble_mod + tooltip_aux_mod).properties(width = 600)
coro_type = (type_butt | def_coro).resolve_scale(
    x = 'independent',
    y = 'independent'
)
coro_type | ((legend | hist | def_bubble_mod) & f_sp)<jupyter_output><empty_output><jupyter_text>---
**Extra visualizations** In additon to encoding the requested visualitzations by the project coordinator, we have also elaborated some extra representations to further analyze differnts aspects of our data, as well as gain a deeper knowledge on interaction techniques. **E1 - GRADUATED SYMBOL MAP**This first extra view has the same purpose as the overview in the mandatory section. On this, instead of using a choropleth, we decided to encode our data using a graduated symbol map, providing the same information as before but using a different format.* **VISUAL REPRESENTATION**This representation consists of a graduated symbol map. On it, the average number of available bikes (of the selected type) is encoded using the size of the points. Each district is filled with the same colour it has on the legend, to ease the user the district identification task.* **INTERACTION**In this plot, we can distinguish three interactive objects:- The *Kind* selector.- The *district legend*: This selector was modified, only allowing a single selection. This was done has the following extra plots make no sense when performing a multi-district analysis.- The *Map* itself.As the new legend only allows selecting one district.\ The district selection implies highlighting the region, increasing the thickness of its border and making unselected regions more transparent. These effects are also applied to the graduated symbols.For last, the hovering now is performed over the symbols, to be coherent with the chosen visualization. ---In this part, we encoded the new single selectors.<jupyter_code># District selection (single)
map_single = alt.selection_single(fields=['Districte'], init = {'Districte':'Les Corts'},empty='none')

# Auxiliar condition for single district color encoding
district_color_single = alt.condition(map_single,alt.Color('Districte:N',legend = None,scale = alt.Scale(scheme= 'category10')),alt.value('lightgray'))
# Background map (encoded with district colors)
map = alt.Chart(aux_avg_df).mark_geoshape(stroke = 'black').properties(
    title = "Average number of bikes per district"
).encode(
    color = alt.Color('Districte:N',scale = alt.Scale(scheme= 'category10'), legend = None),
    strokeWidth = alt.condition(map_single,alt.value(3),alt.value(1)),
    opacity = alt.condition(map_single,alt.value(1),alt.value(0.075)),
).transform_lookup(
    lookup = 'Districte',
    from_ = alt.LookupData(map_data,'properties.NOM',fields=["type", "properties", "geometry"])
).add_selection(
    map_single
).properties(width = 750, height = 750)


# Symbol encoding
points = alt.Chart(aux_avg_df).transform_lookup(
    lookup = 'Districte',
    from_ = alt.LookupData(centroids,'Districte',fields=["lat", "lon"])
).mark_circle(color ="black").encode(
    opacity = alt.condition(map_single,alt.value(1),alt.value(0.3)),
    longitude="lon:Q",
    latitude="lat:Q",
    size=alt.Size("Average:Q", legend = None),
    tooltip = ['Districte', 'Average']
).transform_filter(
     type_select 
).add_selection(tooltips)


# Legend for single district selection
single_legend = alt.Chart(df).mark_rect().encode(
    y = alt.Y('Districte',axis = alt.Axis(orient = 'right', title = None)),
    color = district_color_single
).add_selection(
    map_single
).properties(title = 'District')


gsm = (type_butt|map + points).resolve_scale(
    x = 'independent',
    y = 'independent'
)

gsm | single_legend<jupyter_output><empty_output><jupyter_text>**E2 - HEATMAP**As we are showing the data extracted from a month we thought it would be more familiar to the user to use a calendar-shaped view. By doing this, the user will be able to compare the data for the whole month, for a week or for a specific day of the week.- **VISUAL REPRESENTATION**We want to show the bike availability for each type and district during the month in a calendar-shape. Therefore we will use a chart with weekday and week of the month as X and Y-axis respectively to give the chart the shape of a calendar.\As we want to show the number of bikes we will encode it with colour to see the evolution better (rather than showing the numerical values). This leaves us with a heatmap.\- **INTERACTION**In this plot there are three interactive elements:- The *district* legend.- The *type* selector.- The day and value tooltip.\To be able to show the values of each bike type and district we decided to use the two selectors defined for the graduated symbol map. One to select a district and the other to select the bike type (or the total).\We also decided to add a tooltip to show the exact value and the day of the month that box represents to be able to allow more specific queries. ---To be able to plot the desired chart we need two previous steps.First, with the help of an auxiliary dataframe, we need to add a column corresponding to the week of the month for each row to be able to plot the calendar. We also map the numbers to the corresponding day of the week to ease the reading of the data.Then, in the auxiliary dataframe, a groupby operation with the mean function was performed, as well as a long-form transformation of the data set. We also executed a rename operation in the *Type* values to make them easier to understand.<jupyter_code># Auxiliar data frame dayweek/ #week/ daymonth average.

day_weekday = df
day_weekday['week'] = ((day_weekday['day_month'])//7)+1

# Renaming the days of the week and encoding the name of the month as a new variable.
day_weekday['weekday'].replace({1:'Monday', 2:'Tuesday', 3:'Wednesday', 4:'Thursday',5:'Friday', 6:'Saturday', 7:'Sunday'},inplace = True)
day_weekday['date'] = day_weekday.weekday + ", " + day_weekday.day_month.astype(str)

# Computing the average values.
day_weekday = df.groupby(["Districte","weekday","week","date"]).agg('mean')[["available_bikes","mechanical","electrical"]]
day_weekday = day_weekday.reset_index()

# Long-form conversion
day_weekday =  pd.melt(day_weekday, id_vars=['Districte','weekday','week','date'],var_name = 'Type', value_name='Average')
day_weekday['Type'].replace({"available_bikes":"Total","mechanical":"Mechanical","electrical":"Electrical"},inplace = True)

day_weekday.head()
# Heatmap
heatmap = alt.Chart(day_weekday).mark_rect().encode(
    x = alt.X('weekday:O',title = "Weekday",sort=alt.Sort(['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'])),
    y = alt.Y('week:O',title = "Week"),
    color = alt.Color('Average:Q',scale = alt.Scale(scheme= 'yelloworangered')),
    tooltip = [alt.Tooltip('date', title = "Day of the month"),'Average']
).transform_filter(
    map_single
).transform_filter(
    type_select
).add_selection(tooltips).properties(width = 500, height = 300)

type_butt|heatmap|single_legend<jupyter_output><empty_output><jupyter_text>**E3 - LINE CHART**On this visualization, we wanted to show the different daily behaviour depending on the bike type per station. As we wanted to search for trends and there is a connexion between values, the line chart suited well our needs.* **VISUAL REPRESENTATION**In this plot, the day of the month is encoded using the X-axis, and the average number of bikes is encoded using the Y-axis.The colour is used to show the user the district that is being analyzed and the stroke dash is used to differentiate mechanical bikes from electric ones.When hovering over the plot, an auxiliary ruler is shown, to ease the task of detecting in which day the user is located in. Moreover, the exact values for the average metrics are shown for both bikes types, and a circular mark highlights its locations on the lines.* **INTERACTION**In this plot, we can distinguish two interactive objects:- The *district legend*- An *auxiliary hovering selector* that retrieves the X-axis position, used to display the details-on-demand.\ The district selection filters the data, only showing the metrics for the chosen region.<jupyter_code># Auxiliar data frame that computes the average available bikes discerning 
# by type per day of the month per district

aux_line = df.groupby(["Districte","day_month"]).agg('mean')[["mechanical","electrical"]]
aux_line = aux_line.reset_index()

# Long-form conversion
aux_line  =  pd.melt(aux_line , id_vars=['Districte','day_month'],var_name = 'Type', value_name='Average')
aux_line['Type'].replace({"mechanical":"Mechanical","electrical":"Electrical"},inplace = True)
# Create a selection that chooses the nearest point and selects based on X-value
nearest = alt.selection(type='single', nearest=True, on='mouseover',
                        fields=['day_month'], empty='none')

# The basic line
line = alt.Chart(aux_line).mark_line(interpolate = 'cardinal').encode(
    x=alt.X('day_month:N', title = "Day") ,
    y='Average:Q',
    strokeDash=alt.StrokeDash('Type:N',sort=alt.Sort(['Mechanical','Electrical'])),
    color = 'Districte:N'
)

# Invisible selectors that provide information about
# the x-value of the cursor
selectors = alt.Chart(aux_line).mark_point().encode(
     x=alt.X('day_month:N', title = "Day"),
    opacity=alt.value(0),
).add_selection(
    nearest
)

# Draw points on the line, and highlight based on selection
points = line.mark_point().encode(
    opacity=alt.condition(nearest, alt.value(1), alt.value(0))
)

# Draw text labels near the points, and highlight based on selection
text = line.mark_text(align='left', dx=5, dy=-5).encode(
    text=alt.condition(nearest, 'Average:Q', alt.value(' '))
)

# Draw a rule at the location of the selection
rules = alt.Chart(aux_line).mark_rule(color='gray').encode(
      x=alt.X('day_month:N', title = "Day"), 
).transform_filter(
    nearest
)

# Combining the layers
lines =alt.layer(
    line, selectors, points, rules, text
).properties(
    width=600, height=300
).transform_filter(
    map_single
)

single_legend| lines<jupyter_output><empty_output><jupyter_text>**E4 - FINAL** To arrange properly the charts, a modification of the [line chart](E3) legend has been made. We had to exactly set the position for this legend to not distort the general view.<jupyter_code>line_mod =  alt.Chart(aux_line).mark_line(interpolate = 'cardinal').encode(
    x=alt.X('day_month:N', title = "Day") ,
    y='Average:Q',
    strokeDash=alt.StrokeDash('Type:N',sort=alt.Sort(['Mechanical','Electrical']),legend = alt.Legend(orient = "none",legendX= 1575, legendY= 400)),
    color = 'Districte:N'
)

lines_mod=alt.layer(
    line_mod, selectors, points, rules, text
).properties(
    width=650, height=300
).transform_filter(
    map_single
)

gsm |((single_legend| heatmap) & lines_mod)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10106935737990681, 'fraction_numerical': 0.006101035835204988, 'mean_word_length': 5.339141521461963, 'pattern_counts': {'":': 11, '<': 64, '<?xml version=': 0, '>': 62, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '15', 'dir': 'jupyter-structured-clean-dedup', 'id': '249360', 'license': 'MIT', 'n_tokens_mistral': 8633, 'n_tokens_neox': 8270, 'n_words': 3222, 'path': 'Interactive/Visualizations.ipynb', 'repo_name': 'Vinomo4/Bicing-2019-Visual-Analytics', 'size': '29845'}