将 Shapely Multipoint 转换为 Pandas Dataframe 的优雅方式

问题描述

我需要将 Shapely MultiPoints 的 dict 转换为数据帧。我已经编写了一个双循环程序来做到这一点,但我想知道是否有更好的方法来做到这一点。

示例数据和当前代码

object.bookmark

如果有点慢和笨重,这是有效的。这是否可以做得更好,如果可以,如何做?

解决方法

构建框架构造函数的列表理解可能是这里的最佳选择:

df = pd.DataFrame(
    [[k,point.x,point.y]
     for k,v in data.items()
     for point in wkb.loads(v,hex=True)],columns=['ID','X','Y']
)
  ID       X        Y
0  A     3.0      5.0
1  A     3.0      3.0
2  B  -141.0    820.0
3  B   910.0   -332.0
4  C  5102.0   7020.0
5  C    30.0 -20020.0

pandas 操作在这里会很昂贵,尤其是循环中的 append,它需要在每次迭代中生成 DataFrame 的副本。


一些来自 %timeit 的时间信息:

这个答案

def fn(data):
    return pd.DataFrame(
        [[k,point.y]
         for k,v in data.items()
         for point in wkb.loads(v,'Y']
    )
%timeit fn(data)
552 µs ± 11.2 µs per loop (mean ± std. dev. of 7 runs,1000 loops each)

OP's solution

def fn2(data):
    df = pd.DataFrame(columns=["ID","X","Y"])
    for key,wkb_val in data.items():
        for point in wkb.loads(wkb_val,hex=True):
            df = df.append({
                "ID": key,"X": point.x,"Y": point.y
            },ignore_index=True)
    return df
%timeit fn2(data)
10.3 ms ± 77.4 µs per loop (mean ± std. dev. of 7 runs,100 loops each)

Steele Farnsworth's Solution

def fn3(data):
    return pd.concat(
        (
            (
                pd.concat(
                    (pd.Series({"ID": key,"Y": point.y}) for
                     point in
                     wkb.loads(wkb_val,hex=True)),axis=1)
            )
            for key,wkb_val in data.items()
        ),axis=1
    ).T
%timeit fn3(data)
3.42 ms ± 132 µs per loop (mean ± std. dev. of 7 runs,100 loops each)
,

性能缓慢的原因是每次执行 df = df.append(...) 时,您都在创建一个新的 DataFrame 并复制所有现有行。

这个解决方案看起来有点笨拙,但我相信它会奏效。

df = pd.concat(
    (
        (
            pd.concat((pd.Series({"ID": key,"Y": point.y}) for point in wkb.loads(wkb_val,axis=1)
        )
        for key,wkb_val in data.items()
    ),axis=1
).T

最后的 .T 转置 DataFrame,否则会创建一个宽 DataFrame,其中 ID、X 和 Y 作为索引而不是列。