【问题标题】:Keep smallest value for each unique ID with arcpy/numpy使用 arcpy/numpy 为每个唯一 ID 保留最小值
【发布时间】:2014-11-11 15:02:54
【问题描述】:

我有一个 ESRI 点形状文件,其中包含(以及其他)一个 nMSLINK 字段和一个 DIAMETER 字段。由于空间连接,MSLINK 不是唯一的。我想要实现的是仅保留 shapefile 中具有唯一 MSLINK 和最小 DIAMETER 值的特征,以及其他字段中的相应值。我可以使用搜索光标来实现这一点(遍历所有特征并删除每个不符合要求的特征,但这需要很长时间(> 75000 个特征)。我想知道例如 numpy 是否可以在 ArcMap/arcpy 中更快地完成这个技巧。

【问题讨论】:

  • 你能添加一些代码作为一个最小的例子吗?
  • # Select smallest MIDDELLIJN_INWENDIG for each MSLINKrows = arcpy.SearchCursor('Afsluiters_Leidingen', '', '', '', 'MSLINK A; MIDDELLIJN_INWENDIG D')for row in rows:diam = row.MIDDELLIJN_INWENDIGdict[row.MSLINK] = diamfor key in dict:arcpy.SelectLayerByAttribute_management('Afsluiters_Leidingen','NEW_SELECTION','MSLINK = ' + str(key) + 'AND NOT MIDDELLIJN_INWENDIG = ' + str(dict[key]))arcpy.DeleteFeatures_management('Afsluiters_Leidingen')
  • 这可行,但对于大型 shapefile 需要很长时间

标签: python numpy unique arcpy arcmap


【解决方案1】:

我认为,如果您使用内存而不是与 arcgis 交互,那么进行这种处理肯定会快很多。例如,通过将所有行首先放入一个 python 对象(在这里命名元组可能是一个不错的选择)。然后,您可以找出要删除或插入的行。

最快的方法取决于 a) 如果您有很多 (MSLINK) 重复行,那么最快的方法就是在新层中插入您需要的行。或者 b) 如果要删除的行与总行数相比只有几行,则删除速度更快。

对于 a),您需要将所有字段(包括点坐标)提取到元组中,这样您就可以创建一个新要素类并插入新行。

# Example of Variant a:

from collections import namedtuple

# assuming the following:
source_fc # contains name of the fclass
the_path # contains path to the shape
cleaned_fc # the name of the cleaned fclass


# use all fields of source_fc plus the shape token to get a touple with xy
# coordinates (using 'mslink' and 'diam' here to simplify the example)
fields = ['mslink', 'diam', 'field3', ... ]
all_fields = fields + ['SHAPE@XY']

# define a namedtuple to hold and work with the rows, use the name 'point' to
# hold the coordinates-tuple
Row = namedtuple('Row', fields + ['point'])
data = []
with arcpy.da.SearchCursor(source_fc, fields) as sc:
    for r in sc:
        # unzip the values from each row into a new Row (namedtuple) and append
        # to data
        data.append(Row(*r))

# now just delete the rows we don't want, for this, the easiest way, is probably
# to order the tuple first after MSLINK and then after the diamater...
data = sorted(data, key = lambda x : (x.mslink, x.diam))

# ... now just keep the first ones for each mslink
to_keep = []
last_mslink = None
for d in data:
    if last_mslink != d.mslink:
        last_mslink = d.mslink
        to_keep.append(d)

# create a new feature class with the same fields as the source_fc
arcpy.CreateFeatureclass_management(
        out_path=the_path, out_name=cleaned_fc, template=source_fc)
with arcpy.da.InsertCursor(cleaned_fc, all_fields) as ic:
    for r in to_keep:
        ic.insertRow(*r)

对于备选方案 b) 我只获取 3 个字段,一个唯一 ID、MSLINK 和直径。然后创建一个删除列表(这里你只需要唯一的 id)。然后再次遍历要素类并删除删除列表中具有 id 的行。可以肯定的是,我会先复制要素类,然后制作副本。

