Exemplo n.º 1
0
def execMaxpTabu(y, w, threshold=100.0, maxit=2, tabuLength=5, typeTabu="exact"):
    """Max-p-regions model (Tabu) 

    The max-p-regions model, devised by [Duque_Anselin_Rey2010]_ ,
    clusters a set of geographic areas into the maximum number of homogeneous
    regions such that the value of a spatially extensive regional attribute is
    above a predefined threshold value. In clusterPy we measure heterogeneity as
    the within-cluster sum of squares from each area to the attribute centroid
    of its cluster.

    The max-p-regions algorithm is composed of two main blocks: 
    
    - construction of a initial feasible solution. 
    - local improvement.
    
    There are three methods for local improvement: Greedy (execMaxpGreedy),
    Tabu (execMaxpTabu), and Simulated Annealing (execMaxpSa). A detailed
    explanation of each method can be found in Duque, Anselin and Rey (2010) [Duque_Anselin_Rey2010]_.

    For this version, the tabu search algorithm will stop after
    max(10,N/maxP) nonimproving moves. ::

        layer.cluster('maxpTabu',vars,<threshold>,<wType>,<std>,<maxit>,<tabuLength>,<typeTabu>,<dissolve>,<dataOperations>)

    :keyword vars: Area attribute(s). Important: the last variable in vars correspond to the spatially extensive attribute that will be constrained to be above the predefined threshold value (e.g. ['SAR1','SAR2','POP'])  
    :type vars: list
    :keyword threshold: Minimum value of the constrained variable at regional level. Default value threshold = 100.
    :type threshold: integer
    :keyword wType: Type of first-order contiguity-based spatial matrix: 'rook' or 'queen'. Default value wType = 'rook'. 
    :type wType: string
    :keyword std: If = 1, then the variables will be standardized.
    :type std: binary
    :keyword maxit: Number of times that the construction phase is repeated. The larger the value the higher the possibility of getting a large number of regions. Default value maxit = 2.
    :type maxit: integer
    :keyword tabuLength: Number of times a reverse move is prohibited. Default value tabuLength = 85. 
    :type tabuLength: integer
    :keyword typeTabu: Type of tabu search: (a) exact: chooses the best neighbouring solution for evaluation (it implies the enumeration of all the neighbouring solution at each iteration); (b) "random": evaluates a neighbouring solution selected at random and (See Ricca, F.  and Simeone (2008) for more on the difference between exact and random tabu). Default value typeTabu = "exact". 
    :type typeTabu: string 
    :keyword dissolve: If = 1, then you will get a "child" instance of the layer that contains the new regions. Default value = 0. Note: Each child layer is saved in the attribute layer.results. The first algorithm that you run with dissolve=1 will have a child layer in layer.results[0]; the second algorithm that you run with dissolve=1 will be in layer.results[1], and so on. You can export a child as a shapefile with layer.result[<1,2,3..>].exportArcData('filename')
    :type dissolve: binary
    :keyword dataOperations: Dictionary which maps a variable to a list of operations to run on it. The dissolved layer will contains in it's data all the variables specified in this dictionary. Be sure to check the input layer's fieldNames before use this utility.
    :type dataOperations: dictionary

    The dictionary structure must be as showed bellow.

    >>> X = {}
    >>> X[variableName1] = [function1, function2,....]
    >>> X[variableName2] = [function1, function2,....]

    Where functions are strings which represents the name of the
    functions to be used on the given variableName. Functions
    could be,'sum','mean','min','max','meanDesv','stdDesv','med',
    'mode','range','first','last','numberOfAreas. By deffault just
    ID variable is added to the dissolved map.
    """
    print "Running max-p-regions model (Duque, Anselin and Rey, 2010)"
    print "Local search method: Tabu Search"
    print "Number of areas: ", len(y)
    print "threshold value: ", threshold
    distanceType = "EuclideanSquared"
    distanceStat = "Centroid";
    objectiveFunctionType = "SS";
    selectionType = "Minimum";
    numRegionsType = "EndogenousThreshold";

    #  CONSTRUCTION PHASE 1: GROWING FEASIBLE REGIONS

    start = tm.time()

    #  print w
    #  print y

    am = AreaManager(w, y, distanceType)
    maxP = 0
    bestCandidates = {}
    for i in range(maxit):

        #  print "**** Iteration %d of %d ..."%(i+1,maxit)

        rm = RegionMaker(am,
                        distanceType = distanceType,
                        distanceStat = distanceStat,
                        selectionType = selectionType,
                        objectiveFunctionType = objectiveFunctionType,
                        numRegionsType = numRegionsType,
                        threshold = threshold)
        numRegions = len(rm.feasibleRegions)
        rm.getObj()

        #  print "rm.feasibleRegions",rm.feasibleRegions
        #  print "obj",rm.getObj()

        if numRegions > maxP:
            bestCandidates = {}
            maxP = numRegions
            obj = rm.objInfo
            bestCandidates[obj] = rm.feasibleRegions
        if numRegions == maxP:
            obj = rm.objInfo
            if obj in bestCandidates:
                pass
            else:
                bestCandidates[obj] = rm.feasibleRegions
        else:
            pass

    #   print "bestCandidates", bestCandidates

    ofValues = bestCandidates.keys()
    basicMemory = BasicMemory()
    while len(ofValues) >= 1:

        #  RECREATE SOLUTION

        rm.resetNow()
        minOfValue = min(ofValues)
        ofValues.remove(minOfValue)
        partialSolution = bestCandidates[minOfValue]

        #  print "ASSIGNING ENCLAVES"
        #  print partialSolution

        regionId = 0
        for growReg in partialSolution:
            seedGrowReg = partialSolution[growReg][0]
            rm.assignSeeds(seedGrowReg, regionId)
            partialSolution[growReg].remove(seedGrowReg)
            if len(partialSolution[growReg]) >= 1:
                for areaInGrow in partialSolution[growReg]:
                    rm.assignArea(areaInGrow, regionId)
            regionId += 1

        # CONSTRUCTION PHASE 2: ENCLAVES ASSIGNATION

        rm.feasibleRegions = copy.deepcopy(rm.region2Area)
        rm.getIntraBorderingAreas()
        rm.newExternal = set(rm.unassignedAreas)
        if len(rm.unassignedAreas) != 0:
            rm.constructionStage = "enclaves"
            while len(rm.unassignedAreas) != 0:
                rm.constructRegions()
        rm.objInfo = rm.getObjective(rm.region2Area)
        rm.feasibleRegions = copy.deepcopy(rm.region2Area)
        rm.getIntraBorderingAreas()

        #  print "ASSIGNED SOLUTION"
        #  print "OBJ: ", rm.getObjective(rm.region2Area), rm.returnRegions()

        rm.calculateRegionValueThreshold()

        #  LOCAL SEARCH

        rm.calcObj()
        convTabu = min(10,len(y)/maxP)  #   convTabu=230*numpy.sqrt(maxP)

        #  print "###ENTERING TABU",rm.objInfo,rm.returnRegions()

        rm.tabuMove(tabuLength, convTabu = convTabu, typeTabu=typeTabu)
        rm.calcObj()

        #  print "***** AFTER TABU",rm.objInfo,rm.returnRegions()
        #  EVALUATE SOLUTION

        if rm.objInfo < basicMemory.objInfo:
            basicMemory.updateBasicMemory(rm)
    time = tm.time() - start
    Sol = basicMemory.regions
    Of = basicMemory.objInfo
    print "FINAL SOLUTION: ", Sol
    print "FINAL OF: ", Of
    output = { "objectiveFunction": Of,
        "runningTime": time,
        "algorithm": "maxpTabu",
        "regions": len(Sol),
        "r2a": Sol,
        "distanceType": distanceType,
        "distanceStat": distanceStat,
        "selectionType": selectionType,
        "ObjectiveFuncionType": objectiveFunctionType}
    print "Done"
    return output
