Post No. 12.25i: And Now Let’s Explore Irrational Numbers

Advertisements

Hello everybody,

Michael here, and today for my 150th post I thought I’d do something a little different. This time, something with a broader scope-irrational nnumbers in programming. This isn’t my first broad post (after all, I did Colors in Programming in November 2021) but hey, what better time for a broad post than my 150th overall post (did anyone catch the little detail in the post title hinting that this is the 150th post?).

But first, a little bit on irrational numbers

What are irrational numbers? You’ve likely heard of them if you’ve at least taken pre-algebra but for those who don’t know or don’t remember, irrational numbers arre simply numbers that can’t be expressed as fractions.

What would a rational number look like? Well, think of the 10 cardinal digits from 0-9, any fraction like 1/2 or 3/4 (yes, even improper fractions count as rational numbers), and any decimal (regardless of how many decimal points it carries on for).

Now, what would an irrational number look like? I went over one of them in an earlier post-e, also known as Euler’s Number-2.71828 (check out R Lesson 30: Logarithms for more information on Euler’s number). Some other irrational numbers include imaginary numbers (like 3+2i), the square roots of all negative numbers and positive prime numbers (which in turn are imaginary), the golden ratio, and of course, PI.

Imaginary numbers are their own special type of irrational numbers-and it’s important to remember that while all imaginary numbers are irrational, not all irrational numbers are imaginary. Imaginary numbers always contains an imaginary unit i (though the letter used to represent that imaginary unity can vary-more on that later), which equals the square root of -1.

If a number has a “real” and “imaginary” part, then it is referred to as a complex number. Take the complex number 3+2i. The 3 would be the “real” part and the 2i would be the “imaginary” part. The same logic would apply for a number like 6i, which, even though it only has a single part, is still consered a complex number since it has a “real” part (a hidden 0) and an imaginary part (6i).

  • For those that haven’t heard of the golden ratio before, it’s a mathematical constant (just like PI and e) that equals approximately 1.61 that is often considered as an “aesthethically pleasing” number as it can be found in fields such as art, nature, and photography (just to name a few).

Imaginary numbers in programming

Now that we’ve discussed the basics of what irrational numbers are all about, let’s explore how they’re used in programming.

First off, let’s see how imaginary numbers work in programming! Here’s an example of simple arthemic with imaginary numbers in Python:

print(3+5j*2)
(3+10j)

As you can see, Python is capabale of simple arithmetic with imaginary numbers (and is likely capabale of far more complex mathematics too). Also notice how Python uses a j to represent an imaginary unit instead of an i. Watch what happens when you try to use an i in this expression:

print(3+5i*2)

File "<ipython-input-5-f1daeb22835b>", line 1
    print(3+5i*2)
             ^
SyntaxError: invalid syntax

Trying to use i for the imaganiary unit will give you an error in this expression.

Now let’s take a look at how imaginary numbers are used in other programming languages! Here are two examples of expressions with imaginary numbers in R:

(3i+4)*(2i-2)
[1] -14+2i

(7i+3)-2
[1] 1+7i

As you can see, R-just like Python-performs simple imaginary number arithmetic quite well. One interesting quirk with R is that, no matter how I list the imaginary number in my expression (whether in the form of real part+imaginary part or imaginary part+real part), the output always displays in the form of real part+imaginary part.

Other irrational numbers in programming

Now that we briefly explored imaginary numbers in programming, let’s turn our attention to other irrational numbers!

Here’s an example in Python showing how to obtain irrational, non-imaginary numbers-e, the golden ratio, and pi.

from scipy import constants

from scipy import constants

print(constants.golden)
print(constants.pi)
print(constants.e)

1.618033988749895
3.141592653589793
1.602176634e-19

In case you ever wanted to use pi, the golden ratio (denoted by scipy.constants.golden) or e in any Python calculation, you now know the easy way to access these irrational numbers-just use the from scipy import constants line to access the scipy constants package and then use the line scipy.constants.[number you wish to use] to access the irrational number.

If that seems cool, here are some other constants the scipy.constants package contains (and there are far too many to mention for this entry):

from scipy import constants

print(constants.year)
print(constants.day)
print(constants.hour)

31536000.0
86400.0
3600.0

These are just a few of the time constants that the scipy.constants module contains-and in case your wondering why year gave you such a large number, it’s because these time constants are stored in seconds (yes, a year contains 31,536,000 seconds). The same logic applies as to why day and hour give you seemingly odd large numbers.

  • In case you’re wondering what the SciPy package is, first of all, it stands for scientific Python. The SciPy package is built upon the numpy package and contains various helpful mathematical and scientific functions and modules (like the constants module we just discussed).
  • Let me know if you all would like to see a SciPy series of lessons!

Now that we know how to obtain these irrational, non-imaginary numbers in Python, let’s see them used in computations!

print(constants.golden*3)
print(constants.pi**2)
print(constants.e/100)

4.854101966249685
9.869604401089358
1.6021766339999998e-21

Not gonna lie, Python’s computations of these mathematical constants work like a charm!

Now to explore these irrational, non-imaginary numbers in another programming language! I know, let’s try Java!

import java.lang.Math;

public class Numbers {


    public static void main(String[] args) {
        long product = (long) (Math.E*6);
        long sum = (long) (Math.PI+12);
        
        System.out.println(product);
        System.out.println(Math.PI);
        System.out.println(Math.E);
        System.out.println(sum);
        
    }
    
}

16
3.141592653589793
2.718281828459045
15

As you can see, Java can be a bit trickier than R or Python when it comes to working with irrational numbers (after all, there are more imports necessary-plus the need to cast any expression with these irrational numbers into type long)!

Another thing you’ll notice with Java is that in it’s Math class, PI and E are the only irrational number constants built-in to the class. The golden ratio isn’t built in, but you can easily define it. Here’s how you’d do so in Java:

long golden = (long) (1+Math.sqrt(5)/2)

And here’s what that number would look like in the output:

2

For some odd reason, if I use a long for my golden ratio in Java, I get a 2 in return (even though the approximation of the golden ratio equals 1.61).

Thanks for reading these past 12.25i posts. Here’s to the next 12.25i!

Michael

R Lesson 31: Logarithmic Graphs

Advertisements

Hello everybody,

Michael here, and today’s lesson will be a sort-of contiuation of my previous post on R logarithms (R Lesson 30: Logarithms). However, in this post, I’ll cover how to create logarithmic graphs in R.

Let’s begin!

Two types of log plots

There are two main types of logarithmic plots in R-logarithmic scale plots and log-log plots.

What do these log plots do, exactly? Well, in the case of the logarithmic scale plot, only one of the plot’s axes uses a logarithmic scale while the other maintains a linear scale while with the log-log plot both axes use logarithmic scales.

When would you use each type of plot? In the case of logarithmic scale plots, you’d use them for analyses such as exponential growth/decay or percentage changes over a period of time. As for the log-log plots, they’re better suited for analyses such as comparative analyses (which involve comparing datasets with different scales or units) and data where both the x-axis and y-axis have a wide range of values.

And now for the logarithmic scale plots!

Just as the header says, let’s create a logarithmic scale plot in R!

Before we begin, let’s be sure we have the necessary data that we’ll use for this analysis-

This dataset is simpler than most I’ve worked with on this blog, as it only contains 11 rows and two columns. Here’s what each column means:

  • Date-the date that the bitcoin mining reward will be halved
  • Reward-the amount of bitcoin you will recieve after a successful mining as of the halving date.

For those unfamiliar with basic Bitcoin mining, the gist of the process is that when you mine Bitcoin you get a reward. However, after every 3-4 years, the reward for mining bitcoin is halved. For instance, on the first ever day that you could mine bitcoin (January 1, 2009), you would be able to recieve 50 bitcoin (BTC) for a successful haul. In 2012, the reward was halved to 25 BTC for a successful haul. The next halving is scheduled to occur in Spring 2024, where the reward will be halved to 3.125 BTC. Guess bitcoin mining isn’t as profitable as it was nearly 15 years ago?

There will likely be no more Bitcoin left to mine by the year 2140, so between now and then, the Bitcoin mining reward will get progressively smaller (a perfect example of exponential decay). I did mention that the Bitcoin mining reward will be less than 1 BTC by the year 2032.

  • I mean, don’t take my word for it, but maybe the supply of mineable bitcoin won’t run out by 2140 and the reward instead would get progressively smaller until it’s well over 1/1,000,000,000th of 1 BTC. Just my theory though :-).

Now, enough about the Bitcoin mining-let’s see some logarithmic scale plots! Take a look at the code below.

First, we’ll read in our dataset:

bitcoin <- read.csv("C:/Users/mof39/OneDrive/Documents/bitcoin halving.csv", encoding="utf-8")

Next, we’ll create our logarithmic scale plot! Since there is only one axis that will use a log-scale (the y-axis in this case), let’s remember to specify that

plot(bitcoin$Date, bitcoin$Reward, log = "y", pch = 16, col = "blue", xlab = "Year", ylab = "BTC Reward")

Error in plot.window(...) : need finite 'xlim' values
In addition: Warning messages:
1: In xy.coords(x, y, xlabel, ylabel, log) : NAs introduced by coercion
2: In min(x) : no non-missing arguments to min; returning Inf
3: In max(x) : no non-missing arguments to max; returning -Inf

If we tried to use the plot() function along with all the parameters specified here, we’d get this error-Error in plot.window(...) : need finite 'xlim' values. Why does this occur?

The simple reason we got the error is because we used a column with non-numeric values for the x-axis. How do we fix this? Let’s create a numeric vector of the years in the Reward column (with the exception of 2009, all of the years in the Reward column are leap years until 2080) and use that vector as the x-axis:

years <- c(2009, 2012, 2016, 2020, 2024, 2028, 2032, 2036, 2040, 2044, 2048, 2052, 2056, 2060, 2064, 2068, 2072, 2076, 2080)

