Python Built-in Types

colab level1

We are going to start using spyndex with the simplest objects: int, float, and… list?.

Before starting, make sure you have spyndex and the latest dask version. If not, you can upgrade them here:

[ ]:
!pip install -U spyndex

Now, let’s start!

First, import spyndex:

[1]:
import spyndex

int

Now, let’s emulate some values for the Near Infrared (NIR) and Red (RED) reflectances of vegetation with an int data type:

Let’s say that the data values go from 0 to 10000.

[2]:
NIR = 6723
RED = 1243

If we check the data types for each one of our variables, we will see that they are int:

[3]:
print(f"NIR type: {type(NIR)}, value: {NIR}")
print(f"RED type: {type(RED)}, value: {RED}")
NIR type: <class 'int'>, value: 6723
RED type: <class 'int'>, value: 1243

Now, let’s go and compute the NDVI! Let’s check the attributes of the index:

[4]:
spyndex.indices.NDVI
[4]:
NDVI: Normalized Difference Vegetation Index (attributes = ['bands', 'contributor', 'date_of_addition', 'formula', 'long_name', 'reference', 'short_name', 'type'])

From these attributes, we need to know the standard name of the bands so we can compute it:

[5]:
spyndex.indices.NDVI.bands
[5]:
('N', 'R')

That’s it! We need the N and R parameters! Let’s create a dict to store them (Psst! This dictionary is useful since we can pass it to the params argument in the index computation!):

[6]:
parameters = {"N": NIR, "R": RED}

Now we just have to compute the index! It can be easily done with spyndex.computeIndex():

[7]:
idx = spyndex.computeIndex("NDVI", parameters)

If we check the data type of our result, we’ll see that it is a float!

Usually, spectral indices go from -1 to 1 since most of them are normalized differences!

[8]:
print(f"idx type: {type(idx)}, value: {idx}")
idx type: <class 'float'>, value: 0.6879236756213909

float

What if we, besides the NDVI, want to compute another index, mmmm, let’s say… the SAVI?

[9]:
spyndex.indices.SAVI.bands
[9]:
('L', 'N', 'R')

Wow! It seems that now, besides the N and R, we need another parameter… the L!? What is that? Well, let’s check it! With the spyndex.constants object we can check what are those weird parameters that are not bands and we don’t know!

[10]:
spyndex.constants.L
[10]:
Canopy background adjustment (default = 1.0)

Ohh, it is the canopy background adjustment! Now we can give it the desired value!

For SAVI, the default L value is 0.5… But don’t get confused! For the EVI the default value is 1.0!

[11]:
parameters = {"N": NIR / 10000, "R": RED / 10000, "L": 0.5}

Did you notice that we divided the values by 10000? Well, the idea of that computation is to scale the values to [-1,1]. This is because SAVI can’t be used with values outside that range! Let’s check our data types now:

[12]:
print(f"NIR type: {type(parameters['N'])}, value: {parameters['N']}")
print(f"RED type: {type(parameters['R'])}, value: {parameters['R']}")
print(f"L type: {type(parameters['L'])}, value: {parameters['L']}")
NIR type: <class 'float'>, value: 0.6723
RED type: <class 'float'>, value: 0.1243
L type: <class 'float'>, value: 0.5

Amazing! Now we can compute our indices! Just pass a list of them to spyndex.computeIndex():

[13]:
idx = spyndex.computeIndex(["NDVI","SAVI"], parameters)

What do you think we will get as a result?…

[14]:
print(f"idx type: {type(idx)}, value: {idx}")
idx type: <class 'list'>, value: [0.687923675621391, 0.6339657565941694]

That’s right! A list! Why? Because we computed more than one spectral index! The length of the resulting list is equal to the number of indices that we computed!

list?

Nice, very nice… But what if now we have a list of reflectance values and for each pair/group we want to compute the indices?

[15]:
NIR = [0.634, 0.654, 0.567]
RED = [0.123, 0.156, 0.198]

Remember, these are lists!

[16]:
print(f"NIR type: {type(NIR)}, value: {NIR}")
print(f"RED type: {type(RED)}, value: {RED}")
NIR type: <class 'list'>, value: [0.634, 0.654, 0.567]
RED type: <class 'list'>, value: [0.123, 0.156, 0.198]

Be careful!

[17]:
parameters = {"N": NIR, "R": RED, "L": 0.5}

Are you ready? Let’s compute the indices!

[18]:
idx = spyndex.computeIndex(["NDVI","SAVI"], parameters)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/tmp/ipykernel_17173/2483861871.py in <module>
----> 1 idx = spyndex.computeIndex(["NDVI","SAVI"], parameters)

~/gh/spyndex/spyndex/spyndex.py in computeIndex(index, params, online, returnOrigin, coordinate)
    206         else:
    207             _check_params(idx, params)
--> 208             result.append(eval(indices[idx]["formula"], {}, params))
    209
    210     if len(result) == 1:

<string> in <module>

TypeError: unsupported operand type(s) for -: 'list' and 'list'

youdied

Mein Gott!!! What happened? An error? This is outrageous! Somebody kill me!

Relax, relax… that was an expected error, and I will tell you why:

  • spyndex doesn’t care what kind of objects you use as long as they support overloaded operators.

  • With int and float types that’s not a problem because they by default support overloaded operators.

  • The bad thing, is that list types DON’T SUPPORT OVERLOADED OPERATORS… (Ehmmmm, they support the +, but that’s a concatenation operation, not an addition)

Well, now what do we do?…

Don’t worry, people, because numpy is here to save us!

[19]:
import numpy as np

Let’s transform those lists to numpy.ndarray objects!

[20]:
parameters = {"N": np.array(NIR), "R": np.array(RED), "L": 0.5}

Let’s check the data types to be sure!

[21]:
print(f"NIR type: {type(parameters['N'])}, value: {parameters['N']}")
print(f"RED type: {type(parameters['R'])}, value: {parameters['R']}")
print(f"L type: {type(parameters['L'])}, value: {parameters['L']}")
NIR type: <class 'numpy.ndarray'>, value: [0.634 0.654 0.567]
RED type: <class 'numpy.ndarray'>, value: [0.123 0.156 0.198]
L type: <class 'float'>, value: 0.5

Since L is a constant, you don’t need to create a repeated list of it ;) Now, let’s compute the indices!

[22]:
idx = spyndex.computeIndex(["NDVI","SAVI"], parameters)

No error! :))) Let’s check our result!

[23]:
print(f"idx type: {type(idx)}, value: {idx}")
idx type: <class 'numpy.ndarray'>, value: [[0.67503303 0.61481481 0.48235294]
 [0.6097852  0.57022901 0.43754941]]

Wow! The result is a numpy.ndarray? YES! As long as returnOrigin=True, spyndex will return the result in the same data type as the input!

Nice, right?