Exemplo n.º 2
0
def execAZPTabu(y, w, pRegions, initialSolution=[], convTabu=0, tabuLength=10):
    """Tabu variant of Automatic Zoning Procedure (AZP-Tabu) 

    
    AZP-Tabu aggregates N zones (areas) into M regions. "The M output regions
    should be formed of internally connected, contiguous, zones." ([Openshaw_Rao1995]_ pp 428).

    AZP-Tabu is a variant of the AZP algorithm that incorporates a search
    process, called Tabu algorithm [Glover1977]_. Tabu "allows the search
    process to escape from local optima whilst avoiding cyclical behaviour."
    ([Openshaw_Rao1995]_ pp 432).
    
    In Openshaw and Rao (1995) the objective function is not defined because
    AZP-Tabu can be applied to any function, F(Z). "F(Z) can be any function
    defined on data for the M regions in Z, and Z is the allocation of each of
    N zones to one of M regions such that each zone is assigned to only one
    region" ([Openshaw_Rao1995]_ pp 428)." In clusterPy we Minimize F(Z),
    where Z is the within-cluster sum of squares from each area to the
    attribute centroid of its cluster.

    Openshaw and Rao (1995) do not specify a value for the two most important
    parameters of Tabu seach: length of the tabu list and convergence
    criteria. See [Duque_Anselin_Rey2010]_ for an evaluation of the performance of
    tabu search within the context of spatial clustering.

    NOTE: The original algorithm proposes to start from a random initial
    feasible solution. Previous computational experience showed us that this
    approach leads to poor quality solutions. In clusterPy we started from an
    initial solution that starts with a initial set of seeds (as many seed as
    regions) selected using the K-means++ algorithm. From those seeds, other
    neighbouring areas are assigned to its closest (in attribute space)
    growing region. This strategy has proven better results. ::

        layer.cluster('azpTabu',vars,regions,<wType>,<std>,<initialSolution>,<convTabu>,<tabuLength>,<dissolve>,<dataOperations>)

    :keyword vars: Area attribute(s) (e.g. ['SAR1','SAR2']) 
    :type vars: list
    :keyword regions: Number of regions 
    :type regions: integer
    :keyword wType: Type of first-order contiguity-based spatial matrix: 'rook' or 'queen'. Default value wType = 'rook'. 
    :type wType: string
    :keyword std: If = 1, then the variables will be standardized.
    :type std: binary
    :keyword initialSolution: List with a initial solution vector. It is useful when the user wants a solution that is not very different from a preexisting solution (e.g. municipalities,districts, etc.). Note that the number of regions will be the same as the number of regions in the initial feasible solution (regardless the value you assign to parameter "regions"). IMPORTANT: make sure you are entering a feasible solution and according to the W matrix you selected, otherwise the algorithm will not converge.
    :type initialSolution: list
    :keyword convTabu: Stop the search after convTabu nonimproving moves (nonimproving moves are those moves that do not improve the current solution. Note that "improving moves" are different to "aspirational moves"). If convTabu=0 the algorithm will stop after max(10,len(N)/M) nonimproving moves. Default value convTabu = 0. 
    :type convTabu: integer
    :keyword tabuLength: Number of times a reverse move is prohibited. Default value tabuLength = 10. 
    :type tabuLength: integer
    :keyword dissolve: If = 1, then you will get a "child" instance of the layer that contains the new regions. Default value = 0. Note:. Each child layer is saved in the attribute layer.results. The first algorithm that you run with dissolve=1 will have a child layer in layer.results[0]; the second algorithm that you run with dissolve=1 will be in layer.results[1], and so on. You can export a child as a shapefile with layer.result[<1,2,3..>].exportArcData('filename')
    :type dissolve: binary
    :keyword dataOperations: Dictionary which maps a variable to a list of operations to run on it. The dissolved layer will contains in it's data all the variables specified in this dictionary. Be sure to check the input layer's fieldNames before use this utility.
    :type dataOperations: dictionary

    The dictionary structure must be as showed bellow.

    >>> X = {}
    >>> X[variableName1] = [function1, function2,....]
    >>> X[variableName2] = [function1, function2,....]

    Where functions are strings which represents the name of the 
    functions to be used on the given variableName. Functions 
    could be,'sum','mean','min','max','meanDesv','stdDesv','med',
    'mode','range','first','last','numberOfAreas. By default just
    ID variable is added to the dissolved map.
    
    """
    print "Running original AZP-Tabu algorithm (Openshaw and Rao, 1995)"
    print "Number of areas: ", len(y)
    if initialSolution != []:
        print "Number of regions: ", len(numpy.unique(initialSolution))
        pRegions = len(numpy.unique(initialSolution))
    else:
        print "Number of regions: ", pRegions
    if pRegions >= len(y):
        message = (
            "\n WARNING: You are aggregating "
            + str(len(y))
            + " into"
            + str(pRegions)
            + " regions!!. The number of regions must be an integer"
            + " number lower than the number of areas being aggregated"
        )
        raise Exception, message

    if convTabu <= 0:
        convTabu = max(10, len(y) / pRegions)  #   convTabu = 230*numpy.sqrt(pRegions)
    distanceType = "EuclideanSquared"
    distanceStat = "Centroid"
    objectiveFunctionType = "SS"
    selectionType = "Minimum"
    am = AreaManager(w, y, distanceType)
    start = tm.time()

    #  CONSTRUCTION

    print "Constructing regions"
    rm = RegionMaker(
        am,
        pRegions,
        initialSolution=initialSolution,
        distanceType=distanceType,
        distanceStat=distanceStat,
        selectionType=selectionType,
        objectiveFunctionType=objectiveFunctionType,
    )
    Sol = rm.returnRegions()
    print "initial Solution: ", Sol
    print "initial O.F: ", rm.objInfo

    # LOCAL SEARCH

    print "Performing local search"
    rm.AZPTabuMove(tabuLength=tabuLength, convTabu=convTabu)
    rm.calcObj()
    time = tm.time() - start
    Sol = rm.returnRegions()
    Of = rm.objInfo
    print "FINAL SOLUTION: ", Sol
    print "FINAL OF: ", Of
    output = {
        "objectiveFunction": Of,
        "runningTime": time,
        "algorithm": "azpTabu",
        "regions": len(Sol),
        "r2a": Sol,
        "distanceType": distanceType,
        "distanceStat": distanceStat,
        "selectionType": selectionType,
        "ObjectiveFuncionType": objectiveFunctionType,
    }
    print "Done"
    return output
