org.opentripplanner.profile.Stats Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of otp Show documentation
Show all versions of otp Show documentation
The OpenTripPlanner multimodal journey planning system
package org.opentripplanner.profile;
import com.google.common.collect.Lists;
import com.google.common.primitives.Ints;
import org.opentripplanner.routing.edgetype.TripPattern;
import org.opentripplanner.routing.trippattern.FrequencyEntry;
import org.opentripplanner.routing.trippattern.TripTimes;
import java.util.Collection;
import java.util.List;
/**
* num may be 0 if there are no observations.
* num will become 1 when adding a scalar or another Stats.
*/
class Stats implements Cloneable {
public int min = 0;
public int avg = 0;
public int max = 0;
public int num = 0;
/** Construct a new empty Stats containing no values. */
public Stats () { }
/** Construct a new Stats for a single int value. */
public Stats (int loneValue) {
min = loneValue;
max = loneValue;
avg = loneValue;
num = 1;
}
/** Construct a new Stats summarizing the given list of ints. */
public Stats (int... values) {
this(Ints.asList(values));
}
/** Copy constructor. */
public Stats (Stats other) {
if (other != null) {
this.min = other.min;
this.avg = other.avg;
this.max = other.max;
}
}
/**
* Adds another Stats into this one in place. This is intended to combine them in series, as for legs of a journey.
* It is not really correct for the average, but min and max values hold and avg is still a useful indicator.
* @return void to avoid thinking that a new object is created.
*/
public void add(Stats s) { // TODO maybe should be called 'chain' rather than add
min += s.min;
max += s.max;
avg += s.avg; // This only makes sense when adding successive legs TODO think through in depth
num = 1; // Num is poorly defined once addition has occurred
}
/** Like add(Stats) but min, max, and avg are all equal. */
public void add(int x) {
min += x;
avg += x;
max += x;
num = 1; // it's poorly defined here
}
/**
* Takes StreetSegments for each different access/egress mode and creates a stats describing the range of
* access/egress times present.
*/
public void add(Collection segs) {
if (segs == null || segs.isEmpty()) return;
List times = Lists.newArrayList();
for (StreetSegment seg : segs) times.add(seg.time);
Stats s = new Stats(times);
add(s);
}
/**
* Combines another Stats into this one in place. This considers the two Stats to be parallel, as for various trips
* or patterns making up a single leg of a journey. In this case, the weighted average is correctly computed.
* @return void to avoid thinking that a new object is created.
*/
public void merge (Stats other) {
if (other.min < min) min = other.min;
if (other.max > max) max = other.max;
avg = (avg * num + other.avg * other.num) / (num + other.num); // TODO should be float math
// FIXME num is not updated?
}
/**
* Combines a single value into this stats in place.
* @return void to indicate that a new object is NOT created.
*/
public void merge (int other) {
if (other < min) min = other;
if (other > max) max = other;
avg = (avg * num + other) / (num + 1); // TODO should be float math
num += 1;
}
/** Build a composite Stats out of a bunch of other Stats. They are combined in parallel, as in merge(Stats). */
public Stats (Iterable stats) {
min = Integer.MAX_VALUE;
num = 0;
for (Stats other : stats) {
if (other.min < min) min = other.min;
if (other.max > max) max = other.max;
avg += other.avg * other.num;
num += other.num;
}
avg /= num; // TODO should perhaps be float math
}
/** Construct a Stats containing the min, max, average, and count of the given ints. */
public Stats (Collection ints) {
if (ints == null || ints.isEmpty()) throw new AssertionError("Stats are undefined if there are no values.");
min = Integer.MAX_VALUE;
double accumulated = 0;
for (int i : ints) {
if (i > max) max = i;
if (i < min) min = i;
accumulated += i;
}
num = ints.size();
avg = (int) (accumulated / num);
}
public void dump() {
System.out.printf("min %d avg %d max %d\n", min, avg, max);
}
/** Scan through all trips on this pattern and summarize those that are running. */
public static Stats create (TripPattern pattern, int stop0, int stop1, TimeWindow window) {
Stats s = new Stats ();
s.min = Integer.MAX_VALUE;
s.num = 0;
/* Scan through all non-frequency trips accumulating them into stats. */
// TODO maybe we should prefilter the triptimes so we aren't constantly iterating over
// the trips whose service is not running
for (TripTimes tripTimes : pattern.scheduledTimetable.tripTimes) {
int depart = tripTimes.getDepartureTime(stop0);
int arrive = tripTimes.getArrivalTime(stop1);
if (window.includes (depart) &&
window.includes (arrive) &&
window.servicesRunning.get(tripTimes.serviceCode)) {
int t = arrive - depart;
if (t < s.min) s.min = t;
if (t > s.max) s.max = t;
s.avg += t;
++s.num;
}
}
/* Do the same thing for any frequency-based trips. */
for (FrequencyEntry freq : pattern.scheduledTimetable.frequencyEntries) {
TripTimes tt = freq.tripTimes;
int overlap = window.overlap(freq.startTime, freq.endTime, tt.serviceCode);
if (overlap == 0) continue;
int n = overlap / freq.headway + 1; // number of trip instances in the overlap. round up, avoid zeros.
int depart = tt.getDepartureTime(stop0);
int arrive = tt.getArrivalTime(stop1);
int t = arrive - depart;
if (t < s.min) s.min = t;
if (t > s.max) s.max = t;
s.avg += (t * n);
s.num += n;
}
if (s.num > 0) {
s.avg /= s.num;
return s;
}
/* There are no running trips within the time range, on the given serviceIds. */
return null;
}
@Override
public String toString() {
return String.format("min=%.1f avg=%.1f max=%.1f", min/60.0, avg/60.0, max/60.0);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy