提高当前由 lambda 函数创建的 LineString 创建的性能

Improve performance of LineString creation, that currently is created by a lambda function

我有一个这样的数据框(这个例子只有四行,但实际上它有 O(10^6) 行):

DF:

    nodeid   lon      lat   wayid
0        1  1.70    42.10      52
1        2  1.80    42.30      52
2        3  1.75    42.20      53
3        4  1.72    42.05      53

我需要按 wayid 分组并连接组中每个元素的 lonlat 列,以获得如下输出:

output:

wayid
52    LINESTRING (1.7 42.1, 1.8 42.3)
53    LINESTRING (1.75 42.2, 1.72 42.05)
dtype: object

我可以通过以下方式创建示例 DataFrame:

DF = pd.DataFrame([[1, 1.7, 42.1, 52], [2, 1.8, 42.3, 52], [3, 1.75, 42.2, 53], [4, 1.72, 42.05, 53]])
DF.columns = ['nodeid', 'lon', 'lat', 'wayid']

我可以获得所需的输出,应用这样的 lambda 函数:

DF.groupby('wayid').apply(lambda r: LineString(np.array(r[['lon','lat']])))

然而,这是一个相当缓慢的过程,我需要以某种方式改进它(除了出现警告消息之外)。

关于如何通过提高性能获得相同结果的任何想法?

注意: 最后,实际上我需要这样的 GeoDataFrame:

GDF = gp.GeoDataFrame(geometry=DF.groupby('wayid')\
                                 .apply(lambda r: LineString(np.array(r[['lon','lat']])))

以防设计出更好的解决方案。

看起来很合理。具有 10^6 lat/lon 对的模拟数据框。通过删除 numpy 数组的创建有一个小的优化,因为 r 是一个数据框,您可以在其中访问 numpy 数组 .values

import geopandas as gpd
import pandas as pd
import numpy as np
from shapely.geometry import LineString
import warnings
warnings.filterwarnings('ignore')

cities = gpd.read_file(gpd.datasets.get_path("naturalearth_cities"))
c = cities.sample(1)
p = c["geometry"].values[0]
SIZE = 1000
N=20

x = np.random.uniform(p.x - 2, p.x + 2, SIZE)
y = np.random.uniform(p.y - 2, p.y + 2, SIZE)
grid = np.random.randint(0, SIZE//N, size=[SIZE, SIZE])

DF = pd.DataFrame(
    [
        {"wayid": way, "lon": x[g[1]], "lat": y[g[0]]}
        for way in range(SIZE//N)
        for g in np.argwhere(grid == way)
    ]
)

%timeit GEOMETRY = DF.groupby('wayid').apply(lambda r: LineString(np.array(r[['lon','lat']])))
%timeit GEOMETRY = DF.groupby('wayid').apply(lambda r: LineString(r.loc[:,["lon","lat"]].values))

GEOMETRY = DF.groupby('wayid').apply(lambda r: LineString(r.loc[:,["lon","lat"]].values))
GDF = gpd.GeoDataFrame(geometry=GEOMETRY, crs=cities.crs)

print(f"""DF: {DF.shape}
GDF: {GDF.shape}""")

输出

137 ms ± 1.8 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
134 ms ± 642 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
DF: (1000000, 3)
GDF: (50, 1)