top of page

Jenks Classification Script

 

 

import arcpy

import numpy

 

if len(sys.argv) > 1:

    #Assign input and output parameters for script tool

    ws = arcpy.GetParameterAsText(0)                                          # work space

    features = arcpy.GetParameterAsText(1)                                 # input feature class or shapefile

    attrib = arcpy.GetParameterAsText(2)                                      # field to classify by

    #attrib = "Shape_Leng"                                                             # field to classify by

    numClass = int(arcpy.GetParameterAsText(3))                        # number of classes output

    if numClass < 5 or numClass > 9:                                             # checks value of numClass and returns a warning for high or low values.

        arcpy.AddWarning("This tool works best with 5-9 classes.")

else:

    # Hard coded parameters for testing

    ws = r"C:\Users\Chris\Desktop\Fall 2013\GEOG 5562"            # work space

    features = r"Test.gdb\building_test"                                          # input feature class or shapefile

    attrib = "Shape_Leng"                                                               # field to classify by

    numClass = 5                                                                            # number of classes output

    if numClass < 5 or numClass > 9:                                             # checks value of numClass and returns a warning for high or low values.

        print "This tool works best with 5-9 classes."

 

#SR = arcpy.Describe(features).spatialReference get Spatial Ref from featureclass  # descripe spatial reference, if needed.

 

fldName = 'CLASS'

symlyr = r"C:\Users\Chris\Desktop\Fall 2013\GEOG 5562\test.lyr"

 

arcpy.env.workspace = ws

 

def getJenksBreaks( dataList, numClass ):

 

    dataList.sort()

 

    mat1 = []

    for i in range(0,len(dataList)+1):

        temp = []

        for j in range(0,numClass+1):

            temp.append(0)

        mat1.append(temp)

 

    mat2 = []

    for i in range(0,len(dataList)+1):

        temp = []

        for j in range(0,numClass+1):

            temp.append(0)

        mat2.append(temp)

 

    for i in range(1,numClass+1):

        mat1[1][i] = 1

        mat2[1][i] = 0

        for j in range(2,len(dataList)+1):

            mat2[j][i] = float('inf')

 

    v = 0.0

    for l in range(2,len(dataList)+1):

        s1 = 0.0

        s2 = 0.0

        w = 0.0

        for m in range(1,l+1):

            i3 = l - m + 1

 

            val = float(dataList[i3-1])

 

            s2 += val * val

            s1 += val

 

            w += 1

            v = s2 - (s1 * s1) / w

            i4 = i3 - 1

 

            if i4 != 0:

                for j in range(2,numClass+1):

                    if mat2[l][j] >= (v + mat2[i4][j - 1]):

                        mat1[l][j] = i3

                        mat2[l][j] = v + mat2[i4][j - 1]

        mat1[l][1] = 1

        mat2[l][1] = v

 

    k = len(dataList)

    kclass = []

    for i in range(0,numClass+1):

        kclass.append(0)

 

    kclass[numClass] = float(dataList[len(dataList) - 1])

 

    countNum = numClass

    while countNum >= 2:

        #print "rank = " + str(mat1[k][countNum])

        id = int((mat1[k][countNum]) - 2)

        #print "val = " + str(dataList[id])

 

        kclass[countNum - 1] = dataList[id]

        k = int((mat1[k][countNum] - 1))

        countNum -= 1

 

    return kclass

 

Test = arcpy.da.FeatureClassToNumPyArray(features, attrib,"", skip_nulls=True)

 

data = []

 

for x in Test:

    data.append(x[0])

 

breaks = getJenksBreaks( data, numClass )

 

if len(sys.argv) <= 1:

    print breaks

 

arcpy.AddField_management(features, fldName, "SHORT")

 

# generate a script to enter into codeblock for calculate field function

blockLen = range(numClass)

codeblock = 'def getClass(attrib):'

for b in blockLen:

    if b == 0:

        codeblock = codeblock + '\n  if attrib < ' + str(breaks[b+1]) + ':\n    return ' + str(b+1)

    elif b > 0 and b < blockLen[-1]:

        codeblock = codeblock + '\n  if attrib < ' + str(breaks[b+1]) + ' and attrib >= ' + str(breaks[b]) + ':\n    return ' + str(b+1)

    elif b == blockLen[-1]:

        codeblock = codeblock + '\n  else:\n    return ' + str(b+1)

if len(sys.argv) <= 1:

    print codeblock

 

expression = 'getClass(float(!' + attrib + '!))'

 

arcpy.CalculateField_management(features, fldName, expression, 'PYTHON_9.3', codeblock)

bottom of page