Exemplo n.º 3
0
def execAZPRTabu(y, w, pRegions, initialSolution=[], convTabu=0):
    """Reactive tabu variant of Automatic Zoning Procedure (AZP-R-Tabu) 

    AZP-R-Tabu aggregates N zones (areas) into M regions. "The M output
    regions should be formed of internally connected, contiguous, zones."
    ([Openshaw_Rao1995]_ pp 428).

    AZP-R-Tabu is a variant of the AZP algorithm that incorporates a seach
    process, called Reactive Tabu Search algorithm [Battiti_Tecchiolli1994]_.
    The main difference between the reactive tabu and the tabu search, devised
    by [Glover1977]_ , is that the former does not require to define
    the number of times a reverse move is prohibited (tabuLength). This
    parameter is dynamically adjusted by the algorithm.
    
    In [Openshaw_Rao1995]_ the objective function is not defined because
    AZP-Tabu can be applied to any function, F(Z). "F(Z) can be any function
    defined on data for the M regions in Z, and Z is the allocation of each of
    N zones to one of M regions such that each zone is assigned to only one
    region" ([Openshaw_Rao1995]_ pp 428)." In clusterPy we Minimize F(Z),
    where Z is the within-cluster sum of squares from each area to the
    attribute centroid of its cluster.

    NOTE: The original algorithm proposes to start from a random initial
    feasible solution. Previous computational experience showed us that this
    approach leads to poor quality solutions. In clusterPy we started from an
    initial solution that starts with a initial set of seeds (as many seed as
    regions) selected using the K-means++ algorithm. From those seeds, other
    neighbouring areas are assigned to its closest (in attribute space)
    growing region. This strategy has proven better results. ::

        layer.cluster('azpRTabu',vars,regions,<wType>,<std>,<initialSolution>,<convTabu>,<dissolve>,<dataOperations>)

    :keyword vars: Area attribute(s) (e.g. ['SAR1','SAR2']) 
    :type vars: list
    :keyword regions: Number of regions 
    :type regions: integer
    :keyword wType: Type of first-order contiguity-based spatial matrix: 'rook' or 'queen'. Default value wType = 'rook'. 
    :type wType: string
    :keyword std: If = 1, then the variables will be standardized.
    :type std: binary
    :keyword initialSolution: List with a initial solution vector. It is useful when the user wants a solution that is not very different from a preexisting solution (e.g. municipalities,districts, etc.). Note that the number of regions will be the same as the number of regions in the initial feasible solution (regardless the value you assign to parameter "regions"). IMPORTANT: make sure you are entering a feasible solution and according to the W matrix you selected, otherwise the algorithm will not converge.
    :type initialSolution: list
    :keyword convTabu: Stop the search after convTabu nonimproving moves (nonimproving moves are those moves that do not improve the current solution. Note that "improving moves" are different to "aspirational moves"). If convTabu=0 the algorithm will stop after Int(M/N) nonimproving moves. Default value convTabu = 0.
    :type convTabu: integer
    :keyword dissolve: If = 1, then you will get a "child" instance of the layer that contains the new regions. Default value = 0. Note:. Each child layer is saved in the attribute ayer.results. The first algorithm that you run with dissolve=1 will have a child layer in layer.results[0]; the second algorithm that you run with dissolve=1 will be in layer.results[1], and so on. You can export a child as a shapefile with layer.result[<1,2,3..>].exportArcData('filename')
    :type dissolve: binary
    :keyword dataOperations: Dictionary which maps a variable to a list of operations to run on it. The dissolved layer will contains in it's data all the variables specified in this dictionary. Be sure to check the input layer's fieldNames before use this utility.
    :type dataOperations: dictionary

    The dictionary structure must be as showed bellow.

    >>> X = {}
    >>> X[variableName1] = [function1, function2,....]
    >>> X[variableName2] = [function1, function2,....]

    Where functions are strings wich represents the name of the 
    functions to be used on the given variableName. Functions 
    could be,'sum','mean','min','max','meanDesv','stdDesv','med',
    'mode','range','first','last','numberOfAreas. By deffault just
    ID variable is added to the dissolved map.
    
    """
    print "Running original AZP-R-Tabu algorithm (Openshaw and Rao, 1995)"
    print "Number of areas: ", len(y)
    if initialSolution != []:
        print "Number of regions: ", len(numpy.unique(initialSolution))
        pRegions = len(numpy.unique(initialSolution))
    else:
        print "Number of regions: ", pRegions
    if pRegions >= len(y):
        message = "\n WARNING: You are aggregating "+str(len(y))+" into"+\
        str(pRegions)+" regions!!. The number of regions must be an integer"+\
        " number lower than the number of areas being aggregated"
        raise Exception, message

    if convTabu <= 0:
        convTabu = len(y) / pRegions  #  convTabu = 230*numpy.sqrt(pRegions)
    distanceType = "EuclideanSquared"
    distanceStat = "Centroid"
    objectiveFunctionType = "SS"
    selectionType = "Minimum"
    am = AreaManager(w, y, distanceType)
    start = tm.time()
    print "Constructing regions"
    rm = RegionMaker(am,
                     pRegions,
                     initialSolution=initialSolution,
                     distanceType=distanceType,
                     distanceStat=distanceStat,
                     selectionType=selectionType,
                     objectiveFunctionType=objectiveFunctionType)
    Sol = rm.returnRegions()
    print "initial Solution: ", Sol
    print "initial O.F: ", rm.objInfo

    # LOCAL SEARCH

    print "Performing local search"
    rm.reactiveTabuMove(convTabu)
    time = tm.time() - start
    Sol = rm.returnRegions()
    Of = rm.objInfo
    print "FINAL SOLUTION: ", Sol
    print "FINAL OF: ", Of
    output = {
        "objectiveFunction": Of,
        "runningTime": time,
        "algorithm": "azpRtabu",
        "regions": len(Sol),
        "r2a": Sol,
        "distanceType": distanceType,
        "distanceStat": distanceStat,
        "selectionType": selectionType,
        "ObjectiveFuncionType": objectiveFunctionType
    }
    print "Done"
    return output
