【问题标题】:Labelled datatypes Python带标签的数据类型 Python
【发布时间】:2016-10-27 09:32:17
【问题描述】:

我正在计算一个点和多个线段之间的测地线距离。每条线段都有一个唯一的标识号。我想从我的距离函数返回距离,以便它们本质上是联系在一起的。我还想保持功能,如对距离进行排序,并用标签或位置对它们进行索引,并取回距离数据和标签。类似于带有索引的 Pandas 系列,但我不能使用系列,因为数据返回到 Pandas DataFrame 中,然后扩展系列并弄得一团糟。这是一个例子:

In [1]: '''Note that all this happens inside an apply function of a Pandas Series'''
        labels = [25622, 25621, 25620, 25619, 25618]
        dist = vect_dist_funct(pt, labels) #vect_dist_funct does the computations, and returns distances in meters
        dist
Out[1]: array([296780.2217658355, 296572.4476883276, 296364.21166884096,
               296156.4366241771, 295948.6610171968], dtype=object)

然而,我想要的是这样的 dict,其中标签和距离本质上是相互关联的:

{25622 : 296780.2217658355,
 25621 : 296572.4476883276,
 25620 : 296364.21166884096,
 25619 : 296156.4366241771,
 25618 : 295948.6610171968}

但现在我失去了这些值的功能。我不能轻易地对它们进行排序,或者比较它们,或者任何东西。我查看了Numpy Structured Arrays,它们似乎可行,但如果我无法对距离进行排序并获得最近段的索引,那么它对我来说没有多大用处。还有其他我可以使用的数据类型吗?

长篇故事和背景

我正在尝试进行空间连接。通过在 RTree (example) 中搜索,我得到了一个点最有可能接近的段的索引。这些是标签中的索引。然后我查看线几何表以找到这些选定标签的线几何,并计算点到每个线段的距离。

接下来的步骤涉及检查空间连接的完整性。在某些情况下,最近不是最佳连接候选者,连接需要在其他参数上进行评估。因此,我的计划是从最近的部分向外工作。这将涉及对距离进行排序,并获取最近段的索引,然后使用该索引查看段表并提取线的其他属性以进行检查。如果可以确认匹配,则接受该段,否则,将其拒绝,并且算法将移动到下一个最近的段。

我正在寻找一种能够完成所有这些工作的数据类型,而不会破坏计算它的段的距离之间的联系。

使用 Pandas 的问题

这就是函数的实际调用方式:

joined = points['geometry'].apply(pointer, centroid=line['centroid'], tree_idx=tree_idx))

然后在pointer 内部,会发生这种情况:

def pointer(point, centroid, tree_idx):
    intersect = list(tree_idx.intersection(point.bounds))
    if len(intersect) > 0:
        points = pd.Series([point.coords[0]]*len(intersect)).values
        polygons = centroid.loc[intersect].values
        dist = vect_dist_funct(points, polygons)
        return pd.Series(dist, index=intercept, name='Dist').sort_values()
    else:
        return pd.Series(np.nan, index=[0], name='Dist')

然后,joined 看起来像这样:

这是因为未计算所有点(行是点)和所有线(列是线)之间的距离。这成本太高了(4M 点,每个状态 180k 行,整个数据集有 50 个状态)。此外,与我返回两个 Numpy 数组时相比,生成 joined 的这个 DataFrame 合并操作将运行时间增加了 7 倍。返回两个 Numpy 数组的问题在于,要始终保持距离和行 ID 对齐并不容易。

点、线、tree_idx 示例

请注意,这是在列和行中截断的数据集。我只包括相关列,而不包括其余数据:

点数:

                        geometry
id      
88400001394219  0.00    POINT (-105.2363291 39.6988139)
                0.25    POINT (-105.2372017334178 39.69899060448157)
                0.50    POINT (-105.2380177896182 39.69933953105642)
                0.75    POINT (-105.2387202141595 39.69988447162143)
                1.00    POINT (-105.2393222 39.7005405)
88400002400701  0.00    POINT (-104.7102833 39.8318348)
                0.25    POINT (-104.7102827 39.831966625)
                0.50    POINT (-104.7102821 39.83209845)
                0.75    POINT (-104.7102815 39.832230275)
                1.00    POINT (-104.7102809 39.8323621)

所以这基本上是线上的插值点。 line id 是第一级索引,第二级是插值点的百分比。这形成了第一个数据集,我想将第二个数据集中的一些属性带到该数据集。

行:

        geometry                                            centroid
