# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# # EuroSciPy 2018: NumPy tutorial (https://github.com/gertingold/euroscipy-numpy-tutorial)

# ## Let's do some slicing

mylist = list(range(10))
print(mylist)

# Use slicing to produce the following outputs:

# [2, 3, 4, 5]



# [0, 1, 2, 3, 4]



# [6, 7, 8, 9]



# [0, 2, 4, 6, 8]



# [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]



# [7, 5, 3]



# ## Matrices and lists of lists

matrix = [[0, 1, 2],
          [3, 4, 5],
          [6, 7, 8]]

# Get the second row by slicing twice



# Try to get the second column by slicing. ~~Do not use a list comprehension!~~



# ## Getting started

# Import the NumPy package



# ## Create an array

np.lookfor('create array')

help(np.array)  # remember Shift + Tab give a pop up help. Wh Shift + Tab 

# Press Shift + Tab when your cursor in a Code cell. This will open a pop up with some helping text. 
# What happens Shift + Tab is pressed a second time?

# The variable `matrix` contains a list of lists. Turn it into an `ndarray` and assign it to the variable `myarray`. Verify that its type is correct.



# For practicing purposes, arrays can conveniently be created with the `arange` method.

myarray1 = np.arange(6)
myarray1


def array_attributes(a):
    for attr in ('ndim', 'size', 'itemsize', 'dtype', 'shape', 'strides'):
        print('{:8s}: {}'.format(attr, getattr(a, attr)))


array_attributes(myarray1)

# ## Data types

# Use `np.array()` to create arrays containing
#  * floats
#  * complex numbers
#  * booleans
#  * strings
#  
# and check the `dtype` attribute.









# Do you understand what is happening in the following statement?

np.arange(1, 160, 10, dtype=np.int8)

# ## Strides/Reshape

myarray2 = myarray1.reshape(2, 3)
myarray2

array_attributes(myarray2)

myarray3 = myarray1.reshape(3, 2)

array_attributes(myarray3)

# ## Views

# Set the first entry of `myarray1` to a new value, e.g. 42.



# What happened to `myarray2`?





# What happens when a matrix is transposed?

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

a.T

# Check the strides!

a.strides

a.T.strides

# ## View versus copy

# identical object

a = np.arange(4)
b = a
id(a), id(b)

# view: a different object working on the same data

b = a[:]
id(a), id(b)

a[0] = 42
a, b

# an independent copy

a = np.arange(4)
b = np.copy(a)
id(a), id(b)

a[0] = 42
a, b

# ## Some array creation routines

# ### numerical ranges

# `arange(`*start*, *stop*, *step*`)`, *stop* is not included in the array

np.arange(5, 30, 5)

# `arange` resembles `range`, but also works for floats
#
# Create the array [1, 1.1, 1.2, 1.3, 1.4, 1.5]



# `linspace(`*start*, *stop*, *num*`)` determines the step to produce *num* equally spaced values, *stop* is included by default
#
# Create the array [1., 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.]



# For equally spaced values on a logarithmic scale, use `logspace`.

np.logspace(-2, 2, 5)

np.logspace(0, 4, 9, base=2)

# ### Application

import matplotlib.pyplot as plt

# %matplotlib inline

x = np.linspace(0, 10, 100)
y = np.cos(x)

plt.plot(x, y)

# ### Homogeneous data

np.zeros((4, 4))

# Create a 4x4 array with integer zeros



np.ones((2, 3, 3))

# Create a 3x3 array filled with tens



# ### Diagonal elements

np.diag([1, 2, 3, 4])

# `diag` has an optional argument `k`. Try to find out what its effect is.



# Replace the 1d array by a 2d array. What does `diag` do?



np.info(np.eye)

# Create the 3x3 array
#
# ```[[2, 1, 0],
#  [1, 2, 1],
#  [0, 1, 2]]
# ```



# ### Random numbers
# What is the effect of np.random.seed?

np.random.seed()
np.random.rand(5, 2)

np.random.seed(1234)
np.random.rand(5, 2)