Exemplo n.º 4
0
def execAZPSA(y, w, pRegions, initialSolution=[], maxit=1):
    """Simulated Annealing variant of Automatic Zoning Procedure (AZP-SA) 

    
    AZP-SA aggregates N zones (areas) into M regions. "The M output regions
    should be formed of internally connected, contiguous, zones." ([Openshaw_Rao1995]_ pp 428).


    AZP-SA is a variant of the AZP algorithm that incorporates a seach
    process, called Simulated Annealing algorithm [Kirkpatrick_Gelatt_Vecchi1983]_.
    Simulated annealing algorithm "permits moves which result in a worse value
    of the objective function but with a probability that diminishes
    gradually, through iteration time" ([Openshaw_Rao1995]_ pp 431).
    
    
    In Openshaw and Rao (1995) the objective function is not defined because
    AZP-Tabu can be applied to any function, F(Z). "F(Z) can be any function
    defined on data for the M regions in Z, and Z is the allocation of each of
    N zones to one of M regions such that each zone is assigned to only one
    region" ([Openshaw_Rao1995]_ pp 428)." In clusterPy we Minimize F(Z),
    where Z is the within-cluster sum of squares from each area to the
    attribute centroid of its cluster.


    In order to make the cooling schedule robust the units of measure of the
    objective function, we set the Boltzmann's equation as: R(0,1) <
    exp((-(Candidate Solution - Current Solution) / Current Solution)/T(k)).
    The cooling schedule is T(k) = 0.85 T(k-1) ([Openshaw_Rao1995]_ pp
    431), with an initial temperature T(0)=1. 
    
    NOTE: The original algorithm proposes to start from a random initial
    feasible solution. Previous computational experience showed us that this
    approach leads to poor quality solutions. In clusterPy we started from an
    initial solution that starts with a initial set of seeds (as many seed as
    regions) selected using the K-means++ algorithm. From those seeds, other
    neighbouring areas are assigned to its closest (in attribute space)
    growing region. This strategy has proven better results. :: 

        layer.cluster('azpSa',vars,regions,<wType>,<std>,<initialSolution>,<maxit>,<dissolve>,<dataOperations>)

    :keyword vars: Area attribute(s) (e.g. ['SAR1','SAR2']) 
    :type vars: list
    :keyword regions: Number of regions 
    :type regions: integer
    :keyword wType: Type of first-order contiguity-based spatial matrix: 'rook' or 'queen'. Default value wType = 'rook'. 
    :type wType: string
    :keyword std: If = 1, then the variables will be standardized.
    :type std: binary
    :keyword initialSolution: List with a initial solution vector. It is useful when the user wants a solution that is not very different from a preexisting solution (e.g. municipalities,districts, etc.). Note that the number of regions will be the same as the number of regions in the initial feasible solution (regardless the value you assign to parameter "regions"). IMPORTANT: make sure you are entering a feasible solution and according to the W matrix you selected, otherwise the algorithm will not converge.
    :type initialSolution: list
    :keyword maxit: For a given temperature, perform SA maxit times (see Openshaw and Rao (1995) pp 431, Step b).  Default value maxit = 1.  NOTE: the parameter Ik, in Step d was fixed at 3.
    :type maxit: integer
    :keyword dissolve: If = 1, then you will get a "child" instance of the layer that contains the new regions. Default value = 0. Note:. Each child layer is saved in the attribute layer.results. The first algorithm that you run with dissolve=1 will have a child layer in layer.results[0]; the second algorithm that you run with dissolve=1 will be in layer.results[1], and so on. You can export a child as a shapefile with layer.result[<1,2,3..>].exportArcData('filename')
    :type dissolve: binary
    :keyword dataOperations: Dictionary which maps a variable to a list of operations to run on it. The dissolved layer will contains in it's data all the variables specified in this dictionary. Be sure to check the input layer's fieldNames before use this utility.
    :type dataOperations: dictionary

    The dictionary structure must be as showed bellow.

    >>> X = {}
    >>> X[variableName1] = [function1, function2,....]
    >>> X[variableName2] = [function1, function2,....]

    Where functions are strings wich represents the name of the 
    functions to be used on the given variableName. Functions 
    could be,'sum','mean','min','max','meanDesv','stdDesv','med',
    'mode','range','first','last','numberOfAreas. By deffault just
    ID variable is added to the dissolved map.

    """
    print "Running original AZP-SA algorithm (Openshaw and Rao, 1995)"
    print "Number of areas: ", len(y)
    if initialSolution != []:
        print "Number of regions: ", len(numpy.unique(initialSolution))
        pRegions = len(numpy.unique(initialSolution))
    else:
        print "Number of regions: ", pRegions
    print "Boltzmann's equation: "
    print "     R(0,1) < exp((-(Candidate Soution - Current Solution) / Current Solution)/T(k))"
    print "Cooling schedule: T(k) = 0.85 T(k-1)"
    if pRegions >= len(y):
        message = "\n WARNING: You are aggregating "+str(len(y))+" into"+\
        str(pRegions)+" regions!!. The number of regions must be an integer"+\
        " number lower than the number of areas being aggregated"
        raise Exception, message 
    
    distanceType = "EuclideanSquared" 
    distanceStat = "Centroid"
    objectiveFunctionType = "SS"
    selectionType = "Minimum"
    alpha = 0.85
    am = AreaManager(w, y, distanceType)
    start = tm.time()

    #  CONSTRUCTION

    rm = RegionMaker(am, pRegions,
                    initialSolution=initialSolution,
                    distanceType=distanceType,
                    distanceStat=distanceStat,
                    selectionType=selectionType,
                    objectiveFunctionType=objectiveFunctionType)
    print "initial solution: ", rm.returnRegions()
    print "initial O.F: ", rm.objInfo

    #  LOCAL SEARCH
    rm.AZPSA(alpha, maxit)

    time = tm.time() - start
    Sol = rm.returnRegions()
    Of = rm.objInfo
    print "FINAL SOLUTION: ", Sol
    print "FINAL OF: ", Of
    output = { "objectiveFunction": Of,
    "runningTime": time,
    "algorithm": "azpSa",
    "regions": len(Sol),
    "r2a": Sol,
    "distanceType": distanceType,
    "distanceStat": distanceStat,
    "selectionType": selectionType,
    "ObjectiveFuncionType": objectiveFunctionType}
    print "Done"
    return output
