与迭代两个大Pandas数据帧相比,提高了效率

2024-04-25 22:04:55 发布

您现在位置:Python中文网/ 问答频道 /正文

我有两个巨大的Pandas数据帧,它们具有基于位置的值,我需要用df2的记录数更新df1['count'],这些记录数与df1中每个点的距离都小于1000m。在

这是我的数据的一个例子,导入熊猫

df1 =       lat      long    valA   count
        0   123.456  986.54  1      0
        1   223.456  886.54  2      0
        2   323.456  786.54  3      0
        3   423.456  686.54  2      0
        4   523.456  586.54  1      0

df2 =       lat      long    valB
        0   123.456  986.54  1
        1   223.456  886.54  2
        2   323.456  786.54  3
        3   423.456  686.54  2
        4   523.456  586.54  1

实际上,df1有大约1000万行,df2有大约100万行

我用熊猫创建了一个工作嵌套FOR循环数据框()方法,对于较小的测试数据集(df1=1k行和df2=100行)很好地工作,但完整的数据集要成倍地大,根据我的计算,需要几年才能完成。这是我的工作代码。。。在

^{pr2}$

df2中的每个点只需计数一次,因为df1中的点是均匀分布的。在一次迭代结束时,他们把希望从2行中删除。我最初也尝试创建merge/join语句,而不是嵌套循环,但没有成功。在

现阶段,如有助于提高效率,我们将不胜感激!在

编辑: 目标是用df2的点数更新df1中的“count”列(如下所示),这些点数是<;1km,并输出到一个新文件中。在

df1 =       lat      long    valA   count
        0   123.456  986.54  1      3
        1   223.456  886.54  2      1
        2   323.456  786.54  3      9
        3   423.456  686.54  2      2
        4   523.456  586.54  1      5

Tags: 数据方法距离pandasforcount记录long
2条回答

我最近也做过类似的事情,但不是用lat,lon做的,我只需要找到最近的点和它的距离。为此,我使用了scipy.space.cKDTree包装。速度相当快。 cKDTree

我认为在您的例子中,您可以使用查询\u ball_point()函数。在

from scipy import spatial
import pandas as pd

file1 = 'C:\\path\\file1.csv'    
file2 = 'C:\\path\\file2.csv' 

df1 = pd.read_csv(file1)
df2 = pd.read_csv(file2)
# Build the index
tree = spatial.cKDTree(df1[['long', 'lat']])
# Then query the index

你应该试试看。在

我经常这样做,我发现了一些最佳实践:

1)尽量使用numpy和numba

2)尽量利用并行化

3)跳过矢量化代码的循环(我们在这里使用带numba的循环来利用并行化)。在

在这个特殊的例子中,我想指出geopy带来的减速。虽然它是一个很好的包,并且可以产生相当精确的距离(与Haversine方法相比),但是它要慢得多(没有考虑实现的原因)。在

import numpy as np
from geopy import distance

origin = (np.random.uniform(-90,90), np.random.uniform(-180,180))
dest = (np.random.uniform(-90,90), np.random.uniform(-180,180))

%timeit distance.distance(origin, dest)

每个回路216µs±363 ns(平均值±标准偏差,7次运行,每个回路1000次)

也就是说,在这个时间间隔内,计算1000万x 100万的距离大约需要2160亿秒或60万小时。即使是并行也只能起到这么大的作用。在

因为当点非常接近时,您会感兴趣,所以我建议使用Haversine distance(这在较大距离下不太准确)。在

^{pr2}$

每个回路1.85µs±53.9 ns(平均值±标准偏差,7次运行,每次100000次循环)

这已经提高了100倍。但我们可以做得更好。您可能已经注意到我从numba添加的@vectorize装饰器。这使得以前标量的Haversine函数可以矢量化并将向量作为输入。我们将在下一步中利用这一点:

@jit(nopython=True, parallel=True)
def get_nearby_count(coords, coords2, max_dist):
    '''
    Input: `coords`: List of coordinates, lat-lngs in an n x 2 array
           `coords2`: Second list of coordinates, lat-lngs in an k x 2 array
           `max_dist`: Max distance to be considered nearby
    Output: Array of length n with a count of coords nearby coords2
    '''
    # initialize
    n = coords.shape[0]
    k = coords2.shape[0]
    output = np.zeros(n)

    # prange is a parallel loop when operations are independent
    for i in prange(n):
        # comparing a point in coords to the arrays in coords2
        x, y = coords[i]
        # returns an array of length k
        dist = haversine(x, y, coords2[:,0], coords2[:,1])
        # sum the boolean of distances less than the max allowable
        output[i] = np.sum(dist < max_dist)

    return output

希望现在有一个数组等于第一组坐标的长度(在您的例子中是1000万)。然后你就可以把它分配给你的数据帧作为你的计数!在

测试时间100000 x 10000:

n = 100_000
k = 10_000

coords1 = np.zeros((n, 2))
coords2 = np.zeros((k, 2))

coords1[:,0] = np.random.uniform(-90, 90, n)
coords1[:,1] = np.random.uniform(-180, 180, n)
coords2[:,0] = np.random.uniform(-90, 90, k)
coords2[:,1] = np.random.uniform(-180, 180, k)

%timeit get_nearby_count(coords1, coords2, 1.0)

每个回路2.45 s±73.2 ms(7次运行的平均值±标准偏差,每个回路1次)

不幸的是,这仍然意味着你将看到大约20000秒的东西。{ish使用了一个基于cd2}的机器。在

这是我目前所能做的最好的,祝你好运(同时,第一篇文章,感谢你激励我做出贡献!)在

PS:您还可以研究Dask数组和map_block()函数来并行化这个函数(而不是依赖prange)。如何划分数据可能会影响总的执行时间。在

PPS:1000000 x 100000(比全套小100倍)用了3分钟27秒(207秒),所以缩放看起来是线性的,有点宽容。在

购买力平价:通过简单的纬度差滤波器实现:

@jit(nopython=True, parallel=True)
def get_nearby_count_vlat(coords, coords2, max_dist):
    '''
    Input: `coords`: List of coordinates, lat-lngs in an n x 2 array
           `coords2`: List of port coordinates, lat-lngs in an k x 2 array
           `max_dist`: Max distance to be considered nearby
    Output: Array of length n with a count of coords nearby coords2
    '''
    # initialize
    n = coords.shape[0]
    k = coords2.shape[0]
    coords2_abs = np.abs(coords2)
    output = np.zeros(n)

    # prange is a parallel loop when operations are independent
    for i in prange(n):
        # comparing a point in coords to the arrays in coords2
        point = coords[i]
        # subsetting coords2 to reduce haversine calc time. Value .02 is from playing with Gmaps and will need to change for max_dist > 1.0
        coords2_filtered = coords2[np.abs(point[0] - coords2[:,0]) < .02]
        # in case of no matches
        if coords2_filtered.shape[0] == 0: continue
        # returns an array of length k
        dist = haversine(point[0], point[1], coords2_filtered[:,0], coords2_filtered[:,1])
        # sum the boolean of distances less than the max allowable
        output[i] = np.sum(dist < max_dist)

    return output

相关问题 更多 >