plot(years, bitcoin$Reward, log = "y", pch = 16, col = "red", xlab = "Year", ylab = "BTC Reward", main = "BTC Rewards 2009-2080")

Voila! As you can see here, we have a nice log-scale plot showing the exponential decay in bitcoin mining rewards from 2009 to 2080.

How did create this nice-looking plot? Well, we set the value of the log parameter of the plot() function equal to y, as we are only creating a log-scale plot. If we wanted to create a log-log plot, we would se the value of the log parameter to xy, which would indicate that we would use a logarithmic scale for both the x and y axes.

As for the rest of the values of the parameters in the plot() function, keep them the same as you would for a normal, non-logarithmic R scatter plot (except of course adapting your x- and y-axes and title to fit the scatterplot).

Now, one thing I did want to address on this graph is the scale on the y-axis, which might look strange to you if you’re not familiar with log-scale plots. See, the plot() function’s log parameter in R uses base-10 logs by default, and in turn, the y-axis will use powers of 10 in the scale (the scientific notation makes the display a little neater). For instance, 1e+01 represents 10, 1e+00 represents 0, and so on. Don’t worry, all the data points in the dataset were plotted correctly here.

And now, let’s create a log-log plot

Now that we’ve created a log-scale plot, it’s time to explore how to create a log-log plot in R!

loglog <- data.frame(x=c(2, 4, 8, 16, 32, 64), y=c(3, 9, 27, 81, 243, 729))

plot(loglog$x, loglog$y, log = "xy", pch = 16, col = "red", xlab = "Power of 2", ylab = "Power of 3", main = "Sample Log-Log Plot")

In this example, I created the dataframe loglog and filled both axes with powers of 2 and 3 to provide a simple way to demonstrate the creation of log-log plots in R.

As for the plot() function, I made sure to set the value of the log parameter to xy since we’re creating a log-log plot and thus need both axes to use a logarithmic scale. Aside from that, remember to change the plot’s axes, labels, and titles as appropriate for your plot.

Now, you might’ve noticed somthing about this graph. In R, both log-log and log-scale plots utilize base-10 logs for creating the plot. However, you likely noticed that the scale for the log-scale plot displays its values using scientific notation and powers of 10. The scale (or should I say scales) for the log-log plot doesn’t use scientifc notation and powers of 10 to display its values. Rather, the log-log plot uses conventional scaling to display its values-in other words, the scale for the log-log plot bases its values of the range of values in both axes rather than a powers-of-10 system. Honestly, I think this makes sense since the plot is already using a logarithmic scale for both axes, which would make the whole powers-of-10 thing in the scale values redundant.

  • Of course, if you want to change the scale displays in either the log-log or log-scale plots, all you would need to do is utilize the axis() function in R after creating the plot. Doing so would allow you to customize your plot’s axis displays to your liking.

Thnaks for reading!

Michael

R Lesson 30: Logarithms

Advertisements

Hello everybody,

Michael here, and I hope you had a little fun with my two-part fifth anniversary post (particularly the puzzle). For the next several posts, we’ll be exploring more mathematics with R. Today we’ll discuss the wonderful world of R logarithms (and logarithms in general).

But first, a word on logarithms

As you may have noticed from my previous R mathematics posts, I always explain the concept in both the context of R and the context of manual calculation so you can have a basic understanding of the concept itself before exploring it in R. I plan to do the same thing here today.

So, what are logarithms exactly? They’re simply another type of mathematical operation. Take a look at this illustration below:

In this example, I used the simple example of 4^3=64. I also noted that the logarithmic form of this expression is log4(64)=3.

What does this mean? The base of the exponentional expression (4^3=64)-4-serves at the base of the logarithm. The number in parentheses-64-serves as the logarithm’s argument, which is the value used to find the logarithm’s exponent (or result), which is 3 in this case.

How do you read a logarithmic expression? In this example, you’d read the expression as log base-4 of 64 equals 3.

That’s how logarithms work. Now let’s explore how to manage them in R.

Logarithms, R style

So, how would you work with logarithms in R? Let’s take a look at the code below:

> log(32, base=2)
[1] 5

Calculating logs in R is as simple as using R’s built-in log() function and adding in two parameters-the argument and the base. With these two parameters and the log() function, R will return the logarithm’s exponent, which in this case is 5 since log base-2 of 32 equals 5.

However, what I discussed above was a very, very basic example of logarithms in R. Let’s look a more specific scenario:

> log10(1000)
[1] 3

In this example, I’m showing what is known as a base-10 logarithm, which is a logarithm with a base of, well, 10. In this case, I’m showing you what log base-10 of 1000 equals-in this case, the exponent/result is 3. Notice how this expression uses the log10() function rather than the log() function.

  • NOTE: To calculate this base-10 logarithm you can also use the code log(1000, base=10), but I just wanted to point out the log10() function as another way to solve a logarithm.

Another specific logarithmic scenario is binary, or base-2 logarithms. Let’s take a look at those:

> log2(8)
[1] 3

In this example, I’m showing a base-2 logarithm, which is a logarithm with a base of, well, 2. In this case, the base-2 log of 8 is 3, since 2^3=8.

  • NOTE: Just like the case with the base-10 logarithm, you can also use the code log(8, base=2) to calculate this base-2 algorithm.

Now let’s explore two rather unusual logarithmic scenarios-logarithms of imaginary numbers and logarithms with base e (e as in the mathematical constant). What does it all mean? Let me explain!

> log(11)
[1] 2.397895

> log(3+2i, base=4)
[1] 0.9251099+0.4241542i

The first example above shows the expression log base-e of 11 along with the result, 2.397895. The second example shows the expression log base-4 of 3+2i along with the result, 0.9251099+0.4241542i.

If you’re not familiar with the concept of imaginary numbers and the mathematical constant e, fear not, for I will explain both concepts right here!

A rational section about some irrational numbers

If you have even the most basic knowledge of numbers, you’ll be quite familiar with the numbers 0-9. After all, when you first learned basic counting, you very likely learned how to count to 10.

However, if you study more advanced math (like alegbra and calculus), you’ll notice there are a few numbers that aren’t so neat (and no, I’m not talking about decimals, fractions or negative numbers). These are known as irrational numbers, which are non-terminating, non-repeating numbers that can’t be expressed as simple fractions or decimals.

We just explored calculating logarithms with two types of irrational numbers-e and imaginary numbers. How do these types of numbers work?

In the case of e, e is a mathematical constant known as Euler’s number-named after Swiss mathematician Leonhard Euler. e is an irrational number that stretches on for infinity, but its approximate value is 2.71828. The number e is used in various exponential growth/decay functions, such as a city’s population growth/decline over a certain time period or a substance’s radioactive decay.

A log with a base e is also known as a natural logarithm (like the log(11) example I used earlier). In R, natural logarithms are denoted as log(number) with no base parameter specified. Here’s what natural logarithms look like:

In the case of the imaginary numbers, such as 3+2i, they provide an easy way to handle complex mathematical situations, such as the square roots of negative numbers (which will always be imaginary numbers). Imaginary numbers always consist of a real part multiplied by the imaginary unit i (such as 2i). The number 3+2i is known as a complex imaginary number since it has a real part (3) and an imaginary part (2i). One well known application of imaginary numbers is fractal geometry, which you can find quite a bit of in nature (like in conch shells).

Three more logarithmic scenarios that I think you should know

Before I go, I want to discuss three more logarithmic scenarios with you all, first with natural logs and then with logs that have a numerical base. Take a look at the code below:

> log(1)
[1] 0
> log(0)
[1] -Inf
> log(-12)
[1] NaN
Warning message:
In log(-12) : NaNs produced

In this example, I’m showing you three different natural logarithm scenarios that you should know-logs with arguments of 1, 0, and a negative number. Notice that a log with an argument of 1 yields 0, an log with an argument of 0 yields negative infinity and a log with a negative number yields an NaN (indicating that logs with negative arguments aren’t valid). Why might this be the case?

  • In the case of log(1), you get 0 because raising a number to the power of 0 always yields 1.
  • In the case of log(0), you get -Inf (negative infinity) because there is no possible power you can raise e to obtain 0.
  • In the case of log(-12), you get NaN (not a number) because logs only work with positive number arguments.

Now here are three scenarios with the same arguments, but using a base of 2 instead of e:

> log(1, base=2)
[1] 0
> log(0, base=2)
[1] -Inf
> log(-12, base=2)
[1] NaN
Warning message:
NaNs produced 

Notice how you get the same results here as you did for the natural logarithms.

Thanks for reading,

Michael

The Glorious Five-Year Plan Part Two

Advertisements

Hello everybody!

Michael here, and as I previously promised, here’s part 2 of my five-year blog anniversary post (after all, what fun it is to split the 5-year anniversary letter into two posts).

My Answer To Michael’s Five-Year Coding Challenge

