I: Import the files we need

The Python standard library consists of packages (folders) containing modules (Python code files) to perform various tasks.

To make one available in your code, use

import <package or module name>

To make it available under a different (usually shorter) name, use

import <package or module name> as <nickname>

To import a module from a package or to import a sub-package, use

from <higher-level item> import <lower-level item>

The as keyword can still be used.

In [1]:
import numpy as np
from matplotlib import pyplot as plt

II: Load Some Data

With numpy, the best way to load data, as long as all of the data are numerical, is with

np.loadtxt('<path-to-file>', <opts>...)

Common Options

  • delimiter: a string indicating what characters separate the columns (default: ' '; usually, you'll want ',')
  • skiprows: the number of rows to skip (default: 0; set to 1 if the columns have headings)
  • usecols: a tuple of integers specifying which columns to read
  • unpack: if True, the output will be a tuple of arrays. If False (the default), the output will be a single, 2D grid.
  • max_rows: the maximum number of rows to import. The default is to import all of the rows.
In [2]:
# CISV
data = np.loadtxt('LRC_resonant_data.csv', delimiter=',', skiprows=1)
In [3]:
data
Out[3]:
array([[1.00000000e+02, 2.95363226e-03],
       [1.00230524e+02, 2.96044356e-03],
       [1.00461579e+02, 2.96727058e-03],
       ...,
       [9.95405417e+05, 1.59898062e-03],
       [9.97700064e+05, 1.59530268e-03],
       [1.00000000e+06, 1.59163319e-03]])

III: Indexing and Slicing

Accessing an Element

In a normal Python 2D list, to access the second column in the fifth row, you would use data[2][1]. With a numpy array, use data[2,1].

In [4]:
data[2,1]
Out[4]:
0.002967270577816

Accessing a Row

In a normal Python grid, getting a whole row is easy. To get the fifth row, just use data[4]. With a numpy array, that still works, but you can also use data[4,:].

In [5]:
data[4]
Out[5]:
array([1.00925289e+02, 2.98097192e-03])
In [6]:
data[4,:]
Out[6]:
array([1.00925289e+02, 2.98097192e-03])

Accessing a Column

What's the point in the longer command? It suggests what to do to get a whole column! In normal Python, you'd basically have to transpose the array. Now, to get the whole second column, you can use data[:,1].

In [7]:
data[:,1]
Out[7]:
array([0.00295363, 0.00296044, 0.00296727, ..., 0.00159898, 0.0015953 ,
       0.00159163])

Slicing in General

The : is used for slicing, and it indicates a range. For a 2D numpy array, the general syntax is

data[row_start:row_stop, col_start:col_stop]

If either start value is blank, numpy starts with the first. If either stop value is blank, numpy goes all the way to the end.

In [8]:
# CISV
data[0:10, 0:2]
Out[8]:
array([[1.00000000e+02, 2.95363226e-03],
       [1.00230524e+02, 2.96044356e-03],
       [1.00461579e+02, 2.96727058e-03],
       [1.00693167e+02, 2.97411335e-03],
       [1.00925289e+02, 2.98097192e-03],
       [1.01157945e+02, 2.98784632e-03],
       [1.01391139e+02, 2.99473658e-03],
       [1.01624869e+02, 3.00164274e-03],
       [1.01859139e+02, 3.00856484e-03],
       [1.02093948e+02, 3.01550291e-03]])

Let's assign the two columns to separate variables.

In [9]:
# CISV
frequency = data[:,0]
voltage = data[:,1]

IV: Basic Plotting

The following will make our plots interactive. Without this, plots would appear inline.

In [10]:
%matplotlib tk

The simplest way to make quick plots---when you just want to simply visualize your data without much careful customization---is using pyplot (which we imported as plt) from matplotlib.

The basic syntax is

plt.plot(<xdata>, <ydata>, '<format_string>')

As always, we need to label our axes using

plt.xlabel(r'<xlabel>')
plt.ylabel(r'<ylabel>')

Note that we used raw strings for the labels. Why? Because matplotlib understands most LaTeX code, which uses backslashes to indicate commands.

In [11]:
# CISV
plt.plot(frequency, voltage, 'b-')
plt.xlabel(r'$f$ (Hz)')
plt.ylabel(r'$V_\mathrm{L}$ (V)')
Out[11]:
Text(0, 0.5, '$V_\\mathrm{L}$ (V)')

All the interesting stuff happens toward the lower frequencies. In a case like this, a logarithmic plot is more helpful. There are three kinds of log plots we can do:

  • plt.loglog
  • plt.semilogx
  • plt.semilogy

The arguments are the same as for plt.plot. In this case, we want plt.semilogx.

In [12]:
# CISV
plt.semilogx(frequency, voltage)
plt.xlabel(r'$f$ (Hz)')
plt.ylabel(r'$V_\mathrm{L}$ (V)')
Out[12]:
Text(0, 0.5, '$V_\\mathrm{L}$ (V)')

IV: More Numpy

There are two common ways of creating new numpy arrays:

np.arange(start, stop, step=1)

works like range, but step does not need to be an integer.

np.linspace(start, stop, num=50, endpoint=True)

produces an array of num evenly-spaced points, including stop if endpoint=True.

In [13]:
x1 = np.linspace(0, 10, 100)
x2 = np.linspace(0, 20, 100)

Unlike regular Python functions and operations on regular Python lists, most numpy operations act element-by-element.

We can do trig, or other mathematical functions, on the whole array at once.

In [14]:
# CISV
x1+x2
Out[14]:
array([ 0.        ,  0.3030303 ,  0.60606061,  0.90909091,  1.21212121,
        1.51515152,  1.81818182,  2.12121212,  2.42424242,  2.72727273,
        3.03030303,  3.33333333,  3.63636364,  3.93939394,  4.24242424,
        4.54545455,  4.84848485,  5.15151515,  5.45454545,  5.75757576,
        6.06060606,  6.36363636,  6.66666667,  6.96969697,  7.27272727,
        7.57575758,  7.87878788,  8.18181818,  8.48484848,  8.78787879,
        9.09090909,  9.39393939,  9.6969697 , 10.        , 10.3030303 ,
       10.60606061, 10.90909091, 11.21212121, 11.51515152, 11.81818182,
       12.12121212, 12.42424242, 12.72727273, 13.03030303, 13.33333333,
       13.63636364, 13.93939394, 14.24242424, 14.54545455, 14.84848485,
       15.15151515, 15.45454545, 15.75757576, 16.06060606, 16.36363636,
       16.66666667, 16.96969697, 17.27272727, 17.57575758, 17.87878788,
       18.18181818, 18.48484848, 18.78787879, 19.09090909, 19.39393939,
       19.6969697 , 20.        , 20.3030303 , 20.60606061, 20.90909091,
       21.21212121, 21.51515152, 21.81818182, 22.12121212, 22.42424242,
       22.72727273, 23.03030303, 23.33333333, 23.63636364, 23.93939394,
       24.24242424, 24.54545455, 24.84848485, 25.15151515, 25.45454545,
       25.75757576, 26.06060606, 26.36363636, 26.66666667, 26.96969697,
       27.27272727, 27.57575758, 27.87878788, 28.18181818, 28.48484848,
       28.78787879, 29.09090909, 29.39393939, 29.6969697 , 30.        ])
In [15]:
# CISV
y1 = np.sin(x1)
plt.plot(x1, y1)
Out[15]:
[<matplotlib.lines.Line2D at 0x7f80b45eb400>]
In [16]:
# CISV
y2 = x2**2
plt.plot(x2, y2)
Out[16]:
[<matplotlib.lines.Line2D at 0x7f80b45c3e10>]

We could have created the log plot by taking the log first.

In [17]:
# CISV
log_f = np.log10(frequency)
plt.plot(log_f, voltage)
Out[17]:
[<matplotlib.lines.Line2D at 0x7f80b452e6a0>]

V: What are Log Plots For?

CISV

If a graph looks linear on a semilog-y plot, then the independent variable depends exponentially on the dependent variable.

In [18]:
x3 = np.linspace(0, 5, 100)
y3 = 10**x3
plt.semilogy(x3, y3)
Out[18]:
[<matplotlib.lines.Line2D at 0x7f80b44da630>]

CISV

If a graph looks linear on a semilog-x plot, then the independent variable depends logarithmically on the dependent variable.

In [19]:
x4 = np.linspace(0.001, 5, 100)
y4 = np.log10(x4)
plt.semilogx(x4, y4)
Out[19]:
[<matplotlib.lines.Line2D at 0x7f80b4402be0>]

CISV

If an expression has the form

y=axm,
taking the logarithm gives
logy=mlogx+loga.

Then, defining X=logx, Y=logy, and b=loga, this is

Y=mX+b
which describes a straight line. So power-law functions appear linear on a log-log graph.

In [20]:
x5 = np.linspace(1, 30, 100)
y5a = 3*x5**2
y5b = 2*x5**3
plt.loglog(x5, y5a)
plt.loglog(x5, y5b)
plt.grid()

CISV

When x=1=100, then logx=X=0. Then

Y=logy=b=loga,
so y=a. Therefore, the coefficient a is given by the value of y when x=100.

Now select two points on the graph, so that

Y1=mX1+b
Y2=mX2+b.
Subtract them,
Y2Y1=m(X2X1),
and solve for m,
m=Y2Y1X2X1=logy2logy1logx2logx1=log(y2/y1)log(x2/x1)

In [21]:
m5a = np.log(y5a[99]/y5a[50])/np.log(x5[99]/x5[50])
m5a
Out[21]:
1.9999999999999996
In [22]:
m5b = np.log(y5b[99]/y5b[50])/np.log(x5[99]/x5[50])
m5b
Out[22]:
2.9999999999999996
In [ ]: