@@ -73,7 +73,7 @@ def time_series(data, regions, uk_data):
7373 fig .update_layout (
7474 title = {
7575 'text' : '<span style="font-weight:normal;">' +
76- '<br>' .join (textwrap .wrap (f"Time Series of { regions [0 ]} against { regions [1 ]} - <b>GVA per hour (chained 2022 )</b>" , width = 100 )) +
76+ '<br>' .join (textwrap .wrap (f"Time Series of { regions [0 ]} against { regions [1 ]} - <b>GVA per hour (chained 2008 )</b>" , width = 100 )) +
7777 '</span>' ,
7878 'font' : {'size' : 14 },
7979 'x' : 0.05 , # move slightly to the right (0=left, 1=right)
@@ -82,7 +82,7 @@ def time_series(data, regions, uk_data):
8282 'yanchor' : 'top' , # align title relative to y
8383 },
8484 xaxis_title = "Year" ,
85- yaxis_title = f"GVA per hour (chained 2022 ) (%)" ,
85+ yaxis_title = f"GVA per hour (chained 2008 ) (%)" ,
8686 autosize = True ,
8787 template = "plotly_white" , # Use a clean white background
8888 legend = dict (
@@ -108,42 +108,79 @@ def spider(data, indicators, region, colour, driver):
108108 # Merge so values align by code
109109 row = row .merge (sub , on = 'name' , how = 'left' )
110110
111- # Compute the medians for each column in the indicators
112- medians = row .drop (columns = 'name' ).median ()[indicators ]
113-
114- temp = row .loc [row ['name' ] == region , indicators ].copy ()
115- opposite_indicators = ['Low Skilled' , 'Inactive due to Illness' ]
111+ # Compute percentile ranks for each indicator
112+ percentile_ranks = pd .DataFrame ({'name' : data ['name' ].unique ()})
113+
116114 for ind in indicators :
117- if ind in opposite_indicators :
118- temp [ind ] = (medians [ind ] / temp [ind ]) * 100
119- else :
120- temp [ind ] = (temp [ind ] / medians [ind ]) * 100
115+ # For each indicator, get the series
116+ series = row [ind ]
121117
118+ # Compute percentile rank of each value
119+ ranks = series .rank (pct = True ) * 100 # gives 0–100
120+ percentile_ranks [ind ] = ranks
121+
122+ # Subset to your region of interest
123+ temp = percentile_ranks .loc [percentile_ranks ['name' ] == region , indicators ].copy ()
124+
125+ # Handle "opposite indicators" (lower values = better)
126+ opposite_indicators = ['Low Skilled' , 'Inactive due to Illness' ]
127+ for ind in opposite_indicators :
128+ if ind in temp .columns :
129+ temp [ind ] = 100 - temp [ind ] # invert percentile so "lower = better"
122130 # Handle missing data by filtering out NaN values
123131 temp = temp .dropna (axis = 1 ) # Drop columns with NaN values
124- valid_indicators = temp .columns .tolist () # Get the corresponding valid indicators
132+ # Compute real values for the region
133+ real_values = row .loc [row ['name' ] == region , indicators ].copy ()
125134
126- # Close the loop by appending the first value to the end
127- r_values = temp .values .flatten ().tolist ()
128- r_values .append (r_values [0 ]) # Append the first value to close the loop
135+ # Compute medians for each indicator
136+ medians = row [indicators ].median ()
137+
138+ # Handle missing values consistently
139+ valid_indicators = [ind for ind in indicators if ind in temp .columns ]
140+
141+ # Close the loop
142+ r_values = temp [valid_indicators ].values .flatten ().tolist ()
143+ r_values .append (r_values [0 ])
129144
130145 theta_values = ['<br>' .join (textwrap .wrap (ind , width = 10 )) for ind in valid_indicators ]
131- theta_values .append (theta_values [0 ]) # Close the loop
146+ theta_values .append (theta_values [0 ])
132147
133- # Create a time series plot
148+ # Build custom hover text
149+ hover_texts = []
150+ for ind in valid_indicators :
151+ raw_val = real_values [ind ].values [0 ]
152+ median_val = medians [ind ]
153+ percentile_val = temp [ind ].values [0 ]
154+ if ind in ['GVA per hour worked' , 'GFCF per job' , 'ICT per job' , 'Intangibles per job' ]:
155+ hover_texts .append (
156+ f"<b>Indicator:</b> { ind } <br>"
157+ f"<b>Value:</b> £{ raw_val :,.2f} <br>"
158+ f"<b>UK Median:</b> £{ median_val :,.2f} <br>"
159+ f"<b>Percentile:</b> { percentile_val :.2f} %"
160+ )
161+ else :
162+ hover_texts .append (
163+ f"<b>Indicator:</b> { ind } <br>"
164+ f"<b>Value:</b> { raw_val :,.2%} <br>"
165+ f"<b>UK Median:</b> { median_val :,.2%} <br>"
166+ f"<b>Percentile:</b> { percentile_val :.2f} %"
167+ )
168+ # Close the loop
169+ hover_texts .append (hover_texts [0 ])
170+
171+ # Create the figure
134172 fig = go .Figure ()
135- # Add the trace for the selected region
136173 fig .add_trace (go .Scatterpolar (
137- r = r_values , # Values for the radar plot
138- theta = theta_values , # Categories (indicators)
139- fill = 'toself' , # Fill the area under the curve
174+ r = r_values ,
175+ theta = theta_values ,
176+ text = hover_texts , # supply custom hover text
177+ hoverinfo = "text" , # only show text
178+ fill = 'toself' ,
140179 name = region ,
141- line = dict (color = colour , width = 2 ), # Customize line color and width
142- hoverinfo = "text" , # Enable custom hover text
143- hovertemplate = "<b>Indicator:</b> %{theta}<br><b>Relative to UK Median:</b> %{r:.2f}%<extra></extra>" # Custom hover text
180+ line = dict (color = colour , width = 2 ),
144181 ))
145182 fig .add_trace (go .Scatterpolar (
146- r = [100 ]* len (r_values ), # Values for the radar plot
183+ r = [50 ]* len (r_values ), # Values for the radar plot
147184 theta = theta_values , # Categories (indicators)
148185 fill = 'toself' , # Fill the area under the curve
149186 name = f"UK median" ,
@@ -158,7 +195,7 @@ def spider(data, indicators, region, colour, driver):
158195 polar = dict (
159196 radialaxis = dict (
160197 visible = True ,
161- range = [20 , 200 ], # Adjust the range as needed
198+ range = [0 , 100 ], # Adjust the range as needed
162199 showticklabels = False , # Remove radial axis ticks
163200 ),
164201 angularaxis = dict (
0 commit comments