<jupyter_start><jupyter_text>Number of routes<jupyter_code>bus_sequences = pd.read_csv('data/london/bus-sequences.csv')
bus_sequences<jupyter_output><empty_output><jupyter_text>Cleaning up  Drop virtual bus stops<jupyter_code>bus_sequences = bus_sequences[bus_sequences.Virtual_Bus_Stop == 0]
bus_sequences.Route.unique().shape
# number of routes per km2
774 / 1_569
# number of routes per capita
774 / 8_899_375<jupyter_output><empty_output><jupyter_text>Remove incorrect data<jupyter_code># The problem is that there are duplicated [Route, Run, Sequences], that makes it impossible to
# split up the two apparent different bus routes with the same name
bus_sequences[bus_sequences.Route == 'UL29']
bus_sequences = bus_sequences[bus_sequences.Route != 'UL29']<jupyter_output><empty_output><jupyter_text>Number of stops The reference on LBSL stop code says:> *** KEY FIELD *** This is the alphanumeric identifier of a bus stop used by LBSL.This number is pretty similar to the "bus stop code", so it must be "double counting" stops by distinguishing direction<jupyter_code>bus_sequences.Stop_Code_LBSL.unique().shape<jupyter_output><empty_output><jupyter_text>The reference on Bus stop code says:> The area code for London is “1”.  As an example, the bus stop code for Southwark Station heading northbound is: 77293.  The NaPTAN NaptanCode for this stop is therefore 177293Should this "double count" stops by distinguishing direction?<jupyter_code>bus_sequences.Bus_Stop_Code.unique().shape
bus_sequences.Stop_Name.unique().shape
bus_sequences.Naptan_Atco.unique().shape
# number of stops per km2
18872 / 1_569
# number of stops per capita
18872 / 8_899_375<jupyter_output><empty_output><jupyter_text>Average number of stops per route Let's accomodate all the different measures...<jupyter_code>measures = [
    lambda x: x.Stop_Code_LBSL,
    lambda x: x.Bus_Stop_Code,
    lambda x: x.Stop_Name,
    lambda x: x.Naptan_Atco
]
for measure in measures:
    print(measure(bus_sequences.groupby('Route')).count().mean())
bus_sequences.groupby('Route').Stop_Code_LBSL.count().median()
counts = bus_sequences.groupby('Route').Stop_Code_LBSL.count()
sns.histplot(x=counts)
plt.xlabel('Number of stops')
plt.title('London')
sns.despine()
plt.savefig('figures/l_nstops.png')
counts.to_csv('gen/l_nstops.csv')<jupyter_output><empty_output><jupyter_text>Average route length The CRS is British National Grid (27700), which is in metres<jupyter_code>c = bus_sequences[['Location_Easting', 'Location_Northing']].apply(Point, axis=1)
c
bus_sequences['geometry'] = c
def to_linestring(x):
    geom = x.geometry
    if len(geom) == 1:
        return None
    return LineString(geom.reset_index(drop=True))

# For every route and run...
linestring_by_runs = bus_sequences.groupby(['Route', 'Run']).apply(to_linestring)
linestring_by_runs
route_lengths = linestring_by_runs.dropna().apply(lambda x: x.length)
route_lengths
# Global Average route length in metres
route_lengths.mean()
route_lengths.median()
fig, axes = plt.subplots(nrows=2, figsize=(10, 5), sharex=True, gridspec_kw={'height_ratios': [3, 1]})
plt.tight_layout()  # has to be before subplots_adjust
# Remove horizontal space between axes
fig.subplots_adjust(hspace=0)
sns.histplot(x=route_lengths,ax=axes[0])
sns.boxplot(x=route_lengths, ax=axes[1])
plt.xlabel('Route length (m)')
plt.title('London')
#plt.savefig('figures/l_rlengths.png')
sns.histplot(x=route_lengths)
sns.despine()
plt.title('London')
plt.xlabel('Route length (m)')
plt.savefig('figures/l_rlengths.png')
route_lengths.to_csv('gen/l_rlengths.csv')
route_lengths.sort_values(ascending=False).head()<jupyter_output><empty_output><jupyter_text>Map of bus network<jupyter_code>gdf = gpd.GeoDataFrame(linestring_by_runs.reset_index(drop=True), columns=['geometry'])
gdf.plot()
gdf.to_file('data/london/london_bus_routes.shp')<jupyter_output><empty_output><jupyter_text>Average distance between stops<jupyter_code># TODO: copied from delhi.ipynb
def calc_distances(x):
    '''
    input: [(float, float)]
        a list of coordinates
    output: [float]
        the distances between each coordinates
    '''
    return [Point(a).distance(Point(b)) for a, b in zip(x, x[1:])]
def f(x):
    geom = x.geometry
    if len(geom) == 1:
        return None
    return calc_distances(geom.reset_index(drop=True))

route_stop_dists = bus_sequences.groupby(['Route', 'Run']).apply(f)
route_stop_dists
route_avg_stop_dists = route_stop_dists.dropna().apply(np.mean)
route_avg_stop_dists
# Global average distance between stops meters
route_avg_stop_dists.mean()
# Using the median is better because it is resistant to outliers, see below
route_avg_stop_dists.median()
sns.boxplot(x=route_avg_stop_dists)
# The individual distances between every stops in every route
sns.boxplot(x=route_stop_dists.explode().values.astype(float))
exploded = route_stop_dists.explode().dropna()
#exploded.to_csv('gen/l_stop_dists.csv')
route_avg_stop_dists.to_csv('gen/l_stop_dists.csv')
every_run = exploded.values.astype(float)
every_run
fig, axes = plt.subplots(nrows=2, figsize=(10, 5), sharex=True, gridspec_kw={'height_ratios': [3, 1]})
plt.tight_layout()  # has to be before subplots_adjust
# Remove horizontal space between axes
fig.subplots_adjust(hspace=0)
sns.histplot(x=every_run,ax=axes[0])
sns.boxplot(x=every_run, ax=axes[1])
plt.xlabel('Distance between two stops (m)')
plt.savefig('figures/l_dist_b_stops.png')
exploded.sort_values(ascending=False).head(10)
LineString(bus_sequences[(bus_sequences.Route == 'X68') & (bus_sequences.Run == 1)].geometry.reset_index(drop=True))<jupyter_output><empty_output><jupyter_text>Median bus speed I don't have static time data for London buses (at least, not in this dataset)But tbf, in the delhi dataset, the time data is unreliable too, as it is the scheduled times not the actual times  Network diameter<jupyter_code>route_stop_coords = bus_sequences.groupby(['Route', 'Run'])
route_stop_coords
def f(x):
    return [(n, x, y) for n, x, y in zip(x.Stop_Code_LBSL, x.Location_Easting, x.Location_Northing)]
    
route_stop_coords = route_stop_coords.apply(f)
route_stop_coords
exploded = route_stop_coords.reset_index(drop=True).explode()
exploded
df = pd.DataFrame()
df['names'] = exploded.map(lambda x: x[0])
df['x'] = exploded.map(lambda x: x[1])
df['y'] = exploded.map(lambda x: x[2])
g = nx.from_dict_of_lists(df.to_dict())
nx.algorithms.distance_measures.diameter(g)<jupyter_output><empty_output><jupyter_text>Network density<jupyter_code>nx.classes.function.density(g)
nx.draw(g, node_size=5)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1223744292237443, 'fraction_numerical': 0.014155251141552512, 'mean_word_length': 6.869461077844312, 'pattern_counts': {'":': 0, '<': 45, '<?xml version=': 0, '>': 47, 'https://': 0, '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': '11', 'dir': 'jupyter-structured-clean-dedup', 'id': '545603', 'license': 'MIT', 'n_tokens_mistral': 2463, 'n_tokens_neox': 2324, 'n_words': 504, 'path': 'london.ipynb', 'repo_name': 'akazukin5151/network-and-accessiblity-analysis', 'size': '6570'}