Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

preserving node id types from original graph #164

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions peartree/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ def generate_cross_feed_edges(G: nx.MultiDiGraph,
lat = float(row.stop_lat)
lon = float(row.stop_lon)
point = (lat, lon)

nearest_nodes = get_nearest_nodes(node_df,
point,
connection_threshold,
Expand All @@ -199,6 +200,7 @@ def generate_cross_feed_edges(G: nx.MultiDiGraph,
to_nodes.append(node_id)
edge_costs.append(dist_val)


return pd.DataFrame({'stop_id': stop_ids,
'to_node': to_nodes,
'distance': edge_costs})
Expand Down Expand Up @@ -247,6 +249,7 @@ def _add_cross_feed_edges(G: nx.MultiDiGraph,
walk_speed_kmph: float) -> nx.MultiDiGraph:
# Add the cross feed edge connectors to the graph to
# capture transfer points

for i, row in cross_feed_edges.iterrows():
# Extract the row column values as discrete variables
sid = row.stop_id
Expand All @@ -264,16 +267,20 @@ def _add_cross_feed_edges(G: nx.MultiDiGraph,

# And then add it to the graph
G.add_edge(full_sid, to, length=in_seconds, mode='walk')
# also add reverse edge
G.add_edge(to, full_sid, length=in_seconds, mode='walk')


def _add_nodes_and_edges(G: nx.MultiDiGraph,
name: str,
stops_df: pd.DataFrame,
summary_edge_costs: pd.DataFrame,
bidirectional: bool=False) -> Dict[str, str]:
bidirectional: bool=False,
add_trips_per_edge: bool=False) -> Dict[str, str]:
# As we convert stop ids to actual nodes, let's keep track of those names
# here so that we can reference them when we add connector edges across
# the various feeds loaded into the graph

sid_lookup = {}

for i, row in stops_df.iterrows():
Expand All @@ -291,12 +298,18 @@ def _add_nodes_and_edges(G: nx.MultiDiGraph,
for i, row in summary_edge_costs.iterrows():
sid_fr = nameify_stop_id(name, row.from_stop_id)
sid_to = nameify_stop_id(name, row.to_stop_id)
G.add_edge(sid_fr, sid_to, length=row.edge_cost, mode='transit')
if add_trips_per_edge:
G.add_edge(sid_fr, sid_to, length=row.edge_cost, trips=row.trips_per_edge, mode='transit')
else:
G.add_edge(sid_fr, sid_to, length=row.edge_cost, mode='transit')

# If want to add both directions in this step, we can
# by reversing the to/from node order on edge
if bidirectional:
G.add_edge(sid_to, sid_fr, length=row.edge_cost, mode='transit')
if add_trips_per_edge:
G.add_edge(sid_to, sid_fr, length=row.edge_cost, trips=row.trips_per_edge, mode='transit')
else:
G.add_edge(sid_to, sid_fr, length=row.edge_cost, mode='transit')

return sid_lookup

Expand All @@ -308,7 +321,8 @@ def populate_graph(G: nx.MultiDiGraph,
summary_edge_costs: pd.DataFrame,
connection_threshold: Union[int, float],
walk_speed_kmph: float=4.5,
impute_walk_transfers: bool=True) -> nx.MultiDiGraph:
impute_walk_transfers: bool=True,
add_trips_per_edge: bool=False) -> nx.MultiDiGraph:
"""
Takes an existing network graph object and adds all new nodes, transit \
edges, and connector edges between existing and new nodes (stop ids).
Expand Down Expand Up @@ -351,8 +365,10 @@ def populate_graph(G: nx.MultiDiGraph,
stops_df = _merge_stop_waits_and_attributes(
wait_times_and_modes, feed.stops)


# Mutates the G network object
sid_lookup = _add_nodes_and_edges(G, name, stops_df, summary_edge_costs)
sid_lookup = _add_nodes_and_edges(G, name, stops_df, summary_edge_costs, False, add_trips_per_edge)


# Default to exempt new edges created, unless imputing internal
# walk transfers is requested as well
Expand Down
10 changes: 6 additions & 4 deletions peartree/parallel.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,17 @@ def generate_wait_times(
stop_cost_method: Any) -> Dict[int, List[float]]:
wait_times = {0: {}, 1: {}}
for stop_id in trips_and_stop_times.stop_id.unique():
constraint_1 = (trips_and_stop_times.stop_id == stop_id)
stop_times_by_stop = trips_and_stop_times[constraint_1]
#constraint_1 = (trips_and_stop_times.stop_id == stop_id)
#stop_times_by_stop = trips_and_stop_times[constraint_1]
stop_times_by_stop = trips_and_stop_times[trips_and_stop_times.stop_id == stop_id]

# Handle both inbound and outbound directions
for direction in [0, 1]:
# Check if direction_id exists in source data
if 'direction_id' in trips_and_stop_times:
constraint_2 = (trips_and_stop_times.direction_id == direction)
direction_subset = stop_times_by_stop[constraint_2]
#constraint_2 = (trips_and_stop_times.direction_id == direction)
#direction_subset = stop_times_by_stop[constraint_2]
direction_subset = trips_and_stop_times[(trips_and_stop_times.stop_id == stop_id) & (trips_and_stop_times.direction_id == direction)]
else:
direction_subset = stop_times_by_stop.copy()

Expand Down
10 changes: 7 additions & 3 deletions peartree/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ def load_feed_as_graph(feed: ptg.gtfs.Feed,
fallback_stop_cost: bool=FALLBACK_STOP_COST_DEFAULT,
interpolate_times: bool=True,
impute_walk_transfers: bool=False,
use_multiprocessing: bool=False):
use_multiprocessing: bool=False,
add_trips_per_edge: bool=False):
"""
Convert a feed object into a NetworkX Graph, or connect to an existing \
NetworkX graph if one is supplied.
Expand Down Expand Up @@ -197,7 +198,6 @@ def load_feed_as_graph(feed: ptg.gtfs.Feed,
networkx.Graph, the loaded, combined representation of the schedule \
data from the feed subset by the time parameters provided
"""

# Generate a random name for name if it is None
if not name:
name = generate_random_name()
Expand All @@ -219,6 +219,9 @@ def load_feed_as_graph(feed: ptg.gtfs.Feed,
stop_cost_method,
use_multiprocessing)

#print("print summary_edge_costs")
#print(summary_edge_costs)

# This is a flag used to check if we need to run any additional steps
# after the feed is returned to ensure that new nodes and edge can connect
# with existing ones (if they exist/a graph is passed in)
Expand All @@ -240,7 +243,8 @@ def load_feed_as_graph(feed: ptg.gtfs.Feed,
summary_edge_costs,
connection_threshold,
walk_speed_kmph,
impute_walk_transfers)
impute_walk_transfers,
add_trips_per_edge)


def load_synthetic_network_as_graph(
Expand Down
10 changes: 9 additions & 1 deletion peartree/summarizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,20 @@ def summarize_edge_costs(
# operator out from the nested summarize_edge_costs method
avg_cost = unique_edge_operator(df[to_mask].edge_cost)

# number of trips that travel on edge
trips_per_edge = len(df[to_mask])

# Add each row-array to the reference matrix, which...
results_mtx.append([avg_cost,
trips_per_edge,
from_stop_id,
to_stop_id])

# add a new column
new_columns = ['edge_cost', 'trips_per_edge', 'from_stop_id', 'to_stop_id']

# ...will be converted into a DataFrame upon being returned
return pd.DataFrame(results_mtx, columns=df.columns)
return pd.DataFrame(results_mtx, columns=new_columns)


def generate_summary_edge_costs(all_edge_costs: pd.DataFrame) -> pd.DataFrame:
Expand All @@ -85,6 +92,7 @@ def generate_summary_edge_costs(all_edge_costs: pd.DataFrame) -> pd.DataFrame:
Edge costs grouped by from-to node pairs.
"""
summary_groupings = all_edge_costs.groupby('from_stop_id')

summary = summary_groupings.apply(summarize_edge_costs)
summary = summary.reset_index(drop=True)
return summary
Expand Down
7 changes: 4 additions & 3 deletions peartree/toolkit.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,12 @@ def generate_graph_node_dataframe(G):
x = float(data['x'])
y = float(data['y'])
clist.append([node, x, y])
coords = np.array(clist)
#coords = np.array(clist)
coords = np.array(clist, dtype='O')

# Then make into a Pandas DataFrame, with the node as index (type string)
df = pd.DataFrame(coords, columns=['node', 'x', 'y'])
df['node'] = df['node'].astype(str)
#df['node'] = df['node'].astype(str)
df = df.set_index('node')
return df

Expand All @@ -107,7 +108,7 @@ def get_nearest_nodes(df_orig: pd.DataFrame,
df = df_orig.copy()

if exempt_id is not None:
df.index = df.index.astype(str)
#df.index = df.index.astype(str)
mask = ~(df.index == exempt_id)
df = df[mask]

Expand Down