【问题标题】:Pandas efficiently filter dynamic date rangePandas 有效过滤动态日期范围
【发布时间】:2021-10-21 18:53:55
【问题描述】:

我有一个在特定时间范围内发生的行程 (50,000) 列表,并与特定设备 ID (150) 相关联,并且每个行程都有一个行程 ID。

我还有一个 gps 坐标列表 (2,000,000),它们发生在给定时间并与设备 ID 相关联。有的积分不会上一趟

我的问题是将行程 ID 添加到给定设备 ID 在该日期范围内发生的所有 gps 坐标的最快方法是什么。

这是我目前的解决方案。现在每次行程大约需要 0.3 秒。我听说 for 循环对 Pandas 来说很糟糕,但我没有足够的经验来做其他事情。

示例输入:

旅行:

开始、停止、设备、trip_id

2021-08-24 15:50:27.063000+00:00, 2021-08-24 16:54:54+00:00, "B8", 1

要点:

日期时间、设备、point_id

2020-04-23 19:50:28.063000+00:00,"B8", 1

df_trips.sort_values(by="device", inplace=True)
device = ""
points = pd.DataFrame()
for idx, trip in df_trips.iterrows():
    if device != trip['device']:
        device = trip['device']
        id_points = df_points[df_points['device'] == trip['device']]
    if trip['stop'] > trip['start']:
        trip_points = id_points[(id_points['dateTime'] > trip['start']) & (id_points['dateTime'] < trip['stop'])]
        trip_points['trip'] = trip['Unnamed: 0']
        points = points.append(trip_points)
    i += 1
print(len(points))
points.to_csv('TripPoints.csv')
print("Got Trips")

谢谢!

【问题讨论】:

  • 你能分享两个数据框的一些输入吗?
  • 我添加了它们来自的 csv 文件的示例行,遗憾的是我无法显示更多数据。如果您知道如何在 Pandas 中通过更改我认为需要的每一行的过滤器查询来进行过滤。
  • 我建议您看看merge_asof 没有循环,假设您不能为同一设备进行重叠行程,它可能会起作用
  • 谢谢,我去看看

标签: python pandas dataframe date sorting


【解决方案1】:

假设可以像这样重现您的数据(请注意未来的问题,您可以如何创建虚拟数据以帮助您更快地回答您的问题)。

df_trips = pd.DataFrame([
    ['2021-08-24 15:50:27.063000+00:00','2021-08-24 16:54:54+00:00', "B8", 1], 
    ['2021-08-28 15:50:27.063000+00:00','2021-08-30 16:54:54+00:00', "B8", 2], 
    ['2021-08-24 16:50:27.063000+00:00','2021-08-24 16:54:54+00:00', "A7", 3], 
], columns=['start', 'stop', 'device', 'trip_id'])
df_trips['start'] = pd.to_datetime(df_trips['start'])
df_trips['stop'] = pd.to_datetime(df_trips['stop'])
print(df_trips)
                             start                      stop device  trip_id
0 2021-08-24 15:50:27.063000+00:00 2021-08-24 16:54:54+00:00     B8        1
1 2021-08-28 15:50:27.063000+00:00 2021-08-30 16:54:54+00:00     B8        2
2 2021-08-24 16:50:27.063000+00:00 2021-08-24 16:54:54+00:00     A7        3

df_points = pd.DataFrame([
    ['2021-08-24 15:52:27.063000+00:00',"B8", 1],
    ['2021-08-25 15:50:27.063000+00:00',"B8", 2],
    ['2021-08-28 16:50:27.063000+00:00',"B8", 3],
    ['2021-08-29 15:50:27.063000+00:00',"B8", 4],
    ['2021-08-24 16:51:27.063000+00:00',"A7", 5],
], columns=['dateTime', 'device', 'point_id'])
df_points['dateTime'] = pd.to_datetime(df_points['dateTime'])
print(df_points)
                          dateTime device  point_id
0 2021-08-24 15:52:27.063000+00:00     B8         1 # in trip 1
1 2021-08-25 15:50:27.063000+00:00     B8         2 # no trip
2 2021-08-28 16:50:27.063000+00:00     B8         3 # trip 2
3 2021-08-29 15:50:27.063000+00:00     B8         4 # trip 2
4 2021-08-24 16:51:27.063000+00:00     A7         5 # trip 3, overlap time other device

你的工作也是如此,首先按设备使用merge_asof,然后从 dateTime 列向后看到上一个开始(同一设备)

points = pd.merge_asof(
    # sort both dataframe for  merging column mandatory
    df_points.sort_values('dateTime'), 
    df_trips.sort_values('start'), 
    # first merge by devce
    by='device', 
    # merge_asof on dateTime and start 
    left_on='dateTime',
    right_on='start', 
    # look for start before dateTime
    direction='backward'
)
print(points.sort_values('point_id'))
                          dateTime device  point_id  \
0 2021-08-24 15:52:27.063000+00:00     B8         1   
2 2021-08-25 15:50:27.063000+00:00     B8         2   
3 2021-08-28 16:50:27.063000+00:00     B8         3   
4 2021-08-29 15:50:27.063000+00:00     B8         4   
1 2021-08-24 16:51:27.063000+00:00     A7         5   

                             start                      stop  trip_id  
0 2021-08-24 15:50:27.063000+00:00 2021-08-24 16:54:54+00:00        1  
2 2021-08-24 15:50:27.063000+00:00 2021-08-24 16:54:54+00:00        1  
3 2021-08-28 15:50:27.063000+00:00 2021-08-30 16:54:54+00:00        2  
4 2021-08-28 15:50:27.063000+00:00 2021-08-30 16:54:54+00:00        2  
1 2021-08-24 16:50:27.063000+00:00 2021-08-24 16:54:54+00:00        3  

几乎不错,但是您可以看到第二行 dateTime 不在 start 和 stop 之间,因此当 dateTime 高于 stop 时,您可以将 trip_id 替换为 pd.NANone找到行程,或删除此类行。

points.loc[points['dateTime']>points['stop'], 'trip_id'] = pd.NA
points = points[list(df_points.columns)+['trip_id']]
# or remove the rows without trip_id
#points = points.loc[points['dateTime']<=points['stop'], 
#                    list(df_points.columns)+['trip_id']]
print(points)
                          dateTime device  point_id trip_id
0 2021-08-24 15:52:27.063000+00:00     B8         1       1
1 2021-08-24 16:51:27.063000+00:00     A7         5       3
2 2021-08-25 15:50:27.063000+00:00     B8         2    <NA>
3 2021-08-28 16:50:27.063000+00:00     B8         3       2
4 2021-08-29 15:50:27.063000+00:00     B8         4       2

【讨论】:

    猜你喜欢
    • 2020-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    • 2013-03-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多