import numpy as np

"""
Explanation: Numpy exercices
End of explanation
"""


a = np.arange(9).reshape(3,3)

a.mean?

"""
Explanation: Working with arrays
Create the following array (without typing the elements!):
 array([[0, 1, 2, 3, 4],
        [5, 6, 7, 8, 9]])

Extending arrays
Because arrays do no have the append method  np.concatenate, np.vstack and np.hstack functions are very useful. 

Create: array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
add the raw [10, 20, 30]
add [100, 200, 300, 400] as a column to the new a in 2.
End of explanation
"""


np.random.seed(3)
a = np.random.random((4,3))-.2

a

"""
Explanation: Saving and retrieving arrays
Let's create a couple of large arrays:
a=random.random([1000, 1000])
b=arange([1000])

Save them to disk with save with savez and with savetxt. Delete a and b (del a,b) and recover them from disk. Check the size of the saved text file. Now append a .gz to the filename when saving with savetxt and see what happens. How do you load this file?
Removing negative numbers with Fancy Indexing
We have an array that has small negative numbers and we want to remove them, converting them to zero. First, we generate a random array.
End of explanation
"""


index = #Finish

"""
Explanation: Find the indices where this array is negative:
End of explanation
"""


np.random.seed(3) #This way we can reproduce the same "random" array as before.
a = np.random.random((4,3))-.2
a[a<0] = 0
a

"""
Explanation: Convert those indices into a zero:
We can also generte the indices in the same expression where we set a
End of explanation
"""


np.random.seed(3) #This way we can reproduce the same "random" array as before.
a = np.random.random((4,3))-.2
np.where(a<0,0, a) #This does not change a

"""
Explanation: We could have also used the where method. But this method does not change a in place.
End of explanation
"""


a = np.arange(12).reshape((4,3))
a

"""
Explanation: More on slicing
Now we have an array and we need to substract the minimum value of each of the columns.
End of explanation
"""


amin = a.min(axis=0)
amin

"""
Explanation: we can use the 'min method (or numpy function)
End of explanation
"""


a = np.arange(12).reshape((4,3))
a

amin = a.min(axis=1)

a-amin

"""
Explanation: Finally we substract this from a:
Imagine we want to do the same for rows:
End of explanation
"""


#Transposing (use .T method):



#Extending axis


#Extending axis


"""
Explanation: How can we solve this? We could transpose the arrays to get the right dimensions for broadcasting, or extend the dimensions of amin get again the right dimensions for broadcastinc
End of explanation
"""


a = np.arange(5)
b = np.arange(5)+10

result = #Finish

assert result == a.dot(b)

"""
Explanation: dot product and Outer product
The dot product is so common that it is implemented in numpy. But if it was not there, could you code it?
End of explanation
"""


a = np.random.random(10000)
b = np.random.random(10000)

%timeit a.dot(b)

%timeit #Your code

"""
Explanation: Let's check which is faster for large vectors:
End of explanation
"""


a = np.arange(5)
b = np.arange(5)+10
a,b

m=np.random.random((10,10))-np.random.normal(scale=0.2, size=(10,10))

np.random.normal?

"""
Explanation: Try writing the code in a for loop (if you haven't) and check its performance with the %timeit magic function.
The dot vector can be represented as $\vec x^T \vec y$ if vectors are matrices of dimensions $(N, 1)$. As such we could also mupliply them this way $\vec x \vec y^T$. This returns a matrix of dimensions $(N, N)$, where each element $i, j$ of this matrix is $x_i y_j$. Can you code that?
End of explanation
"""