id      
71345   POLYGON ((-103.2077992965318 40.58026765162965...   (-103.20073265160862, 40.576450381964975)
71346   POLYGON ((-103.2069505830457 40.58155121711739...   (-103.19987394433825, 40.57774903464972)
71347   POLYGON ((-103.2061017677045 40.58283487609803...   (-103.19901204453959, 40.57905245493993)
71348   POLYGON ((-103.2052000154291 40.58419853220472...   (-103.19815200508097, 40.58035300329024)
71349   POLYGON ((-103.2043512639656 40.58548197865339...   (-103.19729445792181, 40.58164972491414)
71350   POLYGON ((-103.2035025651746 40.5867652936463,...   (-103.1964362470977, 40.5829473948391)
71351   POLYGON ((-103.2026535431035 40.58804903349249...   (-103.19557847342394, 40.58424434094705)
71352   POLYGON ((-103.201804801526 40.58933229190573,...   (-103.19472966696722, 40.58552767098465)
71353   POLYGON ((-103.2009557884142 40.59061590473365...   (-103.19388484652855, 40.58680427447224)
71354   POLYGON ((-103.2001001699726 40.59190793446012...   (-103.19303392095904, 40.5880882237994)

这是第二个数据集的一部分(这个答案开头提到的标签是这个数据集的索引)。目标是以智能的方式将属性从该数据集传输到点数据集。第一步是找到离每个点最近的线。然后,我将比较 points 数据集中的一些属性与lines 数据集,并确认或拒绝连接,就像我提到的那样。

tree_idx:

tree_idx 是使用以下代码创建的:

import rtree
lines_bounds = lines['geometry'].apply(lambda x: x.bounds)
tree_idx = rtree.index.Index()
for i in lines_bounds.index:
    tree_idx.insert(i, lines_bounds.loc[i])

【问题讨论】:

  • 您能否详细说明您希望拥有但无法通过字典实现的功能?
  • 我正在尝试进行空间连接。在这样做的过程中,最接近的并不总是正确的加入伙伴。在确认加入之前,我还需要查看其他参数。所以我的计划是从最近的地方向外移动,边走边检查、确认或拒绝。字典不是结构化的,不能按顺序存储。此外,字典不能按位置索引,而且我无法事先知道所有段的键。
  • 您能解释一下为什么在您的情况下使用 pandas Series/DataFrame 不起作用吗?由于 pandas 完全适用于您解释的情况(保持值和标签之间的链接,但仍然能够使用这些值,对它们进行排序,..)
  • 此外,也许geopandas 会很有趣:geopandas.readthedocs.io/en/latest/index.html
  • @Kartik:听起来你的计算效率很低。我认为我们需要查看整个代码来提供帮助。我认为这是 XY 问题的一个示例,您在其中寻求数据结构帮助,而您的问题可能在您的算法中。

标签: python numpy pandas


【解决方案1】:

在一切之后,在尝试让 TheBlackCat 的答案工作了大约 3 个小时之后,我决定使用xarray。所以现在pointer 函数看起来像这样:

def pointer(point, centroid, tree_idx):
    intersect = list(tree_idx.intersection(point.bounds))
    if len(intersect) > 0:
        points = pd.Series([point.coords[0]]*len(intersect)).values
        polygons = centroid.loc[intersect].values
        dist = vect_dist_funct(points, polygons)
        sorter = np.argsort(dist)
        return xr.DataArray(dist[sorter], [('dim0', np.asarray(intersect)[sorter])])
    else:
        return xr.DataArray(np.nan)

完成。这适合我的需要。我有它们一起计算的距离和段 ID,这样一个上的转换会影响另一个。而且距离还是可以操作的,xarray还给了我分组、合并等高级功能。

此外,在 0.1% 的数据上运行一个状态大约需要 1 分钟,而 10% 的数据需要 10 分钟。因此,我预计 100% 的数据大约是 100 分钟。但老实说,即使一个状态需要 3 个小时,我仍然可以在一天内完成所有 50 个状态(在 16 核服务器上使用多线程)。所以我暂时对此感到满意。感谢我得到的所有建议。尤其是@TheBlackCat、@michael_j_ward 和@hpaulj。

【讨论】:

    【解决方案2】:

    所以我认为您的整体问题是您正在创建一个DataFrame,其中列标签是intercept 值。我认为您想要做的是创建一个DataFrame,其中一列包含截距值,而另一列包含距离。我会尝试为您提供我认为会有所帮助的代码,但是如果没有您的原始数据就很难确定,因此您需要对其进行一些修改才能使其完美运行。

    首先,我将修改vect_dist_funct,因此如果第一个参数是标量,它会创建正确长度的列表,如果第二个参数为空,则返回NaN

    接下来我会将所有有用的值作为列添加到 DataFrame:

    points['intersect'] = points['geometry'].apply(lambda x: np.array(tree_idx.intersection(x.bounds)))
    points['polygons'] = points['intersect'].apply(lambda x: centroid.loc[x].values)
    points['coords0'] = points['geometry'].apply(lambda x: x.coords[0])
    points['dist'] = points.apply(lambda x: vect_dist_funct(x.coords0, x.polygons), axis=1)
    

    这将为您提供一个包含所有距离的列。如果您真的希望截距值可访问,则可以创建一个仅包含截距和距离的 DataFrame,然后将截距作为另一个多索引级别,以避免过多的 NaN 值:

    pairs = points.apply(lambda x: pd.DataFrame([x['intersect'], x['dist']], index=['intersect', 'dist']).T.stack(), axis=1)
    pairs = pairs.stack(level=0).set_index('intersect', append=True)
    pairs.index = pairs.index.droplevel(level=2)
    

    这应该会给你一个Series,其中第一个索引是 id,第二个是百分比,第三个是相交,值是距离。

    【讨论】:

    • 赞!非常感谢你!它引起了一些“啊哈”,并导致了几个耳光。我被迷住了,以至于我被困在那里。
    • 当我实现你的答案时,结果发现有很多问题:首先,tree_idx.intersection 提供了一个生成器。但这很容易通过使用centroid.loc[list(x)] 解决。然后,由于这会在某些地方返回一个列表,因此距离计算并不是那么简单。我仍在努力弄清楚如何做到这一点......
    • 任何想法@TheBlackCat?问题是 points['coords0'] 有一个 x 和 y 坐标的元组。然而points['polygons'] 是一个包含 x 和 y 坐标的元组列表。该列表的长度可以是 0 到 151 之间的任意值(在真正密集的区域中可能更多)。所以基本上,'coords0' 需要重复到与'多边形'一样长,并为此列表的每个元素计算距离,对于points DataFrame 中的所有行...
    • @Kartik:计算是在vect_dist_funct 中完成的,对吧?您是否有理由不能每次只使用coords0 进行计算,而不是匹配coords0polygons 的相应元素?如果这是一个真正的问题,您可以在 vect_dist_funct 内部进行复制,但我无法想到真正需要这种复制的场景。
    • @Kartik:目前,您在Pointer 中创建了许多coords[0] 的副本。我的意思是你可以在vect_dist_funct 中制作这些副本。您甚至可能不需要进行显式复制,查看 geopy 源代码 itertools.repeat 应该可以解决问题,避免在内存中存储大量重复值。
    【解决方案3】:

    所以,我认为索引为标签的数据框可能是最简单的

    distances = {25622 : 296780.2217658355,
     25621 : 296572.4476883276,
     25620 : 296364.21166884096,
     25619 : 296156.4366241771,
     25618 : 295948.6610171968}
    
    df = pd.DataFrame([tup for tup in distances.items()],columns=["label", "dist"]).sort_values('dist').set_index('label')
    df
    

    输出:

        dist
    label   
    25618   295948.661017
    25619   296156.436624
    25620   296364.211669
    25621   296572.447688
    25622   296780.221766
    

    那么如果你想通过标签名称访问距离

    df.loc[25620]
    Out:
    dist    296364.211669
    Name: 25620, dtype: float64
    

    然后,如果您想找到“靠近”该点的标签,您可以使用

    获取行号
    row_num = df.index.get_loc(25620)
    print(row_num)
    Out: 2
    

    然后您可以使用df.iloc[row_number] 访问“附近”点

    df.iloc[3]
    Out: 
    dist    296572.447688
    Name: 25621, dtype: float64
    

    这涵盖了您需要的一切吗?

    【讨论】:

    • 不,因为如果我返回一个系列/数据帧,它会将整个事物扩展为许多 NaN 值。调用函数是一个GeoDataFrame上的apply,所以当返回类型是另一个Pandas Series时,会合并到输出中,整个过程耗时太长。
    • 等等,我从您的问题中假设您可以将您的数据放入{key: value} 对中,不是这样吗?
    • 此解决方案既易于排序又易于比较,并保持标签与其距离之间的关系,同时保持轻松找到“附近”标签的能力。这怎么不是你要求的?
    • 我可以将它们放入 key : value 对中,Michael,但请参阅问题中的编辑。我已经尝试过熊猫系列。
    • 在我看来,你和df.apply 太结婚了。您尝试做的逻辑更多是“对于每一列,计算距离,检查最短距离,执行完整性检查并重复,直到我们找到正确的标签。继续下一列”也许你应该分开你的行动更符合您的流程逻辑
    猜你喜欢
    • 2016-03-11
    • 2017-07-22
    • 2013-01-25
    • 1970-01-01
    • 2020-12-03
    • 1970-01-01
    • 1970-01-01
    • 2013-12-15
    • 1970-01-01
    相关资源
    最近更新 更多