2

我正在处理纽约市 MTA 地铁系统的 GTFS 数据。我需要在特定站点找到每条路线的停止时间。为此,我从我拥有的 StopTimes DataTable 中获取特定 stop_id 的停止时间。我只想要现在和接下来 2 小时之间的停止时间。

然后,我需要使用 trip_id 值查找每个停止时间的行程。从那次旅行开始,我必须使用 route_id 值查找路线,以便获取路线名称或停靠时间的编号。

以下是每个 DataTable 的计数:StopTimes(522712)、Trips(19092)、Routes(27)。

现在,这需要 20 秒到 40 秒的时间来执行。我怎样才能加快速度?任何和所有的建议表示赞赏。谢谢!

foreach (var r in StopTimes.OrderBy(z => z.Field<DateTime>("departure_time").TimeOfDay)
                           .Where(z => z.Field<string>("stop_id") == stopID &&
                                  z["departure_time"].ToString() != "" &&
                                  z.Field<DateTime>("departure_time").TimeOfDay >= DateTime.UtcNow.AddHours(-5).TimeOfDay &&
                                  z.Field<DateTime>("departure_time").TimeOfDay <= DateTime.UtcNow.AddHours(-5).AddHours(2).TimeOfDay))
        {
            var trip = (from z in Trips
                        where z.Field<string>("trip_id") == r.Field<string>("trip_id") &&
                              z["route_id"].ToString() != ""
                        select z).Single();

            var route = (from z in Routes
                         where z.Field<string>("route_id") == trip.Field<string>("route_id")
                         select z).Single();

            // do stuff (not time-consuming)
        }
4

3 回答 3

2

试试这个:

var now = DateTime.UtcNow;
var tod0 = now.AddHours(-5).TimeOfDay;
var tod1 = now.AddHours(-5).AddHours(2).TimeOfDay;

var sts =
    from st in StopTimes
    let StopID = st.Field<string>("stop_id")
    where StopID == stopID
    where st["departure_time"].ToString() != ""
    let DepartureTime = st.Field<DateTime>("departure_time").TimeOfDay
    where DepartureTime >= tod0
    where DepartureTime >= tod1
    let TripID = st.Field<string>("trip_id")
    select new
    {
        StopID,
        TripID,
        DepartureTime,
    };

请注意,此查询中没有orderby,我们返回的是匿名类型。要运行您的“做事(不费时)”代码,您可能需要添加更多属性。

同样的方法也发生在Trips&Routes上。

var ts =
    from t in Trips
    where t["route_id"].ToString() != ""
    let TripID = t.Field<string>("trip_id")
    let RouteID = t.Field<string>("route_id")
    select new
    {
        TripID,
        RouteID,
    };

var rs =
    from r in Routes
    let RouteID = r.Field<string>("route_id")
    select new
    {
        RouteID,
    };

由于每次查找都会获得一条记录,因此 usingToDictionary(...)是一个不错的选择。

var tripLookup = ts.ToDictionary(t => t.TripID);
var routeLookup = rs.ToDictionary(r => r.RouteID);

现在您的查询如下所示:

var query = from StopTime in sts.ToArray()
            let Trip = tripLookup[StopTime.TripID]
            let Route = routeLookup[Trip.RouteID]
            orderby StopTime.DepartureTime
            select new
            {
                StopTime,
                Trip,
                Route,
            };

请注意,我已经使用过.ToArray()并且我已经把orderby右边放在最后。

你像这样运行你的代码:

foreach (var q in query)
{
    // do stuff (not time-consuming)
}

让我知道这是否有帮助。

于 2011-03-04T03:50:07.927 回答
1

我会Dictionary<int, Trip>在关键是 的地方从 Trips 中创建一个,从关键所在的位置创建trip_id一个Dictionary<int, Route> 。您的代码对 19092 项中的每一项进行迭代一次。同样的交易,但至少那里只有 27 件商品。Routesroute_idTripsIEnumerable<StopTime>Routes

编辑:

实际上更仔细地查看它,第一本字典将是Dictionary<int, int>值所在的位置route_id。鉴于和之间的一对一关系trip_idroute_id您可以构建一个Dictionary<trip_id, Route>并进行一次查找。

于 2011-03-04T03:22:05.733 回答
-1

它有助于理解延迟查询的执行,因此您可以根据具体情况决定如何优化运行时。这是一篇很好的博客文章,可以帮助您入门: http: //ox.no/posts/linq-vs-loop-a-performance-test

于 2011-03-25T23:00:16.367 回答