In [1]:

```
%matplotlib inline #this is a formatting statement for jupyter notebooks
```

This is an introduction to moving your science from an IDL environment to Python. If you havne't done so yet, please download and install the the latest versions of anaconda (www.continuum.io/downloads) and sunpy (docs.sunpy.org/en/stable/guide/installation/).

Let's make sure that your instillation of anaconda is up to date. From the terminal, type:

Now, lets launch an ipython (interactive python) session

The first thing to note is that by itself, Python is just a platform. You can think of Python as an engine - it is very powerful but unless you connect it to a transmission, axle, and wheels, it isn't very effective at getting you down the road.

Fortunatly in most cases you don't need to reinvent the wheel and you can just find some that someone else created that will fit your needs.

The Anaconda package comes with literally thousands of tools to help you work with data.

Let's explore some basic tools. NumPy is the fundamental package for scientific computing with Python. Let's load it in to our current session.

In [2]:

```
import numpy as np
```

Notice the syntax we are using: import "the library" as "what we want to call it." Now whenever we want to call a tool in numpy, we can use "np."

Next we will want to plot our data, so lets grab some plotting tools. We will use a package called matplotlib. Matplotlib is a 2D plotting library, which produces all kinds figures. We don't need everything that matplotlib can do, so let's import just the tool that produces line plots. This tool is called pyplot.

In [3]:

```
import matplotlib.pyplot as plt
```

Again notice the syntax we are using: import "library.tool" as "what we want to call it."

Okay, we have the tools 'np' and 'plt' defined. Loading in the tools and libraries you want to use is something you have to do every time you write a program or load a python session.

Now lets put our tools to use:

In [5]:

```
dt = 0.1
Fs = 1/dt
t=np.arange(0,Fs,dt)
```

We can dynamically define variables (i.e. we don't have to specify the type) and use conventional assignments to make new variables.

We also use a tool within np to create t: np.arange. Np.arange is similar to IDL's FINDGEN( ). It creates an evenly spaced array with the syntax of output=np.arange(start,stop,step).

So what does t look like? We just type the variable or use a print statement:

In [6]:

```
t
```

Out[6]:

In [7]:

```
print(t)
```

But what are the dimensions of t? How many elements are in it?

In IDL we would have to call a function to return this information, such as MAX( ). In Python, since it is an object oriented programming language, the variable t is actually more than just a simple array. It is self-aware! So let's ask t about itself.

In [8]:

```
t.min
```

Out[8]:

In [9]:

```
t.min()
```

Out[9]:

Also we can ask:

In [10]:

```
t.max()
```

Out[10]:

In [11]:

```
t.mean()
```

Out[11]:

In [12]:

```
t.var()
```

Out[12]:

In [13]:

```
t.size
```

Out[13]:

To see the shape of the array:

In [14]:

```
t.shape
```

Out[14]:

If you want to see all of the functions already available in t, type "t." and then 'tab'. This will show you dynamically all of the functions that exist. Go ahead, try it!

You'll note that .size and .shape don't have brackets after them, but .min and .var do. This is because the size and the shape of the array are attributes of the array. The reason .min (and other methods) have brackets is that these functions have different options that you may want to use.

Let's create a 2d random array

In [15]:

```
rand_nn = np.random.randn(t.size, t.size)
```

We can find the minimum values along one dimension of the array...

In [34]:

```
rand_nn.min(axis=0)
```

Out[34]:

or the other one...

In [35]:

```
rand_nn.min(axis=1)
```

Out[35]:

Or the whole array.

In [36]:

```
rand_nn.min(), rand_nn.min(axis=None)
```

Out[36]:

**None**, which indicates **no value**. In this case, this means that axis has no assigned value. The axis function then resorts to a default behavior, which is to give you the minimum value over the entire array.

Now let's create an array of random numbers with the same number of elements as t. We will use the Numpy tool 'random', but there are lots of ways to generate random numbers. In IDL we might use RANDOMN( ) or RANDOMU( ) depending on the distribution we want to sample from. Numpy.random had many ways of calculating random numbers too.

Try typing "np.random." and then 'tab'.

Let's sample from a normal distribution:

In [37]:

```
rand_n = np.random.randn(t.size)
```

Notice that we can use the t.size attribute of 't' without having to define another variable or call a separate function.

Let's plot our variables. To do this we will use the matplotlib's plot function.

In [38]:

```
plt.plot(t,rand_n)
plt.show()
```

Matplotlib generates a plot and then in a separate command you have to display it to the screen. Yeah, it's a pain but you get use to it.

Let's now generate some more complex data:

In [39]:

```
r = np.exp(-t/0.05)
r_dat = np.convolve(rand_n, r)*dt
r_dat.size
```

Out[39]:

In [22]:

```
r_dat=r_dat[:t.size]
```

In [23]:

```
ss = 0.1*np.sin(2*np.pi*t) + r_dat
```

What does all of this generated data look like? Why don't we make five different plots to highlight some different plotting capabilities.

First, we will define a plotting space, but instead of showing the plot this time, we are going to plot some more values. First, we will plot the magnitude spectrum of the generated data with a linear scale and a dB (logarithmic) scale. This can all be done within matplotlib. Next, We can easily show the wrapped and unwrapped phase spectrum.

In [24]:

```
plt.subplot(3, 2, 1)
plt.plot(t, ss)
plt.subplot(3, 2, 3)
plt.magnitude_spectrum(ss, Fs=Fs)
plt.subplot(3, 2, 4)
plt.magnitude_spectrum(ss, Fs=Fs, scale='dB')
plt.subplot(3, 2, 5)
plt.angle_spectrum(ss, Fs=Fs)
plt.subplot(3, 2, 6)
plt.phase_spectrum(ss, Fs=Fs)
plt.show()
```

To showcase some other basic programming techniques using the skills we just learned, lets analytically calculate $\pi$.

The easiest way of doing this is using the Monty Carlo Method through finding random points inside a circle inscribed in a square.

In [25]:

```
import numpy as np #always good to be explicit about our libraries used
```

Next, we will initialize our counting variables

In [26]:

```
total = 0
inside = 0
```

In [27]:

```
for ii in range(10000):
# Generate two coordinates in the interval 0-1:
x_coord = np.random.uniform()
y_coord = np.random.uniform()
# Calculate the distance to the origin
r = np.sqrt(x_coord**2 + y_coord**2)
# Count this if it is inside the circle.
if r< 1:
inside += 1
total += 1
```

In [28]:

```
print('Pi=',4.0*inside/(1.0*total))
```

In [33]:

```
for value in 'Jack':
print(value)
print
for iteration, value in enumerate('Jack'):
print(iteration, value)
```

Lets take this same python script and make it into a simple function we can iterate over.

In [30]:

```
import numpy as np #always good to be explicit about our libraries used
```

In [31]:

```
def pitest():
# Create the coordinate
xtemp = np.random.uniform()
ytemp = np.random.uniform()
# Calculate the distance to the origin
r = np.sqrt(xtemp**2 + ytemp**2)
# Count this if it is inside the circle.
if r< 1:
return True
return False
```

In [32]:

```
count = 0
inside = 0
for i in range(10000):
is_inside = pitest()
inside += is_inside
count += 1.
print('Pi=',((inside/count)*4.))
```

**True** and **False**. These are also understood to have the integer values 1 and 0 respectively.

So now I have writen a script, how do I run it from the command line? Let's say that the function we wrote, **'pitest,'** is saved with the name **'Pifunction.py'**. First, change to the directory where your program is located.

Then import the name of your script.

And then run your function like we have all along:

In [ ]:

```
```

In [ ]:

```
```