Package: gtfs-functions
GTFS functionsThis package allows you to create various layers directly from the GTFS and visualize the results in the most straightforward way possible.Update November 2023:Possibility to check theservice_idfor a given date:parsed_calendar=Feed(gtfs_path).parse_calendar()or if you want it already grouped by date:date_service=Feed(gtfs_path).get_dates_service_id()Update August 2023:Possibility to parse the GTFS for a specific date range.feed=Feed(gtfs_path,start_date='2023-03-31',end_date='2023-04-04')Update March 2023:Removed dependency withpartridge. As much as we love this package and think it is absolutely great, removing a dependency gives us more control and keeps this package from failing whenever something changes inpartridge.We treat the GTFS as a class, where each file is a property. See examples below to find out how to work with it. We hope this simplifies your code.Fixed and enhancedsegment cutting. Shout out toMattijs De PaepeSupport to identify route patterns!! Check it out usingfeed.routes_patterns. Shout out toTobias BartschThe rest should stay the same.Warning!Make surestop_times.txthas noNullvalues in the columnsarrival_timeanddeparture_time. If this is not the case, some functions on this package might fail.Table of contentsInstallationGTFS parsingStop frequenciesLine frequenciesCut in Bus segmentsSpeedsSegment frequenciesMapping the resultsOther plotsPython versionThe package requirespython>=3.8. You can create a new environment with this version using conda:conda create -n new-env python=3.8InstallationYou can install the package running the following in your console:pip install gtfs_functionsImport the package in your script/notebookfromgtfs_functionsimportFeedGTFS ImportNow you can interact with your GTFS with the classFeed. Take a look at the class with?Feedto check what arguments you can specify.gtfs_path='data/sfmta.zip'# It also works with URL'sgtfs_path='https://transitfeeds.com/p/sfmta/60/latest/download'feed=Feed(gtfs_path,time_windows=[0,6,10,12,16,19,24])routes=feed.routesroutes.head(2)route_idagency_idroute_short_nameroute_long_nameroute_descroute_typeroute_urlroute_colorroute_text_color015761SFMTA1CALIFORNIA3https://SFMTA.com/1115766SFMTA5FULTON3https://SFMTA.com/5stops=feed.stopsstops.head(2)stop_idstop_codestop_namestop_desczone_idstop_urlgeometry03901039019th Avenue & Holloway StPOINT (-122.47510 37.72119)13016130163rd St & 4th StPOINT (-122.38979 37.77262)stop_times=feed.stop_timesstop_times.head(2)trip_idarrival_timedeparture_timestop_idstop_sequencestop_headsignpickup_typedrop_off_typeshape_dist_traveledroute_idservice_iddirection_idshape_idstop_codestop_namestop_desczone_idstop_urlgeometry0941314781840.081840.040151NaNNaN157611017992814015Clay St & Drumm StPOINT (-122.39682 37.79544)1941314781902.081902.062942NaNNaN157611017992816294Sacramento St & Davis StPOINT (-122.39761 37.79450)trips=feed.tripstrips.head(2)trip_idroute_idservice_iddirection_idshape_id095473461580410180140195473451580410180140shapes=feed.shapesshapes.head(2)shape_idgeometry0179928LINESTRING (-122.39697 37.79544, -122.39678 37...1179929LINESTRING (-122.39697 37.79544, -122.39678 37...Stop frequenciesReturns a geodataframe with the frequency for each combination ofstop,time of dayanddirection. Each row with aPointgeometry. The user can optionally specifycutoffsas a list in case the default is not good. Thesecutoffsshould be specified at the moment of reading theFeedclass. Thesecutoffsare the times of days to use as aggregation.time_windows=[0,6,9,15.5,19,22,24]feed=Feed(gtfs_path,time_windows=time_windows)stop_freq=feed.stops_freqstop_freq.head(2)stop_iddir_idwindowntripsmin_per_tripstop_namegeometry81575763Inbound0:00-6:001360Noriega St & 48th AvePOINT (-122.50785 37.75293)131027982Outbound0:00-6:001360Moscow St & RussiaAvetPOINT (-122.42996 37.71804)95396113Inbound0:00-6:001360Portola Dr & Laguna Honda BlvdPOINT (-122.45526 37.74310)126547719Inbound0:00-6:001360Middle Point & AcaciaPOINT (-122.37952 37.73707)95536116Inbound0:00-6:001360Portola Dr & San Pablo AvePOINT (-122.46107 37.74040)Line frequenciesReturns a geodataframe with the frequency for each combination ofline,time of dayanddirection. Each row with aLineStringgeometry. The user can optionally specifycutoffsas a list in case the default is not good. Thesecutoffsshould be specified at the moment of reading theFeedclass. Thesecutoffsare the times of days to use as aggregation.line_freq=feed.lines_freqline_freq.head()route_idroute_namedir_idwindowmin_per_tripntripsgeometry3761580844 O'SHAUGHNESSYInbound0:00-6:003601LINESTRING (-122.46459 37.78500, -122.46352 37...3781580844 O'SHAUGHNESSYInbound0:00-6:003601LINESTRING (-122.43416 37.73355, -122.43299 37...2421578725 TREASURE ISLANDInbound0:00-6:003601LINESTRING (-122.39611 37.79013, -122.39603 37...4511581454 FELTONInbound0:00-6:003601LINESTRING (-122.38845 37.73994, -122.38844 37...2411578725 TREASURE ISLANDInbound0:00-6:003601LINESTRING (-122.39542 37.78978, -122.39563 37...Bus segmentsReturns a geodataframe where each segment is a row and has aLineStringgeometry.segments_gdf=feed.segmentssegments_gdf.head(2)route_iddirection_idstop_sequencestart_stop_nameend_stop_namestart_stop_idend_stop_idsegment_idshape_idgeometrydistance_m01576101Clay St & Drumm StSacramento St & Davis St401562944015-6294179928LINESTRING (-122.39697 37.79544, -122.39678 37...205.28165311576102Sacramento St & Davis StSacramento St & Battery St629462906294-6290179928LINESTRING (-122.39761 37.79446, -122.39781 37...238.047505Scheduled SpeedsReturns a geodataframe with thespeed_kmhfor each combination ofroute,segment,time of dayanddirection. Each row with aLineStringgeometry. The user can optionally specifycutoffsas explained in previous sections.# Cutoffs to make get hourly valuesspeeds=feed.avg_speedsspeeds.head(1)route_idroute_namedirection_idsegment_idwindowspeed_kmhstart_stop_idstart_stop_nameend_stop_idend_stop_namedistance_mstop_sequenceruntime_secsegment_max_speed_kmhgeometry0157611 CALIFORNIAInbound4015-629410:00-11:0012.04015Clay St & Drumm St6294Sacramento St & Davis St205.281653161.912.0LINESTRING (-122.39697 37.79544, -122.39678 37...Segment frequenciessegments_freq=feed.segments_freqsegments_freq.head(2)route_idroute_namedirection_idsegment_namewindowmin_per_tripntripsstart_stop_idstart_stop_nameend_stop_namegeometry23191ALL_LINESAll linesNA3628-36220:00-6:0036013628Alemany Blvd & St Charles AveAlemany Blvd & Arch StLINESTRING (-122.46949 37.71045, -122.46941 37...61601578725 TREASURE ISLANDInbound7948-80170:00-6:0036017948Transit Center Bay 29Shoreline Access RoadLINESTRING (-122.39611 37.79013, -122.39603 37...Map your workStop frequencies# Stopsfromgtfs_functions.gtfs_plotsimportmap_gdfcondition_dir=stop_freq.dir_id=='Inbound'condition_window=stop_freq.window=='6:00-9:00'gdf=stop_freq.loc[(condition_dir&condition_window),:].reset_index()map_gdf(gdf=gdf,variable='ntrips',colors=["#d13870","#e895b3",'#55d992','#3ab071','#0e8955','#066a40'],tooltip_var=['min_per_trip'],tooltip_labels=['Frequency: '],breaks=[10,20,30,40,120,200])Line frequencies# Line frequenciesfromgtfs_functions.gtfs_plotsimportmap_gdfcondition_dir=line_freq.direction_id=='Inbound'condition_window=line_freq.window=='6:00-9:00'gdf=line_freq.loc[(condition_dir&condition_window),:].reset_index()map_gdf(gdf=gdf,variable='ntrips',colors=["#d13870","#e895b3",'#55d992','#3ab071','#0e8955','#066a40'],tooltip_var=['route_name'],tooltip_labels=['Route: '],breaks=[5,10,20,50])SpeedsIf you are looking to visualize data at the segment level for all lines I recommend you go with something more powerful like kepler.gl (AKA my favorite data viz library). For example, to check the scheduled speeds per segment:# Speedsimportkeplerglaskpm=kp.KeplerGl(data=dict(data=speeds,name='Speed Lines'),height=400)mSegment frequencies# Segment frequenciesimportkeplerglaskpm=kp.KeplerGl(data=dict(data=seg_freq,name='Segment frequency'),height=400)mOther plotsHistogram# Histogramimportplotly.expressaspxpx.histogram(stop_freq.loc[stop_freq.min_per_trip<50],x='frequency',title='Stop frequencies',template='simple_white',nbins=20)Heatmap# Heatmapimportplotly.graph_objectsasgodir_0=speeds.loc[(speeds.dir_id=='Inbound')&(speeds.route_name=='1 CALIFORNIA')].sort_values(by='stop_sequence')dir_0['hour']=dir_0.window.apply(lambdax:int(x.split(':')[0]))dir_0.sort_values(by='hour',ascending=True,inplace=True)fig=go.Figure(data=go.Heatmap(z=dir_0.speed_kmh,y=dir_0.start_stop_name,x=dir_0.window,hoverongaps=False,colorscale=px.colors.colorbrewer.RdYlBu,reversescale=False))fig.update_yaxes(title_text='Stop',autorange='reversed')fig.update_xaxes(title_text='Hour of day',side='top')fig.update_layout(showlegend=False,height=600,width=1000,title='Speed heatmap per direction and hour of the day')fig.show()Line chartby_hour=speeds.pivot_table('speed_kmh',index=['window'],aggfunc=['mean','std']).reset_index()by_hour.columns=['_'.join(col).strip()forcolinby_hour.columns.values]by_hour['hour']=by_hour.window_.apply(lambdax:int(x.split(':')[0]))by_hour.sort_values(by='hour',ascending=True,inplace=True)# Scatterfig=px.line(by_hour,x='window_',y='mean_speed_kmh',template='simple_white',#error_y = 'std_speed_kmh')fig.update_yaxes(rangemode='tozero')fig.show()