I have three 2D arrays SandArray
,ClayArray
, and SiltArray
. I also have a function described here. Below is my code, when I run the script I get a ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
def TextureClass(sand, clay, silt):
#if sand clay > 100 or sand < 0 or clay < 0:
# raise Exception('Inputs adds over 100% or are negative')
if silt 1.5*clay < 15:
textural_class = 'sand'
elif silt 1.5*clay >= 15 and silt 2*clay < 30:
textural_class = 'loamy sand'
elif (clay >= 7 and clay < 20 and sand > 52 and silt 2*clay >= 30) or (clay < 7 and silt < 50 and silt 2*clay >= 30):
textural_class = 'sandy loam'
elif clay >= 7 and clay < 27 and silt >= 28 and silt < 50 and sand <= 52:
textural_class = 'loam'
elif (silt >= 50 and clay >= 12 and clay < 27) or (silt >= 50 and silt < 80 and clay < 12):
textural_class = 'silt loam'
elif silt >= 80 and clay < 12:
textural_class = 'silt'
elif clay >= 20 and clay < 35 and silt < 28 and sand > 45:
textural_class = 'sandy clay loam'
elif clay >= 27 and clay < 40 and sand > 20 and sand <= 45:
textural_class = 'clay loam'
elif clay >= 27 and clay < 40 and sand <= 20:
textural_class = 'silty clay loam'
elif clay >= 35 and sand > 45:
textural_class = 'sandy clay'
elif clay >= 40 and silt >= 40:
textural_class = 'silty clay'
elif clay >= 40 and sand <= 45 and silt < 40:
textural_class = 'clay'
else:
textural_class = 'na'
return textural_class
Texture = TextureClass(SandArray,ClayArray,SiltArray)
Texture should be an array with the same shape as SandArray
, ClayArray
, and SiltArray
but with the textural_class
str
as its values.
Is it possible to have an output array of text from a function having conditions and using arrays as its input arguments and if so, what am I missing?
Edit:
Having tried texture = np.array(list(map(TextureClass,SandArray,ClayArray,SiltArray)))
I still get the same ValueError
CodePudding user response:
I'm not really that knowledgeable in numpy, but, from what I searched in other questions, you could use vectorize to wrap your function.
Here's an example: How to apply a function / map values of each element in a 2d numpy array/matrix?
Using my previous approach of mostly built-in python-code:
You could zip the three arrays (either inside or outside your function, but I'd do it outside), then loop over the zipped arrays.
The
zip()
function returns a zip object, which is an iterator of tuples where the first item in each passed iterator is paired together, and then the second item in each passed iterator are paired together etc.If the passed iterators have different lengths, the iterator with the least items decides the length of the new iterator.
Emphasis on the fact that it's assumed the three arrays have the same length. And, in this case, you'd need to bi-dimensionally zip:
So, instead of Texture = TextureClass(SandArray,ClayArray,SiltArray)
, you could use:
soilCompositions = (zip(sands, clays, silts) for sands, clays, silts in zip(SandArray, ClayArray, SiltArray))
Textures = ((TextureClass(sand, clay, silt) for sand, clay, silt in soilCompositionRow) for soilCompositionRow in soilCompositions)
Notice that I used generator comprehension, but you could just as easy use list comprehension instead:
soilCompositions = (zip(sands, clays, silts) for sands, clays, silts in zip(SandArray, ClayArray, SiltArray))
Textures = [[TextureClass(sand, clay, silt) for sand, clay, silt in soilCompositionRow] for soilCompositionRow in soilCompositions]
CodePudding user response:
Applying a function to each element of a matrix requires np.vectorize
. The documentation is available here. An example of a similar questions can be found here:How to apply a function / map values of each element in a 2d numpy array/matrix?.
I think this question is unique in that it shows the range of functions that np.vectorize
works on. My original issue was whether or not np.vectorize
would work for a conditional function like the one in my question.
def TextureClass(sand, clay, silt):
#if sand clay > 100 or sand < 0 or clay < 0:
# raise Exception('Inputs adds over 100% or are negative')
if silt 1.5*clay < 15:
textural_class = 'sand'
elif silt 1.5*clay >= 15 and silt 2*clay < 30:
textural_class = 'loamy sand'
elif (clay >= 7 and clay < 20 and sand > 52 and silt 2*clay >= 30) or (clay < 7 and silt < 50 and silt 2*clay >= 30):
textural_class = 'sandy loam'
elif clay >= 7 and clay < 27 and silt >= 28 and silt < 50 and sand <= 52:
textural_class = 'loam'
elif (silt >= 50 and clay >= 12 and clay < 27) or (silt >= 50 and silt < 80 and clay < 12):
textural_class = 'silt loam'
elif silt >= 80 and clay < 12:
textural_class = 'silt'
elif clay >= 20 and clay < 35 and silt < 28 and sand > 45:
textural_class = 'sandy clay loam'
elif clay >= 27 and clay < 40 and sand > 20 and sand <= 45:
textural_class = 'clay loam'
elif clay >= 27 and clay < 40 and sand <= 20:
textural_class = 'silty clay loam'
elif clay >= 35 and sand > 45:
textural_class = 'sandy clay'
elif clay >= 40 and silt >= 40:
textural_class = 'silty clay'
elif clay >= 40 and sand <= 45 and silt < 40:
textural_class = 'clay'
else:
textural_class = 'na'
return textural_class
vector_func = np.vectorize(TextureClass)
textures = vector_func(SandArray, ClayArray, SiltArray)