data = np.random.rand(20, 20)
plt.imshow(data, cmap=plt.cm.hot, interpolation='none')
plt.colorbar()

casts = np.random.randint(1, 7, (100, 3))
plt.hist(casts, np.linspace(0.5, 6.5, 7))

# ## Indexing and slicing

# ### 1d arrays

a = np.arange(10)

# Create the array [7, 8, 9]



# Create the array [2, 4, 6, 8]



# Create the array [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]



# ### Higher dimensions

a = np.arange(40).reshape(5, 8)

# Create the array [[21, 22, 23], [29, 30, 31], [37, 38, 39]]



# Create the array [ 3, 11, 19, 27, 35]



# Create the array [11, 12, 13]



# Create the array [[ 8, 11, 14], [24, 27, 30]]



# ## Fancy indexing ‒ Boolean mask

a = np.arange(40).reshape(5, 8)

a % 3 == 0

a[a % 3 == 0]

a[(1, 1, 2, 2, 3, 3), (3, 4, 2, 5, 3, 4)]

# ## Axes

# Create an array and calculate the sum over all elements



# Now calculate the sum along axis 0 ...



# and now along axis 1



# Identify the axis in the following array

a = np.arange(24).reshape(2, 3, 4)
a

# ## Axes in more than two dimensions

# Create a three-dimensional array



# Produce a two-dimensional array by cutting along axis 0 ...



# and axis 1 ...



# and axis 2



# What do you get by simply using the index `[0]`?



# What do you get by using `[..., 0]`?



# ## Exploring numerical operations

a = np.arange(4)
b = np.arange(4, 8)
a, b

a+b

a*b

# Operations are elementwise. Check this by multiplying two 2d array...



# ... and now do a real matrix multiplication



# ## Application: Random walk

length_of_walk = 10000
realizations = 5
angles = 2*np.pi*np.random.rand(length_of_walk, realizations)
x = np.cumsum(np.cos(angles), axis=0)
y = np.cumsum(np.sin(angles), axis=0)
plt.plot(x, y)
plt.axis('scaled')

plt.plot(np.hypot(x, y))

plt.plot(np.mean(x**2+y**2, axis=1))
plt.axis('scaled')

# ## Let's check the speed

# %%timeit a = np.arange(1000000)
a**2

# %%timeit xvals = range(1000000)
[xval**2 for xval in xvals]

# %%timeit a = np.arange(100000)
np.sin(a)

import math

# %%timeit xvals = range(100000)
[math.sin(xval) for xval in xvals]

# ## Broadcasting

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

a+1

a+np.arange(4)

a+np.arange(3)

np.arange(3)

np.arange(3).reshape(3, 1)

a+np.arange(3).reshape(3, 1)

# Create a multiplication table for the numbers from 1 to 10 starting from two appropriately chosen 1d arrays.



# As an alternative to `reshape` one can add additional axes with `newaxes` or `None`:

a = np.arange(5)
b = a[:, np.newaxis]

# Check the shapes.



# ## Functions of two variables

x = np.linspace(-40, 40, 200)
y = x[:, np.newaxis]
z = np.sin(np.hypot(x-10, y))+np.sin(np.hypot(x+10, y))
plt.imshow(z, cmap='viridis')

x, y = np.mgrid[-10:10:0.1, -10:10:0.1]

x

y

plt.imshow(np.sin(x*y))

x, y = np.mgrid[-10:10:50j, -10:10:50j]

x

y

plt.imshow(np.arctan2(x, y))

# It is natural to use broadcasting. Check out what happens when you replace `mgrid` by `ogrid`.

















# ## Linear Algebra in NumPy

a = np.arange(4).reshape(2, 2)
eigenvalues, eigenvectors = np.linalg.eig(a)
eigenvalues

eigenvectors

# Explore whether the eigenvectors are the rows or the columns.



# Try out `eigvals` and other methods offered by `linalg` which your are interested in







# ## Application: identify entry closest to ½

# Create a 2d array containing random numbers and generate a vector containing for each row the entry closest to one-half.



StarcoderdataJupyterScriptsDedupFiltered