Also, as I previously promised on the last post, I will discuss my solution for Michael’s Five-Year Coding Challenge. I know I said you can use any programming language you like for this challenge (as long as your code follows certain criteria outlined in my previous post https://michaelsprogrammingbytes.com/2023/06/13/the-glorious-five-year-plan-part-one/), but here’s my approach using Python!

secretCode = {'Z': 'A', 'Y': 'B', 'X': 'C', 'W': 'D', 'V': 'E', 'U': 'F', 'T': 'G', 'S': 'H', 'R': 'I', 'Q': 'J', 'P': 'K', 
              'O': 'L', 'N': 'M', 'M': 'N', 'L': 'O', 'K': 'P', 'J': 'Q', 'I': 'R', 'H': 'S', 'G': 'T', 'F': 'U', 'E': 'V', 
              'D': 'W', 'C': 'X', 'B': 'Y', 'A': 'Z'}
encodedString = """Gszmp blf gl zoo nb ivzwvih uli urev dlmwviufo bvzih. R dlfowm'g szev pvkg gsrh yolt ifmmrmt uli zh olmt zh R wrw drgslfg blfi dlmwviufo hfkklig.  slkv blf ovzimvw hlnvgsrmt zolmt gsv dzb gsvhv ozhg urev bvzih-zmw, svb, nzbyv blf tzrmvw z olev lu kiltiznnrmt (zmw kviszkh mvd qly hprooh) rm gsv kilxvhh. Gszg dlfow xvigzrmob nzpv nv z evib szkkb dirgvi. Sviv'h gl nzmb, nzmb nliv bvzih lu xlwrmt gltvgsvi! Nrxszvo"""
punctuation = ['\'', '.', '!', '(', ')', '-']
decodedString = ''

for e in encodedString:
    for k, v in secretCode.items():
        if e == k:
            decodedString += v
        elif e.lower() == k.lower():
            decodedString += v.lower()
                   
    if e == ' ':
        decodedString += ' '
    elif e in punctuation:
        decodedString += e

print(decodedString)

As you can see, I solved this coding challenge with 20 simple lines of Python code (not including the output). How did I accomplish this? Here are 10 things I kept in mind when solving my own puzzle:

  1. I used the secretCode dictionary to set my reverse substitution cipher (HINT: even though there are both uppercase and lowercase letters, you won’t need two separate dictionaries for the cipher-I’ll explain why).
  2. I created a list containing all of the punctuation in this message-you’ll see why it’s important later. The list is aptly named punctuation.
  3. I used the decodedString variable to store the decoded message-I personally thought it was more convinient than figuring out how to replace all the characters in encodedString one by one.
  4. I created a nested for-loop to iterate both through the characters in encodedString and the items (that is, both keys and values) in the secretCode dictionary.
  5. In the nested loop where I iterate through the items of the secretCode dictionary (for k, v in secretCode.items()), I check if each character in the encodedString string equals the current corresponding key in the secretCode dictionary and if so, I append the corresponding value to the decodedString string.
  6. Remember how I said that even though my secret message contains both upper and lower-case letters you won’t need to create two dictionaries? Well, the statement elif e.lower() == k.lower() handles this scenario in just two lines of code by checking to see if the lowercase version of the character in encodedString equals the lowercase version of the corresponding key and if so, I append the lowercase corresponding value to the decodedString string.
  7. How would I handle spaces and punctuation in the encodedString? Well, in the case of spaces, if a character in encodedString is a space, I append it to the decodedString just as I did with the letters.
  8. The same logic applies for any punctuation in encodedString, but this time, I check to see if a character equals any element in the punctuation list that I mentioned in item #2.
  9. One thing I kept in mind when checking for spaces and punctuation in encodedString-I kept the if-elif statement pair OUTSIDE of the dictionary loop because if I placed those statements inside that loop, I would’ve ended up with a ton of spaces and punctuation. However, placing this code outside of the dictionary loop ensures that I end up with the correct amount of spaces and punctuation.
  10. Last but not least, I printed the decodedString message. Without further ado, here it is:
Thank you to all my readers for five wonderful years. I wouldn't have kept this blog running for as long as I did without your wonderful support.  hope you learned something along the way these last five years-and hey maybe you gained a love of programming (and perhaps new job skills) in the process. That would certainly make me a very happy writer. Here's to many many more years of coding together! Michael

Trust me, dear readers, I mean every word of this. I wouldn’t kept this blog running as long as I did without you!

One More Note

I know I’ve emphasized this over these last two posts, but thank you, thank you, thank you loyal readers for reading my blog for the last five years. Hopefully you’ll keep coming back for more, because boy do I have several more years of great coding content I can provide (and you bet it’ll still be good when I hit year 10 and beyond)!

However, there is someone I wanted to acknowledge on this five-year anniversary. Rather, a furry friend I’d like to mention.

His name is Simba (or Orange Boy) and he is my fluffy orange cat who certainly helped me during the blog’s early days (and yes, he was there when I wrote the welcome post). Here he is on the night of September 17, 2018, likely looking over me as I wrote another post (or searching for a post college job, as I was in the thick of job hunting in fall 2018). He is certainly a good fluffy boy!

Also, here’s Simba with Marbles, his sister (the brown cat aka Pretty Girl), who accompanied me during writing every now and then:

The kitties on Christmas morning 2017-pre blog days.

To many more years of developing together,

Michael

The Glorious Five-Year Plan Part One

Advertisements

Hello everybody,

Michael here, and today is a very big day for Michael’s Programming Bytes (or for the nostalgic and longtime readers, Michael’s Analytics Blog). Know why?

This little pet project of mine turns 5 today! Yes, five wonderful years ago, on June 13, 2018, I was just a month removed from college when I got the idea to start this very blog. Anyone remember my welcome post, aptly titled Welcome?

Reflections, reflections and a glimpse into my thought process these last five years…

Where do I begin? How did I start this blog?

OK, so I know I mention this blog’s origin story on my About page, but for those who don’t feel like navigating to the About page-or just want a quick summary of this blog’s story well, here it is.

As I mentioned earlier in this post, I launched this blog on June 13, 2018 with my Welcome message-my first official lesson was posted 12 days later on June 25, 2018. It was a very simple lesson on how to run basic R commands-here’s the link: R Lesson 1: Basic R commands.

Looking back on that first R lesson, I thought it was a very fitting way to begin my blogging journey. See, I’d been taking some really fun data analytics courses during my last semester of undergrad (this was Spring 2018, a few months before the launch). One such course was in R, which I was quite fascinated with (well, aside from the fact that there was a programming language called R).

After finishing undergrad in April 2018, I intially sought out data analytics jobs. However, I realized that I wanted to have an easy means to showcase my data analytics skills to potential employers. Hence, on June 13, 2018, I launched this blog-then known as Michael’s Analytics Blog.

If you’ve perused the entire 145-post catalog (and kudos if you have), you’ll likely have noticed that almost every single post from 2018-my first year active-is majority data analytics, though my long-lost MySQL series from fall 2018 does go into very very basic database building. There’s a good reason for that. Since I mentioned that I intially wanted a career purely in data analytics, I thought what better way to showcase my analytical skills than through writing? After all, I thought this could certainly double as my “data analyst portfolio”.

I know my earliest posts were purely analytical, but come the beginning of 2019, I wanted to explore other aspects of programming. With that said, on January 11, 2019, I wrote my first real non-analytical post-Java Lesson 1: Introduction to Java & “Hello World”. This post covered a very basic Java concept-System.out.println()-and the Hello world schtick most developers learn about when they first start learning about the craft of coding (if you’re a developer, odds are you’ll remember learning about how to print Hello world during your early coding days).

My first year of blogging only covered three major programming tools-Java, R and MySQL. After all, those were the only three programming tools I had any experience in coming out of college, so it’s not like I could cover more adavanced concepts. However, that changed once I began my tech job in the summer of 2019, as I also introduced Python into my blog’s repetoire with the post Python Lesson 1: Intro to Python on August 5, 2019.

As I gained more on-the-job and self-taught skills, I was able to cover more interesting programming topics (to me at least). Aside from Python, I was able to introduce HTML (February 2, 2021), GitHub (on the blog’s third anniversary), CSS (March 19, 2022), and Bootstrap (September 7, 2022). Throughout this blog’s five-year run, I’ve certainly broadened my focus from purely data analytics content to topics such as creating graphics, natural language processing, AI, web development, code management and much much more. To be honest, when I began this blog, I thought I’d still be posting purely analytics content five years in-after all, that was the intial aim of this blog and why the original name was Michael’s Anlaytics Blog.

However, I realized that as my programming knowledge grew, my blog’s content should expand in scope-and that’s exactly how things have played out. To be honest, I think it not only makes my blog more interesting to anyone who wants to read it (and binge the five years worth of posts) but also serves as a showcase for my versatile programming skills (and creativity too). In case you’ve read my entire blog catalog, I’ve covered 8 programming tools total (and that number will likely grow).

Throughout these last five years, I’ve made sure to keep my blog as interesting as possible for you all. As you might have noticed, I RARELY use the free datasets provided by tools such as R and Python. Why is that? It’s a personal creative choice on my part to ensure more interesting content for you all, my loyal readers. Let’s be honest, what would be a more interesting dataset to analyze-the one on petal and sepal width (if you know, you know) that’s been analyzed to death or one covering COVID in the early days of the pandemic (R Analysis 9: ANOVA, K-Means Clustering & COVID-19)? Throughout this blog’s run, I’ve included analyses on various topics such as COVID (in 2020), the 2020 NBA playoffs, the MCU, and much much more.

The one rare time I did use a pre-made dataset was for my Python MNIST 2-part post in 2022/2023 (Python Lesson 38: Building Your First Neural Network (AI pt. 2) and Python Lesson 39: One Simple Way To Improve Your Neural Network’s Accuracy (AI pt. 3)). However, I only did so because I felt that MNIST was a perfect way to get you all familiar with neural networks-plus, datasets for neural networks are considerably larger than datasets for other kinds of analyses, so I felt using a premade dataset was better in this situation. However, premade datasets will usually be the exception and not the rule for me, so for as long as I’m running this blog (which I hope will be for many years to come) I’ll try to keep things interesting for you all as much as I can.

Another thing I feel makes my blog stand out from the hundreds of other coding blogs out there is that most coding/tech blogs tend to specialize in one area of coding/tech, such as AI, Python, game development, etc. However, my blog is like a jack-of-all-coding trades-you don’t know what I’ll cover next. Plus, if you want to learn how to create amazing data visualizations, work with GitHub, analyze natural language through code, figure out how to solve calculus problems, work with basic web development, and so much more, this blog has you covered (and there’s certainly room to cover many, many more cool things in the long-term).

A 5-year message to you all

Unlike my 4th anniversary post, I don’t have any major updates for this blog (and no, another name change isn’t looming). However, before I go, I did want to leave you all with a message.

Oh, did I say I was gonna leave you with a message. Well, if you thought it was going to be some generic “thank-you-for-five-amazing-years” message, you’re wrong. Instead, to celebrate five years of Michael’s Programming Bytes/Michael’s Analytics Blog (however you want to refer to this blog), I’ve hidden my thank-you message in the form of a coding challenge. You ready?

Michael’s Five Year Coding Challenge

So, you think you’ve got what it takes to solve my five-year coding challenge and decode my five-year message? Before you begin problem solving, here’s the message I’d like you to decode:

Gszmp blf gl zoo nb ivzwvih uli urev dlmwviufo bvzih. R dlfowm'g szev pvkg gsrh yolt ifmmrmt uli zh olmt zh R wrw drgslfg blfi dlmwviufo hfkklig.

R slkv blf ovzimvw hlnvgsrmt zolmt gsv dzb gsvhv ozhg urev bvzih-zmw, svb, nzbyv blf tzrmvw z olev lu kiltiznnrmt (zmw kviszkh mvd qly hprooh) rm gsv kilxvhh. Gszg dlfow xvigzrmob nzpv nv z evib szkkb dirgvi. Sviv'h gl nzmb, nzmb nliv bvzih lu xlwrmt gltvgsvi!

Nrxszvo

But wait, here’s the best part…

…you, yes you, can help me solve this puzzle. Since it’s my five year blog anniversary, I thought I’d experiment with something new-a coding challenge where you (or any of my readers) can participate.

What might you get out of it? Well, I’ll pick my top five submissions to showcase on my next post (assuming I get at least five submissions, of course). Do you also run a coding/programming blog? Let me know and I’d be glad to give it a shoutout here.

Some things to keep in mind before I consider sharing your work

  • To celebrate five years of this blog, I thought I’d celebrate the larger spirit of coding (corny I know)! With that said, feel free to use any programming tool to solve this puzzle-even if I haven’t covered it here.
  • You have until June 21, 2023 at 12AM US Central Time to show me what you created. If you’re wondering what this cutoff means in your timezone, here’s a handy timezone converter-https://www.timeanddate.com/worldclock/converter.html.
  • Although I said you can solve this puzzle using any programming tool of your choosing, there are three rules I’d like you to adhere to:
    • You MUST have the correct answer (which I will share in part 2, along with my approach for solving the problem).
    • You MUST use a reverse substitution cipher to decode the message. If you don’t know what a reverse substitution cipher is, it goes something like this: A = Z, B = Y, C = X and so on until Z = A.
    • You MUST send me both the code and the output (and please, please send the code in a .txt file)
    • Send me your work to the email miketheblogger@outlook.com (yes, I have a separate email for blog matters). Also send me the outputs in whatever formats works best-be it on a .txt file, through screenshots, or really whatever works for you.

Also, before you send me your work, please fill out this questionairre-https://forms.office.com/Pages/ResponsePage.aspx?id=DQSIkWdsW0yxEjajBLZtrQAAAAAAAAAAAAZ__sEfXLlUNE5KRzZJWjNQQlYzTVFXR05TQVBXM0tNRy4u.

Thanks for reading, and be sure to keep an eye out for part 2 of my five-year anniversary celebration. Yes, I decided to pull an Avengers: Infinity War here (if you know, you know) and split my big five-year celebration post into two. After all, let’s keep the fun going for one more post! Can’t wait to see you solve the code (and don’t worry-I’ll share my answer in the next post)!

Michael

R Lesson 29: An Integral Part of R Calculus

Advertisements

Hello everybody,

Michael here, and in today’s post, I’ll be discussing an integral part of R calculus-integrals (see what I did there?).

The integral facts about integrals

What are integrals, exactly? Well, we did spend the last two posts discussing derivatives, which are metrics used to measure the rate at which one quantity changes with respect to another quantity (i.e. like the change in Rotten Tomatoes critic scores from one MCU movie to the next as we discussed in this post-R Lesson 27: Introductory R Calculus). Integrals, on the other hand, measure rates of accumulation of a certain quantity over time.

Simple enough, right? Well, is it possible to think of integrals as reverse derivatives? Yes it is! I did mention that derivatives measure change of a given quantity from point A to point B while integrals measure the accumulation of a quantity over a given time period (which could be from point A all the way to point XFD1048576). In the context of mathematical functions, derivatives break up a function into smaller pieces to measure the rate of change at each point while integrals put the pieces of the function back together to measure the rate of change across the entire duration of the function (which obviously can be infinity)

Calculating integrals, the manual way, part 1

Before we dive into R calculations of integrals, let’s first see how to calculate integrals, the manual way, by hand.

Let’s take this polynomial as an example:

Now, how would we calculate the integral of this polynomial. Take a look at the illustration below:

Just so you know, the polynomial in green is the integral of the original polynomial. With that said, how did we get the polynomial 3/4x^4-2/3x^3+2x^2-7x+C as the integral of the original polynomial?

First of all, just as we did with derivatives, we would need to calculate the integral of each term in the polynomial one-by-one. To do so, you’d need to add one to the exponent of each term, then divide that term by the new exponent. Still confused? Let me explain it another way:

  • The integral for 3x^3 would be 3/4x^4 since 3+1=4 and 3 divided by 4 is, well, 3/4.
  • The integral for -2x^2 would be -2/3x^3 since 2+1=3 and 2 divided by 3 is, well, 2/3.
  • The integral for 4x would be 2x^2 since 1+1=2 and 4 divided by 2 is 2.
  • The integral for -7 would be -7x since constants have a power of 0 and 0+1=1 (and anything divided by 1 equals itself)
  • You will likely have noticed an additional value in the integral that you may not be aware of-the constant C. I’ll explain more about that right now.

So, you may be wondering what’s up with the C at the end of the integral equation. Remember how earlier in this post I mentioned that you can think of derivatives as reverse integrals. You may recall that in our previous lesson-R Lesson 28: Another Way To Work With Derivatives in R-I discussed that during the process of calculating a derivative for a polynomial, the derivative of any constant in that polynomial is 0. This means that when finding the derivative of any polynomial, the constant disappears.

Now, since integrals can be considered as reverse derivatives, we should remember that when we integrate a polynomial, there was likely a constant that disappeared during differentiation (which I forgot to mention is the name of the process used to find the derivative of a polynomial). The C at the end of an integral represents the infinite number of possible constants that could be used for a given integral.

An integral illustration to this lesson

For my more visual learners, here is an illustration of how integrals work:

Just as a derivative would measure the change from one point to another in this curve, the integral would measure the area under the curve. The area in yellow represents the negative integral while the area in red represents the positive integral.

Still confused? Don’t worry-we’ll definitely go more in depth in this lesson!

Calculating integrals, the R way, part 2

OK, now that we’ve discussed how to calculate integrals the manual way, let’s explore how to calculate integrals the R way. You’ll notice that R won’t just spit out the integral of a given polynomial but rather calculate the integral using an upper and lower limit. Don’t worry-I’ll explain this more later, but for now, let’s see how the magic is done:

integral <- function(x) { 3*x^3-2*x^2+4*x-7 }
result <- integrate(integral, lower=1, upper=2)
result
5.583333 with absolute error < 6.6e-14

In this example, I’m showing you how R does definite integration. What is definite integration? Let me explain it like this.

In the previous section of this post, all we were trying to do was to calculate the integral polynomial of a certain expression. This is known as indefinite integration since we were simply trying to find the integration function of a given polynomial with the arbitrary constant C. As I mentioned in the previous section, the constant C could represent nearly anything, which means there are infinite possible integrals for any given polynomial.

However, with definite integration (like I did above), you’ll be calculating the integral at an upper and lower limit-this is certainly helpful if you’re looking for the integral over a specific range in the polynomial function rather than just a general integral, which can stretch for infinity. In R, to calculate the integral of a function over a given range, specify values for the lower and upper parameters (in this case I used 1 and 2). As you can see from the result I obtained, I got ~5.58 with an absolute error of 6.6e-14, which indicates a very, very, very small margin of error for the integral calculation. In other words, R does a great job with definite integration.

  • Keep in mind that the integration calculation approach I discussed above will only work with a finite range of integration (e.g. lower=1, upper=2). It won’t work with an infinite range of integration (e.g. from negative infinity to positive infinity).

Plotting an integration function

Now that we know how to calculate integrals, the next thing we’ll explore is plotting the integration function. Here’s how we’d do so-using the polynomial from the first section and an integration range of (0,5):

integral <- function(x) { 3*x^3-2*x^2+4*x-7 }
integrated <- function(x) { integrate(integral, lower=0, upper=50)$value }
vectorIntegral <- Vectorize(integrated)
x <- seq(0, 50, 1)
plot(x,vectorIntegral(x), xlim=c(0,50), xlab="X-values", ylab="Y-values", main="Definite Integration Example", col="blue", pch=16)

So, how did I manage to create this plot? Let me give you a step-by-step explanation:

  • I first set the function I wish to integrate as the value of the integral value.
  • I then retrieved the integral of this function at the range (0,5). I also grabbed the value of the integral at this range and nested this result into its own function, which I then stored as the value of the integrated variable.
  • I then vectorized the value of the integral at the range (0,5) and stored that value into the vectorIntegral variable.
  • I then created an x-axis sequence to use in my plot that contained the parameters (0, 50, 1) which represent the lower limit, upper limit, and x-axis increment for my plot, respectively. This sequence is stored in the x variable.
  • Last but not least, I used the plot() function to plot the integral of the polynomial 3x^3-2x^2+4x-7. One thing you may be wondering about is the x, vectorIntegral(x) parameter in the function. The x parameter gathers all the x values for the plot (in this case the integers 0 to 5) while the vectorIntegral(x) parameter calculates all of the correpsonding y-values for each possible x-value and gathers them into a vector, or array, for the plot.
    • Why choose vectorization to calculate the corresponding y-values? Well, it’s easier than looping through each possible x-value in the integral range to get the correpsonding y-values, since vectorization simply takes in all possible x-values (0-50 in this case) as the input array and returns an output array containing all possible y-values for each possible x-value (which in this case all seem to be between 4,000,000 and 5,000,000).

Calculating integrals, the manual way, part 3

So, now that I’ve shown you how to do definite integration the R way, let me show you how to do so the manual way. Let’s examine this illustation:

So in this illustration, I’m trying to calculate the integral for the polynomial 3x^3-2x^2+4x-7 using the (7,10) range. How do I do so. Well, first I perform some indefinite integration by finding the integral of the given polynomial-only thing here is that I don’t need the constant C. Next, since my integration range is (7,10), I evaluate the integral function for x=10 and subtract that result from the result I get after evaluating the integral function for x=7. After all my calculations are complete, I get 5342.25 as the value of my integral (rounded to two decimal places) at the integration range of (7,10).

  • If you’re wondering what that weird-looking S means, that’s just a standard integral writing notation.
  • To calculate the integral of any given expression for a given range, always remember to first find the integral of the polynomial and then evaluate that integral for x=both the upper and lower limits. Subtract the result of the upper limit evaluation from the result of the lower limit evaluation. And remember that, as we saw in our R integral calculations, there will always be a very very very small margin of error.
  • In calculus function notation, the capital F represents the f(x) of the integral while the lowercase f represents the f(x) of that integral’s derivative.

Thanks for reading!

Michael

R Lesson 28: Another Way To Work With Derivatives in R

Advertisements

Hello everybody,

Michael here, and in today’s lesson, I’ll show you another cool way to work with derivatives in R.

In the previous post-R Lesson 27: Introductory R Calculus-I discussed how to work with derivatives in R. However, the derivatives method I discussed in that post doesn’t cover the built-in derivatives method R has to calculate derivatives…rather, methods R uses to calculate derivatives (there are two ways of approaching this). What might that method look like? Well, lets dive in!

Calculating derivatives, the built-in R way, part 1

Now, how can we calculate derivatives with the built-in R way? First, we’ll explore the deriv() function. Let’s take a look at this code below, which contains a simple equation for a parabolic curve:

curve <- expression(3*x^2+4*x+5)
print(deriv(curve, "x"))

expression({
    .value <- 3 * x^2 + 4 * x + 5
    .grad <- array(0, c(length(.value), 1L), list(NULL, c("x")))
    .grad[, "x"] <- 3 * (2 * x) + 4
    attr(.value, "gradient") <- .grad
    .value
})

In this example, I’m using the polynomial 3x^2+4x+5 to represent our hypothetical parabolic curve. To find the derivative function of this polynomial, I ran R’s built in deriv() function and passed in both the curve expression and "x" (yes, in double quotes) to find the derivative expression-as the derivative always relates to x (or whatever character you used to represent an unknown quantity). Now, as you see from the output given, things don’t look too understandable. However, pay attention to the second .grad line (in this case, 3 * (2 * x) + 4), as this will provide the derivative function of the parabolic curve equation-6x+4. We would use this equation to evaluate the rate of change between points in the parabolic curve.

Let’s say we wanted to evaluate the derivative polynomial-6x+4-at x=5. If we use this x value, then the derivative of the parabola at x=5 would be 34.

  • If you thought the output I’m referring to read as 3(2x)+4, you’d be wrong. Remember to multiply the 3 by the 2x to get the correct answer of 6x (6x+4 to be exact).
  • When you’re writing a polynomial in R, remember to include all the multiplication signs (*)-yea, I know it’s super annoying.

In case you wanted a visual representation of this parabola, here it is:

I created this illustation of a parabola using a free online tool called DESMOS, which allows you to quickly create a visual representation of a line, parabola, or other curve. Here’s the link to DESMOS-https://www.desmos.com/calculator/dz0kvw0qjg.

Calculating derivatives, the built-in R way, part 2

Now let’s explore R’s other built-in way to calculate derivatives-this time using the D() function. For this example, let’s use the same parabolic curve equation we used for the deriv() example:

curve <- expression(3*x^2+4*x+5)
print(D(curve, "x"))

3 * (2 * x) + 4

As you can see, we passed in the same parameters for the D() function that we used for the deriv() function and got a much simpler version of the output we got for the deriv() function-simpler as in the derivative expression itself was the only thing that was returned.

Since the derivative expression returned from the D() function is the same as the expression returned from the deriv() function,

Calculating derivatives, the manual way, part 3

Yes, I know I was mostly going to focus on the two built-in R functions used to calculate derivatives-deriv() and D()-but I thought I’d include this bonus section for those of you (especially those who enjoy exploring calculus) who were wondering how to manually calculate derivatives.

Let’s take the same polynomial we were working with for the previous two examples:

How would we arrive at the derivative expression of 6x+4? Check out this illustration below:

From this picture, here are some things to keep in mind when calculating derivatives of polynomials (and it’s not the same as calculating derivatives of regular numbers like we did in the previous post R Lesson 27: Introductory R Calculus):

  • Go term-by-term when calculating derivatives of polynomials. In this example, you’d calculate the derivative of 3x^2, then the derivative of 4x and lastly the derivative of 5.
  • How would you calculate the derivatives of each term? Here’s how (and it’s quite easy).
  • The derivative of 3x^2 would be 6x, as you would multiply the number (3) by the power (2) to get 6. You would then reduce power of the x^2 by 1 to get x (any variable in a polynomial without an exponent is raised to the power of 1). Thus, the derivative of 3x^2 would be 6x.
  • The derivative of 4x would simply be 4. Just as we did with 3x^2, we’d multiply the number (4) by the power (1) in this case to get 4. We would also reduce the power of x by 1 to simply get 4, since x has a power of 1 and 1-1=0. In a polynomial, constants (numbers without variables next to them) have a power of 0.
  • I mean, we could’ve written the derivative polynomial as 6x+4x^0, but 6x+4 looks a lot nicer.
  • As for the constant in this polynomial-5-it has a derivative of 0 since the derivative of a constant is always 0 (after all, any constant in a polynomial has a power of 0, so this makes perfect sense). Thus, the derivative of 5 isn’t included in the derivative polynomial of 6x+4.

Thanks for reading,

Michael

R Lesson 27: Introductory R Calculus

Advertisements

Hello everybody,

Michael here, and in today’s post, I’m going to revisit an old friend of ours-the language R. As you readers may recall, R was the first language I covered on this blog, and since we’re only a few posts away from the blog’s fifth anniversary, I thought it would be fun to revisit this blog’s roots as an analytics blog (remember the Michael’s Analytics Blog days everyone).

Today’s post will provide a basic introduction on doing calculus with R (including graphing). Why am I doing R calculus? Well, I wanted to do some more fun R posts leading up to the blog’s fifth anniversary and I did have fun writing the trigonometry portion of my previous post-Python Lesson 41: Word2Vec (NLP pt.7/AI pt.7)-that I wanted to dive into more mathematical programming topics. With that said, let’s get started with some R calculus!

Setting ourselves up

In this lesson, we’ll be using this dataset-

This dataset contains the Rotten Tomatoes scores for all MCU (Marvel Cinematic Universe) movies from Iron Man (2008) to Guardians of the Galaxy Vol. 3 (2023). Both critic and audience Rotten Tomatoes scores are included for all MCU movies.

Now, let’s open up our R IDE and read in this CSV file:

MCU <- read.csv("C:/Users/mof39/OneDrive/Documents/MCU movies.csv", fileEncoding="UTF-8-BON")
> MCU
                                         Movie Year RT.score Audience.score
1                                     Iron Man 2008     0.94           0.91
2                              Incredible Hulk 2008     0.67           0.69
3                                   Iron Man 2 2010     0.71           0.71
4                                         Thor 2011     0.77           0.76
5           Captain America: The First Avenger 2011     0.80           0.75
6                                 The Avengers 2012     0.91           0.91
7                                   Iron Man 3 2013     0.79           0.78
8                          Thor The Dark World 2013     0.66           0.75
9          Captain America: The Winter Soldier 2014     0.90           0.92
10                     Guradians of the Galaxy 2014     0.92           0.92
11                     Avengers: Age of Ultron 2015     0.76           0.82
12                                     Ant-Man 2015     0.83           0.85
13                  Captain America: Civil War 2016     0.90           0.89
14                              Doctor Strange 2016     0.89           0.86
15               Guardians of the Galaxy Vol 2 2017     0.85           0.87
16                      Spider-Man: Homecoming 2017     0.92           0.87
17                              Thor: Ragnarok 2017     0.93           0.87
18                               Black Panther 2018     0.96           0.79
19                      Avengers: Infinity War 2018     0.85           0.92
20                        Ant-Man and the Wasp 2018     0.87           0.80
21                              Captain Marvel 2019     0.79           0.45
22                           Avengers: Endgame 2019     0.94           0.90
23                   Spider-Man: Far From Home 2019     0.90           0.95
24                                 Black Widow 2021     0.79           0.91
25   Shang-Chi and the Legend of the Ten Rings 2021     0.91           0.98
26                                    Eternals 2021     0.47           0.77
27                     Spider-Man: No Way Home 2021     0.93           0.98
28 Doctor Strange in the Multiverse of Madness 2022     0.74           0.85
29                      Thor: Love and Thunder 2022     0.63           0.77
30              Black Panther: Wakanda Forever 2022     0.84           0.94
31           Ant-Man and the Wasp: Quantumania 2023     0.47           0.83
32               Guardians of the Galaxy Vol 3 2023     0.81           0.95

As you can see, we have read the data-frame into R and displayed it on the IDE (there are only 31 rows here).

Now, before we dive into the calculus of everything, let’s explore our dataset:

  • Movie-the name of the movie
  • Year-the movie’s release year
  • RT.score-the movie’s Rotten Tomatoes score
  • Audience.score-the movie’s audience score on Rotten Tomatoes
  • R tip-when you are reading in a CSV file into R, it might help to add the fileEncoding="UTF-8-BON" parameter into the read.csv() function as this parameter will remove the junk text that appears in the name of the dataframe’s first column.

Calculus 101

Now, before we dive headfirst into the fun calculus stuff with R, let’s first discuss calculus and derivatives, which is the topic of this post.

What is calculus? Simply put, calculus is a branch of mathematics that deals with the study of change. Calculus is a great way to measure how things change over time, like MCU movies’ Rotten Tomatoes scores over the course of its 15-year, 32-movie run.

There are two main types of calculus-differential and integral calculus. Differential calculus focuses on finding the rate of change of, well, any given thing over a period of time. Integral calculus, on the other hand, focuses on the accumulation of any given thing over a certain period of time.

A good example of differential calculus would be modelling changes in a city’s population over a certain period of time; differential calculus would be used in this scenario to find the city’s population change rate over time. A good example of integral calculus would be modelling the spread of a disease over time (e.g. COVID-19) in a certain geographic region to analyze that region’s infection rate over a certain time period.

Now, what is a derivative? In calculus, the derivative is the metric used to measure the rate of change at any given point in the measured example. In this example, the derivative (or rather derivatives since we’ll be using two derivatives) would be the change in Rotten Tomatoes scores (both critic and audience) from one MCU movie to the next.

It’s R calculus time!

Now that I’ve explained the gist of calculus and derivatives to you all, it’s time to implement them into R! Here’s how to do so (and yes, we will be finding the derivatives of both critic and audience scores). First, let’s start with the critic scores derivatives:

criticScores <- MCU$RT.score
criticDerivatives <- diff(criticScores)
criticDerivatives

[1] -0.27  0.04  0.06  0.03  0.11 -0.12 -0.13  0.24  0.02 -0.16  0.07  0.07 -0.01 -0.04  0.07  0.01  0.03 -0.11  0.02 -0.08  0.15 -0.04 -0.11  0.12 -0.44  0.46 -0.19 -0.11  0.21 -0.37
[31]  0.34

To calculate the derivatives for each critic score, I first placed all of the critics’ scores (stored in the column MCU$RT.score) into the vector criticScores. I then used R’s built-in diff() function to calculate the difference in critic scores from one MCU movie to the next and-voila!-I have my 31 derivatives.

  • Even though there are 32 MCU movies, there are only 31 differences to calculate and thus only 31 derivatives that appear.

Calculating the derivatives of the audience scores works exactly the same way, except you’ll just need to pull your data from the MCU$Audience.score column:

audienceScores <- MCU$Audience.score
audienceDerivatives <- diff(audienceScores)
audienceDerivatives
 [1] -0.22  0.02  0.05 -0.01  0.16 -0.13 -0.03  0.17  0.00 -0.10  0.03  0.04 -0.03  0.01  0.00  0.00 -0.08  0.13 -0.12 -0.35  0.45  0.05 -0.04  0.07 -0.21  0.21 -0.13 -0.08  0.17 -0.11
[31]  0.12

Plotting our results

Now that we’ve calculuated the derivatives of both the critic and audience scores, let’s plot them!

Here’s how we’d plot the critic scores:

plot(1:(length(criticScores)-1),criticDerivatives, type = "l", xlab = "MCU Movie Number", ylab = "Change in critic score")

In this example, I used R’s plot() function (which doesn’t require installation of the ggplot2 package) to plot the derivatives of the critic scores. The y-axis represents the change in critic scores, while the x-axis represents the index for a specific MCU movie (e.g. 0 would be Incredible Hulk while 31 would be Guardians of the Galaxy Vol.3).

However, this visual doesn’t seem to helpful. Let’s see how we can fix it!

First, let’s create a vector of the MCU movies to use as labels for this plot:

movies <- MCU$Movie

Next, let’s remove Iron Man from this vector since it won’t have a derivative (after all, it’s the first MCU movie).

movies <- movies[! movies %in% c('Iron Man')]

Great! Now let’s revise our plot to first add a title:

plot(1:(length(criticScores)-1),criticDerivatives, type = "l", main="Changes in MCU movie critic reception", xlab = "MCU Movie Number", ylab = "Change in critic score")

You can see that the plot() function’s main paramater allows you to add a title to the graph.

Next let’s add some labels to our data points-remember to only run this command AFTER you have the initial graph open!

text(1:(length(criticScores)-1),criticDerivatives, labels=movies, pos=3, cex=0.6) 

Voila! With the text() function, we’re able to add labels to our data points so that we can tell which movie corresponds with which data point!

  • Remember to include the same X and Y axes in the text() function as you did in the plot() function! In this case, the X axis would be 1:(length(criticScores)-1) and the Y axis would be criticDerivatives.

Now that we have a title and labelled data points in our graph, let’s gather some insights. From our graph, we can see that the critical reception for the MCU’s Phases 1 & 2 was up-and-down (these include movies from Iron Man to Ant-Man). The critical reception for MCU’s Phase 3 slate (from Captain America: Civil War to Spider-Man: Far From Home) was its most solid to date, as there are no major positive or negative derivatives in either direction. The most interesting area of the graph is Phases 4 & 5 (from Black Widow onwards), as this era of the MCU has seen some sharp jumps in critical reception from movie to movie. Some of the sharpest changes can be seen from Shang-Chi and the Legend of the Ten Rings to Eternals (a 44% drop in critic score) and from Eternals to Spider-Man: No Way Home (a 46% rise in critic score).

All in all, some insights we can gain from this graph is that MCU Phase 3 was its most critically well-recieved (and as some fans would say, the MCU’s prime) while the entries in Phase 4 & 5 have been hit-or-miss critically (ahem, Eternals).

Now that we’ve analyzed critic derivatives, let’s turn our attention to analyzing audience score derivatives. Here’s the plot we’ll use-and it’s pretty much the same code we used to create the updated critic score derivative plot (except replace the word critic with the word audience in each axis variable and in the title):

plot(1:(length(audienceScores)-1),audienceDerivatives, type = "l", main="Changes in MCU movie audience reception", xlab = "MCU Movie Number", ylab = "Change in audience score")

text(1:(length(audienceScores)-1),audienceDerivatives, labels=movies, pos=3, cex=0.6) 

The change in audience reception throughout the MCU’s 15-year, 32-movie run looks a little different than the change in critic reception over that same time period. For one, there are fewer sharp changes in audience score from movie to movie. Also interesting is the greater number of positive derivatives in audience score for the MCU’s Phase 4 & 5 movies-after all, there were far more negative derivatives than positive for the MCU’s Phase 4 & 5 critical reception (this is also interesting because many fans on MCU social media accounts that I follow have griped about the MCU’s quality post-Avengers Endgame). One more interesting insight is that the sharpest changes in audience reception came during the peak of Phase 3 (namely from Black Panther to Avengers: Endgame). As you can see from the graph above, the change in audience reception is fairly high from Black Panther to Avengers: Infinity War then drops from Avengers: Infinity War to Ant-Man and the Wasp. The audience score drops even further from Ant-Man and the Wasp to Captain Marvel before sharply rising from Captain Marvel to Avengers: Endgame. I personally found this insight interesting as some of my favorite MCU movies come from Phase 3 (like Black Panther with its 96% on Rotten Tomatoes-critic score), though I do recall Captain Marvel wasn’t well liked when it came out in March 2019 (but boy oh boy was Avengers: Endgame one of the most hyped things of 2019).

Thanks for reading,

Michael

Python Lesson 41: Word2Vec (NLP pt.7/AI pt.7)

Advertisements

Hello everybody,

Michael here, and in today’s lesson, we’ll discover another AI NLP algorithm-word2vec (recall that a few posts back in Python Lesson 40: The NLP Bag-Of-Words (NLP pt. 6/AI pt.5) we discussed the bag-of-words algorithm).

What is the word2vec algorithm?

So, how does the word2vec algorithm work? Let’s say we were to process a text document using this algorithm. The word2vec algorithm would analyze each word in this document along with all other words commonly found near a certain word. For instance, if the word “family” was found in a document and the words “daughter”, “parents”, and “generations” were found near it-the word “family” would be lumped in with these words, as they are all related to each other.

If you were to ask your program for the three most similar words to “family” (according to our hypothetical document), it would say “daughter”, “parents” and “generations”. How would the word2vec algorithm find the most common words? By using a mathematical metric called cosine similarity-more on that later.

Preparing the data

So, before we dive into the magic of word2vec, let’s gather the data we’re going to use.

In this example, we’ll utilize this ChatGPT-generated essay below that contains five paragraphs on a very relevant topic in the year 2023-returning to the office-including both sides of the debate.

So, let’s open up our Python IDEs and start coding!

First, let’s open and read this file into our IDE:

with open(r'C:\Users\mof39\OneDrive\Documents\return to office.txt', 'r', encoding='utf-8') as file:
    word2VecFile = file.read()
    print(word2VecFile)

In Favor of Returning to Office:

Many argue that returning to the office is crucial for maintaining the productivity and collaboration necessary for businesses to thrive. Working in the same space allows for easier communication, quicker decision-making, and better team building. It also provides opportunities for social interactions that can improve employee morale and job satisfaction.

Another benefit of returning to the office is the separation of work and home life. With many employees working remotely during the pandemic, the lines between work and personal time have become blurred. This can lead to burnout and other negative consequences. By returning to the office, employees can more easily maintain a work-life balance and avoid the mental exhaustion that comes with always being “on.”

Against Returning to Office:

On the other hand, many people argue that remote work has proven to be effective, and there is no need to return to the office. In fact, studies have shown that remote workers are often more productive than those who work in the office. Additionally, remote work allows for more flexibility in scheduling and reduces the amount of time and money spent commuting.

Another important consideration is the health and safety of employees. With the ongoing threat of COVID-19 and the emergence of new variants, returning to the office could put employees at risk. Some may not feel comfortable being in close proximity to their colleagues or may have concerns about the effectiveness of safety protocols. In these cases, continuing to work remotely may be the best option.

Ultimately, the decision to return to the office or continue remote work will depend on a variety of factors, including the type of work being done, the needs of the business, and the preferences of employees. It is important to consider all perspectives and prioritize the health and safety of everyone involved.
  • You can technically open a .txt file with the pd.read_csv method, but I prefer using the with...open method of reading .txt files since the pd.read_csv method outputs the text file in tabluar form, which isn’t going to work for this tutorial.

And now let’s create the word2vec model

Now that we’ve read our text file into the system, the next thing we’ll do is create our word2vec model. Here’s the code to do so:

First, lets gather our list of tokens for the analysis:

import nltk
import gensim

modelData = []
punctuation = [',', '.', '”', '“', ':']

for w in nltk.sent_tokenize(word2VecFile):
    tokens = []
    
    for t in nltk.word_tokenize(w):
        if t not in punctuation:
            tokens.append(t.lower())
        
    modelData.append(tokens)
    
print(modelData)

[['in', 'favor', 'of', 'returning', 'to', 'office', 'many', 'argue', 'that', 'returning', 'to', 'the', 'office', 'is', 'crucial', 'for', 'maintaining', 'the', 'productivity', 'and', 'collaboration', 'necessary', 'for', 'businesses', 'to', 'thrive'], ['working', 'in', 'the', 'same', 'space', 'allows', 'for', 'easier', 'communication', 'quicker', 'decision-making', 'and', 'better', 'team', 'building'], ['it', 'also', 'provides', 'opportunities', 'for', 'social', 'interactions', 'that', 'can', 'improve', 'employee', 'morale', 'and', 'job', 'satisfaction'], ['another', 'benefit', 'of', 'returning', 'to', 'the', 'office', 'is', 'the', 'separation', 'of', 'work', 'and', 'home', 'life'], ['with', 'many', 'employees', 'working', 'remotely', 'during', 'the', 'pandemic', 'the', 'lines', 'between', 'work', 'and', 'personal', 'time', 'have', 'become', 'blurred'], ['this', 'can', 'lead', 'to', 'burnout', 'and', 'other', 'negative', 'consequences'], ['by', 'returning', 'to', 'the', 'office', 'employees', 'can', 'more', 'easily', 'maintain', 'a', 'work-life', 'balance', 'and', 'avoid', 'the', 'mental', 'exhaustion', 'that', 'comes', 'with', 'always', 'being', 'on.', 'against', 'returning', 'to', 'office', 'on', 'the', 'other', 'hand', 'many', 'people', 'argue', 'that', 'remote', 'work', 'has', 'proven', 'to', 'be', 'effective', 'and', 'there', 'is', 'no', 'need', 'to', 'return', 'to', 'the', 'office'], ['in', 'fact', 'studies', 'have', 'shown', 'that', 'remote', 'workers', 'are', 'often', 'more', 'productive', 'than', 'those', 'who', 'work', 'in', 'the', 'office'], ['additionally', 'remote', 'work', 'allows', 'for', 'more', 'flexibility', 'in', 'scheduling', 'and', 'reduces', 'the', 'amount', 'of', 'time', 'and', 'money', 'spent', 'commuting'], ['another', 'important', 'consideration', 'is', 'the', 'health', 'and', 'safety', 'of', 'employees'], ['with', 'the', 'ongoing', 'threat', 'of', 'covid-19', 'and', 'the', 'emergence', 'of', 'new', 'variants', 'returning', 'to', 'the', 'office', 'could', 'put', 'employees', 'at', 'risk'], ['some', 'may', 'not', 'feel', 'comfortable', 'being', 'in', 'close', 'proximity', 'to', 'their', 'colleagues', 'or', 'may', 'have', 'concerns', 'about', 'the', 'effectiveness', 'of', 'safety', 'protocols'], ['in', 'these', 'cases', 'continuing', 'to', 'work', 'remotely', 'may', 'be', 'the', 'best', 'option'], ['ultimately', 'the', 'decision', 'to', 'return', 'to', 'the', 'office', 'or', 'continue', 'remote', 'work', 'will', 'depend', 'on', 'a', 'variety', 'of', 'factors', 'including', 'the', 'type', 'of', 'work', 'being', 'done', 'the', 'needs', 'of', 'the', 'business', 'and', 'the', 'preferences', 'of', 'employees'], ['it', 'is', 'important', 'to', 'consider', 'all', 'perspectives', 'and', 'prioritize', 'the', 'health', 'and', 'safety', 'of', 'everyone', 'involved']]

Before creating our tokens lists to use for our model, I first imported the nltk and gensim packages (as always, if there’s a required package you don’t have, pip install it!).

I then created two lists, modelData and tokens. Then I sentence-tokenized this file before word-tokenizing it, appending MOST of the tokens to the tokens list before appending each tokens list to the modelData list. I say appending MOST (not all) of the tokens as I excluded any tokens that were part of the punctuation list.

Now, you may be wondering if we should remove stopwords here like we’ve done for our previous NLP tutorials. Normally, I’d say yes, but since the text is so short and we’re trying to figure out word connections, I’d say keep the stopwords here to get more accurate word2vec results.

Now that we’ve got our tokens list-or should I say lists-within-a-list-it’s time to build our model!

Rather, I should say model(s), since there are two approaches we can take to build our word2vec analysis-skipgrams and continous-bag-of-words.

Skip-gram

The first word2vec approach we’ll explore is the skip-gram model.

What exactly is the skip-gram model, though? Using our text file as an example, let’s say we were trying to guess which words we’d commonly see before and after the word “working”. The skip-gram model would analyze our text file to guess which words we’d likely see before and after the word “working” such as “remotely” or “hardly”.

Now, how do we implement this model in Python? Take a look at the code below:

skipGram = gensim.models.Word2Vec(modelData, min_count = 1, vector_size = 2, window = 5, sg = 1)

What do each of these parameters mean. Let me explain:

  • min_count-sets the minimum amount of times a word must appear in a document to be factored into the skip-gram analysis; in this example, a word must appear in the document at least once to be factored into the skip-gram analysis
  • vector_size-sets the number of dimensions that each word vector will contain; in this example, each word vector will contain 2 dimensions
  • window-sets the maximum distance between current and predicted words in the document; this skip-gram analysis will look at the five words following and preceding any given word to determine cosine similarity (more on that later)
  • sg-if this value is 1, use the skip-gram analysis; if this value is 0, use the continous bag-of-words analysis (more on that later)

Now that we have our skip-gram word2vec model set up, let’s test it on three word pairs:

print("Cosine similarity between 'returning' and 'office' - Skip Gram : ", skipGram.wv.similarity('returning', 'office'))     
print("Cosine similarity between 'remote' and 'covid-19' - Skip Gram : ", skipGram.wv.similarity('remote', 'covid-19'))
print("Cosine similarity between 'reduces' and 'commuting' - Skip Gram : ", skipGram.wv.similarity('reduces', 'commuting'))

Cosine similarity between 'returning' and 'office' - Skip Gram :  0.9542125
Cosine similarity between 'remote' and 'covid-19' - Skip Gram :  -0.8962952
Cosine similarity between 'reduces' and 'commuting' - Skip Gram :  0.9316714

In this example, we are analyzing the cosine similiarty between three word pairs-returning & office, remote & covid-19 and reduces & commuting. As you can see from the output, two of the word pairs have positive cosine similarity while the other word pair has negative cosine similarity.

What does positive and negative cosine similarity mean? Well, the higher the positive cosine similarity, the more semantically similar the two words are to each other (in the context of the document being analyzed). The lower the negative cosine similarity, the more dissimilar the two words are to each other (again, in the context of the document being analyzed).

  • Just a tip, but using a vector_size of 2 is often not ideal, especially because most NLP analyses work with larger documents. I used a vector_size of 2 here because the document we’re working with is rather small.
  • Using a vector size of 0 or 1 won’t work for either the skip-gram or continuous bag-of-words model, as it will return cosine similarities of 1, -1, or 0, which aren’t ideal for your analysis.

Continuous bag-of-words

Now that we’ve explored the skip-gram model, let’s now analyze the continous bag-of-words model.

What is the continuous bag-of-words model? Well, like the skip-gram model, the continuous bag-of-words model is like a word2vec guessing game. However, while the skip-gram model attempts to predict words that will come before and after a given word, the continuous bag-of-words model will analyze a sentence in a document and try to predict what any given word in a sentence would be based on the surrounding words in the sentence.

Here’s a simple example:

The happy couple decided to close on their first _____ yesterday, eagerly anticipating all the memories they would make there. 

Just from this sentence alone, what do you think the missing word would be? If you guessed house, you’d be right! In this example, the continuous bag-of-words model would look at each word in the sentence and based on the given words, guess the missing word.

Now let’s see how to implement the CBOW (continuous bag-of-words) model in Python.

CBOW = gensim.models.Word2Vec(modelData, min_count = 1, vector_size = 2, window = 5, sg = 0)

Now, here’s the best part about the CBOW model-it’s same up in exactly the same way, with the same parameters, as the skip-gram model. The only difference between the two models is that you’d need to set the sg parameter to 0 to indicate that you’d like to use the CBOW model.

Now let’s test our CBOW word2vec model on the same three word pairs we used for the skip-gram model.

print("Cosine similarity between 'returning' and 'office' - Skip Gram : ", CBOW.wv.similarity('returning', 'office'))     
print("Cosine similarity between 'remote' and 'covid-19' - Skip Gram : ", CBOW.wv.similarity('remote', 'covid-19'))
print("Cosine similarity between 'reduces' and 'commuting' - Skip Gram : ", CBOW.wv.similarity('reduces', 'commuting'))

Cosine similarity between 'returning' and 'office' - Skip Gram :  0.95738184
Cosine similarity between 'remote' and 'covid-19' - Skip Gram :  -0.9320767
Cosine similarity between 'reduces' and 'commuting' - Skip Gram :  0.94031644

Assuming a change in word2vec model but leaving all other parameters unchanged, we can see that the cosine similarity scores between the three word pairs show small differences from the skip-gram cosine similarity scores for the same three word pairs.

Cosine similarity explained

So, now that I’ve shown you all what cosine similarity looks like, it’s time to explain it further.

In the word2vec algorithm (for both the skip-gram and CBOW models), each word in the document is treated like a vector. Still unsure about the whole vector thing? Here’s an illustration that might help:

Imagine a simple right triangle, where the three words returning, office, and working are the angles. A and B are the sides while C is the triangle’s hypotenuse.

Let’s say you wanted to find the cosine similarity between the words returning and office. If you know even basic trigonometry, you’ll know that the cosine of a right triangle is the ratio of the length of the side adjacent to a given angle to the length of the triangle’s hypotenuse. The cosine would represent the cosine similarity between these two words.

In word2vec, cosine similarity scores can range from -1 to 1. Here’s an illustration on cosine similarity scores:

In simple terms, a word pair with a cosine similarity score of -1 or 0 indicates completely different, not-at-all semantic similar words (in the context of the document being analyzed). A word pair with a cosine similarity score greater than -1 but less than 0 indicates that the two words are somewhat, but not entirely, dissimilar. Finally, a word pair with a cosine similarity score of 1 indicates that the two words are identical or very, very similar semantically.

In the example above, the words returning and office have cosine similarity scores of roughly 0.95 (for both the skip-gram and CBOW models), indicating that these two words are quite semantically similar in the context of the document. Interestingly, the words remote and covid-19 have cosine similarity scores lower than -0.85 (for both the skip-gram and CBOW models)-personally I find this interesting as the word remote seems semantically similar to the word covid-19 in the context of this document (to me at least).

  • How is negative cosine similarity possible? Well, think of an obtuse traingle. In an obtuse triangle, one angle has to be greater than 90 degrees, which means the cosine of that angle can possibly be negative. Think of NLP cosine similarity the same way.

Thanks for reading,

Michael

Michael’s New Spinoff Blog!

Advertisements

Hello readers,

It’s Michael, and I’ve got a super cool blog announcement for you all! I know you all wanted some more neural network/AI content, but please here me out here!

So as most of you know, I launched this blog on June 13, 2018, which means that I’ve been keeping up this blog for almost five years now. Through the course of writing 140 blog posts, I thought it would be time to expand the Michael’s Programming Bytes brand.

You may be wondering, how do I plan to expand my brand? Well, I figured that today, April 1, 2023, would be the perfect day to announce my very-first spinoff blog-Michael’s Poetic Programming Bytes! Yes, I thought I’d combine all the fun of coding with the simple creative joy of poetry.

What can you, the readers, expect from my first foray into creative writing? Well, we’ve got odes to the joy of coding:

Oh, joy of programming, how sweet your song,
With every keystroke, we dance along.
A world of code at our fingertips,
Endless possibilities, endless trips.

With loops and functions, we craft our art,
A symphony of logic, a work of heart.
Each line a brushstroke, each file a canvas,
We paint our dreams with logic and balance.

The bugs may bite, the code may crash,
But we rise above and make a dash.
To debug and fix, to learn and grow,
And create a masterpiece, a thing of flow.

Oh, joy of programming, you bring us delight,
A world of creation, a world of might.
We tinker and play, we solve and code,
And with each project, we find our abode.

So let us embrace this world of code,
And let our imaginations be bestowed.
With the joy of programming, we can create,
A world of wonder, a world of fate.

Haikus for those who enjoy this form of poetry, like this one about COBOL:

Legacy language,
COBOL endures through the years,
Business still needs it.

Looking for a fun way to learn about basic coding concepts? Michael’s Poetic Programming Bytes has content for that, like this Shakespearan sonnet explaining the concept of object-oriented programming:

Oh, wondrous world of code, how strange thy ways,
Wherein doth dwell the art of programming,
And rules of logic shape the programmer's days,
While algorithms bring life to every thing.

And yet, there lies a path less traveled by,
A new approach to programming art sublime,
Where objects are the stars that light the sky,
And classes, their domains, rule space and time.

Behold, the paradigm that we call OOP,
Where objects are defined by their attributes,
And methods grant them power, this we do see,
As they perform actions and execute.

Thus, OOP is a world of boundless creation,
Where objects reign supreme, in this new nation.

Or, if you want something a little more modern, here’s a rap about about why you shouldn’t use star imports in Python:

(Intro)
Listen up, y'all, it's time to learn
'Bout Python code and what to discern
We talkin' star imports, don't you know
Why you shouldn't use 'em, I'll tell you so

(Verse 1)
Star imports may seem like a good choice
But let me tell you, it's not worth the noise
You import everything, all at once
But that's a recipe for a coding dunce

'Cause when you import, you want to be clear
What you need, and what you don't want near
But with star imports, it's all or none
And that's not the way to get it done

(Hook)
So don't use star imports, take your time
Import what you need, it's not a crime
Cleaner code, that's what you'll get
And that's the key, you can bet

(Verse 2)
Now let's talk about naming collisions
With star imports, you risk some divisions
Two modules with the same name
Now you got a problem, that's not a game

You gotta qualify the name, make it clear
Which module you want, or it won't adhere
And if you got a lot of imports, it's a mess
So don't use star imports, that's my address

(Hook)
So don't use star imports, take your time
Import what you need, it's not a crime
Cleaner code, that's what you'll get
And that's the key, you can bet

(Bridge)
Now you know the deal, you gotta be wise
Don't take shortcuts, that's not the prize
Clean code is what we strive for
So don't use star imports, that's the score

(Hook)
So don't use star imports, take your time
Import what you need, it's not a crime
Cleaner code, that's what you'll get
And that's the key, you can bet

(Outro)
Yeah, that's right, don't use star imports
Import what you need, that's the report
Clean code, that's the goal
And with Python, you're in control!

I don’t know about you, but I think that’s a good hook!

Feeling a little nostalgic for your favorite old software? We’ve got poems for that too, like this one mourning the loss and remembering the life of Python 2 (which was retired in 2020):

Oh Python 2, we bid thee farewell
With heavy hearts and a mournful knell
For all the years we spent with thee
Were filled with joy and camaraderie

But now the time has come to part
And let Python 2 depart
For though we loved thee to the end
It's time for Python 3 to ascend

Oh how we'll miss thy simple ways
Thy elegance and thy concise phrase
But now we must embrace the new
And bid thee fond adieu

So let us honor thee this day
And remember all the fun we had along the way
We'll miss thee dearly, that much is true
But in our hearts, we'll always remember Python 2.

Michael’s Poetic Programmic Bytes will not only allow you to mourn the losses of your favorite programming tools, but also any piece of software that was near and dear to your heart, like this farewell tribute to Club Penguin (of which I never got into):

Dear Club Penguin, the time has come
To say goodbye, our hearts are numb
We'll miss the island and the snow
And all the friends we've come to know

For years we've waddled on your shores
Played mini-games and explored
Dressed up in our penguin clothes
And danced in clubs with puffle bows

You gave us endless hours of fun
And taught us lessons, one by one
To always be kind and to share
And help others, to show we care

We'll miss your parties and your quests
And all the joy that you expressed
But as we say our last goodbye
We'll keep your memory alive

So thank you Club Penguin, for it all
For the laughter and the thrill
We'll never forget the love we felt
For you, and always will.

Last but certainly not least, this blog will allow you to submit your own programming-themed poems that I’d love to showcase, such as this one on the joys of HTML written by a first-grader:

HTML, oh HTML,
You make websites so pretty,
With colors and pictures and text,
You make the internet look so witty!

I love how you make things bold,
And add links to click and see,
It's so cool to make a webpage,
With you, it's easy as can be!

Sometimes I forget a tag,
And my page looks kinda funny,
But I know I can always fix it,
And make my site look sunny!

HTML, oh HTML,
You're my favorite thing to code,
I'll keep making webpages with you,
And sharing them all over the globe!

Or this gem from a 60-year-old celebrating the life of dial-up Internet:

Dial-up, oh dial-up,
You were slow but steady,
You brought the world to our fingertips,
And made us feel so heady.

With your hissing and buzzing sounds,
And the beeps that signaled our connection,
We knew we were in for a wait,
But we savored every moment of your affection.

You were our gateway to the internet,
A time when things were simpler,
We couldn't stream or download much,
But you gave us the world on a platter.

I remember waiting for pages to load,
And watching as images took shape,
It was a different time, a slower time,
But one that we embraced.

Now we have fiber and Wi-Fi,
And we can stream without a hitch,
But I'll always remember dial-up,
And the way you made us rich.

Dial-up, oh dial-up,
You may be a relic of the past,
But your memory lives on in our hearts,
A time that will always last.

Wow-that’s a beautifully written tribute to the joys of dial-up internet (hey, I’m just 27 and still recall the dial-up days).

So if you’re ever in the mood to learn basic coding concepts or just pay homage to your favorite programming tools or software (ahem Club Penguin fans), then you’ll definitely enjoy Michael’s Poetic Programming Bytes with a whole library of poems that were certainly not spit out of an AI chatbot.

More details on this expansion of the Michael’s Programming Bytes brand to come, and also,

Will this be a new Michael’s Programming Bytes tradition? Time will tell.