【问题标题】:Spatial temporal query in python with many records具有许多记录的python中的时空查询
【发布时间】:2014-04-26 22:36:18
【问题描述】:

我有一个包含 600 000 x/y 点的数据框,其中包含日期时间信息、另一个字段“状态”以及额外的描述性信息

我的目标是,对于每条记录:

  • 按特定时空缓冲区内的记录对列“状态”求和

具体缓冲区在 t - 8 小时内

目前我在 pandas 数据框中有数据。

我可以遍历行,并为每条记录子集感兴趣的日期,然后计算距离并进一步限制选择。但是,有这么多记录,这仍然会很慢。

  • 这需要 4.4 小时才能运行。

我可以看到我可以创建一个 3 维 kdtree,其中 x、y、日期作为纪元时间。但是,我不确定在合并日期和地理距离时如何正确限制距离。

这里有一些可重现的代码供你们测试:

导入

import numpy.random as npr
import numpy
import pandas as pd
from pandas import DataFrame, date_range
from datetime import datetime, timedelta

创建数据

np.random.seed(111)

生成测试数据的功能

def CreateDataSet(Number=1):

    Output = []

    for i in range(Number):

        # Create a date range with hour frequency
        date = date_range(start='10/1/2012', end='10/31/2012', freq='H')

        # Create long lat data
        laty = npr.normal(4815862, 5000,size=len(date))
        longx = npr.normal(687993, 5000,size=len(date))

        # status of interest
        status = [0,1]

        # Make a random list of statuses
        random_status = [status[npr.randint(low=0,high=len(status))] for i in range(len(date))]

        # user pool
        user = ['sally','derik','james','bob','ryan','chris']

        # Make a random list of users 
        random_user = [user[npr.randint(low=0,high=len(user))] for i in range(len(date))]

        Output.extend(zip(random_user, random_status, date, longx, laty))

    return pd.DataFrame(Output, columns = ['user', 'status', 'date', 'long', 'lat'])

#Create data  
data = CreateDataSet(3)
len(data)
#some time deltas
before = timedelta(hours = 8)
after = timedelta(minutes = 1)

加速功能

def work(df):

    output = []
    #loop through data index's
    for i in range(0, len(df)):
    l = []
        #first we will filter out the data by date to have a smaller list to compute distances for

        #create a mask to query all dates between range for date i
        date_mask = (df['date'] >= df['date'].iloc[i]-before) & (df['date'] <= df['date'].iloc[i]+after)
        #create a mask to query all users who are not user i (themselves)
        user_mask = df['user']!=df['user'].iloc[i]
        #apply masks
        dists_to_check = df[date_mask & user_mask]

        #for point i, create coordinate to calculate distances from
        a = np.array((df['long'].iloc[i], df['lat'].iloc[i]))
        #create array of distances to check on the masked data
        b = np.array((dists_to_check['long'].values, dists_to_check['lat'].values))

        #for j in the date queried data
        for j in range(1, len(dists_to_check)):
            #compute the ueclidean distance between point a and each point of b (the date masked data)
            x = np.linalg.norm(a-np.array((b[0][j], b[1][j])))

            #if the distance is within our range of interest append the index to a list
            if x <=100:
                l.append(j)
            else:
                pass
        try:
            #use the list of desired index's 'l' to query a final subset of the data
            data = dists_to_check.iloc[l]
            #summarize the column of interest then append to output list
            output.append(data['status'].sum())
        except IndexError, e:
            output.append(0)
            #print "There were no data to add"

    return pd.DataFrame(output)

运行代码并计时

start = datetime.now()
out = work(data)
print datetime.now() - start

有没有办法以矢量化方式进行此查询?或者我应该追求另一种技术。

【问题讨论】:

  • nn 列是每行 100 米内所有其他 x/y 坐标的列表,还是 100 米内所有坐标的 nn 值都相同?
  • 'nn' 是 100m 内记录的所有索引的列表。我能够将索引列表传递给.iloc []。因此,我可以遍历数据并使用这些点查询 df,然后为每条记录创建日期查询。如果我继续,我会更新答案。我觉得这是一个相关的问题。
  • 您能否展示您现在使用的代码的可重现示例?这将更容易提出可能的加速建议。
  • 我认为你可以使用kd_tree找到距离最大为100m的所有点对,然后再通过时间戳进一步过滤结果。
  • 这仍然让我循环浏览整个文件。通常,首先按日期过滤会将我需要计算距离的点数减少到

标签: python pandas spatial temporal


【解决方案1】:

这至少在一定程度上解决了我的问题。由于循环可以独立地对数据的不同部分进行操作,因此并行化在这里很有意义。

使用 Ipython...

from IPython.parallel import Client
cli = Client()
cli.ids

cli = Client()
dview=cli[:]

with dview.sync_imports():
    import numpy as np
    import os
    from datetime import timedelta
    import pandas as pd

#We also need to add the time deltas and output list into the function as 
#local variables as well as add the Ipython.parallel decorator

@dview.parallel(block=True)
def work(df):
    before = timedelta(hours = 8)
    after = timedelta(minutes = 1)
    output = []

最终时间1:17:54.910206,大约是原始时间的1/4

我仍然对任何人在函数体中提出小的速度改进建议非常感兴趣。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-27
    • 1970-01-01
    • 2018-04-26
    相关资源
    最近更新 更多