Exemplo n.º 5
0
def execRandom(y, w, regions):
    """Generate random regions
    
    This algorithm aggregates, at random, a set of areas into a predefined
    number of spatially contiguous regions. ::

        layer.cluster('random',vars,regions,<wType>,<dissolve>,<dataOperations>)

    :keyword vars: Area attribute(s) (e.g. ['SAR1','SAR2']) 
    :type vars: list
    :keyword regions: Number of regions 
    :type regions: integer
    :keyword wType: Type of first-order contiguity-based spatial matrix: 'rook' or 'queen'. Default value wType = 'rook'. 
    :type wType: string
    :keyword dissolve: If = 1, then you will get a "child" instance of the layer that contains the new regions. Default value = 0. Note:. Each child layer is saved in the attribute layer.results. The first algorithm that you run with dissolve=1 will have a child layer in layer.results[0]; the second algorithm that you run with dissolve=1 will be in layer.results[1], and so on. You can export a child as a shapefile with layer.result[<1,2,3..>].exportArcData('filename')
    :type dissolve: binary
    :keyword dataOperations: Dictionary which maps a variable to a list of operations to run on it. The dissolved layer will contains in it's data all the variables specified in this dictionary. Be sure to check the input layer's fieldNames before use this utility.
    :type dataOperations: dictionary

    The dictionary structure must be as showed bellow.

    >>> X = {}
    >>> X[variableName1] = [function1, function2,....]
    >>> X[variableName2] = [function1, function2,....]

    Where functions are strings which represents the name of the 
    functions to be used on the given variableName. Functions 
    could be,'sum','mean','min','max','meanDesv','stdDesv','med',
    'mode','range','first','last','numberOfAreas. By default just
    ID variable is added to the dissolved map.
      
    """
    if regions >= len(y):
        message = "\n WARNING: You are aggregating "+str(len(y))+" into"+\
        str(regions)+" regions!!. The number of regions must be an integer"+\
        " number lower than the number of areas being aggregated"
        raise Exception, message

    distanceType = "EuclideanSquared"
    distanceStat = "Centroid"
    objectiveFunctionType = "SS"
    selectionType = "FullRandom"
    am = AreaManager(w, y, distanceType)
    start = tm.time()

    #  CONSTRUCTION

    rm = RegionMaker(am,
                     regions,
                     distanceType=distanceType,
                     distanceStat=distanceStat,
                     selectionType=selectionType,
                     objectiveFunctionType=objectiveFunctionType)
    time = tm.time() - start
    Sol = rm.returnRegions()
    Of = rm.objInfo
    print "FINAL SOLUTION: ", Sol
    print "FINAL OF: ", Of
    output = {
        "objectiveFunction": Of,
        "runningTime": time,
        "algorithm": "random",
        "regions": len(Sol),
        "r2a": Sol,
        "distanceType": distanceType,
        "distanceStat": distanceStat,
        "selectionType": selectionType,
        "ObjectiveFuncionType": objectiveFunctionType
    }
    print "Done"
    return output
