Оригинальный набор:
Создайте его псевдокопию (перетаскиванием CNTRL в оглавлении) и сделайте пространственное соединение один ко многим с клоном. В этом случае я использовал расстояние 500м. Выходная таблица:
Удалить записи из этой таблицы, где PAR_ID = PAR_ID_1 - легко.
Выполните итерацию по таблице и удалите записи, где (PAR_ID, PAR_ID_1) = (PAR_ID_1, PAR_ID) любой записи над ней. Не так просто, используйте acrpy.
Вычислить центроиды водосбора (UniqID = PAR_ID). Это узлы или сеть. Соедините их линиями, используя таблицу пространственного соединения. Это отдельная тема, наверняка освещенная где-то на этом форуме.
В приведенном ниже сценарии предполагается, что таблица узлов выглядит следующим образом:
где MUID получен из посылок, P2013 - это поле для подведения итогов. В этом случае = 1 только для подсчета. [rcvnode] - вывод сценария для сохранения идентификатора группы, равного NODEREC первого узла в определенной группе / кластере.
Связывает структуру таблицы с выделенными важными полями
Times хранит вес ссылки / края, то есть стоимость перемещения от узла к узлу. В этом случае равен 1, так что стоимость проезда всех соседей одинакова. [fi] и [ti] - последовательный номер подключенных узлов. Чтобы заполнить эту таблицу, поищите в этом форуме информацию о том, как назначать узлы и ссылки на них.
Скрипт настроен под мой рабочий стол mxd. Должен быть изменен, жестко запрограммирован с указанием имен полей и источников:
import arcpy, traceback, os, sys,time
import itertools as itt
scriptsPath=os.path.dirname(os.path.realpath(__file__))
os.chdir(scriptsPath)
import COMMON
sys.path.append(r'C:\Users\felix_pertziger\AppData\Roaming\Python\Python27\site-packages')
import networkx as nx
RATIO = int(arcpy.GetParameterAsText(0))
try:
def showPyMessage():
arcpy.AddMessage(str(time.ctime()) + " - " + message)
mxd = arcpy.mapping.MapDocument("CURRENT")
theT=COMMON.getTable(mxd)
НАЙТИ СЛОЙ СЛОЯ
theNodesLayer = COMMON.getInfoFromTable(theT,1)
theNodesLayer = COMMON.isLayerExist(mxd,theNodesLayer)
ПОЛУЧИТЬ ССЫЛКИ СЛОЯ
theLinksLayer = COMMON.getInfoFromTable(theT,9)
theLinksLayer = COMMON.isLayerExist(mxd,theLinksLayer)
arcpy.SelectLayerByAttribute_management(theLinksLayer, "CLEAR_SELECTION")
linksFromI=COMMON.getInfoFromTable(theT,14)
linksToI=COMMON.getInfoFromTable(theT,13)
G=nx.Graph()
arcpy.AddMessage("Adding links to graph")
with arcpy.da.SearchCursor(theLinksLayer, (linksFromI,linksToI,"Times")) as cursor:
for row in cursor:
(f,t,c)=row
G.add_edge(f,t,weight=c)
del row, cursor
pops=[]
pops=arcpy.da.TableToNumPyArray(theNodesLayer,("P2013"))
length0=nx.all_pairs_shortest_path_length(G)
nNodes=len(pops)
aBmNodes=[]
aBig=xrange(nNodes)
host=[-1]*nNodes
while True:
RATIO+=-1
if RATIO==0:
break
aBig = filter(lambda x: x not in aBmNodes, aBig)
p=itt.combinations(aBig, 2)
pMin=1000000
small=[]
for a in p:
S0,S1=0,0
for i in aBig:
p=pops[i][0]
p0=length0[a[0]][i]
p1=length0[a[1]][i]
if p0<p1:
S0+=p
else:
S1+=p
if S0!=0 and S1!=0:
sMin=min(S0,S1)
sMax=max(S0,S1)
df=abs(float(sMax)/sMin-RATIO)
if df<pMin:
pMin=df
aBest=a[:]
arcpy.AddMessage('%s %i %i' %(aBest,sMax,sMin))
if df<0.005:
break
lSmall,lBig,S0,S1=[],[],0,0
arcpy.AddMessage ('Ratio %i' %RATIO)
for i in aBig:
p0=length0[aBest[0]][i]
p1=length0[aBest[1]][i]
if p0<p1:
lSmall.append(i)
S0+=p0
else:
lBig.append(i)
S1+=p1
if S0<S1:
aBmNodes=lSmall[:]
for i in aBmNodes:
host[i]=aBest[0]
for i in lBig:
host[i]=aBest[1]
else:
aBmNodes=lBig[:]
for i in aBmNodes:
host[i]=aBest[1]
for i in lSmall:
host[i]=aBest[0]
with arcpy.da.UpdateCursor(theNodesLayer, "rcvnode") as cursor:
i=0
for row in cursor:
row[0]=host[i]
cursor.updateRow(row)
i+=1
del row, cursor
except:
message = "\n*** PYTHON ERRORS *** "; showPyMessage()
message = "Python Traceback Info: " + traceback.format_tb(sys.exc_info()[2])[0]; showPyMessage()
message = "Python Error Info: " + str(sys.exc_type)+ ": " + str(sys.exc_value) + "\n"; showPyMessage()
Пример вывода для 6 групп:
Вам понадобится пакет сайта NETWORKX
http://networkx.github.io/documentation/development/install.html
Скрипт принимает необходимое количество кластеров в качестве параметра (6 в примере выше). Он использует таблицы узлов и ссылок для построения графа с равным весом / расстоянием ребер перемещения (Times = 1). Он учитывает объединение всех узлов на 2 и вычисляет общее количество [P2013] в двух группах соседей. Когда требуемое соотношение достигнуто, например, (6-1) / 1 на первой итерации, продолжается с уменьшенным целевым отношением, т.е. 4 и т. Д. До 1. Начальные точки имеют огромное значение, поэтому убедитесь, что ваши «конечные» узлы расположены сверху вашей таблицы узлов (сортировка?) Смотрите первые 3 группы в примере вывода. Это помогает избежать «обрезки веток» на каждой следующей итерации.
Настройка скрипта для работы с mxd:
- вам не нужно импортировать COMMON. Это моя собственная вещь, которая читает мою собственную таблицу окружения, в которой указаны NodesLayer, theLinksLayer, linksFromI, linksToI. Замените соответствующие строки собственным именованием узлов и слоев ссылок.
- Обратите внимание, что в поле P2013 может храниться что угодно, например количество арендаторов или площадь посылки. Если это так, вы можете кластеризовать полигоны, чтобы держать примерно одинаковое количество людей и т. Д.