Difference between revisions of "Pioneer21/Summaries"
(→Dr. G's Game of Averages) |
|||
(6 intermediate revisions by the same user not shown) | |||
Line 113: | Line 113: | ||
== Lecture 02 - 7/18/2021 == | == Lecture 02 - 7/18/2021 == | ||
+ | |||
+ | During this lecture, we did three main things: | ||
+ | * Developed a program meant to track and display multiple coin-flipping trials, | ||
+ | * Looked at some ways of using the scikit image library, and | ||
+ | * Wrote a program that started with an array of random images and evolved it using a particular rule for what each pixel will look like in the next evolution. | ||
+ | |||
+ | === Coin Flipping === | ||
+ | The end goal here is to perform multiple trials of a coin-flipping experiment where each trial consists of flipping a coin a certain number of times. We will be keeping track of how many times he flip "heads" on a fair, two-sided coin. | ||
+ | |||
+ | ==== Flipping One Coin ==== | ||
+ | Flipping a coin is a random process. We are going to assume a fair, two-sided coin and thus need to figure out how to represent "heads" and "tails" numerically. We ended up using np.random.randint() ([https://numpy.org/doc/stable/reference/random/generated/numpy.random.randint.html Documentation at numpy.org]) to generate a single random number that is either a 0 or a 1 and then returned a 1 if the coin was "heads" (or 1) and a 0 otherwise. Here are some helpful tutorials from [https://www.w3schools.com/python/default.asp w3schools' Python Tutorials]: | ||
+ | * [https://www.w3schools.com/python/python_functions.asp Python Functions] | ||
+ | * [https://www.w3schools.com/python/python_conditions.asp Python If...Else] | ||
+ | * [https://www.w3schools.com/python/numpy/numpy_array_indexing.asp NumPy Array Indexing] | ||
+ | * [https://www.w3schools.com/python/numpy/numpy_random.asp NumPy Random Intro] | ||
+ | ** Note - this module uses "from numpy import random" which means "random.X" will expand to "numpy.random.X" -- we used "import numpy as np" which means we would need to write "np.random.X" to get the same function. Any command in the tutorial that starts with "random." needs to start with "np.random." the way we do things. | ||
+ | |||
+ | ==== Flipping 100 Coins ==== | ||
+ | Now that we can flip a single coin, we want to flip 100 coins and track what the flips are and how many total ended up being heads. The "keeping track" part means starting an empty list and then appending each new flip to it. Once the list is completed, we can convert it into an array in case we want to do math on it. We can then use the .sum() method on the array to get the total number of 1's in the array. Here are some helpful tutorials from [https://www.w3schools.com/python/default.asp w3schools' Python Tutorials]: | ||
+ | * [https://www.w3schools.com/python/python_lists.asp Python Lists] | ||
+ | ** Also [https://www.w3schools.com/python/python_lists_add.asp Python - Add List Items] | ||
+ | * [https://www.w3schools.com/python/python_for_loops.asp Python For Loops] | ||
+ | |||
+ | |||
+ | ==== Flipping 100 Coins 200 Times ==== | ||
+ | Next, we can look at multiple 100-flip trials to see what each trial yields. The goal here is '''not''' to simply look at 20000 coin-flips but rather see how each trial differs from the next. We started by moving the trial mechanics to a function so I could just call it from main and get the number of times heads was flipped in a 100-flip trial. In the main program, we ran a loop that in some ways mirrored the trial structure - create an empty list and then loop some number of times, appending the new results to the list as we go along. We then looked at the information three different ways: | ||
+ | * Printing the total sum of heads across all trials, | ||
+ | * Bar chart of the number of heads in a trial as a function of the trial number, and | ||
+ | * Histogram of the number of heads in the trials. | ||
+ | Here are some helpful tutorials from [https://www.w3schools.com/python/default.asp w3schools' Python Tutorials]: | ||
+ | * [https://www.w3schools.com/python/matplotlib_bars.asp Matplotlib Bars] | ||
+ | * [https://www.w3schools.com/python/matplotlib_histograms.asp Matplotlib Histograms] | ||
+ | Also see the Pundit page on [[Python:Plotting]]. Note that the <syntaxhighlight lang=Python> | ||
+ | fig, ax = plt.subplots(num=1, clear=True)</syntaxhighlight> code in our program does two things at once: it creates a figure numbered as requested and assigns it to the variable <code>fig</code> and within that figure, it creates a set of axes we can use to make a plot and assigns it to the variable <code>ax</code>. To make a plot, tell <code>ax</code> what to do. If you want to make two figures, you can either re-use the variable names or create new ones; that is <syntaxhighlight lang=Python> | ||
+ | fig, ax = plt.subplots(num=1, clear=True) | ||
+ | PLOT 1 STUFF | ||
+ | fig, ax = plt.subplots(num=1, clear=True) | ||
+ | PLOT 2 STUFF</syntaxhighlight>works the same as<syntaxhighlight lang=Python> | ||
+ | fig1, ax1 = plt.subplots(num=1, clear=True) | ||
+ | PLOT 1 STUFF | ||
+ | fig2, ax2 = plt.subplots(num=2, clear=True) | ||
+ | PLOT 2 STUFF</syntaxhighlight>with the difference being you can still access the figure 1 variables even after creating figure 2. The two-step version of all of the above (which more closely resembles what is in the Pundit page on plotting) would be:<syntaxhighlight lang=Python> | ||
+ | fig = plt.figure(num=1, clear=True) | ||
+ | ax = fig.add_subplot(1, 1, 1) | ||
+ | PLOT 1 STUFF | ||
+ | fig = plt.figure(num=2, clear=True) | ||
+ | ax = fig.add_subplot(1, 1, 1) | ||
+ | PLOT 2 STUFF</syntaxhighlight>where the "1,1,1" part means "in this figure, make one row and one column of axes and give me access to the first one." See [[Python:Plotting#Example_Redux]] if you are interested in learning more on how to use more than one row or column of subplots. | ||
+ | |||
+ | ==== Flipping C Coins N Times ==== | ||
+ | Finally, if we want the program to be more flexible, we can use variables to store how many coins to flip each trial and how many trials to run. This involves giving a parameter to "run_trials()" that defaults to 100 flips per trial but can be overridden. Also, we can use a variable to store the number of trials to run - this will impact both the loop that runs the trials and the x-axis of the bar chart. | ||
+ | |||
+ | === Scikit-Image === | ||
+ | The scikit-image module ([https://scikit-image.org/ https://scikit-image.org/]), also called skimage, will be very helpful in loading, manipulating, displaying, and saving images! The module '''should''' be a part of your Anaconda distribution - if it is not, let me know and I can send installation instructions. | ||
+ | |||
+ | We looked at some very basic uses of skimage; here's the summary: | ||
+ | * Use <code>import skimage as ski</code> to bring the module in under the nickname "ski" | ||
+ | * The module has several built-in images; these are accessed with <code>ski.data.NAME()</code> where NAME is the name of the image. If you go to the [https://scikit-image.org/docs/stable/auto_examples/index.html General Examples] page, the "Data" section has a list of three categories of data files. If you want to view a picture (for example, the cat image in the general purpose images section), you can use: <syntaxhighlight lang=python> | ||
+ | fig, ax = plt.subplots(num=1, clear=True) | ||
+ | cat_data = ski.data.cat() | ||
+ | ski.io.imshow(cat_data)</syntaxhighlight> where <code>cat_data</code> is an arbitrary variable name but everything else needs to match. Next week, we will see that there are different ways skimage stores information, including boolean (True/False), integers, and floats. | ||
+ | * <code>ski.io.imshow(IMG_DATA)</code> will display the image in the currently-active set of axes. | ||
+ | * Later we will see how to load and save images and manipulate the content. | ||
+ | |||
+ | === Evolving an Image === | ||
+ | Part of the homework will be to write a program that simulates [https://playgameoflife.com/ John Conway's Game of Life]. | ||
+ | |||
+ | ==== John Conway's Game of Life ==== | ||
+ | The general idea is that an array is initially populated with a series of 0's and 1's. There is an algorithm (in the Explanation section of the web page) for determining whether an individual entry in the array will be a 1 or a zero in the next iteration. Another way of stating the algorithm is as follows: | ||
+ | * Each pixel that is currently a 1 will be a 1 in the next iteration when two or three of its eight neighbors are also 1 | ||
+ | * Each pixel that is currently a 0 will be a 1 in the next iteration when three of its eight neighbors are 1 | ||
+ | * Any pixel not satisfying the above rules will be a 0 in the next round. | ||
+ | |||
+ | ==== Dr. G's Game of Averages ==== | ||
+ | To help you achieve your goal, I have written a program that has a decidedly different algorithm: an entry in the array will be the rounded version of the average value of its 3x3 neighborhood in the next iteration. Regardless of whether an entry is currently a 1 or a 0, that entry will be whatever value is in the majority of those nine values in the next iteration. Here's the breakdown of the program: | ||
+ | ===== evolve ===== | ||
+ | The evolve function will be given a 3x3 neighborhood from the array and returns what the value of the center of the neighborhood should be in the next round. At the moment, all it does is calculates the average of the 9 entries (which should range from 0/9 to 1/9) and rounds it; 4/9 and below go to 0 and 5/9 and above go to 1. | ||
+ | |||
+ | This will be the only function you actually need to change to create John Conway's Game of Life. I will explain the rest of the program, but your focus should be on this function. | ||
+ | |||
+ | ===== edges ===== | ||
+ | This part is a little complicated. When looking at neighborhood-based algorithms, you have to be careful about the edge and corner cases. What I decided to do is this: | ||
+ | * Assume the "active" array entries are from the second to second-to-last row and second to second-to-last (penultimate) column. That is - the extreme top, bottom, left, and right are not really participants. | ||
+ | * Make the array "wrap" so an array entry in the second row includes in its neighborhood the array entries in the penultimate row; an array entry in the penultimate column includes entries in the second column. The easiest way to do this is to: | ||
+ | ** Update the active array entries | ||
+ | ** Copy the penultimate row into the top row and copy the second row into the last row | ||
+ | ** Copy the penultimate column into the left column and copy the second column into the last column | ||
+ | : This means that when I slice an active entry's 3x3 neighborhood, the ones along the edge will see their neighbors on the other side of the array! | ||
+ | * As I said, it is a little complicated... | ||
+ | |||
+ | ===== main ===== | ||
+ | The main program: | ||
+ | * Sets variable to store the number of rows and columns for the image | ||
+ | * Generates a random array of integers [0, 1] | ||
+ | * Displays this array twice - the second time, assigning the image to a variable which will allow more efficient updating later for the animation | ||
+ | * Runs a loop up to N times updating the image. This loop will make a copy of the values in an array oldimg so new entries are not used in the evolution. Then it: | ||
+ | ** Goes through each active row, and for each active row, | ||
+ | *** Goes through each active column. For each active row and column, | ||
+ | **** Make a 3x3 slice of the array made from the rows above and below the entry and columns left and right of the entry | ||
+ | **** Send that slice to the evolve function and put the result in the img array | ||
+ | **** This will update all the active entries in img | ||
+ | * Once the active entries are updated, fix the edges so the new image wraps | ||
+ | * Change the data in the second figure to reflect the new image | ||
+ | * Check if the image has changed -- if all the entries are the same before and after evolution, break out of the loop early |
Latest revision as of 03:20, 19 July 2021
This page will provide lecture summaries for the Pioneer Academics Summer 2021 course on Python and Image Processing.
Lecture 01 - 7/11/2021
- Introductions
- Overleaf Document Preparation System
- Once you have your account, you can go to the Learn LaTeX in 30 minutes tutorial on Overleaf!
- Free document preparation system using $$\LaTeX$$
- Website: https://www.overleaf.com; sign up for a free account
- To create the sample document:
- Create a new blank project in Overleaf and call it whatever you would like (I called mine PioneerDemo); this will move that tab to the project page with a "main.tex" document
- Open another tab and get the demonstration files in a zip file either on Schoology->.Session 01 Items->Sample LaTeX Docs->PioneerDemo.zip or on the Box drive in the Session 01 LaTeX folder
- Expand the zip file to get three files: main.tex, MakeSample.py, and SamplePyplot.png
- Go back to the Overleaf tab for your project.
- Click the upload icon (third one from the left under the Menu icon at the top left of the screen)
- Drag the three files into the window or select them. When Overleaf asks if you want to overwrite the main.tex, select Overwrite to replace the default with the sample.
- On the right-half of the Overleaf window, click the Recompile button to see the sample file
- The document infrastructure will always be provided; this includes something similar to the following:
\documentclass{article} \usepackage{amsmath} % loads AMS-Math package \usepackage{graphicx} % bring in graphics \usepackage{listings} % allows lstlisting environment \usepackage{hyperref} % web page links \usepackage[letterpaper, margin=1.0in]{geometry} % set paper size/margins \begin{document} \begin{center} \rule{6.5in}{0.5mm}\\~\\ \textbf{\large Pioneer - Summer 2021}\\~\\ \textbf{\huge TITLE}\\~\\ NAME\\ \rule{6.5in}{0.5mm}\\ \end{center} \tableofcontents \listoffigures \pagebreak % Repeat as needed \section{SECTION ONE} Stuff \pagebreak \appendix \section{Code} % Repeat as needed - uncomment the lstinputlisting lines when the codes exists \subsection{CODE DESCRIPTION 1} %%%\lstinputlisting[language=Python]{code1.py} \pagebreak \section{Figures \label{FigureList}} % Repeat as needed - uncomment the lstinputlisting lines when the graphs exists \begin{figure}[!h] \begin{center} %%%\includegraphics[width=5in]{figure1.png} \caption{FIGURE 1 CAPTION.} \end{center} \end{figure} \pagebreak \addcontentsline{toc}{section}{References} \begin{thebibliography}{9} % Repeat as needed \bibitem{REFCODE1} REFERENCE 1 \end{thebibliography} \end{document}
- Some commands from the demonstration document:
- Document divisions
- Use
\section{NAME}
and \subsection{NAME} to divide your document
- Use
- Text styles and sizes - for more info see https://www.overleaf.com/learn/latex/Font_sizes,_families,_and_styles
- Text can be made bold, italics, or typewriter font with
\textbf{WORDS}
,\textit{WORDS}
, or\texttt{WORDS}
- Text can be made large or huge with
{\large WORDS}
or{\huge WORDS}
; there are other sizes available
- Text can be made bold, italics, or typewriter font with
- Use
\begin{center} ... \end{center}
to center items such as text, figures, and tables - Use $ ... $ to put math-mode items in a sentence - for instance "The hypotenuse $$c$$ or a right triangle with sides $$a$$ and $$b$$ is $$c=\sqrt{a^2+b^2}$$" which was produced by the code
The hypotenuse $c$ or a right triangle with sides $a$ and $b$ is $c=\sqrt{a^2+b^2}$
. (Note: on Pundit, getting into math mode requires to dollar signs instead of one). - Use
\begin{align} ... \end{align}
for a series of numbered, formal equations and\begin{align} ... \end{align}
for a series of unnumbered, formal equations. For example: $$ \begin{align} \begin{bmatrix} A \end{bmatrix} &= \begin{bmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \end{bmatrix}\\ D &= \begin{vmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \end{vmatrix} =a_{11}a_{22}-a_{12}a_{21} \end{align} $$ which was created with\begin{align} \begin{bmatrix} A \end{bmatrix} &= \begin{bmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \end{bmatrix}\\ D &= \begin{vmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \end{vmatrix} =a_{11}a_{22}-a_{12}a_{21} \end{align}
- Superscripts and subscripts are in math mode; if the superscript or subscript is more than one character, the superscript or subscript needs to be enclosed in curly brackets.
- If you are unsure of the command for a specific symbol, Detexify can be a real help!
- Some math terms should be in regular type versus italics in math mode; they have command versions like
\max
. Otherwise, if you need regular text in math mode, use\mbox{WORDS}
- $$\LaTeX$$ can import text files using
\lstinputlisting[OPTION]{FILE}
where the main option is to set the language; for example:\lstinputlisting[language=Python]{MakeSample.py}
- $$\LaTeX$$ can also import graphics files using
\includegraphics[OPTION]{file}
where the main option is to set the width or height of the imagel for example:\includegraphics[width=5in]{SamplePyplot.png}
- Document divisions
- Anaconda and Python
- Website: Anaconda; download the appropriate version for your operating system.
- Long way to open Spyder is to start Anaconda Navigator and launch Spyder from there; shortcut on Windows is to go to Start -> Anaconda -> Spyder
Lecture 02 - 7/18/2021
During this lecture, we did three main things:
- Developed a program meant to track and display multiple coin-flipping trials,
- Looked at some ways of using the scikit image library, and
- Wrote a program that started with an array of random images and evolved it using a particular rule for what each pixel will look like in the next evolution.
Coin Flipping
The end goal here is to perform multiple trials of a coin-flipping experiment where each trial consists of flipping a coin a certain number of times. We will be keeping track of how many times he flip "heads" on a fair, two-sided coin.
Flipping One Coin
Flipping a coin is a random process. We are going to assume a fair, two-sided coin and thus need to figure out how to represent "heads" and "tails" numerically. We ended up using np.random.randint() (Documentation at numpy.org) to generate a single random number that is either a 0 or a 1 and then returned a 1 if the coin was "heads" (or 1) and a 0 otherwise. Here are some helpful tutorials from w3schools' Python Tutorials:
- Python Functions
- Python If...Else
- NumPy Array Indexing
- NumPy Random Intro
- Note - this module uses "from numpy import random" which means "random.X" will expand to "numpy.random.X" -- we used "import numpy as np" which means we would need to write "np.random.X" to get the same function. Any command in the tutorial that starts with "random." needs to start with "np.random." the way we do things.
Flipping 100 Coins
Now that we can flip a single coin, we want to flip 100 coins and track what the flips are and how many total ended up being heads. The "keeping track" part means starting an empty list and then appending each new flip to it. Once the list is completed, we can convert it into an array in case we want to do math on it. We can then use the .sum() method on the array to get the total number of 1's in the array. Here are some helpful tutorials from w3schools' Python Tutorials:
Flipping 100 Coins 200 Times
Next, we can look at multiple 100-flip trials to see what each trial yields. The goal here is not to simply look at 20000 coin-flips but rather see how each trial differs from the next. We started by moving the trial mechanics to a function so I could just call it from main and get the number of times heads was flipped in a 100-flip trial. In the main program, we ran a loop that in some ways mirrored the trial structure - create an empty list and then loop some number of times, appending the new results to the list as we go along. We then looked at the information three different ways:
- Printing the total sum of heads across all trials,
- Bar chart of the number of heads in a trial as a function of the trial number, and
- Histogram of the number of heads in the trials.
Here are some helpful tutorials from w3schools' Python Tutorials:
Also see the Pundit page on Python:Plotting. Note that the
fig, ax = plt.subplots(num=1, clear=True)
code in our program does two things at once: it creates a figure numbered as requested and assigns it to the variable fig
and within that figure, it creates a set of axes we can use to make a plot and assigns it to the variable ax
. To make a plot, tell ax
what to do. If you want to make two figures, you can either re-use the variable names or create new ones; that is
fig, ax = plt.subplots(num=1, clear=True)
PLOT 1 STUFF
fig, ax = plt.subplots(num=1, clear=True)
PLOT 2 STUFF
works the same as
fig1, ax1 = plt.subplots(num=1, clear=True)
PLOT 1 STUFF
fig2, ax2 = plt.subplots(num=2, clear=True)
PLOT 2 STUFF
with the difference being you can still access the figure 1 variables even after creating figure 2. The two-step version of all of the above (which more closely resembles what is in the Pundit page on plotting) would be:
fig = plt.figure(num=1, clear=True)
ax = fig.add_subplot(1, 1, 1)
PLOT 1 STUFF
fig = plt.figure(num=2, clear=True)
ax = fig.add_subplot(1, 1, 1)
PLOT 2 STUFF
where the "1,1,1" part means "in this figure, make one row and one column of axes and give me access to the first one." See Python:Plotting#Example_Redux if you are interested in learning more on how to use more than one row or column of subplots.
Flipping C Coins N Times
Finally, if we want the program to be more flexible, we can use variables to store how many coins to flip each trial and how many trials to run. This involves giving a parameter to "run_trials()" that defaults to 100 flips per trial but can be overridden. Also, we can use a variable to store the number of trials to run - this will impact both the loop that runs the trials and the x-axis of the bar chart.
Scikit-Image
The scikit-image module (https://scikit-image.org/), also called skimage, will be very helpful in loading, manipulating, displaying, and saving images! The module should be a part of your Anaconda distribution - if it is not, let me know and I can send installation instructions.
We looked at some very basic uses of skimage; here's the summary:
- Use
import skimage as ski
to bring the module in under the nickname "ski" - The module has several built-in images; these are accessed with
ski.data.NAME()
where NAME is the name of the image. If you go to the General Examples page, the "Data" section has a list of three categories of data files. If you want to view a picture (for example, the cat image in the general purpose images section), you can use:wherefig, ax = plt.subplots(num=1, clear=True) cat_data = ski.data.cat() ski.io.imshow(cat_data)
cat_data
is an arbitrary variable name but everything else needs to match. Next week, we will see that there are different ways skimage stores information, including boolean (True/False), integers, and floats. ski.io.imshow(IMG_DATA)
will display the image in the currently-active set of axes.- Later we will see how to load and save images and manipulate the content.
Evolving an Image
Part of the homework will be to write a program that simulates John Conway's Game of Life.
John Conway's Game of Life
The general idea is that an array is initially populated with a series of 0's and 1's. There is an algorithm (in the Explanation section of the web page) for determining whether an individual entry in the array will be a 1 or a zero in the next iteration. Another way of stating the algorithm is as follows:
- Each pixel that is currently a 1 will be a 1 in the next iteration when two or three of its eight neighbors are also 1
- Each pixel that is currently a 0 will be a 1 in the next iteration when three of its eight neighbors are 1
- Any pixel not satisfying the above rules will be a 0 in the next round.
Dr. G's Game of Averages
To help you achieve your goal, I have written a program that has a decidedly different algorithm: an entry in the array will be the rounded version of the average value of its 3x3 neighborhood in the next iteration. Regardless of whether an entry is currently a 1 or a 0, that entry will be whatever value is in the majority of those nine values in the next iteration. Here's the breakdown of the program:
evolve
The evolve function will be given a 3x3 neighborhood from the array and returns what the value of the center of the neighborhood should be in the next round. At the moment, all it does is calculates the average of the 9 entries (which should range from 0/9 to 1/9) and rounds it; 4/9 and below go to 0 and 5/9 and above go to 1.
This will be the only function you actually need to change to create John Conway's Game of Life. I will explain the rest of the program, but your focus should be on this function.
edges
This part is a little complicated. When looking at neighborhood-based algorithms, you have to be careful about the edge and corner cases. What I decided to do is this:
- Assume the "active" array entries are from the second to second-to-last row and second to second-to-last (penultimate) column. That is - the extreme top, bottom, left, and right are not really participants.
- Make the array "wrap" so an array entry in the second row includes in its neighborhood the array entries in the penultimate row; an array entry in the penultimate column includes entries in the second column. The easiest way to do this is to:
- Update the active array entries
- Copy the penultimate row into the top row and copy the second row into the last row
- Copy the penultimate column into the left column and copy the second column into the last column
- This means that when I slice an active entry's 3x3 neighborhood, the ones along the edge will see their neighbors on the other side of the array!
- As I said, it is a little complicated...
main
The main program:
- Sets variable to store the number of rows and columns for the image
- Generates a random array of integers [0, 1]
- Displays this array twice - the second time, assigning the image to a variable which will allow more efficient updating later for the animation
- Runs a loop up to N times updating the image. This loop will make a copy of the values in an array oldimg so new entries are not used in the evolution. Then it:
- Goes through each active row, and for each active row,
- Goes through each active column. For each active row and column,
- Make a 3x3 slice of the array made from the rows above and below the entry and columns left and right of the entry
- Send that slice to the evolve function and put the result in the img array
- This will update all the active entries in img
- Goes through each active column. For each active row and column,
- Goes through each active row, and for each active row,
- Once the active entries are updated, fix the edges so the new image wraps
- Change the data in the second figure to reflect the new image
- Check if the image has changed -- if all the entries are the same before and after evolution, break out of the loop early