Exemplo n.º 6
0
def execRandom(y, w, regions):
    """Generate random regions
    
    This algorithm aggregates, at random, a set of areas into a predefined
    number of spatially contiguous regions. ::

        layer.cluster('random',vars,regions,<wType>,<dissolve>,<dataOperations>)

    :keyword vars: Area attribute(s) (e.g. ['SAR1','SAR2']) 
    :type vars: list
    :keyword regions: Number of regions 
    :type regions: integer
    :keyword wType: Type of first-order contiguity-based spatial matrix: 'rook' or 'queen'. Default value wType = 'rook'. 
    :type wType: string
    :keyword dissolve: If = 1, then you will get a "child" instance of the layer that contains the new regions. Default value = 0. Note:. Each child layer is saved in the attribute layer.results. The first algorithm that you run with dissolve=1 will have a child layer in layer.results[0]; the second algorithm that you run with dissolve=1 will be in layer.results[1], and so on. You can export a child as a shapefile with layer.result[<1,2,3..>].exportArcData('filename')
    :type dissolve: binary
    :keyword dataOperations: Dictionary which maps a variable to a list of operations to run on it. The dissolved layer will contains in it's data all the variables specified in this dictionary. Be sure to check the input layer's fieldNames before use this utility.
    :type dataOperations: dictionary

    The dictionary structure must be as showed bellow.

    >>> X = {}
    >>> X[variableName1] = [function1, function2,....]
    >>> X[variableName2] = [function1, function2,....]

    Where functions are strings which represents the name of the 
    functions to be used on the given variableName. Functions 
    could be,'sum','mean','min','max','meanDesv','stdDesv','med',
    'mode','range','first','last','numberOfAreas. By default just
    ID variable is added to the dissolved map.
      
    """
    if regions >= len(y):
        message = "\n WARNING: You are aggregating "+str(len(y))+" into"+\
        str(regions)+" regions!!. The number of regions must be an integer"+\
        " number lower than the number of areas being aggregated"
        raise Exception, message 

    distanceType = "EuclideanSquared" 
    distanceStat = "Centroid"
    objectiveFunctionType = "SS"
    selectionType = "FullRandom"
    am = AreaManager(w, y, distanceType)
    start = tm.time()

    #  CONSTRUCTION

    rm = RegionMaker(am, regions, 
                    distanceType = distanceType,
                    distanceStat = distanceStat,
                    selectionType = selectionType,
                    objectiveFunctionType = objectiveFunctionType)
    time = tm.time() - start
    Sol = rm.returnRegions()
    Of = rm.objInfo
    print "FINAL SOLUTION: ", Sol
    print "FINAL OF: ", Of
    output = { "objectiveFunction": Of,
    "runningTime": time,
    "algorithm": "random",
    "regions": len(Sol),
    "r2a": Sol,
    "distanceType": distanceType,
    "distanceStat": distanceStat,
    "selectionType": selectionType,
    "ObjectiveFuncionType": objectiveFunctionType} 
    print "Done"
    return output