【讨论】:

    【解决方案2】:

    您可以采取一些步骤来更有效地完成这项任务。首先,使用数据分析师游标而不是旧版本的游标将提高您的处理速度。这假设您在 10.1 或更高版本中工作。然后您可以使用汇总统计,即它能够根据案例字段找到最小值。对于您,case 字段将是 nMSLINK。

    下面的代码首先创建一个统计表,其中包含所有唯一的“nMSLINK”值及其对应的最小“DIAMETER”值。然后,我使用表选择来仅选择表中“频率”字段不是 1 的行。从这里我遍历我的新表并开始构建将构成最终 sql 语句的字符串列表。在这次迭代之后,我使用 python join 函数创建了一个如下所示的 sql 字符串:

    ("nMSLINK" = 'value1' AND "DIAMETER" <> 624.0) OR ("nMSLINK" = 'value2' AND "DIAMETER" <> 1302.0) OR ("nMSLINK" = 'value3' AND "DIAMETER" <> 1036.0) ...
    

    sql 选择 nMSLINK 值不唯一且 DIAMETER 值不是最小值的行。使用此 SQL,我按属性选择并删除选定的行。

    编写此 SQL 语句时假设您的要素类位于文件地理数据库中,并且“nMSLINK”是一个字符串字段,“DIAMETER”是一个数字字段。

    代码有以下输入:

    特征:要分析的特征

    工作区:临时存储几个中间表的文件夹

    TempTableName1:一个临时表的名称。

    TempTableName2:第二个临时表的名称

    Field1 = 非唯一字段

    Field2 = 您希望找到最小值的数值字段

    代码:

    # Import modules
    from arcpy import *
    import os
    # Local variables
    
    #Feature to analyze
    Feature = r"C:\E1B8\ScriptTesting\Workspace\Workspace.gdb\testfeatureclass"
    #Workspace to export table of identicals
    Workspace = r"C:\E1B8\ScriptTesting\Workspace"
    #Name of temp DBF table file
    TempTableName1 = "Table1"
    TempTableName2 = "Table2"
    
    #Field names
    Field1 = "nMSLINK" #nonunique
    Field2 = "DIAMETER" #field with numeric values
    
    #Make layer to allow selection
    MakeFeatureLayer_management (Feature, "lyr")
    
    #Path for first temp table
    Table = os.path.join (Workspace, TempTableName1)
    
    #Create statistics table with min value
    Statistics_analysis (Feature, Table, [[Field2, "MIN"]], [Field1])
    
    #SQL Select rows with frequency not equal to one
    sql = '"FREQUENCY" <> 1'
    # Path for second temp table
    Table2 = os.path.join (Workspace, TempTableName2)
    # Select rows with Frequency not equal to one
    TableSelect_analysis (Table, Table2, sql)
    
    #Empty list for sql bits
    li = []
    
    # Iterate through second table
    cursor = da.SearchCursor (Table2, [Field1, "MIN_" + Field2])
    for row in cursor:
        # Add SQL bit to list
        sqlbit = '("' + Field1 + '" = \'' + row[0] + '\' AND "' + Field2 + '" <> ' + str(row[1]) + ")"
        li.append (sqlbit)
    del row
    del cursor
    
    #Create SQL for selection of unwanted features
    sql = " OR ".join (li)
    print sql
    #Select based on SQL
    SelectLayerByAttribute_management ("lyr", "", sql)
    
    #Delete selected features
    DeleteFeatures_management ("lyr")
    
    #delete temp files
    Delete_management ("lyr")
    Delete_management (Table)
    Delete_management (Table2)
    

    这应该比直线光标快。让我知道这是否有意义。祝你好运!

    【讨论】:

    • 我用一个非常大的数据集测试了我的方法,但它并不可靠。具体来说,最终的 SQL 字符串变得太大并导致问题。我还测试了andzep的方法,他的工作正常。我推荐他的方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-15
    • 2021-04-14
    • 2017-06-11
    • 2022-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多