2

I want to calculate scores in one field based on the percentile (in steps of 10) the row falls into in another field. I found a solution here (Calculating Percentiles in ArcMap?) that I tried out, but I now get the error:

unsupported operand type(s) for *: 'numpy.ndarray' and 'numpy.ndarray'

My code looks as follows:

input = "Buffer"
arr = arcpy.da.FeatureClassToNumPyArray(input, ('Shape_Area'))

p1 = np.percentile(arr, 10) 
p2 = np.percentile(arr, 20)
p3 = np.percentile(arr, 30)  
p4 = np.percentile(arr, 40)  
p5 = np.percentile(arr, 50)  
p6 = np.percentile(arr, 60)  
p7 = np.percentile(arr, 70)  
p8 = np.percentile(arr, 80)  
p9 = np.percentile(arr, 90)  
p10 = np.percentile(arr, 100)  
#use cursor to update the new rank field
with arcpy.da.UpdateCursor(input , ['Shape_Area','ConS']) as cursor:
    for row in cursor:
        if row[0] < p1:
            row[1] = 0.1  #rank 0
        elif p1 <= row[0] and row[0] < p2:
             row[1] = 0.2
        elif p2 <= row[0] and row[0] < p3:
             row[1] = 0.3
        elif p3 <= row[0] and row[0] < p4:
             row[1] = 0.4
        elif p4 <= row[0] and row[0] < p5:
             row[1] = 0.5
        elif p5 <= row[0] and row[0] < p6:
             row[1] = 0.6
        elif p6 <= row[0] and row[0] < p7:
             row[1] = 0.7
        elif p7 <= row[0] and row[0] < p8:
             row[1] = 0.8
        elif p9 <= row[0] and row[0] < p10:
             row[1] = 0.9                      
        else:
             row[1] = 1

        cursor.updateRow(row)

When searching on the internet I found that the problem behind this error often lies in the values being strings rather than floats or integers so I edited my code to read:

p1 = np.percentile(arr, 10, np.float) 
p2 = np.percentile(arr, 20, np.float)
p3 = np.percentile(arr, 30, np.float)

And now I got the error message:

'type' object is not iterable

What can I do to solve this issue, or is there maybe another way to calculate the scores?

  • See documentation for numpy.percentile here. You are providing a type (np.float) as a parameter where the parameter needs to be the axis along which the percentiles are computed. – GeoSharp Oct 22 '15 at 8:51
  • Ok, that explains why the second version does not work, but what can I do to get the first version to work? – sdots Oct 22 '15 at 8:54
  • Can you provide a sample of some of the values in your variable arr? – GeoSharp Oct 22 '15 at 8:56
  • This would be an example: >>> print(arr) [(39710.930139345284,) (32032.62901900672,) (13822.309180032586,) ..., (33956.1676006977,) (35127.24443114165,) (23696.81216139415,)] – sdots Oct 22 '15 at 9:43
  • Can you confirm that for the results of numpy.percentile, for example p1, is a single value and that all of the records in your feature class have a value for 'Shape_Area'? – GeoSharp Oct 22 '15 at 9:48
3

The issue here is that the values in arr are tuples (value_one, value_two) with the first item only containing a value as is shown in your comment explaining the values in arr:

[(39710.930139345284,) (32032.62901900672,) (13822.309180032586,) ..., (33956.1676006977,) (35127.24443114165,) (23696.81216139415,)]

In your case you only want to use the first item of the tuple because the second item does not have a value. To do this use list comprehension:

correct_array = [float(x[0]) for x in numpy.ndarray.flatten(arr)]

In a nutshell this will flatten arr into a 1d array and convert the first element of each tuple, x[0], into a float during the process. With the new array, that only contains the values of the first item of each tuple, you can run your code as usual:

p1 = numpy.percentile(correct_array, 50)

In this case, converting to a 1d array is inconsequential because you are only interested in the values and not there specific order or location in the array. This should produce the percentile values that you are looking for.

  • That gives a new error message: cannot convert to a float; scalar object is not a number – sdots Oct 22 '15 at 11:02
  • @sdots I have updated the answer to address this new error message. Does the updated answer fix the issue for you? – GeoSharp Oct 22 '15 at 19:15
  • Sorry, I was gone over the weekend, yes that seems to have worked! Thanks :) – sdots Oct 27 '15 at 9:33
0

As an alternate to flattening the array, could also just access a single field of the array.

If you substitute this:

arr = arcpy.da.FeatureClassToNumPyArray(input, ('Shape_Area'))['Shape_Area']

in place of:

arr = arcpy.da.FeatureClassToNumPyArray(input, ('Shape_Area'))

Should be more efficient, although it may not matter much depending on the size of your data.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.