Exemplo n.º 7
0
def execMaxpTabu(y,
                 w,
                 threshold=100.0,
                 maxit=2,
                 tabuLength=5,
                 typeTabu="exact"):
    """Max-p-regions model (Tabu) 

    The max-p-regions model, devised by [Duque_Anselin_Rey2010]_ ,
    clusters a set of geographic areas into the maximum number of homogeneous
    regions such that the value of a spatially extensive regional attribute is
    above a predefined threshold value. In clusterPy we measure heterogeneity as
    the within-cluster sum of squares from each area to the attribute centroid
    of its cluster.

    The max-p-regions algorithm is composed of two main blocks: 
    
    - construction of a initial feasible solution. 
    - local improvement.
    
    There are three methods for local improvement: Greedy (execMaxpGreedy),
    Tabu (execMaxpTabu), and Simulated Annealing (execMaxpSa). A detailed
    explanation of each method can be found in Duque, Anselin and Rey (2010) [Duque_Anselin_Rey2010]_.

    For this version, the tabu search algorithm will stop after
    max(10,N/maxP) nonimproving moves. ::

        layer.cluster('maxpTabu',vars,<threshold>,<wType>,<std>,<maxit>,<tabuLength>,<typeTabu>,<dissolve>,<dataOperations>)

    :keyword vars: Area attribute(s). Important: the last variable in vars correspond to the spatially extensive attribute that will be constrained to be above the predefined threshold value (e.g. ['SAR1','SAR2','POP'])  
    :type vars: list
    :keyword threshold: Minimum value of the constrained variable at regional level. Default value threshold = 100.
    :type threshold: integer
    :keyword wType: Type of first-order contiguity-based spatial matrix: 'rook' or 'queen'. Default value wType = 'rook'. 
    :type wType: string
    :keyword std: If = 1, then the variables will be standardized.
    :type std: binary
    :keyword maxit: Number of times that the construction phase is repeated. The larger the value the higher the possibility of getting a large number of regions. Default value maxit = 2.
    :type maxit: integer
    :keyword tabuLength: Number of times a reverse move is prohibited. Default value tabuLength = 85. 
    :type tabuLength: integer
    :keyword typeTabu: Type of tabu search: (a) exact: chooses the best neighbouring solution for evaluation (it implies the enumeration of all the neighbouring solution at each iteration); (b) "random": evaluates a neighbouring solution selected at random and (See Ricca, F.  and Simeone (2008) for more on the difference between exact and random tabu). Default value typeTabu = "exact". 
    :type typeTabu: string 
    :keyword dissolve: If = 1, then you will get a "child" instance of the layer that contains the new regions. Default value = 0. Note: Each child layer is saved in the attribute layer.results. The first algorithm that you run with dissolve=1 will have a child layer in layer.results[0]; the second algorithm that you run with dissolve=1 will be in layer.results[1], and so on. You can export a child as a shapefile with layer.result[<1,2,3..>].exportArcData('filename')
    :type dissolve: binary
    :keyword dataOperations: Dictionary which maps a variable to a list of operations to run on it. The dissolved layer will contains in it's data all the variables specified in this dictionary. Be sure to check the input layer's fieldNames before use this utility.
    :type dataOperations: dictionary

    The dictionary structure must be as showed bellow.

    >>> X = {}
    >>> X[variableName1] = [function1, function2,....]
    >>> X[variableName2] = [function1, function2,....]

    Where functions are strings which represents the name of the
    functions to be used on the given variableName. Functions
    could be,'sum','mean','min','max','meanDesv','stdDesv','med',
    'mode','range','first','last','numberOfAreas. By deffault just
    ID variable is added to the dissolved map.
    """
    print "Running max-p-regions model (Duque, Anselin and Rey, 2010)"
    print "Local search method: Tabu Search"
    print "Number of areas: ", len(y)
    print "threshold value: ", threshold
    distanceType = "EuclideanSquared"
    distanceStat = "Centroid"
    objectiveFunctionType = "SS"
    selectionType = "Minimum"
    numRegionsType = "EndogenousThreshold"

    #  CONSTRUCTION PHASE 1: GROWING FEASIBLE REGIONS

    start = tm.time()

    #  print w
    #  print y

    am = AreaManager(w, y, distanceType)
    maxP = 0
    bestCandidates = {}
    for i in range(maxit):

        #  print "**** Iteration %d of %d ..."%(i+1,maxit)

        rm = RegionMaker(am,
                         distanceType=distanceType,
                         distanceStat=distanceStat,
                         selectionType=selectionType,
                         objectiveFunctionType=objectiveFunctionType,
                         numRegionsType=numRegionsType,
                         threshold=threshold)
        numRegions = len(rm.feasibleRegions)
        rm.getObj()

        #  print "rm.feasibleRegions",rm.feasibleRegions
        #  print "obj",rm.getObj()

        if numRegions > maxP:
            bestCandidates = {}
            maxP = numRegions
            obj = rm.objInfo
            bestCandidates[obj] = rm.feasibleRegions
        if numRegions == maxP:
            obj = rm.objInfo
            if obj in bestCandidates:
                pass
            else:
                bestCandidates[obj] = rm.feasibleRegions
        else:
            pass

    #   print "bestCandidates", bestCandidates

    ofValues = bestCandidates.keys()
    basicMemory = BasicMemory()
    while len(ofValues) >= 1:

        #  RECREATE SOLUTION

        rm.resetNow()
        minOfValue = min(ofValues)
        ofValues.remove(minOfValue)
        partialSolution = bestCandidates[minOfValue]

        #  print "ASSIGNING ENCLAVES"
        #  print partialSolution

        regionId = 0
        for growReg in partialSolution:
            seedGrowReg = partialSolution[growReg][0]
            rm.assignSeeds(seedGrowReg, regionId)
            partialSolution[growReg].remove(seedGrowReg)
            if len(partialSolution[growReg]) >= 1:
                for areaInGrow in partialSolution[growReg]:
                    rm.assignArea(areaInGrow, regionId)
            regionId += 1

        # CONSTRUCTION PHASE 2: ENCLAVES ASSIGNATION

        rm.feasibleRegions = copy.deepcopy(rm.region2Area)
        rm.getIntraBorderingAreas()
        rm.newExternal = set(rm.unassignedAreas)
        if len(rm.unassignedAreas) != 0:
            rm.constructionStage = "enclaves"
            while len(rm.unassignedAreas) != 0:
                rm.constructRegions()
        rm.objInfo = rm.getObjective(rm.region2Area)
        rm.feasibleRegions = copy.deepcopy(rm.region2Area)
        rm.getIntraBorderingAreas()

        #  print "ASSIGNED SOLUTION"
        #  print "OBJ: ", rm.getObjective(rm.region2Area), rm.returnRegions()

        rm.calculateRegionValueThreshold()

        #  LOCAL SEARCH

        rm.calcObj()
        convTabu = min(10, len(y) / maxP)  #   convTabu=230*numpy.sqrt(maxP)

        #  print "###ENTERING TABU",rm.objInfo,rm.returnRegions()

        rm.tabuMove(tabuLength, convTabu=convTabu, typeTabu=typeTabu)
        rm.calcObj()

        #  print "***** AFTER TABU",rm.objInfo,rm.returnRegions()
        #  EVALUATE SOLUTION

        if rm.objInfo < basicMemory.objInfo:
            basicMemory.updateBasicMemory(rm)
    time = tm.time() - start
    Sol = basicMemory.regions
    Of = basicMemory.objInfo
    print "FINAL SOLUTION: ", Sol
    print "FINAL OF: ", Of
    output = {
        "objectiveFunction": Of,
        "runningTime": time,
        "algorithm": "maxpTabu",
        "regions": len(Sol),
        "r2a": Sol,
        "distanceType": distanceType,
        "distanceStat": distanceStat,
        "selectionType": selectionType,
        "ObjectiveFuncionType": objectiveFunctionType
    }
    print "Done"
    return output
