Numpy¶
Welcome to the second level: spyndex + numpy!
Remember to install spyndex!
[ ]:
!pip install -U spyndex
Now, let’s start!
First, import spyndex and numpy:
[1]:
import spyndex
import numpy as np
numpy.int*¶
We all know the amazing world of numpy, right? Let’s simulate some integer values!
[19]:
NIR = np.int16(6578)
RED = np.int16(1435)
If we check the data types for each one of our variables, we will see that they are numpy.int16:
[20]:
print(f"NIR type: {type(NIR)}")
print(f"RED type: {type(RED)}")
NIR type: <class 'numpy.int16'>
RED type: <class 'numpy.int16'>
And let’s check what we need to compute the DVI:
[21]:
spyndex.indices.DVI
[21]:
DVI: Difference Vegetation Index (attributes = ['bands', 'contributor', 'date_of_addition', 'formula', 'long_name', 'reference', 'short_name', 'type'])
Let’s see the standard names for the bands:
[22]:
spyndex.indices.DVI.bands
[22]:
('N', 'R')
With this we can create our dict!
[23]:
parameters = {"N": NIR, "R": RED}
Now, let’s compute the index!
[24]:
idx = spyndex.computeIndex("DVI",parameters)
If we check the type of the result, we’ll see that it is also a numpy.int16!
[25]:
print(f"idx type: {type(idx)}")
idx type: <class 'numpy.int16'>
And the value of the index:
[28]:
idx
[28]:
5143
That’s great! Now, let’s exagerate a little bit… Imagine we have, for some reason, very big values and we need a different data type: numpy.int64
Note that using this kind of data type is not recommended for most spectral indices!
[29]:
NIR = np.int64(657812517257537)
RED = np.int64(143532873927932)
If we check the types:
[31]:
print(f"NIR type: {type(NIR)}")
print(f"RED type: {type(RED)}")
NIR type: <class 'numpy.int64'>
RED type: <class 'numpy.int64'>
And if we compute the DVI:
[32]:
idx = spyndex.computeIndex("DVI",{"N": NIR, "R": RED})
We’ll see that the data type of the result is now numpy.int64!
[33]:
print(f"idx type: {type(idx)}")
idx type: <class 'numpy.int64'>
Here the resulting index:
[34]:
idx
[34]:
514279643329605
numpy.float*¶
Now, let’s do things as they are supposed to be: Using floats!
[38]:
NIR = np.float32(0.63241)
RED = np.float32(0.13641)
If we check the data types, we’ll see the numpy.float32:
[39]:
print(f"NIR type: {type(NIR)}")
print(f"RED type: {type(RED)}")
NIR type: <class 'numpy.float32'>
RED type: <class 'numpy.float32'>
Now, let’s compute the DVI again:
[41]:
idx = spyndex.computeIndex("DVI",{"N": NIR, "R": RED})
And the result is a beautiful numpy.float32!
[42]:
print(f"idx type: {type(idx)}")
idx type: <class 'numpy.float32'>
Here our DVI value:
[43]:
idx
[43]:
0.496
If required, different float datatypes, such as numpy.float64, can be used too!
numpy.ndarray¶
Let’s get spicy! We are going to work with the main data type of numpy: arrays.
Let’s simulate some arrays for the visible spectrum!
[2]:
BLUE = np.random.normal(0.13,0.05,1000)
GREEN = np.random.normal(0.23,0.06,1000)
RED = np.random.normal(0.14,0.07,1000)
If we check the data types for each one of our variables, we will see that they are numpy.ndarray:
[3]:
print(f"BLUE type: {type(BLUE)}, shape: {BLUE.shape}")
print(f"GREEN type: {type(GREEN)}, shape: {GREEN.shape}")
print(f"RED type: {type(RED)}, shape: {RED.shape}")
BLUE type: <class 'numpy.ndarray'>, shape: (1000,)
GREEN type: <class 'numpy.ndarray'>, shape: (1000,)
RED type: <class 'numpy.ndarray'>, shape: (1000,)
Amazing! Let’s compute some spectral indices with the visible spectrum: GLI and VARI!
[4]:
spyndex.indices.GLI
[4]:
GLI: Green Leaf Index (attributes = ['bands', 'contributor', 'date_of_addition', 'formula', 'long_name', 'reference', 'short_name', 'type'])
[5]:
spyndex.indices.VARI
[5]:
VARI: Visible Atmospherically Resistant Index (attributes = ['bands', 'contributor', 'date_of_addition', 'formula', 'long_name', 'reference', 'short_name', 'type'])
Let’s check the standard names!
[6]:
spyndex.indices.GLI.bands
[6]:
('G', 'R', 'B')
[7]:
spyndex.indices.VARI.bands
[7]:
('G', 'R', 'B')
Since both use the visible spectrum, we just need to create a dict with the B, G and R parameters!
[8]:
parameters = {"B": BLUE, "G": GREEN, "R": RED}
Now we just have to compute the indices! It can be easily done with spyndex.computeIndex():
[9]:
idx = spyndex.computeIndex(["GLI","VARI"], parameters)
Let’s check our result!
[10]:
print(f"idx type: {type(idx)}, shape: {idx.shape}")
idx type: <class 'numpy.ndarray'>, shape: (2, 1000)
Yes! It is a numpy.ndarray! And check the shape of it: (2, 1000), why is that? Well, our initial shapes were (1000, ) and (1000, ), and spyndex concatenates the resulting indices along the first axis, so, since we computed 2 indices, the result has the shape (2, 1000). Let’s try other shapes!
[11]:
BLUE = np.random.normal(0.13,0.05,(100,100,100))
GREEN = np.random.normal(0.23,0.06,(100,100,100))
RED = np.random.normal(0.14,0.07,(100,100,100))
We have created 3 cubes of spectral data! Let’s confirm it:
[12]:
print(f"BLUE type: {type(BLUE)}, shape: {BLUE.shape}")
print(f"GREEN type: {type(GREEN)}, shape: {GREEN.shape}")
print(f"RED type: {type(RED)}, shape: {RED.shape}")
BLUE type: <class 'numpy.ndarray'>, shape: (100, 100, 100)
GREEN type: <class 'numpy.ndarray'>, shape: (100, 100, 100)
RED type: <class 'numpy.ndarray'>, shape: (100, 100, 100)
Nice! Now we can define our parameters!
[13]:
parameters = {"B": BLUE, "G": GREEN, "R": RED}
And let’s compute the indices again!
[14]:
idx = spyndex.computeIndex(["GLI","VARI"], parameters)
What shape has the result?…
[15]:
print(f"idx type: {type(idx)}, shape: {idx.shape}")
idx type: <class 'numpy.ndarray'>, shape: (2, 100, 100, 100)
That’s right! Now we have two cubes! (2, 100, 100, 100), one for the GLI and another one for the VARI!
Everything is beautiful, and so are shapes… pay attention! if your shapes are not equal, you’re going to be in trouble!
[16]:
BLUE = np.random.normal(0.13,0.05,(200,100,100))
GREEN = np.random.normal(0.23,0.06,(100,300,100))
RED = np.random.normal(0.14,0.07,(100,100,400))
What kind of monster creates these cubes with different shapes?
[17]:
print(f"BLUE type: {type(BLUE)}, shape: {BLUE.shape}")
print(f"GREEN type: {type(GREEN)}, shape: {GREEN.shape}")
print(f"RED type: {type(RED)}, shape: {RED.shape}")
BLUE type: <class 'numpy.ndarray'>, shape: (200, 100, 100)
GREEN type: <class 'numpy.ndarray'>, shape: (100, 300, 100)
RED type: <class 'numpy.ndarray'>, shape: (100, 100, 400)
Be careful!
[18]:
parameters = {"B": BLUE, "G": GREEN, "R": RED}
Oh God, I’m scared…
[19]:
idx = spyndex.computeIndex(["GLI","VARI"], parameters)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
/tmp/ipykernel_11829/2841937182.py in <module>
----> 1 idx = spyndex.computeIndex(["GLI","VARI"], 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>
ValueError: operands could not be broadcast together with shapes (100,300,100) (100,100,400)

Of course you died! Never use parameters with different shapes! :) Unless you want to use some of the parameters as constants!
[20]:
BLUE = np.random.normal(0.13,0.05,(100,100,100))
GREEN = 0.23
RED = np.random.normal(0.14,0.07,(100,100,100))
Is this going to work?
[21]:
parameters = {"B": BLUE, "G": GREEN, "R": RED}
I hope so…
[22]:
idx = spyndex.computeIndex(["GLI","VARI"], parameters)
There you go! It did! That’s because you can use constants in combination with numpy.ndarray objects!
[23]:
print(f"idx type: {type(idx)}, shape: {idx.shape}")
idx type: <class 'numpy.ndarray'>, shape: (2, 100, 100, 100)
And the shape will be generated according to the shape of teh input data!
Wonderfuuul!
