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?

6
  • 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
    Commented Oct 22, 2015 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
    Commented Oct 22, 2015 at 8:54
  • Can you provide a sample of some of the values in your variable arr?
    – GeoSharp
    Commented Oct 22, 2015 at 8:56
  • This would be an example: >>> print(arr) [(39710.930139345284,) (32032.62901900672,) (13822.309180032586,) ..., (33956.1676006977,) (35127.24443114165,) (23696.81216139415,)]
    – sdots
    Commented Oct 22, 2015 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
    Commented Oct 22, 2015 at 9:48

2 Answers 2

4

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.

3
  • That gives a new error message: cannot convert to a float; scalar object is not a number
    – sdots
    Commented Oct 22, 2015 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
    Commented Oct 22, 2015 at 19:15
  • Sorry, I was gone over the weekend, yes that seems to have worked! Thanks :)
    – sdots
    Commented Oct 27, 2015 at 9:33
1

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 and acknowledge you have read our privacy policy.

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