Exemplo n.º 8
0
def execAZPSA(y, w, pRegions, initialSolution=[], maxit=1):
    """Simulated Annealing variant of Automatic Zoning Procedure (AZP-SA) 

    
    AZP-SA aggregates N zones (areas) into M regions. "The M output regions
    should be formed of internally connected, contiguous, zones." ([Openshaw_Rao1995]_ pp 428).


    AZP-SA is a variant of the AZP algorithm that incorporates a seach
    process, called Simulated Annealing algorithm [Kirkpatrick_Gelatt_Vecchi1983]_.
    Simulated annealing algorithm "permits moves which result in a worse value
    of the objective function but with a probability that diminishes
    gradually, through iteration time" ([Openshaw_Rao1995]_ pp 431).
    
    
    In Openshaw and Rao (1995) the objective function is not defined because
    AZP-Tabu can be applied to any function, F(Z). "F(Z) can be any function
    defined on data for the M regions in Z, and Z is the allocation of each of
    N zones to one of M regions such that each zone is assigned to only one
    region" ([Openshaw_Rao1995]_ pp 428)." In clusterPy we Minimize F(Z),
    where Z is the within-cluster sum of squares from each area to the
    attribute centroid of its cluster.


    In order to make the cooling schedule robust the units of measure of the
    objective function, we set the Boltzmann's equation as: R(0,1) <
    exp((-(Candidate Solution - Current Solution) / Current Solution)/T(k)).
    The cooling schedule is T(k) = 0.85 T(k-1) ([Openshaw_Rao1995]_ pp
    431), with an initial temperature T(0)=1. 
    
    NOTE: The original algorithm proposes to start from a random initial
    feasible solution. Previous computational experience showed us that this
    approach leads to poor quality solutions. In clusterPy we started from an
    initial solution that starts with a initial set of seeds (as many seed as
    regions) selected using the K-means++ algorithm. From those seeds, other
    neighbouring areas are assigned to its closest (in attribute space)
    growing region. This strategy has proven better results. :: 

        layer.cluster('azpSa',vars,regions,<wType>,<std>,<initialSolution>,<maxit>,<dissolve>,<dataOperations>)

    :keyword vars: Area attribute(s) (e.g. ['SAR1','SAR2']) 
    :type vars: list
    :keyword regions: Number of regions 
    :type regions: integer
    :keyword wType: Type of first-order contiguity-based spatial matrix: 'rook' or 'queen'. Default value wType = 'rook'. 
    :type wType: string
    :keyword std: If = 1, then the variables will be standardized.
    :type std: binary
    :keyword initialSolution: List with a initial solution vector. It is useful when the user wants a solution that is not very different from a preexisting solution (e.g. municipalities,districts, etc.). Note that the number of regions will be the same as the number of regions in the initial feasible solution (regardless the value you assign to parameter "regions"). IMPORTANT: make sure you are entering a feasible solution and according to the W matrix you selected, otherwise the algorithm will not converge.
    :type initialSolution: list
    :keyword maxit: For a given temperature, perform SA maxit times (see Openshaw and Rao (1995) pp 431, Step b).  Default value maxit = 1.  NOTE: the parameter Ik, in Step d was fixed at 3.
    :type maxit: integer
    :keyword dissolve: If = 1, then you will get a "child" instance of the layer that contains the new regions. Default value = 0. Note:. Each child layer is saved in the attribute layer.results. The first algorithm that you run with dissolve=1 will have a child layer in layer.results[0]; the second algorithm that you run with dissolve=1 will be in layer.results[1], and so on. You can export a child as a shapefile with layer.result[<1,2,3..>].exportArcData('filename')
    :type dissolve: binary
    :keyword dataOperations: Dictionary which maps a variable to a list of operations to run on it. The dissolved layer will contains in it's data all the variables specified in this dictionary. Be sure to check the input layer's fieldNames before use this utility.
    :type dataOperations: dictionary

    The dictionary structure must be as showed bellow.

    >>> X = {}
    >>> X[variableName1] = [function1, function2,....]
    >>> X[variableName2] = [function1, function2,....]

    Where functions are strings wich represents the name of the 
    functions to be used on the given variableName. Functions 
    could be,'sum','mean','min','max','meanDesv','stdDesv','med',
    'mode','range','first','last','numberOfAreas. By deffault just
    ID variable is added to the dissolved map.

    """
    print "Running original AZP-SA algorithm (Openshaw and Rao, 1995)"
    print "Number of areas: ", len(y)
    if initialSolution != []:
        print "Number of regions: ", len(numpy.unique(initialSolution))
        pRegions = len(numpy.unique(initialSolution))
    else:
        print "Number of regions: ", pRegions
    print "Boltzmann's equation: "
    print "     R(0,1) < exp((-(Candidate Soution - Current Solution) / Current Solution)/T(k))"
    print "Cooling schedule: T(k) = 0.85 T(k-1)"
    if pRegions >= len(y):
        message = "\n WARNING: You are aggregating "+str(len(y))+" into"+\
        str(pRegions)+" regions!!. The number of regions must be an integer"+\
        " number lower than the number of areas being aggregated"
        raise Exception, message

    distanceType = "EuclideanSquared"
    distanceStat = "Centroid"
    objectiveFunctionType = "SS"
    selectionType = "Minimum"
    alpha = 0.85
    am = AreaManager(w, y, distanceType)
    start = tm.time()

    #  CONSTRUCTION

    rm = RegionMaker(am,
                     pRegions,
                     initialSolution=initialSolution,
                     distanceType=distanceType,
                     distanceStat=distanceStat,
                     selectionType=selectionType,
                     objectiveFunctionType=objectiveFunctionType)
    print "initial solution: ", rm.returnRegions()
    print "initial O.F: ", rm.objInfo

    #  LOCAL SEARCH
    rm.AZPSA(alpha, maxit)

    time = tm.time() - start
    Sol = rm.returnRegions()
    Of = rm.objInfo
    print "FINAL SOLUTION: ", Sol
    print "FINAL OF: ", Of
    output = {
        "objectiveFunction": Of,
        "runningTime": time,
        "algorithm": "azpSa",
        "regions": len(Sol),
        "r2a": Sol,
        "distanceType": distanceType,
        "distanceStat": distanceStat,
        "selectionType": selectionType,
        "ObjectiveFuncionType": objectiveFunctionType
    }
    print "Done"
    return output