diff --git a/analysis/bin-comparison.py b/analysis/bin-comparison.py index 76457c4..2ca64b6 100644 --- a/analysis/bin-comparison.py +++ b/analysis/bin-comparison.py @@ -5,22 +5,31 @@ sig_level = 0.05 backend = { - 'production': 'http://localhost:8070', +# 'production': 'http://localhost:8070', 'development': 'http://localhost:8072' } dates = { - 'before': '2024-07-02/2024-07-09', - 'after': '2024-07-11/2024-07-18' + 'before': '2024-09-01/2024-10-11', + 'after': '2024-10-12/2024-11-20' } -corridor = '30364284/30363982' # Eglinton westbound from Bathurst to Allen -#corridor = '30363865/30363947' # Eglinton eastbound from Oakwood to Allen -#corridor = '30361437/30363947' # Allen Southbound to Eglinton +#corridor = '30357505/30345882' # Bloor westbound from Runnymede to Aberfoyle +#corridor = '30345882/30357505' # Bloor eastbound from Aberfoyle to Runnymede -#time = '15/18' # PM Peak +#corridor = '30345882/970252141' # Bloor eastbound from Aberfoyle to Royal York (NS) +#corridor = '970252141/30347302' # Bloor eastbound from Royal York to Kingsway (NS) +#corridor = '30347302/30347896' # Bloor eastbound from Kingsway to Jane (significant AM peak decrease) +#corridor = '30347896/30357505' # Bloor eastbound from Jane to Runnymede + +#corridor = '970252141/30345882' # Bloor westbound from Aberfoyle to Royal York (NS) +#corridor = '30347302/970252141' # Bloor westbound from Royal York to Kingsway (NS) +corridor = '30347896/30347302' # Bloor westbound from Kingsway to Jane (significant AM peak decrease) +#corridor = '30357505/30347896' # Bloor westbound from Jane to Runnymede + +time = '15/18' # PM Peak #time = '07/09' # AM Peak -time = '9/16' # midday +#time = '9/16' # midday def getObs(responseData): return [ tt['seconds'] for tt in responseData['results']['observations'] ] @@ -59,7 +68,7 @@ def getObs(responseData): # plot histograms side by side from matplotlib import pyplot - bins = numpy.linspace(0, 600, 20) + bins = numpy.linspace(0, 1200, 20) pyplot.hist(data[0], bins, alpha=0.5, label='before') pyplot.hist(data[1], bins, alpha=0.5, label='after') pyplot.legend() diff --git a/analysis/corridor-deep-dive.py b/analysis/corridor-deep-dive.py new file mode 100644 index 0000000..d7bdeac --- /dev/null +++ b/analysis/corridor-deep-dive.py @@ -0,0 +1,81 @@ +# We know (or suspect) that something has changed in a corridor with +# regard to travel times. But where exactly did the change happen? +# When? Can we isolate the change by further segmenting the analysis? +# +# I'll build this out around the case of the Bloor West Complete Street Phase 1 +# but hope that it's applicable beyond that. + +import requests, scipy, numpy, pandas +from datetime import date + +sig_level = 0.05 +min_length_to_analyse = 250 # meters + +backend = 'http://localhost:8072' + +dates = { + 'before': '2024-09-01/2024-10-11', + 'after': '2024-10-16/2024-11-23' +} +time = '15/18' # PM Peak +corridor = '30345882/30357505' # Bloor eastbound from Aberfoyle to Runnymede + +# fetch all the links for this corridor +links = requests.get(f"{backend}/link-nodes/{corridor}").json()['links'] + +# create a list of node OD pairs to iterate over +# basically a spatial moving window over the corridor +queries = [] +cumWindowStartM = 0 +for i, link in enumerate(links): + cumWindowLength = link['length_m'] + for next_link in links[i+1:]: + if cumWindowLength >= min_length_to_analyse: + queries.append({ + 'ODpair': f'{link["source"]}/{next_link["target"]}', + # start and end positions of the rolling window + 'windowStartM': cumWindowStartM, + 'windowEndM': cumWindowStartM + cumWindowLength + }) + break + cumWindowLength += next_link['length_m'] + cumWindowStartM += link['length_m'] + + +def getObs(responseData): + return [ tt['seconds'] for tt in responseData['results']['observations'] ] + +for query in queries: + # get data for both date ranges + data = [ + requests.get( + f"{backend}/aggregate-travel-times/{query['ODpair']}/{time}/{dateRanges}/false/12345" + ).json() for dateRanges in dates.values() + ] + before_response = data[0] + after_response = data[1] + before_data = getObs(before_response) + after_data = getObs(after_response) + # store the means + query['tt_before'] = before_response['results']['travel_time']['seconds'] + query['tt_after'] = after_response['results']['travel_time']['seconds'] + # Apply a one-tailed Mann-Whitney U test + stat, pvalue = scipy.stats.ranksums( + before_data, + after_data, + 'greater' # one-tailed test that before > after (times decreased) + ) + query['p'] = pvalue + + print( + query['ODpair'], + '(travel times higher', + 'before)' if numpy.mean(before_data) > numpy.mean(after_data) else 'after)', + pvalue + ) + +results = pandas.DataFrame(queries) + +print(results) + +results.to_csv('corridor-windows.csv') diff --git a/analysis/rolling-window.r b/analysis/rolling-window.r new file mode 100644 index 0000000..71fc7c6 --- /dev/null +++ b/analysis/rolling-window.r @@ -0,0 +1,36 @@ +library('tidyverse') + +setwd('C:\\Users\\nwessel\\Downloads') + +read_csv('corridor-windows-250m-2tail.csv') %>% + mutate( + change = if_else(tt_before > tt_after, 'decrease', 'increase'), + p = if_else(p <= 0.05, p, 1) + ) %>% + ggplot() + + geom_rect( + aes( + xmin = windowStartM, + xmax = windowEndM, + ymin = 0, + ymax = log(p), + fill = change + ), + alpha = 0.1 + ) + + scale_fill_manual(values = c( + 'increase' = 'red', + 'decrease' = 'green' + )) + + scale_x_continuous( + # label cross streets by distance from start + minor_breaks = NULL, + breaks = c(0,371,657,1200,1700,2000,2800,3000,3100,3300,3500,3700), + labels = c('Aberfolye','Montgomery','Royal York','PED','Kingsway','Old Mill Trail','S Kingsway','Jane','Armadale','Windermere','Durie','Runnymede') + ) + + scale_y_reverse() + + labs( + title='Significance of Eastbound travel time changes', + y='log(P)' + ) +