-
Notifications
You must be signed in to change notification settings - Fork 10
Usage: 5.2. Modifying Network: PT Schedule
GeNet has a number of methods to change the Schedule objects. Available as a jupyter notebook or wiki page. Make sure you validate the end result.
# read example network
import pandas as pd
from genet import Network, Stop, Route, Service, read_matsim
import os
from pprint import pprint
path_to_matsim_network = '../example_data/pt2matsim_network'
network = os.path.join(path_to_matsim_network, 'network.xml')
schedule = os.path.join(path_to_matsim_network, 'schedule.xml')
vehicles = os.path.join(path_to_matsim_network, 'vehicles.xml')
n = read_matsim(
path_to_network=network,
epsg='epsg:27700',
path_to_schedule=schedule,
path_to_vehicles=vehicles
)
# you don't need to read the vehicles file, but doing so ensures all vehicles
# in the schedule are of the expected type and the definition of the vehicle
# is preserved
n.print()
Graph info: Name:
Type: MultiDiGraph
Number of nodes: 1662
Number of edges: 3166
Average in degree: 1.9049
Average out degree: 1.9049
Schedule info: Schedule:
Number of services: 9
Number of routes: 68
Number of stops: 118
You can add Route
s and Service
s. To add a Route
, you need to identify which existing Service
it should come under.
You can make Routes by specifying a headway parameter, that will generate explicit trips for you:
route = Route(
route_short_name='N55',
mode='bus',
headway_spec={('07:00:00','08:00:00'): 15},
arrival_offsets=['00:00:00', '00:02:00', '00:04:00', '00:06:00'],
departure_offsets=['00:00:00', '00:02:00', '00:04:00', '00:06:00'],
id='new_route',
# route= ['834', '1573', '3139', '3141', '574', '3154', '979', '980', '981'],
await_departure= [True, True, True, True],
stops=[n.schedule.stop('490000235X'),
Stop(id='new_stop', x=529500, y=181300,
name='New Stop', epsg='epsg:27700'),
Stop(id='other_new_stop', x=529502, y=181302,
name='Other New Stop', epsg='epsg:27700'),
n.schedule.stop('490010689KB')]
)
route.trips
{'trip_id': ['new_route_07:00:00',
'new_route_07:15:00',
'new_route_07:30:00',
'new_route_07:45:00',
'new_route_08:00:00'],
'trip_departure_time': ['07:00:00',
'07:15:00',
'07:30:00',
'07:45:00',
'08:00:00'],
'vehicle_id': ['veh_bus_new_route_07:00:00',
'veh_bus_new_route_07:15:00',
'veh_bus_new_route_07:30:00',
'veh_bus_new_route_07:45:00',
'veh_bus_new_route_08:00:00']}
Or you can use provide the exact trips and vehicles on this route:
route = Route(
route_short_name='N55',
mode='bus',
trips={'trip_id': ['fun_trip_1', 'fun_trip_2'],
'trip_departure_time': ['03:53:00', '16:23:00'],
'vehicle_id': ['fun_bus_1', 'fun_bus_2']},
arrival_offsets=['00:00:00', '00:02:00', '00:04:00', '00:06:00'],
departure_offsets=['00:00:00', '00:02:00', '00:04:00', '00:06:00'],
id='new_route',
# route= ['834', '1573', '3139', '3141', '574', '3154', '979', '980', '981'],
await_departure= [True, True, True, True],
stops=[n.schedule.stop('490000235X'),
Stop(id='new_stop', x=529500, y=181300,
name='New Stop', epsg='epsg:27700'),
Stop(id='other_new_stop', x=529502, y=181302,
name='Other New Stop', epsg='epsg:27700'),
n.schedule.stop('490010689KB')]
)
You can create and add a new Service
, or add the route to an existing Service
new_service = Service(id='new_service', routes=[route])
new_service.print()
Service ID: new_service
Name: N55
Number of routes: 1
Number of stops: 4
n.schedule.add_service(Service(id='new_service', routes=[route]))
2022-07-14 15:49:53,678 - Added Services with IDs `['new_service']` and Routes: [['new_route']]
n.schedule.add_route('20274', route)
2022-07-14 15:49:54,010 - Route with ID `new_route` for Service 20274 within already exists in the Schedule. This Route will be reindexed to `20274_4`
2022-07-14 15:49:54,016 - Reindexed Route from new_route to 20274_4
2022-07-14 15:49:54,018 - Added Routes with IDs ['20274_4'], to Services `['20274']` within the Schedule
/Users/kasia.kozlowska/PycharmProjects/ABM/genet/genet/schedule_elements.py:1384: UserWarning: DataFrame columns are not unique, some columns will be omitted.
self.vehicles = {**df.T.to_dict(), **self.vehicles}
n.schedule['new_service'].print()
Service ID: new_service
Name: N55
Number of routes: 1
Number of stops: 4
n.schedule.route('20274_4').ordered_stops
['490000235X', 'new_stop', 'other_new_stop', '490010689KB']
You can also refer to existing stops in the Schedule
when creating aRoute
to be added. You can either just pass Stop IDs as strings or use a stop method on the schedule to take and use that stop object. Note that in the case of the former (passing ID strings), the route will not have the spatial information for those stops until it is added to the Schedule.
r = Route(
route_short_name='N55',
mode='bus',
trips={'trip_id': ['some_trip_1'],
'trip_departure_time': ['16:23:00'],
'vehicle_id': ['some_bus_2']},
arrival_offsets=['00:00:00', '00:06:00'],
departure_offsets=['00:00:00', '00:06:00'],
id='another_new_route',
# route= ['834', '1573', '3139', '3141', '574', '3154', '979', '980', '981'],
await_departure= [True, True],
stops=['490000235X.link:834',
'490010689KB.link:981']
)
r.graph().nodes['490000235X.link:834']
{'routes': {'another_new_route'}}
n.schedule.add_route('20274', r)
2022-07-14 15:49:54,284 - Added Routes with IDs ['another_new_route'], to Services `['20274']` within the Schedule
r.graph().nodes['490000235X.link:834']
{'services': {'14134', '18853', '20274'},
'routes': {'VJ12ba6089dfb2733e29c415a1a0015fef30fd5305',
'VJ256e98df611ff48afe737ddc81cbcde82e4e81c8',
'VJ2aba67e3ed98f2ed5f5966c1ac394cbf6d1943d7',
'VJ375a660d47a2aa570aa20a8568012da8497ffecf',
'VJ4e2b897edf0e7b8a8e3b5516ab43ce56f72c5cff',
'VJa7f37392e276aeac26c7e73bbc05e6a71af38dba',
'VJd78967364a302cf232c5139d40622dcb6c238c9e',
'VJdf3936da1a51eb33db594ef99738802c14b19995',
'VJf3e316e5e605bb512147dee2a989be5a82ef1b5f',
'VJf9a22035ae6f25bb420df833474943ad76065c89',
'another_new_route'},
'id': '490000235X.link:834',
'x': 529981.7958802709,
'y': 181412.0975758662,
'epsg': 'epsg:27700',
'name': 'Tottenham Court Road Station (Stop X)',
'lon': -0.12809598708996447,
'lat': 51.51668503324075,
's2_id': 5221390722025467597,
'linkRefId': '834',
'isBlocking': 'false'}
r = Route(
route_short_name='N55',
mode='bus',
trips={'trip_id': ['some_trip_1'],
'trip_departure_time': ['16:23:00'],
'vehicle_id': ['some_bus_2']},
arrival_offsets=['00:00:00', '00:06:00'],
departure_offsets=['00:00:00', '00:06:00'],
id='another_new_route_2',
# route= ['834', '1573', '3139', '3141', '574', '3154', '979', '980', '981'],
await_departure= [True, True],
stops=[n.schedule.stop('490000235X.link:834'),
n.schedule.stop('490010689KB.link:981')]
)
n.schedule.add_route('20274', r)
2022-07-14 15:49:54,612 - Added Routes with IDs ['another_new_route_2'], to Services `['20274']` within the Schedule
Note that for a Schedule to be a valid MATSim network, each stop referred to by a route needs a linkRefId
attribute which links the stop to the Network
.
Trying to add Stops with IDs already in the Schedule will result in an error, unless the force=True
is set. The added route/service will inherit the data stored under those stops. The idea is that you can either specify the Stops in the route/service to be added correctly, or if they are to be changed, you use a dedicated method - check section 'Modifying data' below.
route = Route(
route_short_name='N55',
mode='bus',
trips={'trip_id': ['fun_trip_1', 'fun_trip_2'],
'trip_departure_time': ['03:53:00', '16:23:00'],
'vehicle_id': ['fun_bus_1', 'fun_bus_2']},
arrival_offsets=['00:00:00', '00:02:00', '00:04:00', '00:06:00'],
departure_offsets=['00:00:00', '00:02:00', '00:04:00', '00:06:00'],
id='another_new_route_3',
# route= ['834', '1573', '3139', '3141', '574', '3154', '979', '980', '981'],
await_departure= [True, True, True, True],
stops=[
Stop(id='490000235X.link:834', x=529981, y=181412, epsg='epsg:27700'),
Stop(id='new_stop', x=529500, y=181300, epsg='epsg:27700', name='New Stop'),
Stop(id='other_new_stop', x=529502, y=181302, epsg='epsg:27700', name='Other New Stop'),
Stop(id='490010689KB.link:981', x=529166, y=181256, epsg='epsg:27700')
]
)
n.schedule.add_route('20274', route, force=True)
2022-07-14 15:49:54,944 - The following stops will inherit the data currently stored under those Stop IDs in the Schedule: ['490000235X.link:834', '490010689KB.link:981'].
2022-07-14 15:49:54,946 - Added Routes with IDs ['another_new_route_3'], to Services `['20274']` within the Schedule
Note the message above: The following stops will inherit the data currently stored under those Stop IDs in the Schedule: ['490000235X.link:834', '490010689KB.link:981'].
NOTE: adding routes and services results in new vehicles (unless you reuse the ones already in the Schedule---beware that the same vehicle cannot service multiple trips at the same time, genet does not currently have checks for this, the user needs to be mindful of the physics of shared vehicles). New vehicles need definitions, you can add them yourself to schedule.vehicles['vehicle_id'] = {'type': 'bus'}
ensuring this vehicle type is defined in schedule.vehicle_types['bus']
, or you can use a genet method to generate those vehicles, the type will be derived from the mode of the route. Then you can check if all of the types that vehicles are referring to have definitions.
len(n.schedule.vehicles)
13293
n.schedule.generate_vehicles()
n.schedule.validate_vehicle_definitions()
2022-07-14 15:49:55,588 - The following vehicle types are missing from the `vehicle_types` attribute: {'bus'}
2022-07-14 15:49:55,589 - Vehicles affected by missing vehicle types: {'fun_bus_1': {'type': 'bus'}, 'fun_bus_2': {'type': 'bus'}, 'some_bus_2': {'type': 'bus'}}
False
len(n.schedule.vehicles)
13293
n.schedule.change_log().tail()
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
timestamp | change_event | object_type | old_id | new_id | old_attributes | new_attributes | diff | |
---|---|---|---|---|---|---|---|---|
0 | 2022-07-14 15:49:53 | add | service | None | new_service | None | {'id': 'new_service', 'name': 'N55'} | [(add, , [('id', 'new_service'), ('name', 'N55... |
1 | 2022-07-14 15:49:54 | add | route | None | 20274_4 | None | {'route_short_name': 'N55', 'mode': 'bus', 'ar... | [(add, , [('route_short_name', 'N55'), ('mode'... |
2 | 2022-07-14 15:49:54 | add | route | None | another_new_route | None | {'route_short_name': 'N55', 'mode': 'bus', 'ar... | [(add, , [('route_short_name', 'N55'), ('mode'... |
3 | 2022-07-14 15:49:54 | add | route | None | another_new_route_2 | None | {'route_short_name': 'N55', 'mode': 'bus', 'ar... | [(add, , [('route_short_name', 'N55'), ('mode'... |
4 | 2022-07-14 15:49:54 | add | route | None | another_new_route_3 | None | {'route_short_name': 'N55', 'mode': 'bus', 'ar... | [(add, , [('route_short_name', 'N55'), ('mode'... |
There are no methods to add Stop
objects on their own. They are added to the Schedule with Route
and Service
objects.
You can use methods in GeNet to relate the Stops of PT services and find network routes between them. First you need to know the ID of the Service you want to snap (you can also snap the entire schedule, but depending on the size and complexity of your network and schedule it might take a long time). GeNet will then relate all stops in that service to a link in the Network graph and route all of the Route
s of the Service
object. It will do this for directed subsets (subgraphs) of the Service (e.g. if you have a Northbound and Southboud service, the stops may have to find different links in the graph)
There are a lot of different parameters you can add to this method, that give you a bit more control.
-
solver
: You can specify different mathematical solvers. For example GLPK, an open source solver which can be found here: https://www.gnu.org/software/glpk/. Another good open source choice is CBC: https://projects.coin-or.org/Cbc. You specify it as a string e.g. 'glpk', 'cbc', 'gurobi'. The solver needs to support MILP - mixed integer linear programming -
allow_partial
: Sometimes there isn't a link available for snapping within threshold for all stops. If allowed (default) an artificial self-loop link will be created as well as any connecting links to that unsnapped stop, under modal conditions. If set to False and the problem is partial, it will raise PartialMaxStableSetProblem error instead. -
distance_threshold
,step_size
: You can vary the threshold for snapping stops to links. There are two parameters, the overall threshold and a step size; when snapping, the search area for links increases in steps until some links are found, we don't use the threshold right away because we don't want to have too many choices for snapping (makes it a little less heavy computationally)). -
additional_modes
: You can specify additional modes (when snapping, genet will consider a modal subset of the network that matches the pt service, e.g. bus, but you might want to use links that allow cars too). -
allow_directional_split
: You can opt for splitting the problem of snapping by direction. GeNet will then solve a series of problems for subgraphs (disjoint w.r.t. edges) of the Service. This is useful for rail type services that might share the same stop regardless of direction (in comparison to buses which have distinct bus stops, depending on which way they're travelling).
Check the methods' doc strings for up to date details.
n.schedule['new_service'].route('new_route').route
[]
n.schedule['new_service'].route('new_route').ordered_stops
['490000235X', 'new_stop', 'other_new_stop', '490010689KB']
n.route_service('new_service')
2022-07-14 15:49:57,865 - Routing Service new_service with modes = {'bus'}
2022-07-14 15:49:57,899 - Building Maximum Stable Set for PT graph with 4 stops and 3 edges
2022-07-14 15:49:58,036 - This Maximum Stable Set Problem is partially viable.
2022-07-14 15:49:58,037 - Maximum Stable Set problem to snap the PT graph to the network is partially viable, meaning not all stops have found a link to snap to within the distance_threshold.Partial snapping is ON, this problem will proceed to the solver.
2022-07-14 15:49:58,038 - Passing problem to solver
2022-07-14 15:49:58,041 - Initializing ordered Set vertices with a fundamentally unordered data source (type: set). This WILL potentially lead to nondeterministic behavior in Pyomo
2022-07-14 15:49:58,047 - Passing problem to solver
2022-07-14 15:49:58,374 - Successfully snapped 3 stops to network links.
2022-07-14 15:49:58,390 - Stop ID changes detected for Routes: {'new_route'}
2022-07-14 15:49:58,394 - Changed Route attributes for 1 routes
2022-07-14 15:49:58,399 - Added 1 nodes
2022-07-14 15:49:58,505 - Generated 0 link ids.
2022-07-14 15:49:58,510 - Added 2 links
2022-07-14 15:49:58,513 - Changed Link attributes for 4 links
n.schedule['new_service'].route('new_route').route
['artificial_link===from:490000235X===to:490000235X',
'artificial_link===from:490000235X===to:9521035',
'3154',
'979',
'980',
'981']
n.schedule['new_service'].route('new_route').ordered_stops
['490000235X.link:artificial_link===from:490000235X===to:490000235X',
'new_stop.link:3154',
'other_new_stop.link:3154',
'490010689KB.link:981']
n.schedule['new_service'].reindex(new_id='more_appropriate_id')
2022-07-14 15:49:58,565 - Reindexed Service from new_service to more_appropriate_id
n.schedule.route('new_route').reindex(new_id='more_appropriate_route_id')
2022-07-14 15:49:58,583 - Reindexed Route from new_route to more_appropriate_route_id
n.schedule.change_log().tail()
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
timestamp | change_event | object_type | old_id | new_id | old_attributes | new_attributes | diff | |
---|---|---|---|---|---|---|---|---|
3 | 2022-07-14 15:49:54 | add | route | None | another_new_route_2 | None | {'route_short_name': 'N55', 'mode': 'bus', 'ar... | [(add, , [('route_short_name', 'N55'), ('mode'... |
4 | 2022-07-14 15:49:54 | add | route | None | another_new_route_3 | None | {'route_short_name': 'N55', 'mode': 'bus', 'ar... | [(add, , [('route_short_name', 'N55'), ('mode'... |
5 | 2022-07-14 15:49:58 | modify | route | new_route | new_route | {'route_short_name': 'N55', 'mode': 'bus', 'ar... | {'route_short_name': 'N55', 'mode': 'bus', 'ar... | [(add, route, [(0, 'artificial_link===from:490... |
6 | 2022-07-14 15:49:58 | modify | service | new_service | more_appropriate_id | {'id': 'new_service'} | {'id': 'more_appropriate_id'} | [(change, id, (new_service, more_appropriate_i... |
7 | 2022-07-14 15:49:58 | modify | route | new_route | more_appropriate_route_id | {'id': 'new_route'} | {'id': 'more_appropriate_route_id'} | [(change, id, (new_route, more_appropriate_rou... |
n.schedule.remove_service('more_appropriate_id')
/Users/kasia.kozlowska/PycharmProjects/ABM/genet/genet/schedule_elements.py:1384: UserWarning: DataFrame columns are not unique, some columns will be omitted.
self.vehicles = {**df.T.to_dict(), **self.vehicles}
2022-07-14 15:49:58,899 - Removed Services with IDs `more_appropriate_id`, and Routes: {'more_appropriate_route_id'}
for route_id in {'another_new_route', 'another_new_route_2', 'another_new_route_3'}:
n.schedule.remove_route(route_id)
2022-07-14 15:49:59,144 - Removed Routes with IDs ['another_new_route_2'], to Services `20274`.
2022-07-14 15:49:59,467 - Removed Routes with IDs ['another_new_route'], to Services `20274`.
2022-07-14 15:49:59,892 - Removed Routes with IDs ['another_new_route_3'], to Services `20274`.
n.schedule.change_log().tail()
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
timestamp | change_event | object_type | old_id | new_id | old_attributes | new_attributes | diff | |
---|---|---|---|---|---|---|---|---|
7 | 2022-07-14 15:49:58 | modify | route | new_route | more_appropriate_route_id | {'id': 'new_route'} | {'id': 'more_appropriate_route_id'} | [(change, id, (new_route, more_appropriate_rou... |
8 | 2022-07-14 15:49:58 | remove | service | more_appropriate_id | None | {'id': 'more_appropriate_id', 'name': 'N55'} | None | [(remove, , [('id', 'more_appropriate_id'), ('... |
9 | 2022-07-14 15:49:58 | remove | route | another_new_route_2 | None | {'route_short_name': 'N55', 'mode': 'bus', 'ar... | None | [(remove, , [('route_short_name', 'N55'), ('mo... |
10 | 2022-07-14 15:49:59 | remove | route | another_new_route | None | {'route_short_name': 'N55', 'mode': 'bus', 'ar... | None | [(remove, , [('route_short_name', 'N55'), ('mo... |
11 | 2022-07-14 15:49:59 | remove | route | another_new_route_3 | None | {'route_short_name': 'N55', 'mode': 'bus', 'ar... | None | [(remove, , [('route_short_name', 'N55'), ('mo... |
You can also remove Stop
s. This will disconnect Route
s and Service
s using that Stop
s and likely render them invalid. The method will warn you which Route
s and Servce
s are affected.
n.schedule.remove_stop('new_stop')
2022-07-14 15:49:59,946 - Removed Stops with indices `['new_stop']`.Routes affected: {'20274_4'}. Services affected: {'20274'}.
You can also remove Stop
s. This will disconnect Route
s and Service
s using that Stop
s and likely render them invalid. The method will warn you which Route
s and Servce
s are affected.
n.schedule.remove_unused_stops()
2022-07-14 15:49:59,978 - Removed Stops with indices `['9400ZZLUWRR2', 'new_stop.link:3154', '490000235W1', '9400ZZLUESQ1', '9400ZZLUESQ2', '9400ZZLUGDG1', '9400ZZLUTCR1', '490000235X.link:artificial_link===from:490000235X===to:490000235X', '490015196N', '9400ZZLUWRR1', '9400ZZLUWRR4', '9400ZZLUOXC1', '9400ZZLUGPS2', '9400ZZLUOXC3', '9400ZZLURGP1', '490000235N', '9400ZZLURGP2', '9400ZZLUOXC4', '490000091E', '490000173RD', '9400ZZLUWRR3', '9400ZZLUTCR4', '490000356NE', '490010198W', '490015196R', '490019675D', '490000252R', '490000252S', 'other_new_stop.link:3154', '9400ZZLUOXC2', '490000173RF', '9400ZZLUOXC5', '490000091F', '490011126K', '9400ZZLUOXC6', '9400ZZLUTCR3', '490013600C']`.Routes affected: set(). Services affected: set().
Applying changes or new attributes to Services, Routes and Stops can be done via Schedule level methods. They all work with a dictionary where the keys are the object IDs and the values are dictionaries holding attribute names and values. The method to extract a DataFrame on attributes comes in handy here. E.g.
df = n.schedule.service_attribute_data(keys='name')
df.head()
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
name | |
---|---|
14134 | 98 |
20274 | N55 |
18853 | N8 |
15234 | 134 |
17732 | N20 |
DataFrames are easy to work with. Youcould for exmaple manipulate the names or use other data to change these. For demonstration here, let's just set the names to something easy.
df['name'] = df['name'].apply(lambda x: f'Service_{x}')
df.head()
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
name | |
---|---|
14134 | Service_98 |
20274 | Service_N55 |
18853 | Service_N8 |
15234 | Service_134 |
17732 | Service_N20 |
You can then convert this to a dictionary and pass it to the apply_attributes_to_services
method.
n.schedule.apply_attributes_to_services(df.T.to_dict())
2022-07-14 15:50:00,085 - Changed Service attributes for 9 services
n.schedule.change_log().tail()
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
timestamp | change_event | object_type | old_id | new_id | old_attributes | new_attributes | diff | |
---|---|---|---|---|---|---|---|---|
54 | 2022-07-14 15:50:00 | modify | service | 17732 | 17732 | {'id': '17732', 'name': 'N20'} | {'id': '17732', 'name': 'Service_N20'} | [(change, name, (N20, Service_N20))] |
55 | 2022-07-14 15:50:00 | modify | service | 12430 | 12430 | {'id': '12430', 'name': '205'} | {'id': '12430', 'name': 'Service_205'} | [(change, name, (205, Service_205))] |
56 | 2022-07-14 15:50:00 | modify | service | 15660 | 15660 | {'id': '15660', 'name': '113'} | {'id': '15660', 'name': 'Service_113'} | [(change, name, (113, Service_113))] |
57 | 2022-07-14 15:50:00 | modify | service | 18915 | 18915 | {'id': '18915', 'name': 'N5'} | {'id': '18915', 'name': 'Service_N5'} | [(change, name, (N5, Service_N5))] |
58 | 2022-07-14 15:50:00 | modify | service | 14073 | 14073 | {'id': '14073', 'name': '94'} | {'id': '14073', 'name': 'Service_94'} | [(change, name, (94, Service_94))] |
You can do the same for Routes
and Stops
. Your dictionaries cannot however hold changes to indices. You will encounter an error and should use reindex
methods for such operations.
n.schedule.apply_attributes_to_routes(
{'VJ375a660d47a2aa570aa20a8568012da8497ffecf': {
'name': 'my_favourite_route',
'mode': 'piggyback'
}}
)
2022-07-14 15:50:00,132 - Changed Route attributes for 1 routes
n.schedule.apply_attributes_to_stops(
{'490000235YB.link:574': {'new_attribute': 'hello!'}}
)
2022-07-14 15:50:00,148 - Changed Stop attributes for 1 stops
n.schedule.change_log().tail()
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
timestamp | change_event | object_type | old_id | new_id | old_attributes | new_attributes | diff | |
---|---|---|---|---|---|---|---|---|
56 | 2022-07-14 15:50:00 | modify | service | 15660 | 15660 | {'id': '15660', 'name': '113'} | {'id': '15660', 'name': 'Service_113'} | [(change, name, (113, Service_113))] |
57 | 2022-07-14 15:50:00 | modify | service | 18915 | 18915 | {'id': '18915', 'name': 'N5'} | {'id': '18915', 'name': 'Service_N5'} | [(change, name, (N5, Service_N5))] |
58 | 2022-07-14 15:50:00 | modify | service | 14073 | 14073 | {'id': '14073', 'name': '94'} | {'id': '14073', 'name': 'Service_94'} | [(change, name, (94, Service_94))] |
59 | 2022-07-14 15:50:00 | modify | route | VJ375a660d47a2aa570aa20a8568012da8497ffecf | VJ375a660d47a2aa570aa20a8568012da8497ffecf | {'route_short_name': 'N55', 'mode': 'bus', 'ar... | {'route_short_name': 'N55', 'mode': 'piggyback... | [(change, mode, (bus, piggyback)), (add, , [('... |
60 | 2022-07-14 15:50:00 | modify | stop | 490000235YB.link:574 | 490000235YB.link:574 | {'services': {'20274', '18853', '14134'}, 'rou... | {'services': {'20274', '18853', '14134'}, 'rou... | [(add, , [('new_attribute', 'hello!')])] |
You can use trips_to_dataframe
to extract all of the trips, their departures and vehicle IDs associated with the trips in the schedule. Trip ids need not be unique, route IDs provide a secondary index. Associated service IDs are also given for convenience.
trips = n.schedule.trips_to_dataframe(gtfs_day='20210101')
trips.head()
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
mode | service_id | route_id | trip_id | trip_departure_time | vehicle_id | |
---|---|---|---|---|---|---|
0 | bus | 18853 | VJ8cacca9a6722c497c413005568182ecf4d50b160 | VJ2e4c6d83aa4dfd4b71ae27a9568cd3a4d0598f6d_05:... | 2021-01-01 05:25:00 | veh_2205_bus |
1 | bus | 18853 | VJ8cacca9a6722c497c413005568182ecf4d50b160 | VJ4fef3d3f34855f68dab603441550f4e487b0b9b7_03:... | 2021-01-01 03:45:00 | veh_2206_bus |
2 | bus | 18853 | VJ8cacca9a6722c497c413005568182ecf4d50b160 | VJ8cacca9a6722c497c413005568182ecf4d50b160_05:... | 2021-01-01 05:05:00 | veh_2207_bus |
3 | bus | 18853 | VJ8cacca9a6722c497c413005568182ecf4d50b160 | VJ99c16d764175d7337e6a4eb4e96b9f86d229c48f_03:... | 2021-01-01 03:25:00 | veh_2208_bus |
4 | bus | 18853 | VJ8cacca9a6722c497c413005568182ecf4d50b160 | VJb539a6d218dff4d1beedc043251816c61261a18b_04:... | 2021-01-01 04:05:00 | veh_2209_bus |
Let's change all of the trip ids to something shorter
trips['trip_id'] = 'trip_' + trips.index.to_series().astype(str)
trips.head()
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
mode | service_id | route_id | trip_id | trip_departure_time | vehicle_id | |
---|---|---|---|---|---|---|
0 | bus | 18853 | VJ8cacca9a6722c497c413005568182ecf4d50b160 | trip_0 | 2021-01-01 05:25:00 | veh_2205_bus |
1 | bus | 18853 | VJ8cacca9a6722c497c413005568182ecf4d50b160 | trip_1 | 2021-01-01 03:45:00 | veh_2206_bus |
2 | bus | 18853 | VJ8cacca9a6722c497c413005568182ecf4d50b160 | trip_2 | 2021-01-01 05:05:00 | veh_2207_bus |
3 | bus | 18853 | VJ8cacca9a6722c497c413005568182ecf4d50b160 | trip_3 | 2021-01-01 03:25:00 | veh_2208_bus |
4 | bus | 18853 | VJ8cacca9a6722c497c413005568182ecf4d50b160 | trip_4 | 2021-01-01 04:05:00 | veh_2209_bus |
You can set_trips_dataframe
which takes this dataframe and applies changes to all route trips based on the data in the dataframe. This means you can generate this DataFrame as shown below, manipulate trips (delete them, add new ones), change their departure times or change their vehicle ids to be shared for differnt trips, perhaps on some temporal logic and as long as the dataframe has the same schema, you can use it to set new trips in the schedule. This will appear in the changelog as a route level modify event.
Nb removing all trips of the same route from the dataframe will have no effect when being applied. If there is data in the dataframe for a route, all of its trips will be replaced by the data in the dataframe, and if there is no data for a route in the frame, no changes will be applied to that route (i.e. the trips attribute for routes missing from the dataframe will not be set as empty).
n.schedule.set_trips_dataframe(trips)
n.schedule.route_attribute_data(keys=[{'trips': 'trip_id'}]).head()
2022-07-14 15:50:00,520 - Changed Route attributes for 69 routes
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
trips::trip_id | |
---|---|
VJ8cacca9a6722c497c413005568182ecf4d50b160 | [trip_0, trip_1, trip_2, trip_3, trip_4, trip_... |
VJ2aba67e3ed98f2ed5f5966c1ac394cbf6d1943d7 | [trip_8, trip_9, trip_10, trip_11, trip_12, tr... |
VJ1a8cc306354fdc322d739ae644eb73444341d08d | [trip_78, trip_79, trip_80, trip_81, trip_82, ... |
VJ5b511605b1e07428c2e0a7d676d301c6c40dcca6 | [trip_94, trip_95, trip_96, trip_97, trip_98, ... |
VJe18efadf172576fea7989ec1f233f26854c0f66a | [trip_108, trip_109, trip_110, trip_111, trip_... |
You can replace old trips using headway information. This is useful when creating scenario networks. You can do it to a Route or Service (by specifying the route ID to be changed within) objects.
route_id = 'VJ6cf76a4c03cca468cb6954db7f7aad5ae189df13'
n.schedule.route(route_id).trips
{'trip_id': ['trip_1358', 'trip_1359'],
'trip_departure_time': ['07:51:00', '22:50:00'],
'vehicle_id': ['veh_887_bus', 'veh_888_bus']}
n.schedule.generate_trips_from_headway(
route_id=route_id, headway_spec={('07:51:00', '22:50:00'): 120}) # headway in minutes
2022-07-14 15:50:00,583 - Changed Route attributes for 1 routes
n.schedule.route(route_id).trips
{'trip_id': ['VJ6cf76a4c03cca468cb6954db7f7aad5ae189df13_07:51:00',
'VJ6cf76a4c03cca468cb6954db7f7aad5ae189df13_09:51:00',
'VJ6cf76a4c03cca468cb6954db7f7aad5ae189df13_11:51:00',
'VJ6cf76a4c03cca468cb6954db7f7aad5ae189df13_13:51:00',
'VJ6cf76a4c03cca468cb6954db7f7aad5ae189df13_15:51:00',
'VJ6cf76a4c03cca468cb6954db7f7aad5ae189df13_17:51:00',
'VJ6cf76a4c03cca468cb6954db7f7aad5ae189df13_19:51:00',
'VJ6cf76a4c03cca468cb6954db7f7aad5ae189df13_21:51:00'],
'trip_departure_time': ['07:51:00',
'09:51:00',
'11:51:00',
'13:51:00',
'15:51:00',
'17:51:00',
'19:51:00',
'21:51:00'],
'vehicle_id': ['veh_Bus_VJ6cf76a4c03cca468cb6954db7f7aad5ae189df13_07:51:00',
'veh_Bus_VJ6cf76a4c03cca468cb6954db7f7aad5ae189df13_09:51:00',
'veh_Bus_VJ6cf76a4c03cca468cb6954db7f7aad5ae189df13_11:51:00',
'veh_Bus_VJ6cf76a4c03cca468cb6954db7f7aad5ae189df13_13:51:00',
'veh_Bus_VJ6cf76a4c03cca468cb6954db7f7aad5ae189df13_15:51:00',
'veh_Bus_VJ6cf76a4c03cca468cb6954db7f7aad5ae189df13_17:51:00',
'veh_Bus_VJ6cf76a4c03cca468cb6954db7f7aad5ae189df13_19:51:00',
'veh_Bus_VJ6cf76a4c03cca468cb6954db7f7aad5ae189df13_21:51:00']}
If you have some logic that can be written into a function of object's attributes, you can pass this to apply_function_to_x
methods. You need to select location
, which refers to the name of the attribute the result should be stored under. It can already exist and be overwritten. The function passed, is not expected to work with all objects. It will fail silently, only evaluating and generating outputs where possible.
from shapely.geometry import Point
def add_shapely_geometry_points(stop_attribs):
return Point(stop_attribs['x'], stop_attribs['y'])
n.schedule.apply_function_to_stops(add_shapely_geometry_points, location='geometry')
2022-07-14 15:50:00,627 - Changed Stop attributes for 85 stops
n.schedule.change_log().tail(2)
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
timestamp | change_event | object_type | old_id | new_id | old_attributes | new_attributes | diff | |
---|---|---|---|---|---|---|---|---|
214 | 2022-07-14 15:50:00 | modify | stop | 490002124ZZ | 490002124ZZ | {'id': '490002124ZZ', 'x': 529737.7933655808, ... | {'id': '490002124ZZ', 'x': 529737.7933655808, ... | [(add, , [('geometry', <shapely.geometry.point... |
215 | 2022-07-14 15:50:00 | modify | stop | other_new_stop | other_new_stop | {'services': {'20274'}, 'routes': {'20274_4'},... | {'services': {'20274'}, 'routes': {'20274_4'},... | [(add, , [('geometry', <shapely.geometry.point... |
n.schedule.stop_attribute_data(keys=['name', 'x', 'y', 'geometry']).head()
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
name | x | y | geometry | |
---|---|---|---|---|
490000235X.link:834 | Tottenham Court Road Station (Stop X) | 529981.795880 | 181412.097576 | POINT (529981.7958802709 181412.0975758662) |
490000235YB.link:574 | Oxford Street Soho Street (Stop YB) | 529570.781323 | 181336.281593 | POINT (529570.7813227688 181336.2815925331) |
490014214HE.link:3154 | Wardour Street (Stop OM) | 529477.750156 | 181314.437043 | POINT (529477.7501560802 181314.4370430721) |
490010689KB.link:981 | Great Titchfield Street Oxford Circus Station... | 529166.734973 | 181256.336723 | POINT (529166.7349732723 181256.3367228433) |
490010689OJ.link:1787 | Great Titchfield Street Oxford Circus Station... | 529227.773057 | 181280.477507 | POINT (529227.7730568129 181280.4775071898) |
from geopandas import GeoDataFrame
GeoDataFrame(n.schedule.stop_attribute_data(keys='geometry')).plot()
<matplotlib.axes._subplots.AxesSubplot at 0x7fadaf468c50>
n.schedule.stop('490000235YB.link:574').__dict__
{'id': '490000235YB.link:574',
'x': 529570.7813227688,
'y': 181336.2815925331,
'epsg': 'epsg:27700',
'name': 'Oxford Street Soho Street (Stop YB)',
'lat': 51.51609803324077,
'lon': -0.13404398709291904,
's2_id': 5221390696959560815,
'linkRefId': '574',
'isBlocking': 'false',
'new_attribute': 'hello!',
'geometry': <shapely.geometry.point.Point at 0x7fadaf3bec10>}
For this let's say we want to reduce the number of trips. For simplicity of demonstration we don't have about which trips we delete, but logic around timings of trips can be added in this function, as the trips are saved as one of routes attributes (check out the summary methods)
# before
len(n.schedule.route_trips_to_dataframe())
2022-07-14 15:50:00,895 - `route_trips_to_dataframe` method is deprecated and will be replaced by `trips_to_dataframe`in later versions.
1570
def reduce_trips(attribs):
# just delete any other trip
attribs['trips']['trip_id'] = attribs['trips']['trip_id'][::2]
attribs['trips']['trip_departure_time'] = attribs['trips']['trip_departure_time'][::2]
attribs['trips']['vehicle_id'] = attribs['trips']['vehicle_id'][::2]
return attribs['trips']
n.schedule.apply_function_to_routes(reduce_trips, 'trips')
2022-07-14 15:50:00,959 - Changed Route attributes for 69 routes
# after
len(n.schedule.route_trips_to_dataframe())
2022-07-14 15:50:00,965 - `route_trips_to_dataframe` method is deprecated and will be replaced by `trips_to_dataframe`in later versions.
796
Note, this could also be done using the route_trips_to_dataframe
and set_route_trips_dataframe
mentioned above.
Let's give an example of using a mapping. We can re-use the service name DataFrame we generated above.
df['new_name'] = 'Brand_new_name' + df['name']
df.head()
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
name | new_name | |
---|---|---|
14134 | Service_98 | Brand_new_nameService_98 |
20274 | Service_N55 | Brand_new_nameService_N55 |
18853 | Service_N8 | Brand_new_nameService_N8 |
15234 | Service_134 | Brand_new_nameService_134 |
17732 | Service_N20 | Brand_new_nameService_N20 |
name_map = dict(zip(df['name'], df['new_name']))
name_map
{'Service_98': 'Brand_new_nameService_98',
'Service_N55': 'Brand_new_nameService_N55',
'Service_N8': 'Brand_new_nameService_N8',
'Service_134': 'Brand_new_nameService_134',
'Service_N20': 'Brand_new_nameService_N20',
'Service_205': 'Brand_new_nameService_205',
'Service_113': 'Brand_new_nameService_113',
'Service_N5': 'Brand_new_nameService_N5',
'Service_94': 'Brand_new_nameService_94'}
In this case, location
refers to the attribute to be mapped.
n.schedule.apply_function_to_services(name_map, location='name')
2022-07-14 15:50:01,017 - Changed Service attributes for 9 services
n.schedule.change_log().tail()
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
timestamp | change_event | object_type | old_id | new_id | old_attributes | new_attributes | diff | |
---|---|---|---|---|---|---|---|---|
289 | 2022-07-14 15:50:01 | modify | service | 17732 | 17732 | {'id': '17732', 'name': 'Service_N20'} | {'id': '17732', 'name': 'Brand_new_nameService... | [(change, name, (Service_N20, Brand_new_nameSe... |
290 | 2022-07-14 15:50:01 | modify | service | 12430 | 12430 | {'id': '12430', 'name': 'Service_205'} | {'id': '12430', 'name': 'Brand_new_nameService... | [(change, name, (Service_205, Brand_new_nameSe... |
291 | 2022-07-14 15:50:01 | modify | service | 15660 | 15660 | {'id': '15660', 'name': 'Service_113'} | {'id': '15660', 'name': 'Brand_new_nameService... | [(change, name, (Service_113, Brand_new_nameSe... |
292 | 2022-07-14 15:50:01 | modify | service | 18915 | 18915 | {'id': '18915', 'name': 'Service_N5'} | {'id': '18915', 'name': 'Brand_new_nameService... | [(change, name, (Service_N5, Brand_new_nameSer... |
293 | 2022-07-14 15:50:01 | modify | service | 14073 | 14073 | {'id': '14073', 'name': 'Service_94'} | {'id': '14073', 'name': 'Brand_new_nameService... | [(change, name, (Service_94, Brand_new_nameSer... |
{s.name for s in n.schedule.services()}
{'Brand_new_nameService_113',
'Brand_new_nameService_134',
'Brand_new_nameService_205',
'Brand_new_nameService_94',
'Brand_new_nameService_98',
'Brand_new_nameService_N20',
'Brand_new_nameService_N5',
'Brand_new_nameService_N55',
'Brand_new_nameService_N8'}
You can subset the Schedule object using Service IDs.
services = n.schedule.extract_service_ids_on_attributes(
{'name': ['Brand_new_nameService_134', 'Brand_new_nameService_98']})
len(services)
2
n.schedule.print()
Schedule:
Number of services: 9
Number of routes: 69
Number of stops: 85
sub_s = n.schedule.subschedule(service_ids=services)
2022-07-14 15:50:01,252 - The following vehicle types are missing from the `vehicle_types` attribute: {'piggyback'}
2022-07-14 15:50:01,252 - Vehicles affected by missing vehicle types: {'veh_2331_bus': {'type': 'piggyback'}, 'veh_2333_bus': {'type': 'piggyback'}, 'veh_2335_bus': {'type': 'piggyback'}, 'veh_2337_bus': {'type': 'piggyback'}, 'veh_2339_bus': {'type': 'piggyback'}}
2022-07-14 15:50:01,387 - Removed Services with IDs `20274`, and Routes: {'VJ375a660d47a2aa570aa20a8568012da8497ffecf', '20274_4', 'VJ812fad65e7fa418645b57b446f00cba573f2cdaf', 'VJ6c64ab7b477e201cae950efde5bd0cb4e2e8888e'}
2022-07-14 15:50:01,594 - Removed Services with IDs `18853`, and Routes: {'VJfc4917783c2ca3227789fa7c532c9adf47702095', 'VJ8cacca9a6722c497c413005568182ecf4d50b160', 'VJf3e316e5e605bb512147dee2a989be5a82ef1b5f'}
2022-07-14 15:50:01,718 - Removed Services with IDs `17732`, and Routes: {'VJ85c23573d670bab5485618b0c5fddff3314efc89', 'VJ0cb60de3ed229c1413abac506e770b6ab8a7c49a'}
2022-07-14 15:50:01,824 - Removed Services with IDs `12430`, and Routes: {'VJef7f20c3a9bf1419f6401e1e9131fe2c634bcb9a', 'VJ0f3c08222de16c2e278be0a1bf0f9ea47370774e', 'VJ95b4c534d7c903d76ec0340025aa88b81dba3ce4', 'VJ06cd41dcd58d947097df4a8f33234ef423210154', 'VJ948e8caa0f08b9c6bf6330927893942c474b5100', 'VJ15419796737689e742962a625abcf3fd5b3d58b1', 'VJf8e38a73359b6cf743d8e35ee64ef1f7b7914daa', 'VJ8f9aea7491080b0137d3092706f53dc11f7dba45', 'VJ235c8fca539cf931b3c673f9b056606384aff950', 'VJ06420fdab0dfe5c8e7f2f9504df05cf6289cd7d3', 'VJeae6e634f8479e0b6712780d5728f0afca964e64', 'VJeb72539d69ddf8e29f1adf74d43953def196ae41'}
2022-07-14 15:50:01,917 - Removed Services with IDs `15660`, and Routes: {'VJ1cf651142378958b52229bfe1fa552e49136e60e', 'VJf2e0de4f5dad68cb03064e6064e372dde52cc678', 'VJ3716910ec59c370d9f5c69137df7276b68cf0a08'}
2022-07-14 15:50:02,003 - Removed Services with IDs `18915`, and Routes: {'VJ8a4b1ca7dfd0a130abd1de9f55f3b756617dd4ca', 'VJ887921c00645929c5402ac46592e57c368ea63a1', 'VJ0d304b95d39f4bce48e6ff26ddd73a9c06f17f4f', 'VJb08f8a2de01a4ef99d3b7fefd9022117ac307531', 'VJ520ec0c0ca58a849349fa614b5cf9270ac5c93da'}
2022-07-14 15:50:02,084 - Removed Services with IDs `14073`, and Routes: {'VJe8cffad09738ff7b9698b333e3247918d5c45358', 'VJea6046f64f85febf1854290fb8f76e921e3ac96b', 'VJd132b905afc6c0e8e8a994142e301ca5c0f70e22', 'VJe6ba07ef9f19ae40517261ad626bf34dd656491a', 'VJdbc280077e505b4f8d66586ca51751a125cb4ef0', 'VJaa5ee0daec7529d7668c81fe7fac0c4ff545daea', 'VJf6055fdf9ef0dd6d0500b6c11adcfdd4d10655dc', 'VJe18efadf172576fea7989ec1f233f26854c0f66a', 'VJfc35884fc4f11dc408a209c19f56f3b60f634daf', 'VJb4309b7a9598539ab9942ea1bcadc60a91b978ba', 'VJd9dbeefeca6d74ef2594a17514ebc08ee2d503b2', 'VJ6cf76a4c03cca468cb6954db7f7aad5ae189df13', 'VJ93d8207ae8540b4ff59d47c9ee1ec5689084522d', 'VJ24fe211d801738b556a39f815256d7f6bc544ec5', 'VJc8cdbd902dadeebeeb4dbd7332b564ee2e4b00ce'}
2022-07-14 15:50:02,087 - Removed Stops with indices `['other_new_stop', '490000173JB.link:1663']`.Routes affected: set(). Services affected: set().
sub_s.print()
Schedule:
Number of services: 2
Number of routes: 25
Number of stops: 83
There is a convenience method to extract a subset schedule using spatial conditions - refer to Using Schedule notebook or docs to learn more about what spatial inputs are supported.
# pop it in https://s2.sidewalklabs.com/ to see it
region = '48761ad0b14,48761ad0b3,48761ad0b5,48761ad0b7,48761ad0b84,48761ad0d,48761ad0e04,48761ad11f4,48761ad11fc,48761ad13,48761ad145fc,48761ad147,48761ad14c,48761ad153,48761ad41df,48761ad41e4,48761ad41fc,48761ad421,48761ad427,48761ad429,48761ad42b,48761ad5d5,48761ad5d7,48761ad5d9,48761ad5df,48761ad5e1,48761ad5e3,48761ad64,48761ad6c,48761ad71,48761ad73,48761ad744,48761ad74c,48761ad751,48761ad753,48761ad757554,48761ad75c,48761ad77,48761ad79,48761ad7b,48761ad7d,48761ad7e4,48761ad7ec,48761ad7f4,48761ad7f9,48761ad7fb,48761ad7fd,48761ad827,48761ad829,48761ad82b,48761ad9d5,48761ad9d7,48761ad9d84,48761b2802c,48761b2817,48761b281c,48761b283,48761b2847fc,48761b2849,48761b284b,48761b29b5,48761b29b7,48761b29b9,48761b29d,48761b29e4,48761b29e9,48761b29ea4,48761b29ef,48761b29fb4,48761b29fd'
n.subnetwork_on_spatial_condition(region, how='intersect')
2022-07-14 15:50:03,479 - Subsetting a Network will likely result in a disconnected network graph. A cleaner will be ran that will remove links to make the resulting Network strongly connected for modes: car, walk, bike.
2022-07-14 15:50:03,543 - Schedule will be subsetted using given services: ['12430']. Links pertaining to their network routes will also be retained.
2022-07-14 15:50:03,720 - The following vehicle types are missing from the `vehicle_types` attribute: {'piggyback'}
2022-07-14 15:50:03,721 - Vehicles affected by missing vehicle types: {'veh_2331_bus': {'type': 'piggyback'}, 'veh_2333_bus': {'type': 'piggyback'}, 'veh_2335_bus': {'type': 'piggyback'}, 'veh_2337_bus': {'type': 'piggyback'}, 'veh_2339_bus': {'type': 'piggyback'}}
2022-07-14 15:50:03,834 - Removed Services with IDs `14134`, and Routes: {'VJb93a17a405fe502c5b3a2d6544105b0311da9fe2', 'VJ323d02e117552af1565f2ff1273a612655c829c4', 'VJ5909ba51575a9459eb0013fbd31c8205455ca2fd', 'VJ2aba67e3ed98f2ed5f5966c1ac394cbf6d1943d7', 'VJ26095b8f9f9db92ca2e53d4c086a7dcd82a13be9', 'VJ2c87b2a59184888f3175b55bde7b02d024ea8607', 'VJ4e2b897edf0e7b8a8e3b5516ab43ce56f72c5cff', 'VJf9a22035ae6f25bb420df833474943ad76065c89', 'VJd78967364a302cf232c5139d40622dcb6c238c9e', 'VJ12ba6089dfb2733e29c415a1a0015fef30fd5305', 'VJdb0c128567fcbcc063d554ae1c95851cee41b909', 'VJdf3936da1a51eb33db594ef99738802c14b19995', 'VJa7f37392e276aeac26c7e73bbc05e6a71af38dba', 'VJ4c6fa387b0d4be94a6c3679b94790b183e2558ca', 'VJ256e98df611ff48afe737ddc81cbcde82e4e81c8', 'VJ4e311a625836374adf4cfaa841224840dbeb7619'}
2022-07-14 15:50:03,941 - Removed Services with IDs `20274`, and Routes: {'VJ375a660d47a2aa570aa20a8568012da8497ffecf', '20274_4', 'VJ812fad65e7fa418645b57b446f00cba573f2cdaf', 'VJ6c64ab7b477e201cae950efde5bd0cb4e2e8888e'}
2022-07-14 15:50:04,136 - Removed Services with IDs `18853`, and Routes: {'VJfc4917783c2ca3227789fa7c532c9adf47702095', 'VJ8cacca9a6722c497c413005568182ecf4d50b160', 'VJf3e316e5e605bb512147dee2a989be5a82ef1b5f'}
2022-07-14 15:50:04,215 - Removed Services with IDs `15234`, and Routes: {'VJd4cbfb092a104ac6a3164a86e9765f68734fdfcf', 'VJ28a8a6a4ab02807a4fdfd199e5c2ca0622d34d0c', 'VJ8ccf92aa0f351b2e31f1a078b968dff4c2505c02', 'VJ1a8cc306354fdc322d739ae644eb73444341d08d', 'VJ5b511605b1e07428c2e0a7d676d301c6c40dcca6', 'VJ3d50b96792ae8495dbe5a5e372849a60c48b2279', 'VJ9b58a59e3d74941586a5bca7726a8aa624da67fc', 'VJbf9d4fdb976223e6a026c0c669ed290418abefee', 'VJ652c769bc42361cc0308dff59a1fdcf0949bdade'}
2022-07-14 15:50:04,303 - Removed Services with IDs `17732`, and Routes: {'VJ85c23573d670bab5485618b0c5fddff3314efc89', 'VJ0cb60de3ed229c1413abac506e770b6ab8a7c49a'}
2022-07-14 15:50:04,367 - Removed Services with IDs `15660`, and Routes: {'VJ1cf651142378958b52229bfe1fa552e49136e60e', 'VJf2e0de4f5dad68cb03064e6064e372dde52cc678', 'VJ3716910ec59c370d9f5c69137df7276b68cf0a08'}
2022-07-14 15:50:04,428 - Removed Services with IDs `18915`, and Routes: {'VJ8a4b1ca7dfd0a130abd1de9f55f3b756617dd4ca', 'VJ887921c00645929c5402ac46592e57c368ea63a1', 'VJ0d304b95d39f4bce48e6ff26ddd73a9c06f17f4f', 'VJb08f8a2de01a4ef99d3b7fefd9022117ac307531', 'VJ520ec0c0ca58a849349fa614b5cf9270ac5c93da'}
2022-07-14 15:50:04,460 - Removed Services with IDs `14073`, and Routes: {'VJe8cffad09738ff7b9698b333e3247918d5c45358', 'VJea6046f64f85febf1854290fb8f76e921e3ac96b', 'VJd132b905afc6c0e8e8a994142e301ca5c0f70e22', 'VJe6ba07ef9f19ae40517261ad626bf34dd656491a', 'VJdbc280077e505b4f8d66586ca51751a125cb4ef0', 'VJaa5ee0daec7529d7668c81fe7fac0c4ff545daea', 'VJf6055fdf9ef0dd6d0500b6c11adcfdd4d10655dc', 'VJe18efadf172576fea7989ec1f233f26854c0f66a', 'VJfc35884fc4f11dc408a209c19f56f3b60f634daf', 'VJb4309b7a9598539ab9942ea1bcadc60a91b978ba', 'VJd9dbeefeca6d74ef2594a17514ebc08ee2d503b2', 'VJ6cf76a4c03cca468cb6954db7f7aad5ae189df13', 'VJ93d8207ae8540b4ff59d47c9ee1ec5689084522d', 'VJ24fe211d801738b556a39f815256d7f6bc544ec5', 'VJc8cdbd902dadeebeeb4dbd7332b564ee2e4b00ce'}
2022-07-14 15:50:04,462 - Removed Stops with indices `['other_new_stop', '490000173JB.link:1663']`.Routes affected: set(). Services affected: set().
2022-07-14 15:50:04,486 - Param: strongly_connected_modes is defaulting to `{'car', 'walk', 'bike'}` You can change this behaviour by passing the parameter.
2022-07-14 15:50:04,495 - The graph for mode car is not strongly connected. The largest 1 connected components will be extracted.
2022-07-14 15:50:04,509 - Extracting largest connected components resulted in mode: car being deleted from 82 edges
/Users/kasia.kozlowska/PycharmProjects/ABM/genet/genet/core.py:595: UserWarning: Boolean Series key will be reindexed to match DataFrame index.
df = df.loc[links & set(df.index)][df['modes'].apply(lambda x: bool(mode & x))]
2022-07-14 15:50:04,531 - Changed Link attributes for 82 links
2022-07-14 15:50:04,537 - Removed 56 links
2022-07-14 15:50:04,540 - The graph for modes: walk does not have any connected components. This method returns True because if the graph is empty for this mode there is no reason to fail this check.
2022-07-14 15:50:04,544 - The graph for modes: bike does not have any connected components. This method returns True because if the graph is empty for this mode there is no reason to fail this check.
2022-07-14 15:50:04,548 - Subsetted Network is ready - do not forget to validate and visualise your subset!
<Network instance at 140383937227472: with
graph: Name:
Type: MultiDiGraph
Number of nodes: 156
Number of edges: 203
Average in degree: 1.3013
Average out degree: 1.3013 and
schedule Schedule:
Number of services: 1
Number of routes: 12
Number of stops: 83