start cleaing up thruput stats. remove the ability to change window size

This commit is contained in:
Dustin Carlino 2020-05-15 14:25:10 -07:00
parent 7ff5e870d0
commit a0a7cb1c4e
7 changed files with 82 additions and 83 deletions

View File

@ -13,7 +13,7 @@ regenerate the maps!
5. Select what kind of on-street parking the road has
6. Repeat
7. Click **Generate OsmChange file**
8. Upload the diff.osc file by adding a layer in JOSM
8. Upload the diff.osc file by adding a layer in JOSM (or send it to me)
Like all edits to OSM, to figure out ground-truth, you can survey in-person or
use
@ -29,7 +29,7 @@ then use it to strengthen proposals for
[pedestrianized streets](https://dabreegster.github.io/abstreet/lake_wash/proposal.html),
[improving the bike network](https://www.glwstreets.org/45th-st-bridge-overview),
and
[mitigating the West Seattle bridge closure](https://www.westsideseattle.com/robinson-papers/2020/05/04/highland-park-action-coalition-calls-seattle-officials-traffic).
[mitigating the West Seattle bridge closure](https://dabreegster.github.io/abstreet/west_seattle/proposal.html).
A/B Street is only as good as its data, and parking is one of the biggest gaps.
Missing data means unrealistic traffic as vehicles contend for few parking
spots, and roads that look much wider than they are in reality.

View File

@ -51,9 +51,8 @@ pub fn traffic(
app.primary
.sim
.get_analytics()
.thruput_stats
.count_per_intersection
.get(id)
.intersection_thruput
.total_for(id)
)
)));
rows.push(txt.draw(ctx));
@ -64,7 +63,7 @@ pub fn traffic(
throughput(
ctx,
app,
move |a, t| a.throughput_intersection(t, id, opts.bucket_size),
move |a, t| a.throughput_intersection(t, id),
opts.show_before,
)
.margin(10),
@ -103,7 +102,7 @@ fn delay_plot(ctx: &EventCtx, app: &App, i: IntersectionID, opts: &DataOptions)
.into_iter()
.map(|stat| (stat, Vec::new()))
.collect();
for (t, distrib) in a.intersection_delays_bucketized(t, i, opts.bucket_size) {
for (t, distrib) in a.intersection_delays_bucketized(t, i, Duration::hours(1)) {
for (stat, pts) in series.iter_mut() {
if distrib.count() == 0 {
pts.push((t, Duration::ZERO));

View File

@ -146,14 +146,7 @@ pub fn traffic(
let mut txt = Text::from(Line("Traffic over entire road, not just this lane"));
txt.add(Line(format!(
"Since midnight: {} agents crossed",
prettyprint_usize(
app.primary
.sim
.get_analytics()
.thruput_stats
.count_per_road
.get(r.id)
)
prettyprint_usize(app.primary.sim.get_analytics().road_thruput.total_for(r.id))
)));
rows.push(txt.draw(ctx));
@ -164,7 +157,7 @@ pub fn traffic(
throughput(
ctx,
app,
move |a, t| a.throughput_road(t, r, opts.bucket_size),
move |a, t| a.throughput_road(t, r),
opts.show_before,
)
.margin(10),

View File

@ -563,46 +563,26 @@ pub trait ContextualActions {
#[derive(Clone, PartialEq)]
pub struct DataOptions {
pub show_before: bool,
pub bucket_size: Duration,
}
impl DataOptions {
pub fn new(app: &App) -> DataOptions {
DataOptions {
show_before: app.has_prebaked().is_some(),
bucket_size: Duration::minutes(20),
}
}
pub fn to_controls(&self, ctx: &mut EventCtx, app: &App) -> Widget {
Widget::col(vec![
Widget::row(vec![
"In".draw_text(ctx),
Widget::dropdown(
ctx,
"bucket size",
self.bucket_size,
vec![
Choice::new("20 minute", Duration::minutes(20)),
Choice::new("1 hour", Duration::hours(1)),
Choice::new("6 hour", Duration::hours(6)),
],
)
.margin(3),
"buckets".draw_text(ctx),
]),
if app.has_prebaked().is_some() {
Checkbox::text(ctx, "Show before changes", None, self.show_before)
} else {
Widget::nothing()
},
])
Widget::col(vec![if app.has_prebaked().is_some() {
Checkbox::text(ctx, "Show before changes", None, self.show_before)
} else {
Widget::nothing()
}])
}
pub fn from_controls(c: &Composite) -> DataOptions {
DataOptions {
show_before: c.has_widget("Show before changes") && c.is_checked("Show before changes"),
bucket_size: c.dropdown_value("bucket size"),
}
}
}

View File

@ -122,14 +122,15 @@ pub fn throughput(ctx: &mut EventCtx, app: &App, compare: bool) -> Layers {
vec!["0", "50", "90", "99", "100"],
);
let stats = &app.primary.sim.get_analytics().thruput_stats;
let stats = &app.primary.sim.get_analytics();
// TODO If there are many duplicate counts, arbitrarily some will look heavier! Find the
// disribution of counts instead.
// TODO Actually display the counts at these percentiles
// TODO Dump the data in debug mode
{
let roads = stats.count_per_road.sorted_asc();
let cnt = stats.road_thruput.all_total_counts();
let roads = cnt.sorted_asc();
let p50_idx = ((roads.len() as f64) * 0.5) as usize;
let p90_idx = ((roads.len() as f64) * 0.9) as usize;
let p99_idx = ((roads.len() as f64) * 0.99) as usize;
@ -148,7 +149,8 @@ pub fn throughput(ctx: &mut EventCtx, app: &App, compare: bool) -> Layers {
}
// TODO dedupe
{
let intersections = stats.count_per_intersection.sorted_asc();
let cnt = stats.intersection_thruput.all_total_counts();
let intersections = cnt.sorted_asc();
let p50_idx = ((intersections.len() as f64) * 0.5) as usize;
let p90_idx = ((intersections.len() as f64) * 0.9) as usize;
let p99_idx = ((intersections.len() as f64) * 0.99) as usize;
@ -363,7 +365,6 @@ pub fn intersection_demand(ctx: &mut EventCtx, app: &App, i: IntersectionID) ->
.primary
.sim
.get_analytics()
.thruput_stats
.demand
.get(&g.id)
.cloned()

View File

@ -27,7 +27,7 @@ impl Time {
}
// (hours, minutes, seconds, centiseconds)
fn get_parts(self) -> (usize, usize, usize, usize) {
pub fn get_parts(self) -> (usize, usize, usize, usize) {
let mut remainder = self.0;
let hours = (remainder / 3600.0).floor();
remainder -= hours * 3600.0;

View File

@ -10,7 +10,13 @@ use std::collections::{BTreeMap, VecDeque};
#[derive(Clone, Serialize, Deserialize)]
pub struct Analytics {
// TODO rm this
pub thruput_stats: ThruputStats,
pub road_thruput: TimeSeriesCount<RoadID>,
pub intersection_thruput: TimeSeriesCount<IntersectionID>,
// Unlike everything else in Analytics, this is just for a moment in time.
pub demand: BTreeMap<TurnGroupID, usize>,
#[serde(skip_serializing, skip_deserializing)]
pub(crate) test_expectations: VecDeque<Event>,
pub bus_arrivals: Vec<(Time, CarID, BusRouteID, BusStopID)>,
@ -34,28 +40,20 @@ pub struct Analytics {
#[derive(Clone, Serialize, Deserialize)]
pub struct ThruputStats {
#[serde(skip_serializing, skip_deserializing)]
pub count_per_road: Counter<RoadID>,
#[serde(skip_serializing, skip_deserializing)]
pub count_per_intersection: Counter<IntersectionID>,
pub raw_per_road: Vec<(Time, TripMode, RoadID)>,
pub raw_per_intersection: Vec<(Time, TripMode, IntersectionID)>,
// Unlike everything else in Analytics, this is just for a moment in time.
pub demand: BTreeMap<TurnGroupID, usize>,
}
impl Analytics {
pub fn new() -> Analytics {
Analytics {
thruput_stats: ThruputStats {
count_per_road: Counter::new(),
count_per_intersection: Counter::new(),
raw_per_road: Vec::new(),
raw_per_intersection: Vec::new(),
demand: BTreeMap::new(),
},
road_thruput: TimeSeriesCount::new(),
intersection_thruput: TimeSeriesCount::new(),
demand: BTreeMap::new(),
test_expectations: VecDeque::new(),
bus_arrivals: Vec::new(),
bus_passengers_waiting: Vec::new(),
@ -74,30 +72,23 @@ impl Analytics {
return;
}
// TODO Plumb a flag
let raw_thruput = true;
// Throughput
if let Event::AgentEntersTraversable(a, to) = ev {
let mode = TripMode::from_agent(a);
match to {
Traversable::Lane(l) => {
let r = map.get_l(l).parent;
self.thruput_stats.count_per_road.inc(r);
if raw_thruput {
self.thruput_stats.raw_per_road.push((time, mode, r));
}
self.thruput_stats.raw_per_road.push((time, mode, r));
self.road_thruput.record(time, r, mode);
}
Traversable::Turn(t) => {
self.thruput_stats.count_per_intersection.inc(t.parent);
if raw_thruput {
self.thruput_stats
.raw_per_intersection
.push((time, mode, t.parent));
}
self.thruput_stats
.raw_per_intersection
.push((time, mode, t.parent));
self.intersection_thruput.record(time, t.parent, mode);
if let Some(id) = map.get_turn_group(t) {
*self.thruput_stats.demand.entry(id).or_insert(0) -= 1;
*self.demand.entry(id).or_insert(0) -= 1;
}
}
};
@ -201,7 +192,7 @@ impl Analytics {
for step in path.get_steps() {
if let Traversable::Turn(t) = step.as_traversable() {
if let Some(id) = map.get_turn_group(t) {
*self.thruput_stats.demand.entry(id).or_insert(0) += 1;
*self.demand.entry(id).or_insert(0) += 1;
}
}
}
@ -361,37 +352,29 @@ impl Analytics {
.collect()
}
// Slightly misleading -- TripMode::Transit means buses, not pedestrians taking transit
pub fn throughput_road(
&self,
now: Time,
road: RoadID,
window_size: Duration,
) -> BTreeMap<TripMode, Vec<(Time, usize)>> {
self.throughput(now, road, window_size, &self.thruput_stats.raw_per_road)
self.throughput(now, road, &self.thruput_stats.raw_per_road)
}
pub fn throughput_intersection(
&self,
now: Time,
intersection: IntersectionID,
window_size: Duration,
) -> BTreeMap<TripMode, Vec<(Time, usize)>> {
self.throughput(
now,
intersection,
window_size,
&self.thruput_stats.raw_per_intersection,
)
self.throughput(now, intersection, &self.thruput_stats.raw_per_intersection)
}
fn throughput<X: PartialEq>(
&self,
now: Time,
obj: X,
window_size: Duration,
data: &Vec<(Time, TripMode, X)>,
) -> BTreeMap<TripMode, Vec<(Time, usize)>> {
let window_size = Duration::hours(1);
let mut pts_per_mode: BTreeMap<TripMode, Vec<(Time, usize)>> = BTreeMap::new();
let mut windows_per_mode: BTreeMap<TripMode, Window> = BTreeMap::new();
for mode in TripMode::all() {
@ -652,3 +635,46 @@ impl Window {
self.times.len()
}
}
// Slightly misleading -- TripMode::Transit means buses, not pedestrians taking transit
#[derive(Clone, Serialize, Deserialize)]
pub struct TimeSeriesCount<X: Ord + Clone> {
// (Road or intersection, mode, hour block) -> count for that hour
counts: BTreeMap<(X, TripMode, usize), usize>,
}
impl<X: Ord + Clone> TimeSeriesCount<X> {
fn new() -> TimeSeriesCount<X> {
TimeSeriesCount {
counts: BTreeMap::new(),
}
}
fn record(&mut self, time: Time, id: X, mode: TripMode) {
let hour = time.get_parts().0;
*self.counts.entry((id, mode, hour)).or_insert(0) += 1;
}
pub fn total_for(&self, id: X) -> usize {
let mut cnt = 0;
for mode in TripMode::all() {
// TODO Hmm
for hour in 0..24 {
cnt += self
.counts
.get(&(id.clone(), mode, hour))
.cloned()
.unwrap_or(0);
}
}
cnt
}
pub fn all_total_counts(&self) -> Counter<X> {
let mut cnt = Counter::new();
for ((id, _, _), value) in &self.counts {
cnt.add(id.clone(), *value);
}
cnt
}
}