- Don't do lines too long, it's considered bad style as horizontal
scrolling is awkward. Most projects demand lines < 80 or, more rarely, < 100 chars.
- This also helps the case when you want to compare two codes next to each other

- Include a space between argumenst for increased readability:
`Good: myfunc(a, b, c, d, e)`

`Bad: myfunc(a,b,c,d,e)`

- Only use
`elif`

if there's another differing case to check. Otherwise, just use`else`

. - Help yourself by doing unit conversions before the actual equation.
- Otherwise, already awkward looking equations become even harder to read.

- imports at top of module, not inside functions!
- Makes the reader immediately understand the dependencies of your code.
- This paradigm is being softened for parallel processing, where it becomes easier to send a logically complete function (with imports at beginning of function) to the different processors.

```
Narr = [N(i) for i in xArr] # list comprehension, **NOT** okay for vectorial
```

is not the same as

```
Narr = N(xArr) # optimal
```

is not the same as

```
Narr = np.exp(xArr**2/[....]) # kinda cheating...
```

And when the instructions say, call f() on each element of a vector, it means that. So:

```
xList = [f(i) for i in vector]
```

```
In [ ]:
```%matplotlib inline

```
In [ ]:
```import numpy as np
import matplotlib.pyplot as pl

As the colormap default is still the awful 'jet' colormap (creates artificial visual boundaries that don't exist in the data -> it fools you.), I want to switch the default to 'viridis'.

(exercise to the reader: this also can be done in a config file that is being read everytime matplotlib is being loaded!)

```
In [ ]:
```from matplotlib import rcParams

Now, this config dictionary is huge:

```
In [ ]:
```rcParams.keys()

```
In [ ]:
```[key for key in rcParams.keys() if 'map' in key]

```
In [ ]:
```rcParams['image.cmap']

```
In [ ]:
```rcParams['image.cmap'] = 'viridis'

```
In [ ]:
```rcParams['image.interpolation'] = 'none'

Simple example:

```
In [12]:
```x = np.array([[1,2,3], [4,5,6], [7,8,9], [10,11,12]])
x

```
Out[12]:
```

Q. What is the rank of x?

Q. What is the shape of x?

```
In [13]:
```x.shape

```
Out[13]:
```

```
In [14]:
```x.ndim

```
Out[14]:
```

Visualize it:

```
In [16]:
```print(x) # for reference
pl.imshow(x)
pl.colorbar();

```
```

Notice that the first row of the array was plotted at the top of the image.

This may be counterintuitive if when you think of row #0 you think of y=0, which in a normal x-y coordinate system is on the bottom.

This be changed using the "origin" keyword argument.

The reason for this is that this command was made for displaying CCD image data, and often the pixel (0,0) was considered to be the one in the upper left.

But it also matches the standard print-out of arrays, so that's good as well.

```
In [17]:
```print(x) # for reference
pl.imshow(x, origin='lower')
pl.colorbar();

```
```

```
In [18]:
```# Interpolation (by default) makes an image look
# smoother.
# Instead:
pl.imshow(x, origin='lower', interpolation='bilinear')
pl.colorbar()

```
Out[18]:
```

To look up other interpolations, just use the help feature.

And by the way, there shouldn't be any space after the question mark!

```
In [19]:
```pl.imshow?

```
In [20]:
```x # for reference

```
Out[20]:
```

If the rows were to correspond to x and the columns to y, then
we need to regroup the elements.
Q. How could we do that?

```
In [21]:
```print(x)
print()
print(x.T)

```
```

```
In [23]:
```xT = x.T

```
In [24]:
```pl.imshow(xT)
pl.colorbar()

```
Out[24]:
```

```
In [25]:
```xT.shape

```
Out[25]:
```

```
In [26]:
```xT # Reminder

```
Out[26]:
```

```
In [ ]:
```xT[2][1]

Or, more simply:

```
In [27]:
```xT[2,1]

```
Out[27]:
```

```
In [29]:
``````
xT
```

```
Out[29]:
```

```
In [30]:
```print(np.indices(xT.shape))
print("-" * 50)
for i in range(xT.shape[0]):
for j in range(len(xT[0])):
print(i, j)

```
```

The result is clearer if we unpack it. The x and y indices are stored separately:

```
In [34]:
```i, j = np.indices(xT.shape)

```
In [35]:
``````
i
```

```
Out[35]:
```

```
In [36]:
``````
j
```

```
Out[36]:
```

These two arrays, i and j, are the same shape as xT, and each element corresponds to the row number (i) and column number (j).

```
In [37]:
``````
xT
```

```
Out[37]:
```

```
In [38]:
```xT[1,2]

```
Out[38]:
```

or, use boolean logic:

```
In [40]:
```print(xT[np.logical_and(i == 1, j == 2)])
# Q. How did this work?
print(np.logical_and(i == 1, j == 2))
i == 1

```
Out[40]:
```

```
In [41]:
```xT # for reference

```
Out[41]:
```

```
In [42]:
```np.argwhere(xT % 2 == 0)

```
Out[42]:
```

Note you only need this if you want to use these indices somewhere else, e.g. in another array of same shape.

Because if you just wanted the values, you of course would do that:

```
In [43]:
```xT[xT % 2 == 0]

```
Out[43]:
```

```
In [44]:
```xT # for reference

```
Out[44]:
```

Find indices of all elements with values > 5:

```
In [45]:
```np.argwhere(xT > 5)

```
Out[45]:
```

```
In [46]:
``````
xT
```

```
Out[46]:
```

Can add, subtract, multiply, and divide 2 arrays **so long as they have the same shape.**

```
In [49]:
``````
xT
```

```
Out[49]:
```

```
In [53]:
```pl.imshow(xT)
pl.colorbar()
pl.clim(1, 12) # colorbar limits,
# analogous to xlim, ylim

```
```

```
In [56]:
```print(xT + 5)
pl.imshow(xT+5)
pl.colorbar()
# pl.clim(1, 12)

```
Out[56]:
```

Notice how "clim" affects the output!