<jupyter_start><jupyter_text>`ES 157` Notebook 1: Introduction to Signals For the first few weeks we are going to learn how to work with basic signals, examine properties of signals and systems, and get some exposure to the Fourier analysis. Our work will culminate in an analysis and modeling of Massachusetts (or your home state's) COVID-19 data. ![covid_mass](https://i.imgur.com/bI1umxq.png)This week we'll start off by a basic introduction to `Python` and `numpy`. Then, we will create some fundamental signals and we'll perform basic operations on them. At the end of this notebook you will know1. the basics of `Python`'s syntax,2. how to create basic signals using `numpy`, and3. how to use `Python` to perform basic signal operations.First, let us import some of libraries that we will be using.<jupyter_code>import matplotlib.pyplot as plt
import numpy as np
import scipy.signal as signal<jupyter_output><empty_output><jupyter_text>Python introduction 🐍 `Python` is a high-level programming language that is easy to learn and experiment with. Let's familiarze ourselves with the syntax and basic operations.<jupyter_code># let's start with the cliché and print "Hello world!"
print("Hello world!")

# let's now exponentiate 2 by 3 and multiply by 42
print((2 ** 3) * 42)

# let's use `numpy` to create a random vector with 10 elements
x = np.random.randn(10)
print(x)<jupyter_output>Hello world!
336
[-1.61506016  1.78287078 -0.52509041  1.02896115 -1.23698001 -0.34639211
  0.77793917  0.97951207 -0.00823114 -0.36228497]<jupyter_text>In vanilla `Python` almost everything is treated as a _list_ of items. It is important to become proficient with `Python`'s _list slicing_ and _indexing_.<jupyter_code># from the random vector we created, we will print the first element
print(x[0])

# and the last element
print(x[-1])

# print the indices from 3 to 5
print(x[2:6])

# and all the indixes after 2
print(x[2:])

# print every other index
print(x[::2])

# print everything after the 3rd to last element
print(x[-3:])

# print, in reverse order, everything from the 3rd to last to the 3rd index
print(x[-3:2:-1])<jupyter_output>-1.6150601572925818
-0.36228496965888546
[ 1.02896115 -1.23698001]
[-0.52509041  1.02896115 -1.23698001 -0.34639211  0.77793917  0.97951207
 -0.00823114 -0.36228497]
[-1.61506016 -0.52509041 -1.23698001  0.77793917 -0.00823114]
[ 0.97951207 -0.00823114 -0.36228497]
[ 0.97951207  0.77793917 -0.34639211 -1.23698001]<jupyter_text>Fundamental signals Having familiarized oursleves with `Python` we will create and plot a few fundamental signals. We will set the number of samples to `N=20` for all the signals we'll create.<jupyter_code>N = 20

# create unit sample signal
x_1 = np.zeros((N,))
x_1[0] = 1

# plot the signal as a stemplot
plt.stem(x_1)
plt.title("$x_1[n]$")
plt.xlabel("$n$")
plt.ylabel("Amplitude")
# create a unit step
x_2 = np.ones((N,))

# plot the signal as a stemplot
plt.stem(x_2)
plt.title("$x_2[n]$")
plt.xlabel("$n$")
plt.ylabel("Amplitude")
# create an exponential signal
x_3 = range(N)
x_3 = [np.e ** (-0.125 * idx) for idx in x_3]

# plot the signal as a stemplot
plt.stem(x_3)
plt.title("$x_3[n]$")
plt.xlabel("$n$")
plt.ylabel("Amplitude")
# create a sinusoidal signal
x_4 = range(N)
x_4 = [np.sin(1  / 10 * np.pi * idx) for idx in x_4]

# plot the signal as a stemplot
plt.stem(x_4)
plt.title("$x_4[n]$")
plt.xlabel("$n$")
plt.ylabel("Amplitude")<jupyter_output>/usr/local/lib/python3.7/site-packages/ipykernel_launcher.py:6: UserWarning: In Matplotlib 3.3 individual lines on a stem plot will be added as a LineCollection instead of individual lines. This significantly improves the performance of a stem plot. To remove this warning and switch to the new behaviour, set the "use_line_collection" keyword argument to True.<jupyter_text>Signal operations Having computed some basic signals we will turn our attention to basic signal operations.<jupyter_code># add the exponential and the sinusoidal signal
x_5 = [x_3[idx] + x_4[idx] for idx in range(len(x_3))]

# plot the signal as stemplot
plt.stem(x_5)
plt.title("$x_5[n]$")
plt.xlabel("$n$")
plt.ylabel("Amplitude")
# delay the unit step by 5
x_delay = np.zeros((5,))
x_6 = np.concatenate((x_delay, x_2))

# plot the signal as stemplot
plt.stem(x_6)
plt.title("$x_6[n]$")
plt.xlabel("$n$")
plt.ylabel("Amplitude")<jupyter_output>/usr/local/lib/python3.7/site-packages/ipykernel_launcher.py:6: UserWarning: In Matplotlib 3.3 individual lines on a stem plot will be added as a LineCollection instead of individual lines. This significantly improves the performance of a stem plot. To remove this warning and switch to the new behaviour, set the "use_line_collection" keyword argument to True.
{'fraction_non_alphanumeric': 0.11014431239388794, 'fraction_numerical': 0.08828522920203735, 'mean_word_length': 4.906015037593985, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 17, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '174128', 'license': 'MIT', 'n_tokens_mistral': 1917, 'n_tokens_neox': 1636, 'n_words': 581, 'path': 'notebooks/es157_notebook1_solutions.ipynb', 'repo_name': 'manosth/es157-notebooks', 'size': '4712'}