<jupyter_start><jupyter_text>Read GTFS<jupyter_code>feed = importer.GtfsImporter(path=data + r'accra_trotro', dist_units='m')
feed = feed.clean()
feed.describe()
feed.map_stops(
    feed.stops.stop_id
)<jupyter_output><empty_output><jupyter_text>To simplify a GTFS or for modeling purposes, we often want to group trips that are not geographically identical, but close to each other.  The default method to build patterns only group geographically identical trips. It however possible to clusterize the station in order to broaden the definition of a pattern.   Stop clustering This method allows to clusterize the stops based on a distance threshold (and not a number of cluster threshold).It is however not efficient for large feeds.  Tested and working up to 5000 stops until now.<jupyter_code>feed.build_stop_clusters(
    distance_threshold=300  # It is also possible to use the parent stations
)<jupyter_output><empty_output><jupyter_text>This adds a cluster_id column to the stops<jupyter_code>feed.stops.head()
print('N stops:', len(feed.stops))
print('N clusters:', len(feed.stops.cluster_id.unique()))<jupyter_output>N stops: 4171
N clusters: 1095<jupyter_text>Map some stops and clusters<jupyter_code>from shapely.geometry import LineString
%matplotlib inline
from matplotlib import pyplot as plt
plt.rcParams['figure.figsize'] = (16, 6)
stops = feed.geometrize_stops(use_utm=True)
# Selection of close clusters and associated stops
c = feed.stops.sort_values(['stop_lon', 'stop_lat']).head(30)['cluster_id'].unique()
stops_to_plot = stops[stops['cluster_id'].isin(c)]
# GDF to plot
clusters = stops_to_plot.dissolve('cluster_id')
clusters.geometry = clusters.geometry.centroid
s_c = stops_to_plot.copy()
s_c.geometry = s_c.apply(
    lambda x: LineString(
        [x.geometry, clusters.loc[x['cluster_id'], 'geometry']]
    ),
    1
)
# Plot
s_c.plot(
    ax=clusters.plot(
        color='r', ax=stops_to_plot.plot(
            color='k'
        )
    )
)<jupyter_output><empty_output><jupyter_text>Patterns Patterns definition can be based on the cluster_id columns instead of the stop_id.<jupyter_code>feed.build_patterns(on='cluster_id')<jupyter_output><empty_output><jupyter_text>Directions It can also be used to derive the directions of the trips if they are not known<jupyter_code>feed.build_directions(
    on='cluster_id',
    all_trips=True  # With all_trips=False, we only compute undefined directions
)
feed.trips.head()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09862570735650768, 'fraction_numerical': 0.008488278092158448, 'mean_word_length': 5.725543478260869, 'pattern_counts': {'":': 0, '<': 24, '<?xml version=': 0, '>': 24, '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': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '354837', 'license': 'CECILL-B', 'n_tokens_mistral': 838, 'n_tokens_neox': 809, 'n_words': 224, 'path': 'examples-ipynb/gtfs_reader/.ipynb_checkpoints/4-advanced_patterns-checkpoint.ipynb', 'repo_name': 'systragroup/quetzal', 'size': '2474'}