That code is a good start. Before I go about making recommendations you must be made aware that getting an exact percent is very difficult, the sample to be selected from may not be a multiple of the percent, for example 50% of 3 is 1.5 but you can't have half a feature so it becomes either 1 (int/floor) or 2 (ceil).
In order to use any kind of unique field you must pass it to the function, so it is necessary to change the def SelectRandomByPercent (layer, percent):
to def SelectRandomByPercent (layer, percent,uField):
; global variables do exist in python but I would discourage you from pursuing that route.
Every person is different so it is reasonable to say that individuals code differently, I see a lot of unnecessary lines in the code sample where others would see coding for understanding the processes, to this end I started from a blank slate:
def SelectRandomByPercent (layer, percent,uField):
uFieldField = arcpy.ListFields(layer,uField)[0] # get the input layer field as a field object
# Validate the input field type, only some field types are valid
# for the purposes of this script: either simple numeric or string
if uFieldField.type.lower() == 'string':
uFieldString = True # field is a string
elif uFieldField.type.lower() in ['double', 'integer', 'single', 'smallinteger']:
uFieldString = False # field is a valid numeric type
else:
# field is Blob, Date, GUID, Raster etc.. not a valid field for this script
arcpy.AddError('indicated field {} is not a valid field type'.format(uField))
return
# I want the percent to be between 0 and 1 for later calculation
# as I'm using len(list)*percent, alternately you could leave the
# percent as between 0 and 100 and change to len(list)/percent
if percent > 1:
percent = float(percent) # float the int first otherwise the result is 0
percent /= 100
if percent > 1:
print "percent is greater than 100"
return
if percent < 0:
print "percent is less than zero"
return
# get a list of all the unique values in the uField
# and make them into a list to be iterated later
uVals = []
with arcpy.da.SearchCursor(layer,uField) as SCur:
for SRow in SCur:
if not SRow[0] in uVals: # consider using upper or lower case here
uVals.append(SRow[0])
selectOIDs = [] # the list of 'final' selection OIDs, starting with empty
for this_uVal in uVals: # for each unique value in the uField
# get all the ids associated with this unique value
if uFieldString:
# using a quoted where clause for a string field type
with arcpy.da.SearchCursor(layer,'OID@','{} = \'{}\''.format(uField,this_uVal)) as SCur:
defQ_IDS = [] # the OIDs for just this unique value, starting with empty
for SRow in SCur:
defQ_IDS.append(SRow[0]) # add each OID in turn to the list, I don't use += because SRow[0] isn't a list
# Now add a subset to the selection OIDs if you want to round up then import math
# and use int(ceil(len(defQ_IDS)*percent)) either way the sample must be an integer.
# There is no need to get a count of features as the OIDs are loaded into the list
# just get the length of the list and that will be the number of features
selectOIDs += random.sample(defQ_IDS,int(len(defQ_IDS)*percent))
else:
# using an unquoted where clause for numeric types
with arcpy.da.SearchCursor(layer,'OID@','{} = {}'.format(uField,this_uVal)) as SCur:
defQ_IDS = [] # the OIDs for just this unique value, starting with empty
for SRow in SCur:
defQ_IDS.append(SRow[0]) # add each OID in turn to the list, I don't use += because SRow[0] isn't a list
# Now add a subset to the selection OIDs if you want to round up then import math
# and use int(ceil(len(defQ_IDS)*percent)) either way the sample must be an integer.
# There is no need to get a count of features as the OIDs are loaded into the list
# just get the length of the list and that will be the number of features
selectOIDs += random.sample(defQ_IDS,int(len(defQ_IDS)*percent))
# now that we have a percent of each unique values of uField actually
# do the selecting against the layer, but first get the OID field name
Des = arcpy.Describe(layer)
OIDfield = Des.oidFieldName
arcpy.SelectLayerByAttribute_management(layer,'NEW_SELECTION','{} in ({})'.format(OIDfield, ','.join([str(sID) for sID in selectOIDs])))
This works like the example code but has a few minor differences.. Every feature is iterated twice: the first to get the unique value of the given field and secondly to get the list of OIDs that relate to each unique code, from which a random sample of OIDs are added using the += operator for the list, explained on Stack Overflow. Don't forget that you need to import random at the top of your script to use this lib in this function.
{}
button that enables you to format any highlighted code nicely. Please check out our short tour for more about how the site works. Thanks. – PolyGeo♦ Nov 25 '19 at 11:47