<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://gabrielstaples.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://gabrielstaples.com/" rel="alternate" type="text/html" /><updated>2026-03-01T16:24:59+00:00</updated><id>https://gabrielstaples.com/feed.xml</id><title type="html">GabrielStaples.com</title><subtitle>My personal website, covering anything from software development, C, C++, Python, electronics and electrical engineering, radio control aircraft, aeronautical engineering, basic life skills or life hacks, or even views on life, personal opinions, and political and religious discussions, beliefs, and points of view.</subtitle><author><name>Gabriel Staples</name></author><entry><title type="html">5 relationship models from Avatar 3: Fire and Ash</title><link href="https://gabrielstaples.com/avatar-relationship-models/" rel="alternate" type="text/html" title="5 relationship models from Avatar 3: Fire and Ash" /><published>2026-03-01T00:00:00+00:00</published><updated>2026-03-01T00:00:00+00:00</updated><id>https://gabrielstaples.com/avatar-relationship-models</id><content type="html" xml:base="https://gabrielstaples.com/avatar-relationship-models/"><![CDATA[<p>Last night I watched Avatar 3: Fire and Ash.</p>

<p>It was a beautiful movie, with powerful morals.</p>

<p>The thing that stuck with me the most were the wives/women in the various relationships portrayed in the movie, and how equally yoked they were to their husbands/men. I found this deeply touching and inspiring.</p>

<ol>
  <li>
    <p><strong>Jake Sully &amp; Neytiri:</strong> The central couple continues to navigate their life on Pandora, struggling with grief over their son’s death and adapting to new clans. She is as powerful, strong, and loyal as he is. They don’t see eye to eye on everything, but they honor each other with complete fidelity. I love them as a couple.</p>
  </li>
  <li>
    <p><strong>Tonowari &amp; Ronal:</strong> The leaders of the Metkayina clan remain a steady, powerful couple, with Ronal being pregnant in this season. She goes to war pregnant. Ronal is as strong and honorable a woman as there can be, as is her husband. I am inspired by her strength.</p>
  </li>
  <li>
    <p><strong>Varang &amp; Colonel Quaritch:</strong> a “power couple from hell”. This pairing consists of the leader of the fire-worshipping Ash People (Varang) and the resurrected Colonel Quaritch, who form a strategic and romantic alliance. They are equally yoked in power and ambition, and both wicked and willing to do evil. Though I do not admire them, I can see how they are equally matched. Neither of them lacks in this relationship. A wicked woman needs an equally wicked man.</p>
  </li>
  <li>
    <p><strong>Lo’ak &amp; Tsireya:</strong> The romance between Jake and Neytiri’s son, Lo’ak, and the Metkayina free diver, Tsireya, continues from The Way of Water. They are both strong, courageous, independent, yet deeply caring and loyal to each other. They are equally yoked in their love and respect for each other, and in their commitment to their people. I love them as a couple.</p>
  </li>
  <li>
    <p><strong>Spider &amp; Kiri:</strong> The teenaged son of Quaritch, Spider, and the adopted daughter of Jake/Neytiri, Kiri, share a close, evolving relationship that develops into a romance. Both powerful, honorable, and good.</p>
  </li>
</ol>

<p>I found these pairings to be deeply inspiring and a model for my own life and what I am seeking as a single man.</p>

<p><img width="1012" height="1500" alt="Image" src="https://github.com/user-attachments/assets/33e9898e-7efc-4d2f-bc1b-07088e625c02" /></p>]]></content><author><name>Gabriel Staples</name></author><category term="avatar" /><category term="relationships" /><category term="love" /><category term="inspiration" /><category term="dating" /><category term="marriage" /><summary type="html"><![CDATA[This article introduces 5 relationships from Avatar 3: Fire and Ash, highlighting how being equally yoked in power, motivation, and values is a key theme in healthy partnerships.]]></summary></entry><entry><title type="html">`git submodule` “Quick Start” guide</title><link href="https://gabrielstaples.com/git-submodule-guide/" rel="alternate" type="text/html" title="`git submodule` “Quick Start” guide" /><published>2024-11-01T00:00:00+00:00</published><updated>2024-11-01T00:00:00+00:00</updated><id>https://gabrielstaples.com/git-submodule-guide</id><content type="html" xml:base="https://gabrielstaples.com/git-submodule-guide/"><![CDATA[<p><a href="https://hits.seeyoufarm.com"><img src="https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgabrielstaples.com%2Fgit-submodule-guide%2F&amp;count_bg=%2379C83D&amp;title_bg=%23555555&amp;icon=&amp;icon_color=%23E7E7E7&amp;title=views+%28today+%2F+total%29&amp;edge_flat=false" alt="Hits" /></a></p>

<p>This is a <code class="language-plaintext highlighter-rouge">git submodule</code> “Quick Start” guide. Use it to get your team on-boarded.</p>

<h1 id="see-also">See also</h1>

<ol>
  <li>My <a href="https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles">eRCaGuy_dotfiles</a> repo:
    <ol>
      <li><a href="https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/blob/master/README.md">README.md</a></li>
      <li><a href="https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/blob/master/README.md#how-to-clone-whole-repo">README.md: “<em>Git submodules and Git LFS:</em> how to clone this repo and all git submodules and git lfs files”</a></li>
    </ol>
  </li>
</ol>

<h1 id="help-my-company-is-making-me-use-git-submodules-what-do-i-do">Help! My company is making me use git submodules! What do I do?</h1>

<h1 id="quick-summary">Quick summary</h1>

<ol>
  <li>A <code class="language-plaintext highlighter-rouge">git submodule</code> is git’s official way to have a repo within a repo.</li>
  <li>From this point forward, follow every <code class="language-plaintext highlighter-rouge">git clone</code>, <code class="language-plaintext highlighter-rouge">git pull</code>, or <code class="language-plaintext highlighter-rouge">git checkout</code> with <code class="language-plaintext highlighter-rouge">git submodule update --init --recursive</code>, which you run inside the outer repo.
    <ol>
      <li>The clone, pull, and checkout commands update the <em>pointers</em> to the submodules’ commit hashes, without actually updating anything on your disk, while the <code class="language-plaintext highlighter-rouge">git submodule update --init --recursive</code> command updates the <em>contents</em> of the submodule directories on your disk to match the <em>contents</em> of each submodule’s repository at each commit hash that the submodule pointers are pointing to.</li>
    </ol>
  </li>
  <li><code class="language-plaintext highlighter-rouge">cd</code> into a submodule directory within your repo to work within that sub-repo. Submodules are just normal repos. They show up as regular folders within your outer repo, but their files get stored in their own repo on the remote server.</li>
  <li>The outer repo stores a <strong>pointer</strong> to a specific commit hash in the inner repo. The outer repo doesn’t actually store the inner repo’s files on its remote server.</li>
  <li>Once you’ve updated a submodule, <code class="language-plaintext highlighter-rouge">cd</code> back up into your outer repo and run <code class="language-plaintext highlighter-rouge">git add my_submodule</code> and then <code class="language-plaintext highlighter-rouge">git commit</code> to commit and push your changes to the outer repo to point to the new commit hash of the inner repo/submodule.</li>
  <li>Follow the “Update your terminal” instructions below before you begin working with submodules to minimize your chance of making mistakes. This automagically adds a <code class="language-plaintext highlighter-rouge">repo_dir  branch_name  commit_hash  git_tags</code> line above your terminal prompt so you always know which repo, branch, commit hash, and tags you are working on whenever you <code class="language-plaintext highlighter-rouge">cd</code> into a git repo or submodule.</li>
  <li>Once you clone a repo with submodules, you no longer <em>need</em> to separately clone the submodules into your highest working directly like you did the outer repo. Just cd into the submodules within the outer repo to update and work on them there. Whether or not you follow this workflow, though, is up to you.</li>
  <li>Study and read the entire “Details” section below before you begin working with submodules.</li>
</ol>

<h1 id="details">Details</h1>

<h1 id="as-a-user-of-submodules">As a <strong>user</strong> of submodules</h1>

<p>A <code class="language-plaintext highlighter-rouge">git submodule</code> is git’s officially-supported way to allow you to have a repo within a repo. Nested, or “recursive”, submodules are allowed. So you can have a repo within a repo within a repo, and so on.</p>

<p>The way submodules work is that each repo has its own remote URL, so the outer repo containing the inner repo simply stores a <strong>pointer</strong> to a specific commit hash in the inner repo. On your file system, the inner repo is a <strong>folder</strong> within your outer repo, but in the remote git server, the inner repo is a <strong>pointer file</strong> to the other repo.</p>

<p>Submodules are super easy to use, <em>if</em> you understand the above. As a <em>user</em> of a repo with submodules, you only need <strong>1 single extra command</strong>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Recursively update all git submodules to their commit hash pointers as</span>
<span class="c"># currently committed in the outer repo.</span>
git submodule update <span class="nt">--init</span> <span class="nt">--recursive</span>
</code></pre></div></div>

<p>That’s it! Follow every <code class="language-plaintext highlighter-rouge">git clone</code>, <code class="language-plaintext highlighter-rouge">git pull</code>, or <code class="language-plaintext highlighter-rouge">git checkout</code> with <code class="language-plaintext highlighter-rouge">git submodule update --init --recursive</code>, like this:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 1. clone the outer rep and update submodule pointers</span>
git clone path/to/some_repo
<span class="nb">cd </span>some_repo
<span class="c"># 2. update the filesystem (submodule dirs) to match those pointers</span>
git submodule update <span class="nt">--init</span> <span class="nt">--recursive</span>

<span class="c"># 1. update the outer repo and submodule pointers</span>
git pull
<span class="c"># 2. update the filesystem (submodule dirs) to match those pointers</span>
git submodule update <span class="nt">--init</span> <span class="nt">--recursive</span>

<span class="c"># 1. check out a branch and its submodule pointers</span>
git checkout some_branch
<span class="c"># 2. update the filesystem (submodule dirs) to match those pointers</span>
git submodule update <span class="nt">--init</span> <span class="nt">--recursive</span>
</code></pre></div></div>

<p>This ensures that the <strong>contents of your fle system</strong> are always in sync with the <strong>commit hash pointers</strong> that the submodules are pointing to. If there are no submodules, running this command has no effect, so you can just get into the habit of running it if you’re not sure.</p>

<p>If you ever run <code class="language-plaintext highlighter-rouge">git status</code> and see that the submodule has changes but you didn’t change anything within it, then it means you did a pull or checkout or something on the outer repo and the <strong>commit pointer</strong> for that submodule has changed, but you didn’t update the filesystem to match yet. So, run <code class="language-plaintext highlighter-rouge">git submodule update --init --recursive</code> to fix it.</p>

<h1 id="as-a-maintainer-of-the-outer-repo">As a <strong>maintainer</strong> of the outer repo</h1>

<p>A submodule is just a repo. So, <code class="language-plaintext highlighter-rouge">cd</code> into it and work on it like you would any other repo. When done, commit and push your changes.</p>

<p>When you’re ready to update the outer repo to point to the new commit hash of the submodule, do this:</p>
<ol>
  <li><code class="language-plaintext highlighter-rouge">cd</code> back up into the outer repo. Ex: <code class="language-plaintext highlighter-rouge">cd ..</code></li>
  <li>Add and commit your changes to the outer repo to point to the new commit hash of the submodule:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> git add my_submodule
 git commit <span class="nt">-m</span> <span class="s2">"Update my_submodule to commit hash 1234567"</span>
</code></pre></div>    </div>
  </li>
</ol>

<p>If you ever want to forcefully update all submodules to their latest commit hash pointers, you can do this. Notice the addition of the <code class="language-plaintext highlighter-rouge">--remote</code> flag:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git submodule update <span class="nt">--init</span> <span class="nt">--recursive</span> <span class="nt">--remote</span>
git add <span class="nt">-A</span>
git commit <span class="nt">-m</span> <span class="s2">"Update all submodules to their latest upstream changes"</span>
</code></pre></div></div>

<p>Using the <code class="language-plaintext highlighter-rouge">--remote</code> flag is as if you manually <code class="language-plaintext highlighter-rouge">cd</code>ed into each subrepo and manually ran <code class="language-plaintext highlighter-rouge">git pull origin main</code> or equivalent to pull the latest changes from the upstream repo.</p>

<p>See my answer here: <a href="https://stackoverflow.com/a/74470585/4561887">Update Git submodule to latest commit on origin</a>. Study the comments above the commands below:</p>

<blockquote>
  <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Option 1: as a **user** of the outer repo, pull the latest changes of the</span>
<span class="c"># sub-repos as previously specified (pointed to as commit hashes) by developers</span>
<span class="c"># of this outer repo.</span>
<span class="c"># - This recursively updates all git submodules to their commit hash pointers as</span>
<span class="c">#   currently committed in the outer repo.</span>
git submodule update <span class="nt">--init</span> <span class="nt">--recursive</span>

<span class="c"># Option 2. As a **developer** of the outer repo, update all subrepos to force</span>
<span class="c"># them each to pull the latest changes from their respective upstreams (ex: via</span>
<span class="c"># `git pull origin main` or `git pull origin master`, or similar, for each</span>
<span class="c"># sub-repo). </span>
git submodule update <span class="nt">--init</span> <span class="nt">--recursive</span> <span class="nt">--remote</span>
<span class="c">#</span>
<span class="c"># For just Option 2 above: now add and commit these subrepo changes </span>
<span class="c"># you just pulled</span>
git add <span class="nt">-A</span>
git commit <span class="nt">-m</span> <span class="s2">"Update all subrepos to their latest upstream changes"</span>
</code></pre></div>  </div>
</blockquote>

<p>Adding a repo as a submodule inside another repo:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># General format</span>
git submodule add URL_to_repo
<span class="c"># or</span>
git submodule add URL_to_repo path/to/where/you/want/to/put/it
<span class="c"># then commit when done</span>
git commit

<span class="c"># Examples:</span>
git submodule add https://github.com/ElectricRCAircraftGuy/ripgrep_replace.git
git submodule add https://gitlab.com/ElectricRCAircraftGuy/systemd-by-example.git
git commit
<span class="c"># etc.</span>
</code></pre></div></div>

<p>Removing a submodule from a repo (<a href="https://stackoverflow.com/a/1260982/4561887">see here</a>):</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git <span class="nb">rm </span>path/to/my_submodule
git commit
<span class="c"># delete the submodule metadata directory too</span>
<span class="nb">rm</span> <span class="nt">-rf</span> .git/modules/my_submodule

<span class="c"># Example</span>
git <span class="nb">rm </span>ripgrep_replace
git commit
<span class="nb">rm</span> <span class="nt">-rf</span> .git/modules/ripgrep_replace
</code></pre></div></div>

<h1 id="when-building">When building…</h1>

<p>Ensure that <code class="language-plaintext highlighter-rouge">git status</code> does NOT show any changes in the submodules that you don’t expect to see. If you run <code class="language-plaintext highlighter-rouge">git checkout some_branch</code> and that points to a new commit hash for your submodule, then you need to run <code class="language-plaintext highlighter-rouge">git submodule update --init --recursive</code> to update your filesystem to match the new commit hash pointer. If you don’t, and then you build, <strong>your build is broken</strong> because you’re building with the wrong submodule content on your file system!</p>

<h1 id="update-your-terminal-this-works-in-git-bash-on-windows-too-to-always-show-your-git-info-at-the-command-line">Update your terminal (this works in Git Bash on Windows too) to always show your git info at the command-line</h1>

<p>Before you start working with submodules, I highly highly <strong>highly</strong> recommend you do the following to always show your <code class="language-plaintext highlighter-rouge">repo_dir  branch_name  commit_hash  git_tags</code> in your prompt. This way, your prompt will look like this, for example, whenever you <code class="language-plaintext highlighter-rouge">cd</code> into a git repo. As you can see here, above every <code class="language-plaintext highlighter-rouge">$</code> line in your prompt, if you are ever inside a git repo, you will automatically see the repo name, branch name, commit hash, and any tags, all in one line, so you never screw up whether you are editing the outer repo or a submodule, and you always know which branch and commit hash you are on. This is <strong>invaluable</strong> when working with submodules!:</p>

<p><em>What you’ll see in your terminal:</em></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$SHLVL</span>:1 eRCaGuy_dotfiles  master  ca7dc5d <span class="o">(</span>dirty<span class="o">)</span>
gabriel:~/GS/dev/eRCaGuy_dotfiles<span class="err">$</span>
</code></pre></div></div>

<h3 id="installation-instructions">Installation instructions</h3>

<p>Install the above magic into your terminal. This works on any OS, including Windows inside Git Bash:</p>

<ol>
  <li>Ensure your Windows home dir is set correctly by following the instructions in my “Quick summary” here: <a href="https://stackoverflow.com/a/77450145/4561887">Stack Overflow: Change the location of the <code class="language-plaintext highlighter-rouge">~</code> directory in a Windows install of Git Bash</a></li>
  <li>Close and re-opoen all Git Bash terminals you have open to make the home directory change take effect.</li>
  <li>Install the above prompt string magic into your terminal:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c"># download this prompt string magic </span>
 curl <span class="nt">-L</span> https://raw.githubusercontent.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/refs/heads/master/home/.bash_prompt_str <span class="o">&gt;</span> ~/.bash_prompt_str
    
 <span class="c"># add a command to automatically source it in the bottom of your .bashrc file</span>
 <span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">. ~/.bash_prompt_str"</span> <span class="o">&gt;&gt;</span> ~/.bashrc
</code></pre></div>    </div>
  </li>
  <li>Close and reopen all Git Bash terminals you have open, again, to make the prompt string take effect.</li>
  <li>Simply <code class="language-plaintext highlighter-rouge">cd</code> into any git repo and hit <kbd>Enter</kbd> a few times to see the magic in action. <code class="language-plaintext highlighter-rouge">cd</code>-ing into a submodule will show you are inside the submodule repo instead of the outer repo.</li>
  <li>Done.</li>
</ol>

<h1 id="aliases">Aliases</h1>

<p>If <code class="language-plaintext highlighter-rouge">git submodule update --init --recursive</code> is too long for you, you can create a git alias for it so you can just run <code class="language-plaintext highlighter-rouge">git sub</code> instead. Here’s how:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git config <span class="nt">--global</span> alias.sub <span class="s1">'submodule update --init --recursive'</span>
</code></pre></div></div>

<p>The above command adds the following alias to your <code class="language-plaintext highlighter-rouge">~/.gitconfig</code> file:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span><span class="nb">alias</span><span class="o">]</span>
	sub <span class="o">=</span> submodule update <span class="nt">--init</span> <span class="nt">--recursive</span>
</code></pre></div></div>

<p><em>Now, you can just run <code class="language-plaintext highlighter-rouge">git sub</code> instead of <code class="language-plaintext highlighter-rouge">git submodule update --init --recursive</code>.</em></p>

<p>Read out the alias to verify it by running <code class="language-plaintext highlighter-rouge">git config alias.sub</code>. You will see:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>submodule update --init --recursive
</code></pre></div></div>

<p>Or, even better, add <code class="language-plaintext highlighter-rouge">--show-origin</code> to see which config file the alias is stored in: <code class="language-plaintext highlighter-rouge">git config --show-origin alias.sub</code>.</p>

<p>Example run and output:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git config <span class="nt">--show-origin</span> alias.sub
file:/home/gabriel/.gitconfig	submodule update <span class="nt">--init</span> <span class="nt">--recursive</span>
</code></pre></div></div>

<h1 id="references-and-going-further">References and going further</h1>

<ol>
  <li>Official <code class="language-plaintext highlighter-rouge">git submodule</code> documentation: <a href="https://git-scm.com/book/en/v2/Git-Tools-Submodules">https://git-scm.com/book/en/v2/Git-Tools-Submodules</a></li>
  <li>
    <p>My answer: <a href="https://stackoverflow.com/a/62368415/4561887">Git: How to make outer repository and embedded repository work as common/standalone repository?</a></p>

    <p>I mention that there are three tools to manage submodules:</p>

    <blockquote>
      <ol>
        <li><code class="language-plaintext highlighter-rouge">git submodule</code> - <a href="https://git-scm.com/docs/git-submodule">https://git-scm.com/docs/git-submodule</a> - the canonical, officially-supported tool built into <code class="language-plaintext highlighter-rouge">git</code>.</li>
        <li><code class="language-plaintext highlighter-rouge">git subtree</code> - <a href="https://www.atlassian.com/git/tutorials/git-subtree">https://www.atlassian.com/git/tutorials/git-subtree</a></li>
        <li><code class="language-plaintext highlighter-rouge">git subrepo</code> - <a href="https://github.com/ingydotnet/git-subrepo">https://github.com/ingydotnet/git-subrepo</a></li>
      </ol>
    </blockquote>
  </li>
</ol>]]></content><author><name>Gabriel Staples</name></author><category term="tutorial" /><category term="programming" /><category term="code" /><category term="git" /><summary type="html"><![CDATA[This is a `git submodule` "Quick Start" guide. Use it to get your team on-boarded...1. A `git submodule` is git's official way to have a repo within a repo. 2. From this point forward, follow every `git clone`, `git pull`, or `git checkout` with `git submodule update --init --recursive`, which you run inside the outer repo.]]></summary></entry><entry><title type="html">The perfect mini-fridge or freezer for the Tesla Model Y</title><link href="https://gabrielstaples.com/perfect-fridge-for-tesla-model-y/" rel="alternate" type="text/html" title="The perfect mini-fridge or freezer for the Tesla Model Y" /><published>2024-10-30T00:00:00+00:00</published><updated>2024-11-11T00:00:00+00:00</updated><id>https://gabrielstaples.com/perfect-fridge-for-tesla-model-y</id><content type="html" xml:base="https://gabrielstaples.com/perfect-fridge-for-tesla-model-y/"><![CDATA[<p><a href="https://hits.seeyoufarm.com"><img src="https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgabrielstaples.com%2Fperfect-fridge-for-tesla-model-y%2F&amp;count_bg=%2379C83D&amp;title_bg=%23555555&amp;icon=&amp;icon_color=%23E7E7E7&amp;title=views+%28today+%2F+total%29&amp;edge_flat=false" alt="Hits" /></a></p>

<h1 id="the-setpower-ab15-mini-fridgefreezer-for--200-on-amazon-is-the-perfect-fridge-for-the-tesla-model-y">The <a href="https://amzn.to/4fr717k">Setpower AB15 mini-fridge/freezer</a> for &lt; $200 on Amazon is <em>the perfect fridge for the Tesla Model Y</em></h1>

<p>Get the fridge here: <a href="https://amzn.to/4fr717k">https://amzn.to/4fr717k</a>.</p>

<p><a href="https://ts.la/gabriel236516">Get $1000 off the purchase of a new Tesla here</a>. I love my Tesla Model Y, and have lifted it even so I can take it off-roading to remote camping sites only trucks can normally get to.</p>

<p>The Setpower AB15 mini-fridge/freezer fits perfectly in nearly every place you can think of in the 7-seater Tesla Model Y, including:</p>
<ol>
  <li>In the frunk
    <ol>
      <li>…including pushed against the far left or far right sides so long as the back (high side) of the fridge is near the center of the frunk.</li>
    </ol>
  </li>
  <li>In the rear sub-floor trunk.</li>
  <li>In the front passenger seat footwell.</li>
  <li>In the rear passenger seat footwells.</li>
  <li>On the front center console between the two front seats.</li>
  <li>On the rear middle seat when the middle seat only is down.</li>
  <li>Behind the 3rd row seats when in 7-seater mode.
    <ol>
      <li>…including when shoved against the far left or far right sides so long as the back (high side) of the fridge is near the center of the car.</li>
    </ol>
  </li>
  <li>Stacked two-fridges-wide in 6-seater mode with one of the 3rd row seats down.</li>
</ol>

<h2 id="what-it-holds">What it holds</h2>

<p align="left" width="100%">
    <a href="https://github.com/user-attachments/assets/a163d692-5450-470a-9982-1533d021aeda" target="_blank">
        <img width="60%" src="https://github.com/user-attachments/assets/a163d692-5450-470a-9982-1533d021aeda" /> 
    </a>
</p>

<p>The fridge/freezer can hold all of this at once, for example:</p>

<ol>
  <li>1 gallon of milk</li>
  <li>Either:
    <ol>
      <li>This:
        <ol>
          <li>64 oz (half gallon) bottle</li>
          <li>and 28 oz Powerade bottle</li>
        </ol>
      </li>
      <li>OR this:
        <ol>
          <li>1 large plastic tupperware container</li>
        </ol>
      </li>
    </ol>
  </li>
  <li>32 oz (1 quart) paper carton (chocolate milk shown below)</li>
  <li>2 or 3 x 8 oz packs of sliced cheese (only 1 shown in the photos)</li>
  <li>32 oz (2 lb) bag of shredded cheese, partially used but still mostly full</li>
  <li>half loaf of bread</li>
</ol>

<p>Photos of food in the fridge just now. And yes, it closes in all of these configurations:</p>

<p><img src="https://github.com/user-attachments/assets/dfd4ca1e-d3e1-4ee3-87ec-c8c96ec5fb54" alt="PXL_20241111_213906471" /></p>

<p><img src="https://github.com/user-attachments/assets/d9d88f0f-43fa-4bd8-9609-8e88e243f3fc" alt="PXL_20241111_214044007" /></p>

<p><img src="https://github.com/user-attachments/assets/29db5945-45b0-44a7-a60d-ffdae190d7f5" alt="PXL_20241111_213830327" /></p>

<p><img src="https://github.com/user-attachments/assets/2fee0dae-0d11-452e-b6c0-4babd0b0c392" alt="PXL_20241111_213810982" /></p>

<p>More of the large liquids shown fitting into the large compartment:</p>

<p align="left" width="100%">
    <a href="https://github.com/user-attachments/assets/d9ec160e-a606-49f2-90e7-e3854973edb6" target="_blank">
        <img width="100%" src="https://github.com/user-attachments/assets/d9ec160e-a606-49f2-90e7-e3854973edb6" /> 
    </a>
</p>

<p><img src="https://github.com/user-attachments/assets/00a9f217-b7ab-4100-89dd-ae3e4ed9e02f" alt="PXL_20241111_214216849" /></p>

<p><img src="https://github.com/user-attachments/assets/9bfe9611-ab0f-44de-89ae-5b4e7533fcc4" alt="PXL_20241111_214222019" /></p>

<p><img src="https://github.com/user-attachments/assets/945b1a07-2b59-4353-a7e8-e17c802f79ae" alt="PXL_20241111_214309795" /></p>

<p><img src="https://github.com/user-attachments/assets/06c48a38-c541-40a1-994b-f876acd18cc3" alt="PXL_20241111_214326409" /></p>

<p><img src="https://github.com/user-attachments/assets/bb931428-65ac-4591-b890-7eae864f0f4b" alt="PXL_20241111_214440031" /></p>

<h2 id="where-the-setpower-ab15-mini-fridgefreezer-fits-in-the-tesla-model-y">Where the <a href="https://amzn.to/4fr717k">Setpower AB15 mini-fridge/freezer</a> fits in the Tesla Model Y</h2>

<p>See the photos here. This fridge literally fits perfectly, as though it was custom made and custom-fit for the Tesla Model Y 5 and 7-seaters. If it was even a few mm longer in any dimension, it wouldn’t fit in many of these places.</p>

<p><img src="https://github.com/user-attachments/assets/2f30ed17-ed46-4dd5-b9a4-bd6820668742" alt="PXL_20241006_012254058" /></p>

<p><img src="https://github.com/user-attachments/assets/e4d85389-4588-423d-83c6-bb83823c5d24" alt="PXL_20241006_012351968" /></p>

<p><img src="https://github.com/user-attachments/assets/7455971d-5536-4d3d-aeb7-ff787a5a1fb9" alt="PXL_20241006_012513148" /></p>

<p><img src="https://github.com/user-attachments/assets/ba893b8f-d04d-445e-830f-f422658e3cfb" alt="PXL_20241006_012523130" /></p>

<p><img src="https://github.com/user-attachments/assets/7787d11a-a3c3-4ec2-ac50-6868aafdc79d" alt="PXL_20241006_012714777" /></p>

<p><img src="https://github.com/user-attachments/assets/0c4f8bf6-80f5-4a93-a8b2-5fc6f6bd20be" alt="PXL_20241006_013355827" /></p>

<p><img src="https://github.com/user-attachments/assets/bb0d63b4-6595-4c26-8fbc-f5116a5363af" alt="PXL_20241006_013747712" /></p>

<p><img src="https://github.com/user-attachments/assets/072b6cb8-9c0b-4fd9-bc1a-a6882f238799" alt="PXL_20241006_013943126" /></p>

<p><img src="https://github.com/user-attachments/assets/da4a20d1-d4bb-47c2-ace4-90cd28e461d5" alt="PXL_20241006_014346081" /></p>

<p><img src="https://github.com/user-attachments/assets/5b4cd2c2-429c-4ea2-b4d2-0c34d2ff4949" alt="PXL_20241006_014856141" /></p>

<p><img src="https://github.com/user-attachments/assets/0ec09bca-3fed-4faf-b753-cb1ccc7de34e" alt="PXL_20241006_014918484 NIGHT" /></p>

<p><img src="https://github.com/user-attachments/assets/9e92d572-b34f-4076-9b74-b37d03cfe991" alt="PXL_20241006_015032708" /></p>

<p><img src="https://github.com/user-attachments/assets/dd81301e-5727-49f5-b32b-17c7c6630593" alt="PXL_20241006_015056630" /></p>

<p><img src="https://github.com/user-attachments/assets/e9da47ff-d130-4a74-94a0-fff6bbbd2d64" alt="PXL_20241006_015209645" /></p>

<p><img src="https://github.com/user-attachments/assets/f0eea597-f8a0-4197-9850-fb4cfa545135" alt="PXL_20241006_015715565" /></p>

<p><img src="https://github.com/user-attachments/assets/a3104d44-93f7-4137-9e7e-3b515dedd6fc" alt="PXL_20241007_201934879" /></p>

<p>Yes, the sub-trunk cover closes just fine with the fridge in the sub-trunk, without making contact with the fridge: 
<img src="https://github.com/user-attachments/assets/31effb39-420f-4d4d-b0c9-ad7161ff3ed9" alt="PXL_20241007_202001139" /></p>

<p><img src="https://github.com/user-attachments/assets/e888a0c9-ecc5-476f-b42c-7fce925a36d4" alt="PXL_20241007_202019349" /></p>

<p><img src="https://github.com/user-attachments/assets/1ae563b6-0df4-4746-8c91-e0af5141296d" alt="PXL_20241007_202153459" /></p>

<p><img src="https://github.com/user-attachments/assets/d38283a8-cf14-483d-9ad6-6373fb79e8ab" alt="PXL_20241029_221638997" /></p>

<p>Yes, the frunk closes just fine so long as you keep the curved part of the fridge towards the left or right edges, and the high rear part of the fridge near the center of the frunk:
<img src="https://github.com/user-attachments/assets/60e36947-98fe-45f8-ab17-fffda709dc0f" alt="PXL_20241029_221644590" /></p>

<p><img src="https://github.com/user-attachments/assets/ba2faced-b749-48f6-8e08-cd6b4967fdd2" alt="PXL_20241029_221734225" /></p>

<p><img src="https://github.com/user-attachments/assets/1eaeaf33-d2fa-4e60-991e-79c6d6dc9682" alt="PXL_20241029_221737427 MP" /></p>

<p><img src="https://github.com/user-attachments/assets/0847a541-c085-44bf-b27b-73bf52af80bd" alt="PXL_20241029_222218359" /></p>

<p><img src="https://github.com/user-attachments/assets/7ee45bdb-4d40-448e-ac4a-5c76f4fca15b" alt="PXL_20241029_222805270" /></p>

<p><img src="https://github.com/user-attachments/assets/cf8e905b-92e9-48c3-9d30-18bf935f2e8a" alt="PXL_20241029_222817432" /></p>

<p><img src="https://github.com/user-attachments/assets/0625ce9d-4044-42af-b94c-b1f3455c429c" alt="PXL_20241029_223118538" /></p>

<p><strong>This is the most attractive part of this fridge for me! It fits in either position shown here, behind the 3rd row seats 6 and 7 in the 7-seater Model Y!</strong> Just about any other fridge, including the black AB16 further below, does NOT fit here because the trunk is so tight on the left and right sides of the car. The AB15, on the other hand, fits perfectly, due to its rounded corners and perfect dimensions. If it was even a few mm wider or taller or bigger in any dimension, <em>it would not fit.</em> As shown in the two images below, however, the trunk still closes perfectly:
<img src="https://github.com/user-attachments/assets/dbb18ece-0693-4368-a969-4d7398cba3dc" alt="PXL_20241029_223126542" />
<img src="https://github.com/user-attachments/assets/1d2a4763-d71b-4a4a-aa6e-965f4cdf71b1" alt="PXL_20241029_223617014" /></p>

<p>Note: the AB16 black fridge here blocks the door from closing on the far right because the corner sticks out too much. The corners of the blue AB15 are a bit more rounded. If it was two AB15s here instead, they would fit just barely and the trunk door would still close:</p>

<p><img src="https://github.com/user-attachments/assets/85516221-64ba-4d8f-8487-a1ead6d44e9c" alt="PXL_20241029_223321757" /></p>

<p><img src="https://github.com/user-attachments/assets/014e13c8-8649-4503-8068-9c81a5942705" alt="image" /></p>

<p>Again, get the Setpower AB15 mini-fridge/freezer here: <a href="https://amzn.to/4fr717k">https://amzn.to/4fr717k</a>. I just got one for camping and road trips and it’s perfect. It holds a gallon of milk just fine too.</p>

<p><a href="https://ts.la/gabriel236516">Get $1000 off the purchase of a new Tesla here</a>. I love my Tesla Model Y, and have lifted it even so I can take it off-roading to remote camping sites only trucks can normally get to.</p>

<p><em>This content is not sponsored. I bought the fridge myself. But, the Amazon links above to the product are affiliate links, so if you buy the fridge through those links, I get a small commission at no extra cost to you. Thanks for supporting my work!</em></p>]]></content><author><name>Gabriel Staples</name></author><category term="Tesla" /><category term="shopping" /><category term="electric car" /><summary type="html"><![CDATA[The Setpower AB15 mini-fridge/freezer fits perfectly in nearly every place you can think of in the 7-seater Tesla Model Y, including: 1. In the frunk 1. ...including pushed against the far left or far right sides so long as the back (high side) of the fridge is near the center of the frunk.]]></summary></entry><entry><title type="html">7 Types of civilization: Earth is level 0.7; the modern (beginning with Tesla) electric car is a step towards level 1.0</title><link href="https://gabrielstaples.com/7-types-of-civilization/" rel="alternate" type="text/html" title="7 Types of civilization: Earth is level 0.7; the modern (beginning with Tesla) electric car is a step towards level 1.0" /><published>2024-09-08T00:00:00+00:00</published><updated>2024-09-08T00:00:00+00:00</updated><id>https://gabrielstaples.com/7-types-of-civilization</id><content type="html" xml:base="https://gabrielstaples.com/7-types-of-civilization/"><![CDATA[<p><a href="https://hits.seeyoufarm.com"><img src="https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgabrielstaples.com%2F7-types-of-civilization%2F&amp;count_bg=%2379C83D&amp;title_bg=%23555555&amp;icon=&amp;icon_color=%23E7E7E7&amp;title=views+%28today+%2F+total%29&amp;edge_flat=false" alt="Hits" /></a></p>

<p>In theoretical physics, according to the <a href="https://en.wikipedia.org/wiki/Kardashev_scale">Kardashev scale</a>, there are 7 Types of civilization. Earth is level 0.7.</p>

<p>I view the electric car as a step towards level 1.0. It moves towards a system that can harness energy from non-burnable resources. It also enables interplanetary travel. The cost of moving gasoline or burnable fuels to Mars, for instance, is prohibitive. Using electric vehicles allows us to transport vehicles to Mars that can be “fueled” using energy available on Mars; namely: solar power. Electric cars can also utilize nuclear power. They are a clear and unavoidable step towards society’s advancement to eventually becoming a Type 1.0 civilization.</p>

<p>Here’s the video, at the time where Level 0 is introduced:</p>

<p><a href="https://youtu.be/Yb4H098aMRI?t=115" target="_blank">https://youtu.be/Yb4H098aMRI?t=115</a></p>

<p align="left" width="100%">
    <a href="https://youtu.be/Yb4H098aMRI?t=115" target="_blank">
        <img width="65%" src="https://github.com/user-attachments/assets/61fcbdb2-81e9-4fff-9cd8-a76c055619a5" /> 
    </a>
</p>

<p>You might say that God himself is part of a level 7 civilization, for he is truly a God of gods.</p>

<p>When people (typically right-wing conservatives, in my self-acknowledged-<em>biased</em> observation) grab onto their level 0 technology with a death grip and condemn level 1 technology as Level 1 technology is introduced, parts of me feel really angry. So, I get with those emotions and feel and process them. I consider such people to have blinders on and I just want to shake them and say,</p>

<blockquote>
  <p><strong>Wake up! Don’t you know you are blocking humanity’s progression towards becoming a Level 1 civilization!!!??? Your perspective is so limiting and blind!</strong></p>
</blockquote>

<p>To demonstrate my belief: as of a couple months ago: I now own a Tesla Model Y, and have invested $10k in <a href="https://ts.la/gabriel236516">Tesla (get $1000 off here)</a> and $10k into a solar-powered electric car company called <a href="https://lz953.isrefer.com/go/preorder/a125469">Aptera (get $30 off your otherwise $100 pre-order here)</a>, with a 2-seat car that has a range up to 1000 miles. So, this is stuff I believe in with my money, not just my mind. In the next 20 years, I may be able to increase my investment in Tesla into the millions of dollars–which I expect to mostly be achieved by growth, not deposits.</p>

<p>So, hopefully this video illuminates your understanding, taking into account any religious beliefs you may hold as well which resonate with what you see here.</p>

<p>Another interesting perspective I hold which resonates with me is that aging and natural death is an automatic, built-in, natural and critical part of societal growth in order to allow civilizations to progress. As older generations who death-grip their old technology pass away, so does society’s resistance to progress.</p>

<p>These are perspectives to consider, and which resonate with me. Again, with some modifications to how it is presented here, my simultaneously-held both scientific and religious belief is that God himself is, or is part of, a Level 7 civilization.</p>

<p>If you watch <a href="https://youtu.be/Yb4H098aMRI?t=115">the video</a>, it his highly likely your perspectives will be widened.</p>

<p>Sincerely,</p>

<p>Gabriel Staples</p>

<h1 id="referral-links">Referral links</h1>

<p>I first posted these and others here: <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/issues/95">https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/issues/95</a></p>

<h2 id="1-aptera-electric-car-referral-link">1. Aptera electric car referral link</h2>

<p><a href="https://aptera.us/referral-dashboard/">https://aptera.us/referral-dashboard/</a></p>

<blockquote>
  <p>Share this URL with your friends and they’ll receive a promo code for $30 off an Aptera pre-order. For every person you refer, you’ll earn a $1,000 credit towards your Aptera. Referrals must complete delivery of their vehicles to qualify. <a href="https://apteramotors-my.sharepoint.com/:w:/g/personal/audra_leslie_aptera_us/EeUprtNfTAhCjC3eCnbbHD0BJjdCqSTKSQgtWSs1HDN54w?rtime=u_PawdaD20g">Additional terms and conditions apply.</a></p>
</blockquote>

<h3 id="referral-link-reserve-an-aptera-long-range-high-efficiency-solar-recharged-electric-car-for-only-70-save-30-on-your-100-preorder">Referral link: reserve an Aptera long-range high-efficiency solar-recharged electric car for only $70 (save $30 on your $100 preorder)</h3>
<p><a href="https://lz953.isrefer.com/go/preorder/a125469">https://lz953.isrefer.com/go/preorder/a125469</a></p>

<h3 id="videos-to-learn-about-aptera">Videos to learn about Aptera</h3>
<ol>
  <li><a href="https://youtu.be/9QnLOomcGiM?t=58">https://youtu.be/9QnLOomcGiM?t=58</a> - Aptera June 2024 Investor Webinar No Commentary
    <ol>
      <li><a href="https://youtu.be/yAR_7rNNfcw?list=PL-skkg65NTXiG1SBU1EgWDDpM0uyTqaHb">https://youtu.be/yAR_7rNNfcw?list=PL-skkg65NTXiG1SBU1EgWDDpM0uyTqaHb</a></li>
    </ol>
  </li>
  <li>Confronting Aptera’s CEO w/ Investor - WHERE’S MY CAR? - <a href="https://youtu.be/Xvma9paBrh8?list=PL-skkg65NTXiG1SBU1EgWDDpM0uyTqaHb">https://youtu.be/Xvma9paBrh8?list=PL-skkg65NTXiG1SBU1EgWDDpM0uyTqaHb</a></li>
</ol>

<h2 id="2-tesla-car-purchase-referral-link-get-1000-off-your-tesla-purchase-price">2. Tesla car purchase referral link: get $1000 off your Tesla purchase price</h2>

<p>You get $1000 off. I get $500 in credits.</p>

<p>Details Tesla gives to me:</p>

<blockquote>
  <h3 id="earn-500-by-giving-anyone-1000-off-a-new-tesla">Earn $500 by Giving Anyone $1,000 Off a New Tesla</h3>

  <p>Earn $500 toward Supercharging, software upgrades, merchandise, service payments or a new vehicle for each person you refer who takes delivery. Limited to 10 awards. You’ll also have a chance to win an invitation to an upcoming Tesla event. <strong>The first 10 people who order a new Tesla using your referral link will receive $1,000 off the purchase price.</strong></p>
</blockquote>

<p>Referral link: <a href="https://ts.la/gabriel236516">https://ts.la/gabriel236516</a></p>]]></content><author><name>Gabriel Staples</name></author><category term="Levels of civilization" /><category term="Kardashev scale" /><category term="electric car" /><category term="Tesla" /><category term="Aptera" /><category term="Elon Musk" /><category term="Religion" /><category term="God" /><category term="Theoretical Physics" /><summary type="html"><![CDATA[In theoretical physics, according to the [Kardashev scale](https://en.wikipedia.org/wiki/Kardashev_scale), there are 7 Types of civilization. Earth is level 0.7. I view the electric car as a step towards level 1.0.]]></summary></entry><entry><title type="html">Installing Minecraft Mods in Java Edition</title><link href="https://gabrielstaples.com/installing-minecraft-mods/" rel="alternate" type="text/html" title="Installing Minecraft Mods in Java Edition" /><published>2024-07-06T00:00:00+00:00</published><updated>2024-07-06T00:00:00+00:00</updated><id>https://gabrielstaples.com/installing-minecraft-mods</id><content type="html" xml:base="https://gabrielstaples.com/installing-minecraft-mods/"><![CDATA[<p><a href="https://hits.seeyoufarm.com"><img src="https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgabrielstaples.com%2Finstalling-minecraft-mods%2F&amp;count_bg=%2379C83D&amp;title_bg=%23555555&amp;icon=&amp;icon_color=%23E7E7E7&amp;title=views+%28today+%2F+total%29&amp;edge_flat=false" alt="Hits" /></a></p>

<p><em>Notes:</em></p>
<ol>
  <li><em>Tested in Linux Ubuntu 20.04 and 22.04 in July 2024, for my kids.</em></li>
  <li><em>Even though these instructions are tailored for Linux Ubuntu, the concepts are the same for Windows and Mac.</em></li>
  <li><em>I updated the instructions for later versions of the Dragon Survival mod and Forge, so the screenshots may no longer match the text and commands exactly.</em></li>
</ol>

<!-- 
**If you're looking for the easiest way to do this, go to the bottom of this article and see "Option 2: using the CurseForge app".**
-->

<h1 id="option-1-the-manual-method">Option 1: The manual method</h1>

<h2 id="quick-summary">Quick summary</h2>

<ol>
  <li>Download a mod. Ex: from:
    <ol>
      <li><a href="https://www.curseforge.com/">https://www.curseforge.com/</a></li>
    </ol>
  </li>
  <li>Download, install, and configure the correct version of the Forge mod loader for this mod, if you don’t already have it installed.</li>
  <li>Install the mod by copying it into the Forge <code class="language-plaintext highlighter-rouge">mods</code> folder at this path, for instance: <code class="language-plaintext highlighter-rouge">~/.minecraft/versions/1.20.1-forge-47.3.5/mods/</code>.</li>
  <li>Launch the game using the Forge version you just installed, ensure the mod is active, and start a new game to see the mod in action.</li>
</ol>

<h2 id="details">Details</h2>

<h3 id="1-download-a-mod">1. Download a mod</h3>

<ol>
  <li>
    <p>Find a mod you want, such as “Dragon Survival [Forge]” on <a href="https://www.curseforge.com/">https://www.curseforge.com/</a>, here: <a href="https://www.curseforge.com/minecraft/mc-mods/dragons-survival">https://www.curseforge.com/minecraft/mc-mods/dragons-survival</a></p>
  </li>
  <li>
    <p>Click the “Download” button, as shown below:</p>

    <p align="left" width="100%">
     <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/ecb8e835-f4d9-4fc0-abc5-73d0bffdfc34">
         <img width="100%" src="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/ecb8e835-f4d9-4fc0-abc5-73d0bffdfc34" /> 
     </a>
 </p>
  </li>
  <li>Click the “All Game Versions” dropdown menu and select the version of Minecraft you want to install the mod into. If not sure, just choose the latest version available, which is <code class="language-plaintext highlighter-rouge">1.20.1</code> for this mod at the time of this writing.</li>
  <li>
    <p>Click the “All Mod Loaders” dropdown menu and select “Forge”. Then, click “Download File”, as shown below:</p>

    <p align="left" width="100%">
     <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/fbc553fc-98ea-4419-be66-35e5d8faa69c">
         <img width="50%" src="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/fbc553fc-98ea-4419-be66-35e5d8faa69c" /> 
     </a>
 </p>
  </li>
  <li>
    <p>Save the <code class="language-plaintext highlighter-rouge">DragonSurvival-1.20.1-27.06.2024-all.jar</code> Java .jar mod file into the location of your choosing. I like to save mods into a directory at this path, where <code class="language-plaintext highlighter-rouge">~</code> means your home directory:</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ~/Downloads/Install_Files/Minecraft/mods/
    
 <span class="c"># (same thing)</span>
 /home/gabriel/Downloads/Install_Files/Minecraft/mods/
</code></pre></div>    </div>
  </li>
</ol>

<h3 id="2-download-install-and-configure-the-correct-version-of-the-forge-mod-loader-for-this-mod">2. Download, install, and configure the correct version of the Forge mod loader for this mod</h3>

<p><em>This section shows you how to <strong>install and configure a new version of the Forge mod loader</strong> into Minecraft. If you already have the required version of Forge you need for your new mod, skip ahead to the next section: installing the Java mod file you downloaded above.</em></p>

<ol>
  <li>
    <p>Download the version of Minecraft Forge that corresponds to your mod version you downloaded above. In my case, I need Minecraft Forge <strong>1.20.1</strong>.</p>

    <p>Go here: <a href="https://files.minecraftforge.net/net/minecraftforge/forge/">https://files.minecraftforge.net/net/minecraftforge/forge/</a> –&gt; click <code class="language-plaintext highlighter-rouge">1.20.1</code> in the left-hand pane –&gt; under “Download Latest”, click “Installer”, as shown below.</p>

    <p align="left" width="100%">
     <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/b7ebe31f-9228-4f44-9959-8a9ae293ff11">
         <img width="75%" src="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/b7ebe31f-9228-4f44-9959-8a9ae293ff11" /> 
     </a>
 </p>

    <p><em>Note: I generally like to use the “Download Latest” version. If I have any problems with it, however, then I will try the “Download Recommended” version instead.</em></p>

    <p>On the next page, click “SKIP” to skip the advertisement page and begin the download:</p>

    <p align="left" width="100%">
     <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/4cdbba5a-0490-4bd2-88eb-f90823b3b3f4">
         <img width="75%" src="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/4cdbba5a-0490-4bd2-88eb-f90823b3b3f4" /> 
     </a>
 </p>

    <p>Save the <code class="language-plaintext highlighter-rouge">forge-1.20.1-47.3.5-installer.jar</code> into a directory of your choosing, such as:</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ~/Downloads/Install_Files/Minecraft/forge/
</code></pre></div>    </div>
  </li>
  <li>Install the <code class="language-plaintext highlighter-rouge">.jar</code> Java Forge file you downloaded above.
    <ol>
      <li>Open your file explorer. I prefer Nemo (<a href="https://askubuntu.com/a/1446372/327339">my installation instructions are here</a>).</li>
      <li>
        <p>Right-click in some empty space in the same directory as the file, and click “Open in Terminal”:</p>

        <p align="left" width="100%">
     <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/90055fc8-e682-4c88-bf84-d52b257d6159">
         <img width="75%" src="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/90055fc8-e682-4c88-bf84-d52b257d6159" /> 
     </a>
 </p>
      </li>
      <li>
        <p>In the terminal that opens up, run the following commands:</p>

        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c"># Make the Java installation file executable</span>
 <span class="nb">chmod</span> +x forge-1.20.1-47.3.5-installer.jar
</code></pre></div>        </div>

        <p>Now, run the file by:</p>
        <ol>
          <li>Option 1 [recommended/easiest]: double-clicking it in your file explorer.</li>
          <li>Option 2: running it in the terminal:
            <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c"># Run the Java installation file for the Forge mod loader</span>
 java <span class="nt">-jar</span> forge-1.20.1-47.3.5-installer.jar
</code></pre></div>            </div>
          </li>
        </ol>

        <p><em>Expert tips and notes:</em></p>
        <ol>
          <li>Lines that begin with <code class="language-plaintext highlighter-rouge">#</code> above are Bash terminal comments, and do not need to be run.</li>
          <li>Once you begin typing a command or path, you can hit <kbd>Tab</kbd> to auto-complete what you are typing. 
 Example: after typing <code class="language-plaintext highlighter-rouge">./for</code> you can press <kbd>Tab</kbd> to auto-complete <code class="language-plaintext highlighter-rouge">./forge-1.20.1-47.3.5-installer.jar</code>.</li>
          <li>You can also just copy-paste the commands above.
            <ol>
              <li>To paste in a terminal, either right-click and go to <code class="language-plaintext highlighter-rouge">Paste</code>, or press <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>V</kbd>.</li>
            </ol>
          </li>
          <li>Instead of running the <code class="language-plaintext highlighter-rouge">./forge-1.20.1-47.3.5-installer.jar</code> command to run the Java JAR file, you can also just double-click it in your file explorer once you have marked it as “exectuable” (able to be run) with <code class="language-plaintext highlighter-rouge">chmod +x forge-1.20.1-47.3.5-installer.jar</code>.</li>
          <li>If you get this error (which I am seeing on Ubuntu 22.04 but not on Ubuntu 20.04) when trying to run the file via <code class="language-plaintext highlighter-rouge">./forge-1.20.1-47.3.5-installer.jar</code>:
            <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> bash: ./forge-1.20.1-47.3.5-installer.jar: cannot execute binary file: Exec format error
</code></pre></div>            </div>
            <p>…then you can run it via <code class="language-plaintext highlighter-rouge">java -jar forge-1.20.1-47.3.5-installer.jar</code> instead. I mention this <a href="https://askubuntu.com/a/1519773/327339">in my answer here</a>.</p>

            <p>Or, even easier: just run it by double-clicking it in your file explorer!</p>
          </li>
        </ol>
      </li>
      <li>
        <p>Once you run the <code class="language-plaintext highlighter-rouge">forge-1.20.1-47.3.5-installer.jar</code> file you will see a GUI installer as shown below.</p>

        <p>Click “Install client” –&gt; “OK”, and let it install this version of Forge:</p>

        <p align="left" width="100%">
     <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/353d9c5a-a25b-45f4-a5b7-d345d2fe87ec">
         <img width="50%" src="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/353d9c5a-a25b-45f4-a5b7-d345d2fe87ec" /> 
     </a>
 </p>

        <p>When complete, you’ll see something like this. Click “OK”:</p>

        <p align="left" width="100%">
     <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/d64172ac-6d62-4615-9b98-d7529863c284">
         <img width="100%" src="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/d64172ac-6d62-4615-9b98-d7529863c284" /> 
     </a>
 </p>

        <p>This adds the following Forge folder to your computer:</p>
        <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ~/.minecraft/versions/1.20.1-forge-47.3.5/
</code></pre></div>        </div>

        <p>We will need this folder in a future step.</p>
      </li>
    </ol>
  </li>
  <li>Open the Minecraft Launcher and configure it to use the Forge version you just installed:
    <ol>
      <li>
        <p>Open the Minecraft Launcher –&gt; click “Installations” in the top menu –&gt; hover over the new “forge” “1.20.1-forge-47.3.5” entry –&gt; click the 3 dots at the right –&gt; “Edit”, as shown below:</p>

        <p align="left" width="100%">
     <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/5b294ffd-3c1f-4022-a13e-9302440be0f0">
         <img width="100%" src="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/5b294ffd-3c1f-4022-a13e-9302440be0f0" /> 
     </a>
 </p>
      </li>
      <li>(Optional, but recommended) Change the “Name” from “forge” to “forge 1.20.1”.</li>
      <li>In the “Game Directory” path, add this path by either manually typing it or by clicking the “Browse” button and browsing to it:
        <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> /home/gabriel/.minecraft/versions/1.20.1-forge-47.3.5
</code></pre></div>        </div>

        <p><em>If you just upgraded your Forge subversion, ex: from <code class="language-plaintext highlighter-rouge">1.20.1-forge-47.2.30</code> to <code class="language-plaintext highlighter-rouge">1.20.1-forge-47.3.5</code>, then be sure to update this path to the new version number.</em></p>
      </li>
      <li>
        <p>You’ll now see this:</p>

        <p align="left" width="100%">
     <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/5fbc0ba0-d40e-4205-a05b-f316935f93c6">
         <img width="100%" src="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/5fbc0ba0-d40e-4205-a05b-f316935f93c6" /> 
     </a>
 </p>
      </li>
      <li>Click “Save” when done.</li>
    </ol>
  </li>
</ol>

<h3 id="3-install-the-mod">3. Install the mod</h3>

<ol>
  <li>“Install” the Java mod file you downloaded above by copying it into the Forge mods folder (at <code class="language-plaintext highlighter-rouge">~/.minecraft/versions/1.20.1-forge-47.3.5/mods/</code> in my case).
    <ol>
      <li>
        <p>In my case, the Java .jar mod file is called <code class="language-plaintext highlighter-rouge">DragonSurvival-1.20.1-27.06.2024-all.jar</code>, and I previously saved it into directory <code class="language-plaintext highlighter-rouge">~/Downloads/Install_Files/Minecraft/mods</code>.</p>
      </li>
      <li>
        <p>“Install this mod” now by copying it into the Forge mods folder you just created in the previous step by installing that version of Forge.</p>
      </li>
      <li>
        <p><strong>Technique 1: using the command-line:</strong></p>

        <p>In a terminal, run the following commands:</p>

        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c"># Make the mods directory if it doesn't already exist</span>
 <span class="nb">mkdir</span> <span class="nt">-p</span> ~/.minecraft/versions/1.20.1-forge-47.3.5/mods/

 <span class="c"># Copy the mod file into that mods directory</span>
 <span class="nb">cp</span> ~/Downloads/Install_Files/Minecraft/mods/DragonSurvival-1.20.1-27.06.2024-all.jar ~/.minecraft/versions/1.20.1-forge-47.3.5/mods/
</code></pre></div>        </div>
      </li>
      <li>
        <p>Or <strong>Technique 2: using your GUI file manager:</strong></p>

        <ol>
          <li>
            <p>In your file manager, ensure that you have hidden files turned on:</p>

            <p>In Nautilus, the default file manager with Ubuntu, click the 3-horizontal-bar icon in the top-right –&gt; check the box for “Show Hidden Files”.</p>

            <p>In <a href="https://askubuntu.com/a/1446372/327339">Nemo, my preferred file manager</a>, click View –&gt; Show Hidden Files.</p>
          </li>
          <li>Manually create this new folder if it doesn’t already exist: <code class="language-plaintext highlighter-rouge">~/.minecraft/versions/1.20.1-forge-47.3.5/mods/</code>.</li>
          <li>Now copy the <code class="language-plaintext highlighter-rouge">~/Downloads/Install_Files/Minecraft/mods/DragonSurvival-1.20.1-27.06.2024-all.jar</code> file into that directory.</li>
        </ol>
      </li>
    </ol>
  </li>
  <li>If following these instructions for the Dragon Survival mod, then you must also install the Gecko library mod. Get it here: <a href="https://www.curseforge.com/minecraft/mc-mods/geckolib">https://www.curseforge.com/minecraft/mc-mods/geckolib</a>. Install it the same as you just installed the other mod above by saving or copying the <code class="language-plaintext highlighter-rouge">geckolib-forge-1.20.1-4.4.7.jar</code> mod file (or similar version number) into your <code class="language-plaintext highlighter-rouge">~/.minecraft/versions/1.20.1-forge-47.3.5/mods/</code> (or similar) directory.</li>
</ol>

<h3 id="4-launch-the-game-using-the-correct-version-of-forge-you-just-manually-installed-and-ensure-the-mod-is-active">4. Launch the game using the correct version of Forge you just manually installed, and ensure the mod is active</h3>

<ol>
  <li>
    <p>Back at the main Minecraft Launcher, click “Play” at the top –&gt; select the “forge 1.20.1” version via the selection menu at the bottom left –&gt; click “Play” at the bottom, as shown here:</p>

    <p align="left" width="100%">
     <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/ae5ad54e-ffb2-4da3-af73-13d41b6d94a7">
         <img width="100%" src="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/ae5ad54e-ffb2-4da3-af73-13d41b6d94a7" /> 
     </a>
 </p>
  </li>
  <li>
    <p>You’ll now see a warning pop up. Check the “I understand the risks” box and click “Play”:</p>

    <p align="left" width="75%">
     <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/142c8d2d-a511-47d3-8434-d0de882f6783">
         <img width="100%" src="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/142c8d2d-a511-47d3-8434-d0de882f6783" /> 
     </a>
 </p>

    <p>As the game loads, you’ll see this:</p>

    <p align="left" width="75%">
     <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/4f3a2b13-68cd-4639-b7b3-8582ffeddcac">
         <img width="100%" src="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/4f3a2b13-68cd-4639-b7b3-8582ffeddcac" /> 
     </a>
 </p>
  </li>
</ol>

<h3 id="5-possible-launch-failures-or-errors-you-may-encounter">5. Possible launch failures or errors you may encounter</h3>

<p><em>If Minecraft successfully loads, skip this section and go to the next section.</em></p>

<ol>
  <li>
    <p>If it fails to load, and you get this error (as I got with Forge <code class="language-plaintext highlighter-rouge">1.20.1-forge-47.2.30</code> and mod <code class="language-plaintext highlighter-rouge">DragonSurvival-1.20.1-09.01.2024.jar</code>):</p>

    <p align="left" width="75%">
     <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/7cc8d0e5-669b-4770-930f-8c3dea6736e3">
         <img width="100%" src="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/7cc8d0e5-669b-4770-930f-8c3dea6736e3" /> 
     </a>
 </p>

    <blockquote>
      <p>ERROR</p>

      <h3 id="game-crashed">Game Crashed</h3>

      <p>The game crashed whilst initializing game<br />
Error: org.spongepowered.asm.mixin.transformer.throwables.MixinTransformerError: An unexpected critical error was encountered</p>

      <p>Exit Code: 255</p>
    </blockquote>

    <p>…then the problem is a bad mod and/or an outdated version of Forge.</p>

    <p><strong>Solution:</strong></p>
    <ol>
      <li>Install a different version of Forge: ex: the “Recommended” version instead of the “Latest” version, or vice versa, or the absolute latest version now if a newer version has been released since you last tried. Ex: I upgraded from the “latest” version of <code class="language-plaintext highlighter-rouge">1.20.1-forge-47.2.30</code> to the “latest” version of <code class="language-plaintext highlighter-rouge">1.20.1-forge-47.3.5</code> at a later date.</li>
      <li>Install a different version of the mod. Ex: I upgraded from <code class="language-plaintext highlighter-rouge">DragonSurvival-1.20.1-09.01.2024.jar</code> to <code class="language-plaintext highlighter-rouge">DragonSurvival-1.20.1-27.06.2024-all.jar</code>, which became available at a later date when I tried again.</li>
    </ol>

    <p><strong>How do just “uninstall” the failing mod, instead:</strong></p>

    <p>If you remove the mod you just added and try again then Forge will at least launch.</p>

    <p>To remove the mod, do one of these three options:</p>

    <ol>
      <li>Option 1 [my preference]: run this command in your terminal to move the bad mod file from the <code class="language-plaintext highlighter-rouge">mods</code> directory to a <code class="language-plaintext highlighter-rouge">mods/disabled</code> directory:
        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">cd</span> ~/.minecraft/versions/1.20.1-forge-47.2.30/mods
 <span class="nb">mkdir</span> <span class="nt">-p</span> disabled
 <span class="nb">mv </span>DragonSurvival-1.20.1-09.01.2024.jar disabled/ 
</code></pre></div>        </div>
      </li>
      <li>Option 2: simply delete the mod file from the mods directory you copied it into earlier. Manually use your file manager to delete the file at <code class="language-plaintext highlighter-rouge">~/.minecraft/versions/1.20.1-forge-47.2.30/mods/DragonSurvival-1.20.1-09.01.2024.jar</code>.</li>
      <li>Option 3: delete the mod file by running this command in your terminal:
        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">rm</span> ~/.minecraft/versions/1.20.1-forge-47.2.30/mods/DragonSurvival-1.20.1-09.01.2024.jar
</code></pre></div>        </div>
      </li>
    </ol>

    <p>Then, try launching the game again. It will load just fine now. But, the mod we wanted will be missing, which defeats the whole purpose of this exercise, so see the “Solution” steps just above to try again with a different version of the mod and/or Forge, instead.</p>
  </li>
  <li>
    <p>With Forge <code class="language-plaintext highlighter-rouge">1.20.1-forge-47.3.5</code> and mod <code class="language-plaintext highlighter-rouge">DragonSurvival-1.20.1-27.06.2024-all.jar</code>, I got this error:</p>

    <p align="left" width="75%">
     <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/e714242b-54ab-45d2-82f8-75182d85a097">
         <img width="100%" src="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/e714242b-54ab-45d2-82f8-75182d85a097" /> 
     </a>
 </p>

    <blockquote>
      <p>Error loading mods<br />
1 error has occurred during loading</p>

      <p>Mod dragonsurvival requires <code class="language-plaintext highlighter-rouge">geckolib</code> 4.2 or above<br />
Currently, <code class="language-plaintext highlighter-rouge">geckolib</code> is not installed</p>
    </blockquote>

    <p><strong>Solution:</strong></p>

    <p>I did a <a href="https://www.google.com/search?q=install+geckolib&amp;oq=install+geckolib&amp;gs_lcrp=EgZjaHJvbWUyBggAEEUYOdIBCDIwMjhqMGo3qAIAsAIA&amp;sourceid=chrome&amp;ie=UTF-8">Google search for “install geckolib”</a> and found it here: <a href="https://www.curseforge.com/minecraft/mc-mods/geckolib">https://www.curseforge.com/minecraft/mc-mods/geckolib</a>. It turns out it’s just another Minecraft mod that needs to be installed! So, download it for “game version” 1.20.1, and “mod loader” “Forge”, and install it just like you did the other mod, simply copy-pasting it into the <code class="language-plaintext highlighter-rouge">mods</code> directory for the Forge version you are using. For me, I copied the file into here: <code class="language-plaintext highlighter-rouge">~/.minecraft/versions/1.20.1-forge-47.3.5/mods/geckolib-forge-1.20.1-4.4.7.jar</code>. Now the DragonSurvival mod works and the game loads!</p>
  </li>
</ol>

<h3 id="6-verify-that-the-mod-is-loaded-and-start-a-new-game-to-see-the-mod-in-action">6. Verify that the mod is loaded, and start a new game to see the mod in action</h3>

<ol>
  <li>
    <p>Once Forge loads, you’ll see this:</p>

    <p align="left" width="75%">
     <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/57b8f3b3-be16-497a-ba32-4bdc1dc881eb">
         <img width="100%" src="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/57b8f3b3-be16-497a-ba32-4bdc1dc881eb" /> 
     </a>
 </p>
  </li>
  <li>
    <p>Click “Continue”. You’ll now see this. Notice that it says “5 mods loaded” in the bottom-left:</p>

    <p align="left" width="75%">
     <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/5f270f0b-c98f-4353-b029-8f451881f265">
         <img width="100%" src="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/5f270f0b-c98f-4353-b029-8f451881f265" /> 
     </a>
 </p>
  </li>
  <li>
    <p>Click the “Mods” button. You’ll see a page with all of the installed mods listed in the left-hand column, and a description of any mod you click on in the center window. Here, you can see that the “mods” we have installed are:</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Minecraft 1.20.1
 MixinExtras 0.3.6
 Forge 47.3.5
 Dragon Survival 1.5.57
 GeckoLib 4 4.4.7
</code></pre></div>    </div>

    <p>The first 3 are brought in by Forge, and the last 2 are the mods we installed manually:</p>

    <p align="left" width="75%">
     <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/d07ff5d9-7b09-4564-a22c-7f935c155f7f">
         <img width="100%" src="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/d07ff5d9-7b09-4564-a22c-7f935c155f7f" /> 
     </a>
 </p>

    <p>Click “Done”.</p>
  </li>
  <li>
    <p>Back at the main window, click “Singleplayer” or “Multiplayer” to begin your game with those mods loaded.</p>
  </li>
</ol>

<h3 id="7-done">7. Done!</h3>

<p>Enjoy your mods!</p>

<h3 id="8-for-the-dragon-survival-mod-specifically">8. For the Dragon Survival mod specifically</h3>

<ol>
  <li>
    <p>In my case, for the Dragon Survival mod, I clicked “Singleplayer” –&gt; “Create New World” –&gt; choose your desired settings, then “Create New World”.</p>

    <p>Right after the world loads, you’ll see the default human view in Minecraft. About 5 seconds later, this view to “Choose a Dragon Species” automatically opens up:</p>

    <p align="left" width="75%">
     <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/e0fb8873-10b3-45c3-80d2-9893bf741dac">
         <img width="100%" src="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/e0fb8873-10b3-45c3-80d2-9893bf741dac" /> 
     </a>
 </p>

    <p>Click to choose your dragon, then this screen opens up to customize your dragon:</p>

    <p align="left" width="75%">
     <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/a4050140-2f30-44d4-a23d-1777e5205b93">
         <img width="100%" src="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/a4050140-2f30-44d4-a23d-1777e5205b93" /> 
     </a>
 </p>

    <p>Once you save your settings, you’ll be back at the main play screen. Press <kbd>F5</kbd> to change your view. Here is my dragon!:</p>

    <p align="left" width="75%">
     <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/3ad119ed-bd63-425f-a344-0dc8f86b9db7">
         <img width="100%" src="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/3ad119ed-bd63-425f-a344-0dc8f86b9db7" /> 
     </a>
 </p>

    <p>To fly, press <kbd>Space</kbd> to jump, and then press <kbd>G</kbd> to activate flight mode. Hold <kbd>Space</kbd> to go up, and <kbd>Shift</kbd> to go down. Press <kbd>W</kbd> to go forward, <kbd>A</kbd> to go left, <kbd>S</kbd> to go back, and <kbd>D</kbd> to go right.</p>

    <p>For more info. on this particular mod, see the wiki for it on GitHub:</p>
    <ol>
      <li><a href="https://github.com/DragonSurvivalTeam/DragonSurvival/wiki">https://github.com/DragonSurvivalTeam/DragonSurvival/wiki</a>
        <ol>
          <li><a href="https://github.com/DragonSurvivalTeam/DragonSurvival/wiki/5.-Dragon-Features#-flight-and-wings">https://github.com/DragonSurvivalTeam/DragonSurvival/wiki/5.-Dragon-Features#-flight-and-wings</a></li>
        </ol>
      </li>
    </ol>
  </li>
</ol>

<h1 id="option-2-recommended-and-much-easier-using-the-curseforce-app">Option 2 (recommended, and much easier): using the CurseForce app</h1>

<p>TODO</p>

<p>See CurseForge app: <a href="https://www.curseforge.com/">https://www.curseforge.com/</a></p>

<!--
1. Install the CurseForge App: 

    Go to <https://www.curseforge.com/> -> click "Get CurseForge App" in the top-right -> "Download standalone" -> save the file into a directory at `~/Downloads/Install_Files/Minecraft/CurseForge/`. 
    
    The file is called `curseforge-latest-linux.zip` in my case, so its full path is `~/Downloads/Install_Files/Minecraft/CurseForge/curseforge-latest-linux.zip`. 

    Navigate to that location in a File Manager such as `nemo`. Right-click the `curseforge-latest-linux.zip` file and click "Extract Here".

-->]]></content><author><name>Gabriel Staples</name></author><category term="tutorial" /><category term="computers" /><category term="linux" /><category term="ubuntu" /><category term="gaming" /><category term="Minecraft" /><category term="mods" /><category term="Forge" /><category term="DragonSurvival" /><summary type="html"><![CDATA[# Option 1: The manual method. Quick summary 1. Download a mod. Ex: from: 1. 2. Download, install, and configure the correct version of the Forge mod loader for this mod, if you don't already have it installed.]]></summary></entry><entry><title type="html">[WORK-IN-PROGRESS] Dell XPS 15 9530 laptop Windows/Linux setup &amp;amp; dual-boot install</title><link href="https://gabrielstaples.com/dell_xps15_9530_setup/" rel="alternate" type="text/html" title="[WORK-IN-PROGRESS] Dell XPS 15 9530 laptop Windows/Linux setup &amp;amp; dual-boot install" /><published>2024-01-01T00:00:00+00:00</published><updated>2024-01-05T00:00:00+00:00</updated><id>https://gabrielstaples.com/dell_xps15_9530_setup</id><content type="html" xml:base="https://gabrielstaples.com/dell_xps15_9530_setup/"><![CDATA[<p><a href="https://hits.seeyoufarm.com"><img src="https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgabrielstaples.com%2Fdell_xps15_9530_setup%2F&amp;count_bg=%2379C83D&amp;title_bg=%23555555&amp;icon=&amp;icon_color=%23E7E7E7&amp;title=views+%28today+%2F+total%29&amp;edge_flat=false" alt="Hits" /></a></p>

<p>I just bought the brand new top-of-the-line <a href="https://www.dell.com/en-us/shop/dell-laptops/xps-15-laptop/spd/xps-15-9530-laptop">Dell XPS 15 9530 laptop</a>.</p>

<p>Here’s how to set it up for dual-booting Windows 11 and Ubuntu 22.04 LTS.</p>

<p>This is the fastest home or work computer I’ve ever had in my life, while being portable enough (1. as a laptop, 2. both thin, and light, and 3. only ~4 lbs) that I can carry <em>3</em> of them in the same backpack when I travel by plane. Here is a speed test/PassMark benchmark I performed on it in Windows 11: https://www.passmark.com/baselines/V11/display.php?id=197601456490</p>

<h1 id="specs">Specs:</h1>

<ol>
  <li>13th Gen Intel® Core™ i9-13900H (24 MB cache, 14 cores, up to 5.40 GHz Turbo) - has 20 logical cores (6 cores have hyperthreading), so to the OS it looks like 20 cores</li>
  <li>Windows 11 Home (from the manufacturer) and Linux Ubuntu 22.04 (installed by me))</li>
  <li>NVIDIA® GeForce RTX™ 4070, 8 GB GDDR6 video card</li>
  <li>16 GB: 2 x 8 GB, DDR5, 4800 MT/s, dual-channel removable RAM
    <ol>
      <li>After purchase: upgraded to 64 GB 2x32 GB DDR5 5200 MT/s</li>
    </ol>
  </li>
  <li>512 GB, M.2, PCIe NVMe, SSD in slot 1 [for Windows]
    <ol>
      <li>After purchase: added a 2nd 2 TB M.2 7450/6900 MB/s read/write SSD in slot 2 [for Linux]</li>
    </ol>
  </li>
  <li>15.6”, FHD+ 1920x1200, 60Hz, Non-Touch, Anti-Glare, 500 nit, InfinityEdge display</li>
  <li>130W USB-C power adapter</li>
</ol>

<p>In order to end up with the absolute top-of-the-line model possible at the cheapest cost, I intentionally got the smallest SSD and RAM possible, then I manually upgraded them myself after purchase. Almost any computer manufacturer overcharges by 2~3x for RAM and SSD upgrades, so you can save perhaps $300~$700 or so on the upgrades by doing this yourself.</p>

<p>I chose the non-touchscreen display because:</p>
<ol>
  <li>It’s less glossy and will have less glare and reflections when working with a backlight behind you.</li>
  <li>I don’t use touchscreens much, and don’t like fingerprints on my screen.</li>
  <li>It has better brightness at 500 nit vs 400 nit.</li>
</ol>

<p>I’ll dual-boot Windows 11 and Ubuntu 22.04 LTS. To make it simple, Windows will remain on the first SSD, and Linux will be on the second SSD.</p>

<h1 id="notes">Notes</h1>

<p>This article it intended as notes to myself, and to others with some technical experience. It also contains my info. to get started setting up a software developer environment in Windows.</p>

<p>For brand-new beginners, please post questions in the comments below, or do some additional googling or talking to AIs (Bing Chat/Copilot, ChatGPT, Google Bard, GitHub Copilot, etc.) for help. And I’ll try to reply to your comments.</p>

<p>While this article is a work-in-progress, I’ll update it as I go, and check steps off as I complete them.</p>

<h1 id="steps">Steps</h1>

<h2 id="1-buy-everything">1. Buy everything</h2>

<ol class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" /><a href="https://www.dell.com/en-us/shop/dell-laptops/xps-15-laptop/spd/xps-15-9530-laptop">Dell XPS 15 9530 laptop</a>, $2000~$3000
    <ol>
      <li>When you buy the laptop from Dell, if there is a place to do so, <strong>add a note to the order requesting that they install the second (right) SSD thermal plate and screw, even if no SSD is in it</strong>, or else it won’t come with one. If it doesn’t come with one, you can either fight with them over support and possibly get it for as little as $1.99 with free shipping and 2 to 8 weeks waiting, plus dealing with some of the lowest quality support in the world, for many hours on the phone. Or, you can just buy it yourself on Amazon. If you want, do both and return the Amazon one if the Dell one comes in time. Or, to save hassle, just keep the Amazon one and avoid the hassle with Dell and be done!</li>
      <li>Note that even if you do get Dell to send you the thermal plate, it won’t come with the screw, so you’ll need to buy that anyway unless you somehow managed to get them to sell you a screw too (I was unable to get them to, even after 3 hours dealing with them, mostly on the phone).</li>
    </ol>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />2nd (right) SSD slot 2 thermal cover and screw
    <ol class="task-list">
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Option 1 (recommended, to avoid dealing with Dell’s insanely lousy service): <a href="https://amzn.to/47nRdxp">Deal4GO Slot 2 2280 M.2 SSD Heatsink Thermal Shield for Dell XPS 15, $21</a> - It comes with 2 pcs M2x2 (2mm diameter x 2mm length) screws. You only need one of them. This kit works fine.</li>
      <li class="task-list-item">Option 2:
        <ol class="task-list">
          <li>Open a support ticket with Dell and ask them to send you an order form for the thermal plate and screw. Hours and hours and hours later, and weeks of waiting, you may still not get it. I can’t emphasize enough how bad Dell support is. They are very polite, but incompetent.</li>
          <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Then also buy this pack of screws on Amazon: <a href="https://amzn.to/3NSyfrQ">Akuoly Laptop Screw Set PC M2 M3 M2.5 Screw Standoffs for Universal Laptops and Hard Drive Disk M.2 SSD, 355 Pieces, $10</a>. The screw size you need for the SSD and thermal cover is M2x2 (2mm diameter x 2mm length) or M2x3 (2mm diameter x 3mm length). This kit comes with high quality stainless steel screws in that size with a larger head diameter specifically for installing SSDs. It’s a good kit to have around regardless.</li>
        </ol>
      </li>
    </ol>

    <p>Right (SSD2) thermal cover/heat sink for Dell XPS15 9530:</p>
    <ol>
      <li>Top side (<em>Original Dell one on left, Amazon Deal4GO one on right</em>):
        <p align="left" width="100%">
     <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/5d155d9a-cc52-4744-828d-207d0495824c">
         <img width="100%" src="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/5d155d9a-cc52-4744-828d-207d0495824c" /> 
     </a>
 </p>
      </li>
      <li>Bottom side (<em>Original Dell one on left, Amazon Deal4GO one on right</em>):
        <ul>
          <li>be sure to peal the film off of the blue thermal pad before installation, so that it sticks to the top of the SSD to sink heat out of it</li>
        </ul>

        <p align="left" width="100%">
     <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/7deac178-fe0e-4474-8be6-6b6926a672e8">
         <img width="100%" src="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/assets/6842199/7deac178-fe0e-4474-8be6-6b6926a672e8" /> 
     </a>
 </p>
      </li>
    </ol>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" /><a href="https://amzn.to/3TQpTVK">iFixit Pro Tech Toolkit</a>, $75 with fast shipping and easy returns; or from iFixit.com: https://www.ifixit.com/products/pro-tech-toolkit
    <ol>
      <li>You’ll need plastic prying picks from this kit, a spudger (plastic prying and poking tool), a tiny phillips screwdriver, and a T5 Torx screwdriver.</li>
      <li>If you plan on doing only this one job and nothing else, a cheaper kit may work, but if you plan on doing any more than one computer upgrade in your life, this iFixit Pro kit is totally worth it. I got the cheaper iFixit kit originally and regret it.</li>
    </ol>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />64 GB RAM:
    <ol class="task-list">
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />(Recommended): <a href="https://amzn.to/48Cr93n">Crucial RAM 64GB Kit (2x32GB) DDR5 5200MT/s (or 4800MT/s) CL42 Laptop Memory CT2K32G52C42S5, $219</a> - is perhaps up to 4.9% faster than the one below, <a href="https://docs.google.com/spreadsheets/d/1UR3Prq2BEN3nxJrO83IAD66JXn7lts3mtx6E8wyGIbk/edit?usp=sharing">in real-world tests I did on my Dell XPS 9530</a>.</li>
      <li class="task-list-item">Also works fine, and is $55 cheaper: <a href="https://amzn.to/47s2nBg">Crucial RAM 64GB Kit (2x32GB) DDR5 4800MT/s CL40 Laptop Memory CT2K32G48C40S5, $164</a></li>
      <li class="task-list-item">
        <p>The XPS 9530 manual <a href="https://www.dell.com/support/manuals/en-us/xps-15-9530-laptop/xps-15-9530-setup-and-specifications/memory?guid=guid-7ecd08fd-a386-4e28-8aa2-85c2f7375442&amp;lang=en-us">states that the maximum memory supported</a> is:</p>

        <blockquote>
          <p>64 GB, 2 x 32 GB, DDR5, 4800 MT/s, dual-channel</p>
        </blockquote>

        <p>…so I’m not 100% sure if the 5200 MT/s RAM is fully worth it, but the BIOS does recognize that its speed is 5200 MT/s, and it works fine and ran 4.9% faster in my tests, so I’m keeping it.</p>

        <p>If you do decide to buy a different RAM kit, shoot for a lower CL latency. CL42, which is what I got, is on the high end, and <a href="https://www.youtube.com/watch?v=b-WFetQjifc">this video</a> indicates that lower CL latency is better, and can sometimes beat higher speed RAM when the higher speed RAM has a higher CL latency.</p>
      </li>
    </ol>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />SSD: <a href="https://amzn.to/3TKmAPK">Samsung 990 PRO Series - 2TB PCIe Gen4. X4 NVMe 2.0c - M.2 Internal SSD (MZ-V9P2T0B/AM), $175+</a> - lightning fast with “read/write speeds up to 7,450/6,900 MB/s”.
    <ol>
      <li>Get the one with<em>out</em> the heat sink. Technically (I tried so I know), the one with the heat sink will fit well enough that the computer case will still close, but it presses against the inside of the case, bowing it out a couple mm and prevents some of the metal grounding sponges in the case from making contact with the case, as well as some of the thermal cooling locations on the case from making contact with certain chips, so it’s not ideal.</li>
    </ol>
  </li>
</ol>

<h2 id="2-set-up-windows">2. Set up Windows</h2>

<ol class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Log into Windows 11:
    <ol>
      <li>When the computer first arrives, without opening up the case to install hardware yet, boot into Windows 11, which comes on the computer, and log in with your Microsoft account. Set up Windows as you wish.</li>
    </ol>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />///////// can’t!–no drivers to download; see: https://www.cablematters.com/pc-1536-125-foldable-usb-c-multiport-adapter-with-hdmi-usb-power-delivery.aspx ///////Install Windows drivers for “Cable Matters Foldable USB-C Multiport Adapter with HDMI”, Model #201378. ////////// (link)</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Plug in any external monitors you’d like</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Change screen size (resolution and scale)
    <ol>
      <li>Right-click on desktop –&gt; Display settings –&gt;
        <ol class="task-list">
          <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Click on your screen you’d like to change if you have multiple monitors –&gt; scroll down to the “Scale &amp; layout” section –&gt; change “Display resolution” to the largest value, and change “Scale” to 100% to make things small. (You can use 125% or larger if it’s too small).</li>
          <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Click “Multiple displays” to expand it –&gt; <em>un</em>check the box for “Ease cursor movement between displays”. Leaving this option on re-guides your mouse from monitor to monitor when you drag a window between monitors or move your mouse to a dead-end edge of a monitor. And, when monitors are different sizes and offset, it feels really weird and annoying, and it makes it <em>impossible</em> to drag a window to certain parts of the edge of a monitor to make the window expand or snap to a 1/4 of the screen or to 1/2 of the screen, which is immensely frustrating.</li>
        </ol>
      </li>
    </ol>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Install the “Logi Options+” Logitech software app for Logitech mice and keyboards:
    <ol class="task-list">
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Add my MX Vertical mouse. Change the pointer speed button so that in “Pointer speed 1” mode it is 50%, and in “Pointer speed 2” mode it is 75%.<br />
 On the mouse itself, click the “Change pointer speed” button on top to go to the faster 75% mode.</li>
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Update mouse firmware: in the app, click on your mouse image –&gt; Settings –&gt; under the “About” section at the top, it will show the firmware version. Click “CHECK FOR UPDATE”, and follow the steps.</li>
    </ol>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Change mouse and trackpad settings
    <ol class="task-list">
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Mouse:
        <ol class="task-list">
          <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Press the Windows key, type “mouse settings” and click on it –&gt; drag “Mouse pointer speed” to 13 out of 20 or so, which feels pretty good with my Logitech mouse itself set to 75% above.</li>
          <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Set “Scrolling” to “Multiple lines at a time”, then drag the “Lines to scroll at a time” to 10.</li>
        </ol>
      </li>
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Touchpad: press the Windows key and search for “Touchpad settings” and click on it –&gt; drag the “Cursor speed” bar to 10 out of 10. Check all 4 boxes under the “Taps” settings. Under “Scroll and zoom”, set “Scrolling direction” to “Down motion scrolls down”.</li>
    </ol>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Disable adaptive screen brightness ///////</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Install and sign into browsers
    <ul class="task-list">
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Google Chrome</li>
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Microsoft Edge</li>
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Firefox</li>
    </ul>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Install MS VSCode</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Install Sublime Text</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Configure File Explorer (was formerly called “Windows Explorer”) views and behavior:
    <ol class="task-list">
      <li class="task-list-item">
        <p><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Change the view settings:</p>

        <p>Open File Explorer –&gt; click “Home” in the top left –&gt; double-click “Documents” –&gt; View –&gt; List. Now, click the 3 dots at top –&gt; Options –&gt; View –&gt; check the following boxes:</p>

        <ol>
          <li>Display the full path in title bar</li>
          <li>Show hidden files, folders, and drives</li>
        </ol>

        <p><em>Un</em>check “Hide extensions for known file types”.</p>

        <p>At the top, click “Apply to Folders”, then “Yes”. Click “OK” to close the window.</p>
      </li>
      <li class="task-list-item">
        <p><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Enable the delete confirmation dialog when deleting files and folders:</p>

        <p>Press Windows + D to show the desktop –&gt; right-click the “Recycle Bin” –&gt; Properties –&gt; check the box for “Display delete confirmation dialog” –&gt; click “OK”.</p>
      </li>
      <li class="task-list-item">
        <p><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Create folders at <code class="language-plaintext highlighter-rouge">C:\Users\my_username\GS\dev</code>. Then, right-click <code class="language-plaintext highlighter-rouge">dev</code> and go to “Pin to Quick access”. This puts a shortcut to it in the left-hand “Quick access” area of File Explorer. Do the same for the <code class="language-plaintext highlighter-rouge">GS</code> folder.</p>
      </li>
    </ol>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Add Task Manager to the startup applications so that it starts up at boot.
    <ol>
      <li>
        <p>Open File Explorer –&gt; navigate to <code class="language-plaintext highlighter-rouge">C:\Windows\System32\Taskmgr.exe</code>. Right-click on <code class="language-plaintext highlighter-rouge">Taskmgr.exe</code> –&gt; “Show more options” –&gt; “Create shortcut”. A popup window will say:</p>

        <blockquote>
          <p>Windows can’t create a shortcut here. Do you want the shortcut to be placed on the desktop instead?</p>
        </blockquote>

        <p>Click “Yes”.</p>
      </li>
      <li>Cut the shortcut to <code class="language-plaintext highlighter-rouge">Taskmgr.exe</code> on the Desktop and paste it into the <code class="language-plaintext highlighter-rouge">%USERPROFILE%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup</code> dir.</li>
      <li>Now, Task Manager will start up automatically at boot (I think), for your user only. If you want it to start up for all users, put the shortcut into <code class="language-plaintext highlighter-rouge">C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp</code> instead (I think).</li>
      <li>Right-click on some empty space in the bottom-right taskbar area to the left of the icons in the far right –&gt; click “Task Manager” to open it up. Now, with it open, right-click down there again –&gt; click “Taskbar settings” –&gt; “Other system tray icons”. This allows you to “Show or hide additional system tray icons”. Turn ON the slider for “Task Manager”.</li>
    </ol>
  </li>
  <li class="task-list-item">
    <p><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Configure OneDrive to not sync the “Pictures” (and screenshots) folder:</p>

    <p>Open File Explorer –&gt; Home –&gt; right-click “Pictures” –&gt; OneDrive –&gt; “Manage OneDrive backup” –&gt; in the window that pops up, click the slider for “Pictures” to turn it off –&gt; click “OK”. This leaves only the “Documents” and “Desktop” folders syncing to OneDrive.</p>

    <p>Note: to get back here to <em>Configure OneDrive settings</em>, you can also do this:</p>

    <p>Open File Manager –&gt; This PC (bottom left) –&gt; C –&gt; Users –&gt; my_username –&gt; right-click OneDrive –&gt; go to “OneDrive” –&gt; Settings –&gt; this brings up a “Sync and backup” “OneDrive Settings” window.</p>
  </li>
  <li class="task-list-item">[nah, disabling OneDrive for the Pictures folder just above is fine instead] Change the default save location of screenshots to <em>not</em> be in your cloud-synced OneDrive folder:
    <ol class="task-list">
      <li>See here: https://support.microsoft.com/en-au/topic/operation-to-change-a-personal-folder-location-fails-in-windows-ffb95139-6dbb-821d-27ec-62c9aaccd720</li>
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Note: by doing the registry change and then putting it back again, I may have actually finagled the default screenshots dir when I use the <kbd>PrintScr</kbd> button to become <code class="language-plaintext highlighter-rouge">C:\Users\my_username\Pictures_NOT_on_OneDrive\Screenshots</code>. It’s not clear if or how I did that exactly, but the only key I changed was <code class="language-plaintext highlighter-rouge">HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders\My Pictures</code>. I set its value to <code class="language-plaintext highlighter-rouge">C:\Users\my_username\Pictures_NOT_on_OneDrive</code> and then back to <code class="language-plaintext highlighter-rouge">C:\Users\my_username\OneDrive\Pictures</code>.</li>
    </ol>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Enable “Developer Mode”
    <ol>
      <li>Windows key –&gt; search for “Use developer features” –&gt; click on it –&gt; click the slider to enable “Developer Mode” –&gt; click “Yes” to the popup window.</li>
    </ol>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Install <a href="https://www.libreoffice.org/">LibreOffice</a> (Microsoft Office alternative)</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Run Windows Updates
    <ol>
      <li>Press <kbd>Windows</kbd> key –&gt; search for “Check for updates”, and select it. Click “Check for updates” and “Download &amp; install” if any are available.</li>
      <li>/////////</li>
    </ol>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Install <a href="https://gitforwindows.org/">Git for Windows</a>
    <ol>
      <li>Follow my installation notes and screenshots here:
        <ol>
          <li><a href="https://stackoverflow.com/a/76950661/4561887">Adding Git-Bash to the new Windows Terminal</a></li>
          <li><a href="https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/issues/27#issue-1950880578">Installing Git For Windows</a></li>
        </ol>
      </li>
    </ol>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Install Python
    <ol class="task-list">
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Install it directly from here (<em>not</em> from the Windows Store!): https://www.python.org/downloads/</li>
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Follow my steps here to configure it to run in Git Bash: <a href="https://stackoverflow.com/a/76918262/4561887">Python doesn’t work in Git Bash (it just hangs or freezes forever); and getting Linux hash-bangs to work in Windows</a></li>
    </ol>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Install MSYS2 and gcc in Windows
    <ol class="task-list">
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Follow my steps here: <a href="https://stackoverflow.com/a/77407282/4561887">Installing &amp; setting up MSYS2 from scratch, including adding all 7 profiles to Windows Terminal</a></li>
    </ol>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Run PassMark benchmark performance tests</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Back up Bitlocker recovery key</li>
</ol>

<h2 id="3-install-new-ram-and-ssd-and-configure-the-bios">3. Install new RAM and SSD, and configure the BIOS</h2>

<ol class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Install new RAM and SSD</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />configure BIOS
    <ol class="task-list">
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Change from RAID to AHCI; this uses 3x less power when sleeping in Linux</li>
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Disable Secure Boot</li>
    </ol>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />try to turn on; configure BIOS F-keys; install RAM and SSD; turn on again</li>
</ol>

<p>That was awful. I felt like poking my eyes out at times using Windows. It is impossible to stop syncing the <code class="language-plaintext highlighter-rouge">C:\Users\my_user\OneDrive\Pictures\Screenshots</code> folder to OneDrive, for instance, while still keeping the other <code class="language-plaintext highlighter-rouge">C:\Users\my_user\OneDrive\Pictures</code> content synced. And, you can’t change the automatic save location in the Snipping Tool program! And the 125% display zoom is too much on my <a href="https://gabrielstaples.com/work-from-home-work-station/#gsc.tab=0">external 4k 32” curved display</a>, and the 100% zoom too little! Aaaagh. Back to Linux!</p>

<h2 id="4-install-and-set-up-linux-ubuntu">4. Install and set up Linux Ubuntu</h2>

<ol class="task-list">
  <li class="task-list-item">
    <p><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />In Windows, install the Samsung Magician SSD software since I bought a Samsung 990 PRO SSD and need to make sure it has the latest firmware to avoid the bug where it wears out rapidly.</p>
  </li>
  <li class="task-list-item">
    <p><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Run speed test in Windows. Here are my results:</p>
  </li>
</ol>

<h1 id="see-also">See also</h1>

<ol>
  <li>My article: <a href="https://www.electricrcaircraftguy.com/2018/01/how-to-clone-your-hard-drive.html">How to clone your hard drive using free and simple open-source tools (w/any OS on your cloned drive!)</a></li>
</ol>]]></content><author><name>Gabriel Staples</name></author><category term="tutorial" /><category term="computers" /><category term="linux" /><category term="ubuntu" /><summary type="html"><![CDATA[I just bought the brand new top-of-the-line [Dell XPS 15 9530 laptop](https://www.dell.com/en-us/shop/dell-laptops/xps-15-laptop/spd/xps-15-9530-laptop). Here's how to set it up for dual-booting Windows 11 and Ubuntu 22.04 LTS.]]></summary></entry><entry><title type="html">How to add comments in blog posts on GitHub Pages websites</title><link href="https://gabrielstaples.com/github-pages-comments/" rel="alternate" type="text/html" title="How to add comments in blog posts on GitHub Pages websites" /><published>2023-11-05T00:00:00+00:00</published><updated>2023-11-05T00:00:00+00:00</updated><id>https://gabrielstaples.com/github-pages-comments</id><content type="html" xml:base="https://gabrielstaples.com/github-pages-comments/"><![CDATA[<p><a href="https://hits.seeyoufarm.com"><img src="https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgabrielstaples.com%2Fgithub-pages-comments%2F&amp;count_bg=%2379C83D&amp;title_bg=%23555555&amp;icon=&amp;icon_color=%23E7E7E7&amp;title=views+%28today+%2F+total%29&amp;edge_flat=false" alt="Hits" /></a></p>

<p>This, <a href="https://gabrielstaples.com/">https://gabrielstaples.com/</a>, is my <a href="https://pages.github.com/">Github Pages</a> static website and blog, powered by the <a href="https://jekyllrb.com/">Jekyll</a> static site generator, based on a custom-modified version of the <a href="https://mademistakes.com/work/jekyll-themes/minimal-mistakes/">Minimal Mistakes Jekyll theme</a>, and written in the efficient, fast, and easy-to-read markup language called <a href="https://en.wikipedia.org/wiki/Markdown">markdown</a>.</p>

<p>This website is hosted on <a href="https://github.com/">GitHub</a> and configured with my custom domain.</p>

<p>I’ve added GitHub-Issue-based comments to the bottom of this website using the plugin called <a href="https://utteranc.es/">utterances</a>, which is totally awesome and makes it trivial to add a beautiful and user-friendly commenting system to your static website or blog.</p>

<p>Scroll to the bottom and leave a comment if you like. <em>No spam or swearing.</em> I moderate everything.</p>

<p>See also my answer here: <a href="https://webapps.stackexchange.com/a/165529/126659">WebApps.StackExchange.com: How to add comments in blog posts on GitHub-pages websites</a>.</p>

<h2 id="utterances-what-i-currently-use-on-this-very-website-and-recommend">Utterances (what I currently use on this very website, and recommend)</h2>

<ol>
  <li>Utterances main page &amp; installation info.: <a href="https://utteranc.es/">https://utteranc.es/</a></li>
  <li>Utterances GitHub repo: <a href="https://github.com/utterance/utterances">https://github.com/utterance/utterances</a></li>
  <li>You must install the utterances plugin into your GitHub account, here: <a href="https://github.com/apps/utterances">https://github.com/apps/utterances</a>
    <ol>
      <li>GitHub lets you install this app for all of your repositories, or just repositories you select, which is nice. You may just want to install it on GitHub Pages repos you have, since it only needs access to those repos.</li>
      <li>Make sure your repos using utterances are <em>public</em>, or else readers won’t be able to view the issues/comments, or comment.</li>
    </ol>
  </li>
</ol>

<h2 id="other-options">Other options</h2>

<p>Other options also exist. <a href="https://github.com/mmistakes">Michael Rose</a>, the creator of the <a href="https://github.com/topics/jekyll-theme">#1 most popular</a> GitHub-Pages-compatible <a href="https://github.com/mmistakes/minimal-mistakes">minimal-mistakes</a> Jekyll theme (see a <a href="https://mmistakes.github.io/minimal-mistakes/">demo here</a>), has put together a <a href="https://mmistakes.github.io/minimal-mistakes/docs/configuration/#comments">short list of <strong>commenting systems</strong> here</a>.</p>

<ol>
  <li><a href="https://disqus.com/">Disqus</a></li>
  <li><a href="https://www.discourse.org/">Discourse</a></li>
  <li><a href="https://developers.facebook.com/docs/plugins/comments">Facebook</a></li>
  <li><a href="https://utteranc.es/">utterances</a> - my favorite, since it is Free and Open Source (<a href="https://github.com/utterance/utterances/blob/master/LICENSE.md">MIT license</a>) and no-cost, and uses convenient GitHub Issues as the commenting system. GitHub Issues comments have <em>excellent</em> markdown support, allow inserting nicely-formatted code blocks right into them, can be edited indefinitely, moderated by you (including editing or deleting comments which are inappropriate), and responded to with upvote buttons–all reasons I like them.</li>
  <li><a href="https://staticman.net/">Staticman</a></li>
  <li><a href="https://giscus.app/">Giscus</a> - inspired by Utterances, and brought to my attention by <a href="https://webapps.stackexchange.com/questions/165528/how-to-add-comments-in-blog-posts-on-github-pages-websites/165529#comment160794_165529">@Henry C in his comment here</a>.</li>
</ol>

<h2 id="see-also">See also</h2>
<ol>
  <li><a href="https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll">Setting up a GitHub Pages site with Jekyll</a></li>
  <li><a href="https://www.markdownguide.org/getting-started/">https://www.markdownguide.org/getting-started/</a></li>
  <li><a href="https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax">GitHub Docs: Basic writing and formatting syntax</a></li>
  <li><a href="https://bitbucket.org/tutorials/markdowndemo/src/master/">Bitbucket tutorial: MarkdownDemo</a></li>
</ol>

<h2 id="-you-may-leave-test-comments-below-this-article-no-flagrant-spam-or-self-promotion-i-moderate-">» You may leave “test comments” below this article. No flagrant spam or self-promotion. I moderate. «</h2>]]></content><author><name>Gabriel Staples</name></author><category term="tutorial" /><category term="github pages" /><category term="website" /><category term="jekyll" /><summary type="html"><![CDATA[I've added GitHub-Issue-based comments to the bottom of this website using the plugin called [utterances](https://utteranc.es/), which is totally awesome and makes it trivial to add a beautiful and user-friendly commenting system to your static website or blog.]]></summary></entry><entry><title type="html">Speed tests and 13 examples: how to iterate over Pandas `DataFrame`s in Python without iterating</title><link href="https://gabrielstaples.com/python_iterate_over_pandas_dataframe/" rel="alternate" type="text/html" title="Speed tests and 13 examples: how to iterate over Pandas `DataFrame`s in Python without iterating" /><published>2023-11-05T00:00:00+00:00</published><updated>2023-11-05T00:00:00+00:00</updated><id>https://gabrielstaples.com/python_iterate_over_pandas_dataframe</id><content type="html" xml:base="https://gabrielstaples.com/python_iterate_over_pandas_dataframe/"><![CDATA[<p><a href="https://hits.seeyoufarm.com"><img src="https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgabrielstaples.com%2Fpython_iterate_over_pandas_dataframe%2F&amp;count_bg=%2379C83D&amp;title_bg=%23555555&amp;icon=&amp;icon_color=%23E7E7E7&amp;title=views+%28today+%2F+total%29&amp;edge_flat=false" alt="Hits" /></a></p>

<p>Key takeaways:</p>
<ol>
  <li>Use vectorization.</li>
  <li><strong>Speed profile</strong> your code! Don’t assume something is faster because you <em>think</em> it is faster; speed profile it and <em>prove</em> it is faster. The results may surprise you.</li>
</ol>

<h2 id="how-to-iterate-over-pandas-dataframes-without-iterating">How to iterate over Pandas <code class="language-plaintext highlighter-rouge">DataFrame</code>s without iterating</h2>

<ul>
  <li>See also <a href="https://stackoverflow.com/a/77270285/4561887">my answer on Stack Overflow about this here</a>. Give it an upvote if you find this useful.</li>
  <li><a href="https://github.com/sponsors/ElectricRCAircraftGuy">Sponsor me on GitHub ❤️ 😊</a> if you’d like to thank me or support my work.</li>
  <li>You may leave a comment or question below this article.</li>
</ul>

<p>After <em>several weeks</em> of working on this answer, here’s what I’ve come up with:</p>

<p>Here are <strong>13 techniques for iterating over Pandas <code class="language-plaintext highlighter-rouge">DataFrame</code>s</strong>. As you can see, the time it takes varies <em>dramatically</em>. The fastest technique is <strong>~1363x</strong> faster than the slowest technique! The key takeaway, <a href="https://stackoverflow.com/a/55557758/4561887">as @cs95 says here</a>, is <strong><em>don’t</em> iterate! Use vectorization (<a href="https://en.wikipedia.org/wiki/Array_programming">“array programming”</a>) instead.</strong> All this really means is that you should use the arrays directly in mathematical formulas rather than trying to manually iterate over the arrays. The underlying objects must support this, of course, but both Numpy and Pandas <em>do</em>.</p>

<p>There are many ways to use vectorization in Pandas, which you can see in the plot and in my example code below. When using the arrays directly, the underlying looping still takes place, but in (I think) very optimized underlying C code rather than through raw Python.</p>

<h2 id="results">Results</h2>

<p>13 techniques, numbered 1 to 13, were tested. The technique number and name is underneath each bar. The total calculation time is above each bar. Underneath that is the multiplier to show how much longer it took than the fastest technique to the far right:</p>

<p><sub>From <a href="https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world/blob/master/python/pandas_dataframe_iteration_vs_vectorization_vs_list_comprehension_speed_tests.svg"><code class="language-plaintext highlighter-rouge">pandas_dataframe_iteration_vs_vectorization_vs_list_comprehension_speed_tests.svg</code></a> in my <a href="https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world">eRCaGuy_hello_world</a> repo (produced by <a href="https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world/blob/master/python/pandas_dataframe_iteration_vs_vectorization_vs_list_comprehension_speed_tests.py">this code</a>).</sub></p>

<p><a href="https://i.stack.imgur.com/5biMy.png"><img src="https://i.stack.imgur.com/5biMy.png" alt="enter image description here" /></a></p>

<h2 id="summary">Summary</h2>

<p><strong>List comprehension</strong> and <strong>vectorization</strong> (possibly with <strong>boolean indexing</strong>) are all you really need.</p>

<p>Use <strong>list comprehension</strong> (good) and <strong>vectorization</strong> (best). Pure vectorization I think is <em>always</em> possible, but may take extra work in complicated calculations. Search this answer for <strong>“boolean indexing”</strong>, <strong>“boolean array”</strong>, and <strong>“boolean mask”</strong> (all three are the same thing) to see some of the more complicated cases where pure vectorization can thereby be used.</p>

<h4 id="here-are-the-13-techniques-listed-in-order-of-fastest-first-to-slowest-last-i-recommend-never-using-the-last-slowest-3-to-4-techniques">Here are the 13 techniques, listed in order of <em>fastest first to slowest last</em>. I recommend <em>never</em> using the last (slowest) 3 to 4 techniques.</h4>
<ol>
  <li><code class="language-plaintext highlighter-rouge">8_pure_vectorization__with_df.loc[]_boolean_array_indexing_for_if_statment_corner_case</code></li>
  <li><code class="language-plaintext highlighter-rouge">6_vectorization__with_apply_for_if_statement_corner_case</code></li>
  <li><code class="language-plaintext highlighter-rouge">7_vectorization__with_list_comprehension_for_if_statment_corner_case</code></li>
  <li><code class="language-plaintext highlighter-rouge">11_list_comprehension_w_zip_and_direct_variable_assignment_calculated_in_place</code></li>
  <li><code class="language-plaintext highlighter-rouge">10_list_comprehension_w_zip_and_direct_variable_assignment_passed_to_func</code></li>
  <li><code class="language-plaintext highlighter-rouge">12_list_comprehension_w_zip_and_row_tuple_passed_to_func</code></li>
  <li><code class="language-plaintext highlighter-rouge">5_itertuples_in_for_loop</code></li>
  <li><code class="language-plaintext highlighter-rouge">13_list_comprehension_w__to_numpy__and_direct_variable_assignment_passed_to_func</code></li>
  <li><code class="language-plaintext highlighter-rouge">9_apply_function_with_lambda</code></li>
  <li><code class="language-plaintext highlighter-rouge">1_raw_for_loop_using_regular_df_indexing</code></li>
  <li><code class="language-plaintext highlighter-rouge">2_raw_for_loop_using_df.loc[]_indexing</code></li>
  <li><code class="language-plaintext highlighter-rouge">4_iterrows_in_for_loop</code></li>
  <li><code class="language-plaintext highlighter-rouge">3_raw_for_loop_using_df.iloc[]_indexing</code></li>
</ol>

<h4 id="rules-of-thumb">Rules of thumb:</h4>
<ol>
  <li>Techniques 3, 4, and 2 should <em>never</em> be used. They are super slow and have no advantages whatsoever. Keep in mind though: it’s not the indexing technique, such as <code class="language-plaintext highlighter-rouge">.loc[]</code> or <code class="language-plaintext highlighter-rouge">.iloc[]</code> that makes these techniques bad, but rather, it’s <em>the <code class="language-plaintext highlighter-rouge">for</code> loop they are in</em> that makes them bad! I use <code class="language-plaintext highlighter-rouge">.loc[]</code> inside the fastest (pure vectorization) approach, for instance! So, here are the 3 slowest techniques which should <em>never</em> be used:
    <ol>
      <li><code class="language-plaintext highlighter-rouge">3_raw_for_loop_using_df.iloc[]_indexing</code></li>
      <li><code class="language-plaintext highlighter-rouge">4_iterrows_in_for_loop</code></li>
      <li><code class="language-plaintext highlighter-rouge">2_raw_for_loop_using_df.loc[]_indexing</code></li>
    </ol>
  </li>
  <li>Technique <code class="language-plaintext highlighter-rouge">1_raw_for_loop_using_regular_df_indexing</code> should never be used either, but if you’re going to use a raw for loop, it’s faster than the others.</li>
  <li>The <strong><code class="language-plaintext highlighter-rouge">.apply()</code></strong> function (<code class="language-plaintext highlighter-rouge">9_apply_function_with_lambda</code>) is ok, but generally speaking, I’d avoid it too. Technique <code class="language-plaintext highlighter-rouge">6_vectorization__with_apply_for_if_statement_corner_case</code> did perform better than <code class="language-plaintext highlighter-rouge">7_vectorization__with_list_comprehension_for_if_statment_corner_case</code>, however, which is interesting.</li>
  <li><strong>List comprehension</strong> is great! It’s not the fastest, but it is easy to use and very fast!
    <ol>
      <li>The nice thing about it is that it can be used with <em>any</em> function that is intended to work on individual values, or array values. And this means you could have really complicated <code class="language-plaintext highlighter-rouge">if</code> statements and things inside the function. So, the tradeoff here is that it gives you great versatility with really readable and re-usable code by using external calculation functions, while still giving you great speed!</li>
    </ol>
  </li>
  <li><strong>Vectorization</strong> is the fastest and best, and what you should use whenever the equation is simple. You can optionally use something like <code class="language-plaintext highlighter-rouge">.apply()</code> or <strong>list comprehension</strong> on just the more-complicated portions of the equation, while still easily using vectorization for the rest.</li>
  <li><strong>Pure vectorization</strong> is the absolute fastest and best, and what you should use if you <em>are willing to put in the effort to make it work.</em>
    <ol>
      <li>For simple cases, it’s what you should use.</li>
      <li>For complicated cases, <code class="language-plaintext highlighter-rouge">if</code> statements, etc., pure vectorization can be made to work too, through <strong>boolean indexing,</strong> but can add extra work and can decrease readability to do so. So, you can optionally use <strong>list comprehension</strong> (usually the best) or <strong>.apply()</strong> (generally slower, but not always) for just those edge cases instead, while still using vectorization for the rest of the calculation. Ex: see techniques <code class="language-plaintext highlighter-rouge">7_vectorization__with_list_comprehension_for_if_statment_corner_case</code> and <code class="language-plaintext highlighter-rouge">6_vectorization__with_apply_for_if_statement_corner_case</code>.</li>
    </ol>
  </li>
</ol>

<h2 id="the-test-data">The test data</h2>

<p>Assume we have the following Pandas DataFrame. It has 2 million rows with 4 columns (<code class="language-plaintext highlighter-rouge">A</code>, <code class="language-plaintext highlighter-rouge">B</code>, <code class="language-plaintext highlighter-rouge">C</code>, and <code class="language-plaintext highlighter-rouge">D</code>), each with random values from <code class="language-plaintext highlighter-rouge">-1000</code> to <code class="language-plaintext highlighter-rouge">1000</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>df =
           A    B    C    D
0       -365  842  284 -942
1        532  416 -102  888
2        397  321 -296 -616
3       -215  879  557  895
4        857  701 -157  480
...      ...  ...  ...  ...
1999995 -101 -233 -377 -939
1999996 -989  380  917  145
1999997 -879  333 -372 -970
1999998  738  982 -743  312
1999999 -306 -103  459  745
</code></pre></div></div>

<p>I produced this DataFrame like this:</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="n">pd</span>

<span class="c1"># Create an array (numpy list of lists) of fake data
</span><span class="n">MIN_VAL</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1000</span>
<span class="n">MAX_VAL</span> <span class="o">=</span> <span class="mi">1000</span>
<span class="c1"># NUM_ROWS = 10_000_000
</span><span class="n">NUM_ROWS</span> <span class="o">=</span> <span class="mi">2_000_000</span>  <span class="c1"># default for final tests
# NUM_ROWS = 1_000_000
# NUM_ROWS = 100_000
# NUM_ROWS = 10_000  # default for rapid development &amp; initial tests
</span><span class="n">NUM_COLS</span> <span class="o">=</span> <span class="mi">4</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="n">MIN_VAL</span><span class="p">,</span> <span class="n">MAX_VAL</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="p">(</span><span class="n">NUM_ROWS</span><span class="p">,</span> <span class="n">NUM_COLS</span><span class="p">))</span>

<span class="c1"># Now convert it to a Pandas DataFrame with columns named "A", "B", "C", and "D"
</span><span class="n">df_original</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">DataFrame</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">columns</span><span class="o">=</span><span class="p">[</span><span class="s">"A"</span><span class="p">,</span> <span class="s">"B"</span><span class="p">,</span> <span class="s">"C"</span><span class="p">,</span> <span class="s">"D"</span><span class="p">])</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"df = </span><span class="se">\n</span><span class="si">{</span><span class="n">df_original</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="the-test-equationcalculation">The test equation/calculation</h2>

<p>I wanted to demonstrate that all of these techniques are possible on non-trivial functions or equations, so I intentionally made the equation they are calculating require:</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">if</code> statements</li>
  <li>data from multiple columns in the DataFrame</li>
  <li>data from multiple rows in the DataFrame</li>
</ol>

<p>The equation we will be calculating for each row is this. I arbitrarily made it up, but I think it contains enough complexity that you will be able to expand on what I’ve done to perform any equation you want in Pandas with full vectorization:</p>

<p><a href="https://i.stack.imgur.com/W3c12.png"><img src="https://i.stack.imgur.com/W3c12.png" alt="enter image description here" /></a></p>

<p>In Python, the above equation can be written like this:</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Calculate and return a new value, `val`, by performing the following equation:
</span><span class="n">val</span> <span class="o">=</span> <span class="p">(</span>
    <span class="mi">2</span> <span class="o">*</span> <span class="n">A_i_minus_2</span>
    <span class="o">+</span> <span class="mi">3</span> <span class="o">*</span> <span class="n">A_i_minus_1</span>
    <span class="o">+</span> <span class="mi">4</span> <span class="o">*</span> <span class="n">A</span>
    <span class="o">+</span> <span class="mi">5</span> <span class="o">*</span> <span class="n">A_i_plus_1</span>
    <span class="c1"># Python ternary operator; don't forget parentheses around the entire 
</span>    <span class="c1"># ternary expression!
</span>    <span class="o">+</span> <span class="p">((</span><span class="mi">6</span> <span class="o">*</span> <span class="n">B</span><span class="p">)</span> <span class="k">if</span> <span class="n">B</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="k">else</span> <span class="p">(</span><span class="mi">60</span> <span class="o">*</span> <span class="n">B</span><span class="p">))</span>
    <span class="o">+</span> <span class="mi">7</span> <span class="o">*</span> <span class="n">C</span>
    <span class="o">-</span> <span class="mi">8</span> <span class="o">*</span> <span class="n">D</span>
<span class="p">)</span>
</code></pre></div></div>

<p>Alternatively, you could write it like this:</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Calculate and return a new value, `val`, by performing the following equation:
</span>
<span class="k">if</span> <span class="n">B</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
    <span class="n">B_new</span> <span class="o">=</span> <span class="mi">6</span> <span class="o">*</span> <span class="n">B</span>
<span class="k">else</span><span class="p">:</span>
    <span class="n">B_new</span> <span class="o">=</span> <span class="mi">60</span> <span class="o">*</span> <span class="n">B</span>

<span class="n">val</span> <span class="o">=</span> <span class="p">(</span>
    <span class="mi">2</span> <span class="o">*</span> <span class="n">A_i_minus_2</span>
    <span class="o">+</span> <span class="mi">3</span> <span class="o">*</span> <span class="n">A_i_minus_1</span>
    <span class="o">+</span> <span class="mi">4</span> <span class="o">*</span> <span class="n">A</span>
    <span class="o">+</span> <span class="mi">5</span> <span class="o">*</span> <span class="n">A_i_plus_1</span>
    <span class="o">+</span> <span class="n">B_new</span>
    <span class="o">+</span> <span class="mi">7</span> <span class="o">*</span> <span class="n">C</span>
    <span class="o">-</span> <span class="mi">8</span> <span class="o">*</span> <span class="n">D</span>
<span class="p">)</span>
</code></pre></div></div>

<p>Either of those can be wrapped into a function. Ex:</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">calculate_val</span><span class="p">(</span>
        <span class="n">A_i_minus_2</span><span class="p">,</span>
        <span class="n">A_i_minus_1</span><span class="p">,</span>
        <span class="n">A</span><span class="p">,</span>
        <span class="n">A_i_plus_1</span><span class="p">,</span>
        <span class="n">B</span><span class="p">,</span>
        <span class="n">C</span><span class="p">,</span>
        <span class="n">D</span><span class="p">):</span>
    <span class="n">val</span> <span class="o">=</span> <span class="p">(</span>
        <span class="mi">2</span> <span class="o">*</span> <span class="n">A_i_minus_2</span>
        <span class="o">+</span> <span class="mi">3</span> <span class="o">*</span> <span class="n">A_i_minus_1</span>
        <span class="o">+</span> <span class="mi">4</span> <span class="o">*</span> <span class="n">A</span>
        <span class="o">+</span> <span class="mi">5</span> <span class="o">*</span> <span class="n">A_i_plus_1</span>
        <span class="c1"># Python ternary operator; don't forget parentheses around the 
</span>        <span class="c1"># entire ternary expression!
</span>        <span class="o">+</span> <span class="p">((</span><span class="mi">6</span> <span class="o">*</span> <span class="n">B</span><span class="p">)</span> <span class="k">if</span> <span class="n">B</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="k">else</span> <span class="p">(</span><span class="mi">60</span> <span class="o">*</span> <span class="n">B</span><span class="p">))</span>
        <span class="o">+</span> <span class="mi">7</span> <span class="o">*</span> <span class="n">C</span>
        <span class="o">-</span> <span class="mi">8</span> <span class="o">*</span> <span class="n">D</span>
    <span class="p">)</span>
    <span class="k">return</span> <span class="n">val</span>
</code></pre></div></div>

<h2 id="the-techniques">The techniques</h2>

<p>The full code is available to download and run in my <strong><a href="https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world/blob/master/python/pandas_dataframe_iteration_vs_vectorization_vs_list_comprehension_speed_tests.py"><code class="language-plaintext highlighter-rouge">python/pandas_dataframe_iteration_vs_vectorization_vs_list_comprehension_speed_tests.py</code></a></strong> file in my <a href="https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world">eRCaGuy_hello_world</a> repo.</p>

<h4 id="here-is-the-code-for-all-13-techniques">Here is the code for all 13 techniques:</h4>

<ol>
  <li>
    <p><code class="language-plaintext highlighter-rouge">1_raw_for_loop_using_regular_df_indexing</code></p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">val</span> <span class="o">=</span> <span class="p">[</span><span class="n">np</span><span class="p">.</span><span class="n">NAN</span><span class="p">]</span><span class="o">*</span><span class="nb">len</span><span class="p">(</span><span class="n">df</span><span class="p">)</span>
 <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">df</span><span class="p">)):</span>
     <span class="k">if</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">2</span> <span class="ow">or</span> <span class="n">i</span> <span class="o">&gt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">df</span><span class="p">)</span><span class="o">-</span><span class="mi">2</span><span class="p">:</span>
         <span class="k">continue</span>

     <span class="n">val</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">calculate_val</span><span class="p">(</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"A"</span><span class="p">][</span><span class="n">i</span><span class="o">-</span><span class="mi">2</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"A"</span><span class="p">][</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"A"</span><span class="p">][</span><span class="n">i</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"A"</span><span class="p">][</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"B"</span><span class="p">][</span><span class="n">i</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"C"</span><span class="p">][</span><span class="n">i</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"D"</span><span class="p">][</span><span class="n">i</span><span class="p">],</span>
     <span class="p">)</span>
 <span class="n">df</span><span class="p">[</span><span class="s">"val"</span><span class="p">]</span> <span class="o">=</span> <span class="n">val</span>  <span class="c1"># put this column back into the dataframe
</span></code></pre></div>    </div>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">2_raw_for_loop_using_df.loc[]_indexing</code></p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">val</span> <span class="o">=</span> <span class="p">[</span><span class="n">np</span><span class="p">.</span><span class="n">NAN</span><span class="p">]</span><span class="o">*</span><span class="nb">len</span><span class="p">(</span><span class="n">df</span><span class="p">)</span>
 <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">df</span><span class="p">)):</span>
     <span class="k">if</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">2</span> <span class="ow">or</span> <span class="n">i</span> <span class="o">&gt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">df</span><span class="p">)</span><span class="o">-</span><span class="mi">2</span><span class="p">:</span>
         <span class="k">continue</span>

     <span class="n">val</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">calculate_val</span><span class="p">(</span>
         <span class="n">df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="s">"A"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="s">"A"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">i</span><span class="p">,</span>   <span class="s">"A"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="s">"A"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">i</span><span class="p">,</span>   <span class="s">"B"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">i</span><span class="p">,</span>   <span class="s">"C"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">i</span><span class="p">,</span>   <span class="s">"D"</span><span class="p">],</span>
     <span class="p">)</span>

 <span class="n">df</span><span class="p">[</span><span class="s">"val"</span><span class="p">]</span> <span class="o">=</span> <span class="n">val</span>  <span class="c1"># put this column back into the dataframe
</span></code></pre></div>    </div>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">3_raw_for_loop_using_df.iloc[]_indexing</code></p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c1"># column indices
</span> <span class="n">i_A</span> <span class="o">=</span> <span class="mi">0</span>
 <span class="n">i_B</span> <span class="o">=</span> <span class="mi">1</span>
 <span class="n">i_C</span> <span class="o">=</span> <span class="mi">2</span>
 <span class="n">i_D</span> <span class="o">=</span> <span class="mi">3</span>

 <span class="n">val</span> <span class="o">=</span> <span class="p">[</span><span class="n">np</span><span class="p">.</span><span class="n">NAN</span><span class="p">]</span><span class="o">*</span><span class="nb">len</span><span class="p">(</span><span class="n">df</span><span class="p">)</span>
 <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">df</span><span class="p">)):</span>
     <span class="k">if</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">2</span> <span class="ow">or</span> <span class="n">i</span> <span class="o">&gt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">df</span><span class="p">)</span><span class="o">-</span><span class="mi">2</span><span class="p">:</span>
         <span class="k">continue</span>

     <span class="n">val</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">calculate_val</span><span class="p">(</span>
         <span class="n">df</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="n">i_A</span><span class="p">],</span>
         <span class="n">df</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">i_A</span><span class="p">],</span>
         <span class="n">df</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">i</span><span class="p">,</span>   <span class="n">i_A</span><span class="p">],</span>
         <span class="n">df</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">i_A</span><span class="p">],</span>
         <span class="n">df</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">i</span><span class="p">,</span>   <span class="n">i_B</span><span class="p">],</span>
         <span class="n">df</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">i</span><span class="p">,</span>   <span class="n">i_C</span><span class="p">],</span>
         <span class="n">df</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">i</span><span class="p">,</span>   <span class="n">i_D</span><span class="p">],</span>
     <span class="p">)</span>

 <span class="n">df</span><span class="p">[</span><span class="s">"val"</span><span class="p">]</span> <span class="o">=</span> <span class="n">val</span>  <span class="c1"># put this column back into the dataframe
</span></code></pre></div>    </div>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">4_iterrows_in_for_loop</code></p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">val</span> <span class="o">=</span> <span class="p">[</span><span class="n">np</span><span class="p">.</span><span class="n">NAN</span><span class="p">]</span><span class="o">*</span><span class="nb">len</span><span class="p">(</span><span class="n">df</span><span class="p">)</span>
 <span class="k">for</span> <span class="n">index</span><span class="p">,</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">df</span><span class="p">.</span><span class="n">iterrows</span><span class="p">():</span>
     <span class="k">if</span> <span class="n">index</span> <span class="o">&lt;</span> <span class="mi">2</span> <span class="ow">or</span> <span class="n">index</span> <span class="o">&gt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">df</span><span class="p">)</span><span class="o">-</span><span class="mi">2</span><span class="p">:</span>
         <span class="k">continue</span>

     <span class="n">val</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="o">=</span> <span class="n">calculate_val</span><span class="p">(</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"A"</span><span class="p">][</span><span class="n">index</span><span class="o">-</span><span class="mi">2</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"A"</span><span class="p">][</span><span class="n">index</span><span class="o">-</span><span class="mi">1</span><span class="p">],</span>
         <span class="n">row</span><span class="p">[</span><span class="s">"A"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"A"</span><span class="p">][</span><span class="n">index</span><span class="o">+</span><span class="mi">1</span><span class="p">],</span>
         <span class="n">row</span><span class="p">[</span><span class="s">"B"</span><span class="p">],</span>
         <span class="n">row</span><span class="p">[</span><span class="s">"C"</span><span class="p">],</span>
         <span class="n">row</span><span class="p">[</span><span class="s">"D"</span><span class="p">],</span>
     <span class="p">)</span>

 <span class="n">df</span><span class="p">[</span><span class="s">"val"</span><span class="p">]</span> <span class="o">=</span> <span class="n">val</span>  <span class="c1"># put this column back into the dataframe
</span></code></pre></div>    </div>
  </li>
</ol>

<p>For all of the next examples, we must first prepare the dataframe by adding columns with previous and next values: <code class="language-plaintext highlighter-rouge">A_(i-2)</code>, <code class="language-plaintext highlighter-rouge">A_(i-1)</code>, and <code class="language-plaintext highlighter-rouge">A_(i+1)</code>. These columns in the DataFrame will be named <code class="language-plaintext highlighter-rouge">A_i_minus_2</code>, <code class="language-plaintext highlighter-rouge">A_i_minus_1</code>, and <code class="language-plaintext highlighter-rouge">A_i_plus_1</code>, respectively:</p>

<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">df_original</span><span class="p">[</span><span class="s">"A_i_minus_2"</span><span class="p">]</span> <span class="o">=</span> <span class="n">df_original</span><span class="p">[</span><span class="s">"A"</span><span class="p">].</span><span class="n">shift</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>  <span class="c1"># val at index i-2
</span><span class="n">df_original</span><span class="p">[</span><span class="s">"A_i_minus_1"</span><span class="p">]</span> <span class="o">=</span> <span class="n">df_original</span><span class="p">[</span><span class="s">"A"</span><span class="p">].</span><span class="n">shift</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>  <span class="c1"># val at index i-1
</span><span class="n">df_original</span><span class="p">[</span><span class="s">"A_i_plus_1"</span><span class="p">]</span> <span class="o">=</span> <span class="n">df_original</span><span class="p">[</span><span class="s">"A"</span><span class="p">].</span><span class="n">shift</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>  <span class="c1"># val at index i+1
</span>
<span class="c1"># Note: to ensure that no partial calculations are ever done with rows which
# have NaN values due to the shifting, we can either drop such rows with
# `.dropna()`, or set all values in these rows to NaN. I'll choose the latter
# so that the stats that will be generated with the techniques below will end
# up matching the stats which were produced by the prior techniques above. ie:
# the number of rows will be identical to before. 
#
# df_original = df_original.dropna()
</span><span class="n">df_original</span><span class="p">.</span><span class="n">iloc</span><span class="p">[:</span><span class="mi">2</span><span class="p">,</span> <span class="p">:]</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">NAN</span>   <span class="c1"># slicing operators: first two rows, 
</span>                                   <span class="c1"># all columns
</span><span class="n">df_original</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">:,</span> <span class="p">:]</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">NAN</span>  <span class="c1"># slicing operators: last row, all columns
</span></code></pre></div></div>

<p>Running the vectorized code just above to produce those 3 new columns took a total of <strong>0.044961 seconds</strong>.</p>

<p>Now on to the rest of the techniques:</p>

<ol>
  <li>
    <p><code class="language-plaintext highlighter-rouge">5_itertuples_in_for_loop</code></p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">val</span> <span class="o">=</span> <span class="p">[</span><span class="n">np</span><span class="p">.</span><span class="n">NAN</span><span class="p">]</span><span class="o">*</span><span class="nb">len</span><span class="p">(</span><span class="n">df</span><span class="p">)</span>
 <span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">df</span><span class="p">.</span><span class="n">itertuples</span><span class="p">():</span>
     <span class="n">val</span><span class="p">[</span><span class="n">row</span><span class="p">.</span><span class="n">Index</span><span class="p">]</span> <span class="o">=</span> <span class="n">calculate_val</span><span class="p">(</span>
         <span class="n">row</span><span class="p">.</span><span class="n">A_i_minus_2</span><span class="p">,</span>
         <span class="n">row</span><span class="p">.</span><span class="n">A_i_minus_1</span><span class="p">,</span>
         <span class="n">row</span><span class="p">.</span><span class="n">A</span><span class="p">,</span>
         <span class="n">row</span><span class="p">.</span><span class="n">A_i_plus_1</span><span class="p">,</span>
         <span class="n">row</span><span class="p">.</span><span class="n">B</span><span class="p">,</span>
         <span class="n">row</span><span class="p">.</span><span class="n">C</span><span class="p">,</span>
         <span class="n">row</span><span class="p">.</span><span class="n">D</span><span class="p">,</span>
     <span class="p">)</span>

 <span class="n">df</span><span class="p">[</span><span class="s">"val"</span><span class="p">]</span> <span class="o">=</span> <span class="n">val</span>  <span class="c1"># put this column back into the dataframe
</span></code></pre></div>    </div>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">6_vectorization__with_apply_for_if_statement_corner_case</code></p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">def</span> <span class="nf">calculate_new_column_b_value</span><span class="p">(</span><span class="n">b_value</span><span class="p">):</span>
     <span class="c1"># Python ternary operator
</span>     <span class="n">b_value_new</span> <span class="o">=</span> <span class="p">(</span><span class="mi">6</span> <span class="o">*</span> <span class="n">b_value</span><span class="p">)</span> <span class="k">if</span> <span class="n">b_value</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="k">else</span> <span class="p">(</span><span class="mi">60</span> <span class="o">*</span> <span class="n">b_value</span><span class="p">)</span>  
     <span class="k">return</span> <span class="n">b_value_new</span>

 <span class="c1"># In this particular example, since we have an embedded `if-else` statement
</span> <span class="c1"># for the `B` column, pure vectorization is less intuitive. So, first we'll
</span> <span class="c1"># calculate a new `B` column using
</span> <span class="c1"># **`apply()`**, then we'll use vectorization for the rest.
</span> <span class="n">df</span><span class="p">[</span><span class="s">"B_new"</span><span class="p">]</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="s">"B"</span><span class="p">].</span><span class="nb">apply</span><span class="p">(</span><span class="n">calculate_new_column_b_value</span><span class="p">)</span>
 <span class="c1"># OR (same thing, but with a lambda function instead)
</span> <span class="c1"># df["B_new"] = df["B"].apply(lambda x: (6 * x) if x &gt; 0 else (60 * x))
</span>
 <span class="c1"># Now we can use vectorization for the rest. "Vectorization" in this case
</span> <span class="c1"># means to simply use the column series variables in equations directly,
</span> <span class="c1"># without manually iterating over them. Pandas DataFrames will handle the
</span> <span class="c1"># underlying iteration automatically for you. You just focus on the math.
</span> <span class="n">df</span><span class="p">[</span><span class="s">"val"</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span>
     <span class="mi">2</span> <span class="o">*</span> <span class="n">df</span><span class="p">[</span><span class="s">"A_i_minus_2"</span><span class="p">]</span>
     <span class="o">+</span> <span class="mi">3</span> <span class="o">*</span> <span class="n">df</span><span class="p">[</span><span class="s">"A_i_minus_1"</span><span class="p">]</span>
     <span class="o">+</span> <span class="mi">4</span> <span class="o">*</span> <span class="n">df</span><span class="p">[</span><span class="s">"A"</span><span class="p">]</span>
     <span class="o">+</span> <span class="mi">5</span> <span class="o">*</span> <span class="n">df</span><span class="p">[</span><span class="s">"A_i_plus_1"</span><span class="p">]</span>
     <span class="o">+</span> <span class="n">df</span><span class="p">[</span><span class="s">"B_new"</span><span class="p">]</span>
     <span class="o">+</span> <span class="mi">7</span> <span class="o">*</span> <span class="n">df</span><span class="p">[</span><span class="s">"C"</span><span class="p">]</span>
     <span class="o">-</span> <span class="mi">8</span> <span class="o">*</span> <span class="n">df</span><span class="p">[</span><span class="s">"D"</span><span class="p">]</span>
 <span class="p">)</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">7_vectorization__with_list_comprehension_for_if_statment_corner_case</code></p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c1"># In this particular example, since we have an embedded `if-else` statement
</span> <span class="c1"># for the `B` column, pure vectorization is less intuitive. So, first we'll
</span> <span class="c1"># calculate a new `B` column using **list comprehension**, then we'll use
</span> <span class="c1"># vectorization for the rest.
</span> <span class="n">df</span><span class="p">[</span><span class="s">"B_new"</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span>
     <span class="n">calculate_new_column_b_value</span><span class="p">(</span><span class="n">b_value</span><span class="p">)</span> <span class="k">for</span> <span class="n">b_value</span> <span class="ow">in</span> <span class="n">df</span><span class="p">[</span><span class="s">"B"</span><span class="p">]</span>
 <span class="p">]</span>

 <span class="c1"># Now we can use vectorization for the rest. "Vectorization" in this case
</span> <span class="c1"># means to simply use the column series variables in equations directly,
</span> <span class="c1"># without manually iterating over them. Pandas DataFrames will handle the
</span> <span class="c1"># underlying iteration automatically for you. You just focus on the math.
</span> <span class="n">df</span><span class="p">[</span><span class="s">"val"</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span>
     <span class="mi">2</span> <span class="o">*</span> <span class="n">df</span><span class="p">[</span><span class="s">"A_i_minus_2"</span><span class="p">]</span>
     <span class="o">+</span> <span class="mi">3</span> <span class="o">*</span> <span class="n">df</span><span class="p">[</span><span class="s">"A_i_minus_1"</span><span class="p">]</span>
     <span class="o">+</span> <span class="mi">4</span> <span class="o">*</span> <span class="n">df</span><span class="p">[</span><span class="s">"A"</span><span class="p">]</span>
     <span class="o">+</span> <span class="mi">5</span> <span class="o">*</span> <span class="n">df</span><span class="p">[</span><span class="s">"A_i_plus_1"</span><span class="p">]</span>
     <span class="o">+</span> <span class="n">df</span><span class="p">[</span><span class="s">"B_new"</span><span class="p">]</span>
     <span class="o">+</span> <span class="mi">7</span> <span class="o">*</span> <span class="n">df</span><span class="p">[</span><span class="s">"C"</span><span class="p">]</span>
     <span class="o">-</span> <span class="mi">8</span> <span class="o">*</span> <span class="n">df</span><span class="p">[</span><span class="s">"D"</span><span class="p">]</span>
 <span class="p">)</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">8_pure_vectorization__with_df.loc[]_boolean_array_indexing_for_if_statment_corner_case</code></p>

    <p>This uses <strong>boolean indexing</strong>, AKA: a <strong>boolean mask</strong>, to accomplish the equivalent of the <code class="language-plaintext highlighter-rouge">if</code> statement in the equation. In this way, pure vectorization can be used for the entire equation, thereby maximizing performance and speed.</p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c1"># If statement to evaluate:
</span> <span class="c1">#
</span> <span class="c1">#     if B &gt; 0:
</span> <span class="c1">#         B_new = 6 * B
</span> <span class="c1">#     else:
</span> <span class="c1">#         B_new = 60 * B
</span> <span class="c1">#
</span> <span class="c1"># In this particular example, since we have an embedded `if-else` statement
</span> <span class="c1"># for the `B` column, we can use some boolean array indexing through
</span> <span class="c1"># `df.loc[]` for some pure vectorization magic.
</span> <span class="c1">#
</span> <span class="c1"># Explanation:
</span> <span class="c1">#
</span> <span class="c1"># Long:
</span> <span class="c1">#
</span> <span class="c1"># The format is: `df.loc[rows, columns]`, except in this case, the rows are
</span> <span class="c1"># specified by a "boolean array" (AKA: a boolean expression, list of
</span> <span class="c1"># booleans, or "boolean mask"), specifying all rows where `B` is &gt; 0. Then,
</span> <span class="c1"># only in that `B` column for those rows, set the value accordingly. After
</span> <span class="c1"># we do this for where `B` is &gt; 0, we do the same thing for where `B` 
</span> <span class="c1"># is &lt;= 0, except with the other equation.
</span> <span class="c1">#
</span> <span class="c1"># Short:
</span> <span class="c1">#
</span> <span class="c1"># For all rows where the boolean expression applies, set the column value
</span> <span class="c1"># accordingly.
</span> <span class="c1">#
</span> <span class="c1"># GitHub CoPilot first showed me this `.loc[]` technique.
</span> <span class="c1"># See also the official documentation:
</span> <span class="c1"># https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.loc.html
</span> <span class="c1">#
</span> <span class="c1"># ===========================
</span> <span class="c1"># 1st: handle the &gt; 0 case
</span> <span class="c1"># ===========================
</span> <span class="n">df</span><span class="p">[</span><span class="s">"B_new"</span><span class="p">]</span> <span class="o">=</span> <span class="n">df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">df</span><span class="p">[</span><span class="s">"B"</span><span class="p">]</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">,</span> <span class="s">"B"</span><span class="p">]</span> <span class="o">*</span> <span class="mi">6</span>
 <span class="c1">#
</span> <span class="c1"># ===========================
</span> <span class="c1"># 2nd: handle the &lt;= 0 case, merging the results into the
</span> <span class="c1"># previously-created "B_new" column
</span> <span class="c1"># ===========================
</span> <span class="c1"># - NB: this does NOT work; it overwrites and replaces the whole "B_new"
</span> <span class="c1">#   column instead:
</span> <span class="c1">#
</span> <span class="c1">#       df["B_new"] = df.loc[df["B"] &lt;= 0, "B"] * 60
</span> <span class="c1">#
</span> <span class="c1"># This works:
</span> <span class="n">df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">df</span><span class="p">[</span><span class="s">"B"</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">,</span> <span class="s">"B_new"</span><span class="p">]</span> <span class="o">=</span> <span class="n">df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">df</span><span class="p">[</span><span class="s">"B"</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">,</span> <span class="s">"B"</span><span class="p">]</span> <span class="o">*</span> <span class="mi">60</span>

 <span class="c1"># Now use normal vectorization for the rest.
</span> <span class="n">df</span><span class="p">[</span><span class="s">"val"</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span>
     <span class="mi">2</span> <span class="o">*</span> <span class="n">df</span><span class="p">[</span><span class="s">"A_i_minus_2"</span><span class="p">]</span>
     <span class="o">+</span> <span class="mi">3</span> <span class="o">*</span> <span class="n">df</span><span class="p">[</span><span class="s">"A_i_minus_1"</span><span class="p">]</span>
     <span class="o">+</span> <span class="mi">4</span> <span class="o">*</span> <span class="n">df</span><span class="p">[</span><span class="s">"A"</span><span class="p">]</span>
     <span class="o">+</span> <span class="mi">5</span> <span class="o">*</span> <span class="n">df</span><span class="p">[</span><span class="s">"A_i_plus_1"</span><span class="p">]</span>
     <span class="o">+</span> <span class="n">df</span><span class="p">[</span><span class="s">"B_new"</span><span class="p">]</span>
     <span class="o">+</span> <span class="mi">7</span> <span class="o">*</span> <span class="n">df</span><span class="p">[</span><span class="s">"C"</span><span class="p">]</span>
     <span class="o">-</span> <span class="mi">8</span> <span class="o">*</span> <span class="n">df</span><span class="p">[</span><span class="s">"D"</span><span class="p">]</span>
 <span class="p">)</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">9_apply_function_with_lambda</code></p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">df</span><span class="p">[</span><span class="s">"val"</span><span class="p">]</span> <span class="o">=</span> <span class="n">df</span><span class="p">.</span><span class="nb">apply</span><span class="p">(</span>
     <span class="k">lambda</span> <span class="n">row</span><span class="p">:</span> <span class="n">calculate_val</span><span class="p">(</span>
         <span class="n">row</span><span class="p">[</span><span class="s">"A_i_minus_2"</span><span class="p">],</span>
         <span class="n">row</span><span class="p">[</span><span class="s">"A_i_minus_1"</span><span class="p">],</span>
         <span class="n">row</span><span class="p">[</span><span class="s">"A"</span><span class="p">],</span>
         <span class="n">row</span><span class="p">[</span><span class="s">"A_i_plus_1"</span><span class="p">],</span>
         <span class="n">row</span><span class="p">[</span><span class="s">"B"</span><span class="p">],</span>
         <span class="n">row</span><span class="p">[</span><span class="s">"C"</span><span class="p">],</span>
         <span class="n">row</span><span class="p">[</span><span class="s">"D"</span><span class="p">]</span>
     <span class="p">),</span>
     <span class="n">axis</span><span class="o">=</span><span class="s">'columns'</span> <span class="c1"># same as `axis=1`: "apply function to each row", 
</span>                    <span class="c1"># rather than to each column
</span> <span class="p">)</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">10_list_comprehension_w_zip_and_direct_variable_assignment_passed_to_func</code></p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">df</span><span class="p">[</span><span class="s">"val"</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span>
     <span class="c1"># Note: you *could* do the calculations directly here instead of using a
</span>     <span class="c1"># function call, so long as you don't have indented code blocks such as
</span>     <span class="c1"># sub-routines or multi-line if statements.
</span>     <span class="c1">#
</span>     <span class="c1"># I'm using a function call.
</span>     <span class="n">calculate_val</span><span class="p">(</span>
         <span class="n">A_i_minus_2</span><span class="p">,</span>
         <span class="n">A_i_minus_1</span><span class="p">,</span>
         <span class="n">A</span><span class="p">,</span>
         <span class="n">A_i_plus_1</span><span class="p">,</span>
         <span class="n">B</span><span class="p">,</span>
         <span class="n">C</span><span class="p">,</span>
         <span class="n">D</span>
     <span class="p">)</span> <span class="k">for</span> <span class="n">A_i_minus_2</span><span class="p">,</span> <span class="n">A_i_minus_1</span><span class="p">,</span> <span class="n">A</span><span class="p">,</span> <span class="n">A_i_plus_1</span><span class="p">,</span> <span class="n">B</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">D</span>
     <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"A_i_minus_2"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"A_i_minus_1"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"A"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"A_i_plus_1"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"B"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"C"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"D"</span><span class="p">]</span>
     <span class="p">)</span>
 <span class="p">]</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">11_list_comprehension_w_zip_and_direct_variable_assignment_calculated_in_place</code></p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">df</span><span class="p">[</span><span class="s">"val"</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span>
     <span class="mi">2</span> <span class="o">*</span> <span class="n">A_i_minus_2</span>
     <span class="o">+</span> <span class="mi">3</span> <span class="o">*</span> <span class="n">A_i_minus_1</span>
     <span class="o">+</span> <span class="mi">4</span> <span class="o">*</span> <span class="n">A</span>
     <span class="o">+</span> <span class="mi">5</span> <span class="o">*</span> <span class="n">A_i_plus_1</span>
     <span class="c1"># Python ternary operator; don't forget parentheses around the entire
</span>     <span class="c1"># ternary expression!
</span>     <span class="o">+</span> <span class="p">((</span><span class="mi">6</span> <span class="o">*</span> <span class="n">B</span><span class="p">)</span> <span class="k">if</span> <span class="n">B</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="k">else</span> <span class="p">(</span><span class="mi">60</span> <span class="o">*</span> <span class="n">B</span><span class="p">))</span>
     <span class="o">+</span> <span class="mi">7</span> <span class="o">*</span> <span class="n">C</span>
     <span class="o">-</span> <span class="mi">8</span> <span class="o">*</span> <span class="n">D</span>
     <span class="k">for</span> <span class="n">A_i_minus_2</span><span class="p">,</span> <span class="n">A_i_minus_1</span><span class="p">,</span> <span class="n">A</span><span class="p">,</span> <span class="n">A_i_plus_1</span><span class="p">,</span> <span class="n">B</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">D</span>
     <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"A_i_minus_2"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"A_i_minus_1"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"A"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"A_i_plus_1"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"B"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"C"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"D"</span><span class="p">]</span>
     <span class="p">)</span>
 <span class="p">]</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">12_list_comprehension_w_zip_and_row_tuple_passed_to_func</code></p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">df</span><span class="p">[</span><span class="s">"val"</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span>
     <span class="n">calculate_val</span><span class="p">(</span>
         <span class="n">row</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span>
         <span class="n">row</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span>
         <span class="n">row</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span>
         <span class="n">row</span><span class="p">[</span><span class="mi">3</span><span class="p">],</span>
         <span class="n">row</span><span class="p">[</span><span class="mi">4</span><span class="p">],</span>
         <span class="n">row</span><span class="p">[</span><span class="mi">5</span><span class="p">],</span>
         <span class="n">row</span><span class="p">[</span><span class="mi">6</span><span class="p">],</span>
     <span class="p">)</span> <span class="k">for</span> <span class="n">row</span>
     <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"A_i_minus_2"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"A_i_minus_1"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"A"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"A_i_plus_1"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"B"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"C"</span><span class="p">],</span>
         <span class="n">df</span><span class="p">[</span><span class="s">"D"</span><span class="p">]</span>
     <span class="p">)</span>
 <span class="p">]</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">13_list_comprehension_w__to_numpy__and_direct_variable_assignment_passed_to_func</code></p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">df</span><span class="p">[</span><span class="s">"val"</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span>
     <span class="c1"># Note: you *could* do the calculations directly here instead of using a
</span>     <span class="c1"># function call, so long as you don't have indented code blocks such as
</span>     <span class="c1"># sub-routines or multi-line if statements.
</span>     <span class="c1">#
</span>     <span class="c1"># I'm using a function call.
</span>     <span class="n">calculate_val</span><span class="p">(</span>
         <span class="n">A_i_minus_2</span><span class="p">,</span>
         <span class="n">A_i_minus_1</span><span class="p">,</span>
         <span class="n">A</span><span class="p">,</span>
         <span class="n">A_i_plus_1</span><span class="p">,</span>
         <span class="n">B</span><span class="p">,</span>
         <span class="n">C</span><span class="p">,</span>
         <span class="n">D</span>
     <span class="p">)</span> <span class="k">for</span> <span class="n">A_i_minus_2</span><span class="p">,</span> <span class="n">A_i_minus_1</span><span class="p">,</span> <span class="n">A</span><span class="p">,</span> <span class="n">A_i_plus_1</span><span class="p">,</span> <span class="n">B</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">D</span>
         <span class="c1"># Note: this `[[...]]` double-bracket indexing is used to select a
</span>         <span class="c1"># subset of columns from the dataframe. The inner `[]` brackets
</span>         <span class="c1"># create a list from the column names within them, and the outer 
</span>         <span class="c1"># `[]` brackets accept this list to index into the dataframe and
</span>         <span class="c1"># select just this list of columns, in that order.
</span>         <span class="c1"># - See the official documentation on it here:
</span>         <span class="c1">#   https://pandas.pydata.org/docs/user_guide/indexing.html#basics
</span>         <span class="c1">#   - Search for the phrase "You can pass a list of columns to [] to
</span>         <span class="c1">#     select columns in that order."
</span>         <span class="c1">#   - I learned this from this comment here:
</span>         <span class="c1">#     https://stackoverflow.com/questions/16476924/how-to-iterate-over-rows-in-a-dataframe-in-pandas/55557758#comment136020567_55557758
</span>         <span class="c1"># - One of the **list comprehension** examples in this answer here
</span>         <span class="c1">#   uses `.to_numpy()` like this:
</span>         <span class="c1">#   https://stackoverflow.com/a/55557758/4561887
</span>     <span class="ow">in</span> <span class="n">df</span><span class="p">[[</span>
         <span class="s">"A_i_minus_2"</span><span class="p">,</span>
         <span class="s">"A_i_minus_1"</span><span class="p">,</span>
         <span class="s">"A"</span><span class="p">,</span>
         <span class="s">"A_i_plus_1"</span><span class="p">,</span>
         <span class="s">"B"</span><span class="p">,</span>
         <span class="s">"C"</span><span class="p">,</span>
         <span class="s">"D"</span>
     <span class="p">]].</span><span class="n">to_numpy</span><span class="p">()</span>  <span class="c1"># NB: `.values` works here too, but is deprecated. See:
</span>                    <span class="c1"># https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.values.html
</span> <span class="p">]</span>
</code></pre></div>    </div>
  </li>
</ol>

<p>Here are the results again:</p>

<p><a href="https://i.stack.imgur.com/5biMy.png"><img src="https://i.stack.imgur.com/5biMy.png" alt="enter image description here" /></a></p>

<h2 id="using-the-pre-shifted-rows-in-the-4-for-loop-techniques-as-well">Using the pre-shifted rows in the 4 <code class="language-plaintext highlighter-rouge">for</code> loop techniques as well</h2>

<p>I wanted to see if removing this <code class="language-plaintext highlighter-rouge">if</code> check and using the pre-shifted rows in the 4 <code class="language-plaintext highlighter-rouge">for</code> loop techniques would have much effect:</p>

<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">2</span> <span class="ow">or</span> <span class="n">i</span> <span class="o">&gt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">df</span><span class="p">)</span><span class="o">-</span><span class="mi">2</span><span class="p">:</span>
    <span class="k">continue</span>
</code></pre></div></div>

<p>…so I created this file with those modifications: <a href="https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world/blob/master/python/pandas_dataframe_iteration_vs_vectorization_vs_list_comprehension_speed_tests_mod.py"><code class="language-plaintext highlighter-rouge">pandas_dataframe_iteration_vs_vectorization_vs_list_comprehension_speed_tests_mod.py</code></a>. Search the file for “MOD:” to find the 4 new, modified techniques.</p>

<p>It had only a slight improvement. Here are the results of these 17 techniques now, with the 4 new ones having the word <code class="language-plaintext highlighter-rouge">_MOD_</code> near the beginning of their name, just after their number. This is over 500k rows this time, not 2M:</p>

<p><a href="https://i.stack.imgur.com/HxKkJ.png"><img src="https://i.stack.imgur.com/HxKkJ.png" alt="enter image description here" /></a></p>

<h2 id="more-on-iterrtuples">More on <code class="language-plaintext highlighter-rouge">.iterrtuples()</code></h2>

<p>There are actually more nuances when using <code class="language-plaintext highlighter-rouge">.itertuples()</code>. To delve into some of those, read <a href="https://stackoverflow.com/a/59413206/4561887">this answer by @Romain Capron</a>. Here is a bar chart plot I made of his results:</p>

<p><a href="https://i.stack.imgur.com/ws9db.png"><img src="https://i.stack.imgur.com/ws9db.png" alt="enter image description here" /></a></p>

<p>My plotting code for his results is in <strong><a href="https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world/blob/master/python/pandas_plot_bar_chart_better_GREAT_AUTOLABEL_DATA.py"><code class="language-plaintext highlighter-rouge">python/pandas_plot_bar_chart_better_GREAT_AUTOLABEL_DATA.py</code></a></strong> in my <a href="https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world">eRCaGuy_hello_world</a> repo.</p>

<h2 id="future-work">Future work</h2>

<p>Using Cython (Python compiled into C code), or just raw C functions called by Python, could be faster potentially, but I’m not going to do that for these tests. I’d only look into and speed test those options for big optimizations.</p>

<p>I currently don’t know Cython and don’t feel the need to learn it. As you can see above, simply using pure vectorization properly already runs incredibly fast, processing 2 <em>million</em> rows in only 0.1 seconds, or 20 million rows per second.</p>

<h2 id="references">References</h2>

<ol>
  <li>A bunch of the official Pandas documentation, especially the <code class="language-plaintext highlighter-rouge">DataFrame</code> documentation here: <a href="https://pandas.pydata.org/pandas-docs/stable/reference/frame.html">https://pandas.pydata.org/pandas-docs/stable/reference/frame.html</a>.</li>
  <li><a href="https://stackoverflow.com/a/55557758/4561887">This excellent answer by @cs95</a> - this is where I learned in particular how to use list comprehension to iterate over a DataFrame.</li>
  <li><a href="https://stackoverflow.com/a/59413206/4561887">This answer about <code class="language-plaintext highlighter-rouge">itertuples()</code>, by @Romain Capron</a> - I studied it carefully and edited/formatted it.</li>
  <li>All of this is my own code, but I want to point out that I had dozens of chats with GitHub Copilot (mostly), Bing AI, and ChatGPT in order to figure out many of these techniques and debug my code as I went.</li>
  <li>
    <p>Bing Chat produced the pretty LaTeX equation for me, with the following prompt. Of course, I verified the output:</p>

    <blockquote>
      <p>Convert this Python code to a pretty equation I can paste onto Stack Overflow:</p>
      <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    val = (
        2 * A_i_minus_2
        + 3 * A_i_minus_1
        + 4 * A
        + 5 * A_i_plus_1
        # Python ternary operator; don't forget parentheses around the entire ternary expression!
        + ((6 * B) if B &gt; 0 else (60 * B))
        + 7 * C
        - 8 * D
    )
</code></pre></div>      </div>
    </blockquote>
  </li>
</ol>

<h2 id="see-also">See also</h2>

<ol>
  <li>
    <p><a href="https://en.wikipedia.org/wiki/Array_programming">https://en.wikipedia.org/wiki/Array_programming</a> - array programming, or “vectorization”:</p>

    <blockquote>
      <p>In computer science, array programming refers to solutions that allow the application of operations to an entire set of values at once. Such solutions are commonly used in scientific and engineering settings.</p>

      <p>Modern programming languages that support array programming (also known as vector or multidimensional languages) have been engineered specifically to generalize operations on scalars to apply transparently to vectors, matrices, and higher-dimensional arrays. These include APL, J, Fortran, MATLAB, Analytica, Octave, R, Cilk Plus, Julia, Perl Data Language (PDL). In these languages, an operation that operates on entire arrays can be called a vectorized operation,<a href="https://i.stack.imgur.com/5biMy.png">1</a> regardless of whether it is executed on a vector processor, which implements vector instructions.</p>
    </blockquote>
  </li>
  <li><a href="https://stackoverflow.com/q/54028199/4561887">Are for-loops in pandas really bad? When should I care?</a>
    <ol>
      <li><a href="https://stackoverflow.com/a/77270403/4561887">my answer</a></li>
    </ol>
  </li>
  <li><a href="https://stackoverflow.com/q/24870953/4561887">Does pandas iterrows have performance issues?</a>
    <ol>
      <li><a href="https://stackoverflow.com/a/24871316/4561887">This answer</a>
        <ol>
          <li>
            <p><a href="https://stackoverflow.com/questions/24870953/does-pandas-iterrows-have-performance-issues#comment136223122_24871316">My comment underneath it</a>:</p>

            <blockquote>
              <p>…Based on my results, I’d say, however, these are the best approaches, in this order of best first:</p>

              <ol>
                <li>vectorization,</li>
                <li>list comprehension,</li>
                <li><code class="language-plaintext highlighter-rouge">.itertuples()</code>,</li>
                <li><code class="language-plaintext highlighter-rouge">.apply()</code>,</li>
                <li>raw <code class="language-plaintext highlighter-rouge">for</code> loop,</li>
                <li><code class="language-plaintext highlighter-rouge">.iterrows().</code></li>
              </ol>

              <p>I didn’t test Cython.</p>
            </blockquote>
          </li>
        </ol>
      </li>
    </ol>
  </li>
</ol>]]></content><author><name>Gabriel Staples</name></author><category term="python" /><category term="tutorial" /><category term="code" /><category term="programming" /><summary type="html"><![CDATA[Here are 13 techniques for iterating over Pandas DataFrames. As you can see, the time it takes varies dramatically. The fastest technique is ~1363x faster than the slowest technique! The key takeaway,]]></summary></entry><entry><title type="html">My propeller thrust articles</title><link href="https://gabrielstaples.com/propeller-thrust-articles/" rel="alternate" type="text/html" title="My propeller thrust articles" /><published>2023-06-17T00:00:00+00:00</published><updated>2023-06-17T00:00:00+00:00</updated><id>https://gabrielstaples.com/propeller-thrust-articles</id><content type="html" xml:base="https://gabrielstaples.com/propeller-thrust-articles/"><![CDATA[<p><a href="https://hits.seeyoufarm.com"><img src="https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgabrielstaples.com%2Fpropeller-thrust-articles%2F&amp;count_bg=%2379C83D&amp;title_bg=%23555555&amp;icon=&amp;icon_color=%23E7E7E7&amp;title=views+%28today+%2F+total%29&amp;edge_flat=false" alt="Hits" /></a></p>

<p>Here are my two articles on this topic so far:</p>
<ol>
  <li><a href="https://www.electricrcaircraftguy.com/2013/09/propeller-static-dynamic-thrust-equation.html">ElectricRCAircraftGuy.com: Propeller Static &amp; Dynamic Thrust Calculation - Part 1 of 2</a></li>
  <li><a href="https://www.electricrcaircraftguy.com/2014/04/propeller-static-dynamic-thrust-equation-background.html">ElectricRCAircraftGuyc.com: Propeller Static &amp; Dynamic Thrust Calculation - Part 2 of 2 - How Did I Come Up With This Equation?</a></li>
</ol>

<p>If you have questions or comments about those articles, feel free to leave them in the comments here. Leaving comments under those articles is very limited, so leaving comments here is better.</p>

<p>You’ll need to create a GitHub account to leave a comment here, but again, it’s worth it. Here are some of the advantages of leaving comments here:</p>
<ol>
  <li>After leaving a comment, you can edit or delete it.</li>
  <li>You can <a href="https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax">write in Markdown syntax</a> here, which is powerful and efficient.</li>
  <li>You can optionally write your comment using the <a href="https://github.com/ElectricRCAircraftGuy/ElectricRCAircraftGuy.github.io/issues/53">GitHub Issues GUI comment editor here</a>.</li>
  <li>You can insert images, code, quotes, bold, italics, headings, etc., and possibly even MathJax equations (I haven’t tried that yet) here.</li>
</ol>]]></content><author><name>Gabriel Staples</name></author><category term="physics" /><category term="aerodynamics" /><category term="propellers" /><category term="equations" /><summary type="html"><![CDATA[Here are my two articles on this topic so far: 1. [ElectricRCAircraftGuy.com: Propeller Static & Dynamic Thrust Calculation - Part 1 of 2](https://www.electricrcaircraftguy.com/2013/09/propeller-static-dynamic-thrust-equation.html) 2. [ElectricRCAircraftGuyc.com: Propeller Static & Dynamic Thrust Calculation - Part 2 of 2 - How Did I Come Up With This Equation?](https://www.electricrcaircraftguy.com/2014/04/propeller-static-dynamic-thrust-equation-background.html)]]></summary></entry><entry><title type="html">Tutorial: Getting started with `ydotool` to automate key presses (or mouse movements) in Linux</title><link href="https://gabrielstaples.com/ydotool-tutorial/" rel="alternate" type="text/html" title="Tutorial: Getting started with `ydotool` to automate key presses (or mouse movements) in Linux" /><published>2023-06-17T00:00:00+00:00</published><updated>2023-06-17T00:00:00+00:00</updated><id>https://gabrielstaples.com/ydotool-tutorial</id><content type="html" xml:base="https://gabrielstaples.com/ydotool-tutorial/"><![CDATA[<p><a href="https://hits.seeyoufarm.com"><img src="https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgabrielstaples.com%2Fydotool-tutorial%2F&amp;count_bg=%2379C83D&amp;title_bg=%23555555&amp;icon=&amp;icon_color=%23E7E7E7&amp;title=views+%28today+%2F+total%29&amp;edge_flat=false" alt="Hits" /></a></p>

<h2 id="ydotool-works-well-in-both-the-x11-and-wayland-window-managers-to-press-any-keys-including-windows--d-for-example"><code class="language-plaintext highlighter-rouge">ydotool</code> works well in both the X11 <em>and</em> Wayland window managers to press any keys, including <kbd>Windows</kbd> + <kbd>D</kbd>, for example</h2>

<p>Here is a full tutorial.</p>

<p>Note: <code class="language-plaintext highlighter-rouge">ydotool</code> is a universal replacement of <code class="language-plaintext highlighter-rouge">xdotool</code>, which is a tool to move the mouse and do keypresses in the X11 window manager. The <code class="language-plaintext highlighter-rouge">ydotool</code> program was created because <code class="language-plaintext highlighter-rouge">xdotool</code> doesn’t work in Wayland, which is the new window manager that Ubuntu is slowly moving towards, even though Wayland currently <a href="https://askubuntu.com/a/1470563/327339">is still very buggy should be turned <em>off</em> in most cases</a>.</p>

<p><em>Tested on Ubuntu 22.04.2 with both the X11 and Wayland window managers (for how to change between the two, see my notes and screenshots <a href="https://askubuntu.com/a/1470563/327339">in my answer here</a>).</em></p>

<h2 id="1-build-and-install-ydotool">1. Build and install <code class="language-plaintext highlighter-rouge">ydotool</code>:</h2>

<p>See: <a href="https://github.com/ReimuNotMoe/ydotool#build">https://github.com/ReimuNotMoe/ydotool#build</a>.</p>

<p>First, build and install it:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># install dependencies</span>
<span class="nb">sudo </span>apt update
<span class="nb">sudo </span>apt <span class="nb">install </span>git cmake scdoc

<span class="c"># build ydotool</span>
<span class="c"># See: https://github.com/ReimuNotMoe/ydotool#build</span>
git clone https://github.com/ReimuNotMoe/ydotool.git
<span class="nb">cd </span>ydotool
<span class="nb">mkdir </span>build
<span class="nb">cd </span>build
cmake ..                 <span class="c"># takes &lt; 1 second</span>
<span class="nb">time </span>make <span class="nt">-j</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">nproc</span><span class="si">)</span><span class="s2">"</span>  <span class="c"># takes &lt; 1 second</span>
<span class="nb">sudo </span>make <span class="nb">install</span>
<span class="c"># Note: the install command installs here:</span>
<span class="c">#</span>
<span class="c">#       -- Install configuration: ""</span>
<span class="c">#       -- Installing: /usr/local/bin/ydotoold</span>
<span class="c">#       -- Installing: /usr/local/bin/ydotool</span>
<span class="c">#       -- Installing: /usr/lib/systemd/user/ydotool.service</span>
<span class="c">#       -- Installing: /usr/local/share/man/man1/ydotool.1</span>
<span class="c">#       -- Installing: /usr/local/share/man/man8/ydotoold.8</span>

<span class="c"># See the man pages for help, and to prove to yourself it is installed</span>
man ydotool   <span class="c"># for the main program</span>
man ydotoold  <span class="c"># for the background daemon process</span>
<span class="c"># help menu</span>
ydotool <span class="nt">-h</span>    <span class="c"># for the main program</span>
ydotoold <span class="nt">-h</span>   <span class="c"># for the background daemon process [VERY HELPFUL MENU!]</span>

<span class="c"># check the version; my output is `v1.0.4-0-g57ba7d0`</span>
ydotoold <span class="nt">--version</span>
</code></pre></div></div>

<h2 id="2-now-use-it-have-ydotool-press-windows--d">2. Now, use it: have <code class="language-plaintext highlighter-rouge">ydotool</code> press <kbd>Windows</kbd> + <kbd>D</kbd></h2>

<h4 id="quick-summary">Quick summary:</h4>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 1. start the `ydotoold` background daemon (`sudo -b` runs it in the</span>
<span class="c"># background; see `sudo -h`).</span>
<span class="c"># - It takes a couple seconds to fully start up and print "READY". Once it does</span>
<span class="c">#   that, hit Enter a couple times to clear out the command line.</span>
<span class="nb">sudo</span> <span class="nt">-b</span> ydotoold <span class="nt">--socket-path</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.ydotool_socket"</span> <span class="nt">--socket-own</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nb">id</span> <span class="nt">-u</span><span class="si">)</span><span class="s2">:</span><span class="si">$(</span><span class="nb">id</span> <span class="nt">-g</span><span class="si">)</span><span class="s2">"</span>

<span class="c"># 2. Use `ydotool`</span>
<span class="c"># - Have ydotool press Windows + D once to hide all windows, then make it wait 2</span>
<span class="c">#   seconds, then have it press Windows + D again to show all windows:</span>
<span class="nv">YDOTOOL_SOCKET</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.ydotool_socket"</span> ydotool key 125:1 32:1 32:0 125:0<span class="p">;</span> <span class="se">\</span>
    <span class="nb">sleep </span>2<span class="p">;</span> <span class="se">\</span>
    <span class="nv">YDOTOOL_SOCKET</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.ydotool_socket"</span> ydotool key 125:1 32:1 32:0 125:0

<span class="c"># 3. Additional commands you may need: to show, kill, and help</span>

<span class="c"># See the `ydotoold` background running processes</span>
ps auxf | <span class="nb">grep </span>ydotoold

<span class="c"># Kill the `ydotoold` background running processes</span>
<span class="nb">sudo </span>pkill ydotoold

<span class="c"># View the `ydotool` main help menu, including a list of all sub-commands.</span>
ydotool <span class="nt">-h</span>

<span class="c"># View the `ydotool` sub-command help menus. </span>
<span class="c"># - Bug ( https://github.com/ReimuNotMoe/ydotool/issues/206 ): the daemon *must*</span>
<span class="c">#   be running first to see these help menus!</span>
<span class="nv">YDOTOOL_SOCKET</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.ydotool_socket"</span> ydotool key <span class="nt">-h</span>
<span class="nv">YDOTOOL_SOCKET</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.ydotool_socket"</span> ydotool click <span class="nt">-h</span>
<span class="nv">YDOTOOL_SOCKET</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.ydotool_socket"</span> ydotool mousemove <span class="nt">-h</span>
<span class="c"># etc. </span>

<span class="c"># Open the Linux C header file containing all available key codes that `ydotool`</span>
<span class="c"># can press</span>
gedit /usr/include/linux/input-event-codes.h
</code></pre></div></div>

<h4 id="details">Details:</h4>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># In one terminal, start the required background daemon process as root, but</span>
<span class="c"># setting its socket file to be accessible by your main user withOUT `sudo`. </span>
<span class="c"># - Allowing our user to access this daemon withOUT running it as `root` is</span>
<span class="c">#   accomplished by setting the owner of the socket file to our own user via</span>
<span class="c">#   the `--socket-path="$HOME/.ydotool_socket" --socket-own="$(id -u):$(id -g)"` </span>
<span class="c">#   options.</span>
<span class="c"># - It takes a couple seconds to fully start up and print "READY". Once it does</span>
<span class="c">#   that, hit Enter a couple times to clear out the command line.</span>
<span class="nb">sudo</span> <span class="nt">-b</span> ydotoold <span class="nt">--socket-path</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.ydotool_socket"</span> <span class="nt">--socket-own</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nb">id</span> <span class="nt">-u</span><span class="si">)</span><span class="s2">:</span><span class="si">$(</span><span class="nb">id</span> <span class="nt">-g</span><span class="si">)</span><span class="s2">"</span>

<span class="c"># In the same terminal, or another terminal, send a signal to this running</span>
<span class="c"># background daemon via the open socket inter-process-communication</span>
<span class="c"># (IPC) pseudo-file. Here, we send the Windows + D shortcut key:</span>
<span class="c">#</span>
<span class="c"># Windows + D press and release: window toggle: this hides all windows.</span>
<span class="nv">YDOTOOL_SOCKET</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.ydotool_socket"</span> ydotool key 125:1 32:1 32:0 125:0
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">125:1 32:1 32:0 125:0</code> key codes tell the program to, in this order: “press the Windows key, press the D key, release the D key, release the Windows key”.</p>

<p>That’s it!</p>

<p>Manually press <kbd>Windows</kbd> + <kbd>D</kbd> now to show all windows again so you can see your terminal.</p>

<p>I’ll talk about what the <code class="language-plaintext highlighter-rouge">key 125:1 32:1 32:0 125:0</code> part means farther below.</p>

<p>Note that <code class="language-plaintext highlighter-rouge">sudo -b</code> runs this daemon as a background process. If you leave off <code class="language-plaintext highlighter-rouge">-b</code>, then you’ll have to use two separate terminals, as the daemon will take up the first terminal.</p>

<p>You can leave the background <code class="language-plaintext highlighter-rouge">ydotoold</code> background daemon running forever, but if you’d like to kill it, here’s how:</p>
<ol>
  <li>Kill <code class="language-plaintext highlighter-rouge">ydotoold</code>:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">sudo </span>pkill ydotoold
</code></pre></div>    </div>
  </li>
  <li>(Optional) see if <code class="language-plaintext highlighter-rouge">ydotoold</code> is running:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ps auxf | <span class="nb">grep </span>ydotoold
</code></pre></div>    </div>

    <p>Example run and output <em>before</em> killing <code class="language-plaintext highlighter-rouge">ydotoold</code>. As you can see, my single call to <code class="language-plaintext highlighter-rouge">ydotoold</code> above actually spawned <em>3</em> <code class="language-plaintext highlighter-rouge">ydotoold</code> processes, because the one I called spawned a sub process, which spawned a sub sub process.</p>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span>ps auxf | <span class="nb">grep </span>ydotoold
 root     1772293  0.0  0.0  23004  5952 pts/4    S    22:00   0:00  |   |   <span class="se">\_</span> <span class="nb">sudo </span>ydotoold <span class="nt">--socket-path</span><span class="o">=</span>/home/gabriel/.ydotool_socket <span class="nt">--socket-own</span><span class="o">=</span>1000:1000
 root     1772304  0.0  0.0  23004   944 pts/6    Ss+  22:00   0:00  |   |   |   <span class="se">\_</span> <span class="nb">sudo </span>ydotoold <span class="nt">--socket-path</span><span class="o">=</span>/home/gabriel/.ydotool_socket <span class="nt">--socket-own</span><span class="o">=</span>1000:1000
 root     1772305  0.0  0.0   2776   956 pts/6    S    22:00   0:00  |   |   |       <span class="se">\_</span> ydotoold <span class="nt">--socket-path</span><span class="o">=</span>/home/gabriel/.ydotool_socket <span class="nt">--socket-own</span><span class="o">=</span>1000:1000
 gabriel+ 1792351  0.0  0.0  17864  2476 pts/4    S+   22:09   0:00  |   |   <span class="se">\_</span> <span class="nb">grep</span> <span class="nt">--color</span><span class="o">=</span>auto ydotoold
</code></pre></div>    </div>
  </li>
  <li>Here is what the main <code class="language-plaintext highlighter-rouge">ydotool</code> help menu looks like. It shows the various sub-commands available:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span>ydotool <span class="nt">-h</span>
 Usage: ydotool &lt;cmd&gt; &lt;args&gt;
 Available commands:
   click
   mousemove
   <span class="nb">type
   </span>key
   debug
   bakers
 Use environment variable YDOTOOL_SOCKET to specify daemon socket.
</code></pre></div>    </div>
  </li>
  <li>Here are some of the sub-command menus. Note that due to <a href="https://github.com/ReimuNotMoe/ydotool/issues/206">this bug</a>, you <em>must</em> have the <code class="language-plaintext highlighter-rouge">ydotoold</code> daemon running first in order for the sub-command help menus to show up:
    <ol>
      <li><code class="language-plaintext highlighter-rouge">key</code>: <code class="language-plaintext highlighter-rouge">YDOTOOL_SOCKET="$HOME/.ydotool_socket" ydotool key -h</code>
        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ YDOTOOL_SOCKET</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.ydotool_socket"</span> ydotool key <span class="nt">-h</span>
 Usage: key <span class="o">[</span>OPTION]... <span class="o">[</span>KEYCODES]...
 Emit key events.

 Options:
   <span class="nt">-d</span>, <span class="nt">--key-delay</span><span class="o">=</span>N          Delay N milliseconds between key events
   <span class="nt">-h</span>, <span class="nt">--help</span>                 Display this <span class="nb">help </span>and <span class="nb">exit

 </span>Since there<span class="s1">'s no way to know how many keyboard layouts are there in the world,
 we'</span>re using raw keycodes now.

 Syntax: &lt;keycode&gt;:&lt;pressed&gt;
 e.g. 28:1 28:0 means pressing on the Enter button on a standard US keyboard.
      <span class="o">(</span>where :1 <span class="k">for </span>pressed means the key is down and <span class="k">then</span> :0 means the key is released<span class="o">)</span>     
      42:1 38:1 38:0 24:1 24:0 38:1 38:0 42:0 - <span class="s2">"LOL"</span>

 Non-interpretable values, such as 0, aaa, l0l, will only cause a delay.

 See <span class="sb">`</span>/usr/include/linux/input-event-codes.h<span class="s1">' for available key codes (KEY_*).
</span></code></pre></div>        </div>
      </li>
      <li><code class="language-plaintext highlighter-rouge">click</code>: <code class="language-plaintext highlighter-rouge">YDOTOOL_SOCKET="$HOME/.ydotool_socket" ydotool click -h</code>
        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ YDOTOOL_SOCKET</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.ydotool_socket"</span> ydotool click <span class="nt">-h</span>
 Usage: click <span class="o">[</span>OPTION]... <span class="o">[</span>BUTTONS]...
 Click mouse buttons.

 Options:
   <span class="nt">-r</span>, <span class="nt">--repeat</span><span class="o">=</span>N             Repeat entire sequence N <span class="nb">times</span>
   <span class="nt">-D</span>, <span class="nt">--next-delay</span><span class="o">=</span>N         Delay N milliseconds between input events <span class="o">(</span>up/down,                                
                              a <span class="nb">complete </span>click means doubled <span class="nb">time</span><span class="o">)</span>
   <span class="nt">-h</span>, <span class="nt">--help</span>                 Display this <span class="nb">help </span>and <span class="nb">exit

 </span>How to specify buttons:
   Now all mouse buttons are represented using hexadecimal numeric values, with an optional
 bit mask to specify <span class="k">if </span>mouse up/down needs to be omitted.
   0x00 - LEFT
   0x01 - RIGHT
   0x02 - MIDDLE
   0x03 - SIDE
   0x04 - EXTR
   0x05 - FORWARD
   0x06 - BACK
   0x07 - TASK
   0x40 - Mouse down
   0x80 - Mouse up
   Examples:
     0x00: chooses left button, but does nothing <span class="o">(</span>you can use this to implement extra sleeps<span class="o">)</span>
     0xC0: left button click <span class="o">(</span>down <span class="k">then </span>up<span class="o">)</span>
     0x41: right button down
     0x82: middle button up
   The <span class="s1">'0x'</span> prefix can be omitted <span class="k">if </span>you want.
</code></pre></div>        </div>
      </li>
      <li><code class="language-plaintext highlighter-rouge">mousemove</code>: <code class="language-plaintext highlighter-rouge">YDOTOOL_SOCKET="$HOME/.ydotool_socket" ydotool mousemove -h</code>
        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ YDOTOOL_SOCKET</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.ydotool_socket"</span> ydotool mousemove <span class="nt">-h</span>
 Usage: mousemove <span class="o">[</span>OPTION]... <span class="o">[</span><span class="nt">-x</span> &lt;xpos&gt; <span class="nt">-y</span> &lt;ypos&gt;] <span class="o">[</span><span class="nt">--</span> &lt;xpos&gt; &lt;ypos&gt;]
 Move mouse pointer or wheel.

 Options:
   <span class="nt">-w</span>, <span class="nt">--wheel</span>                Move mouse wheel relatively
   <span class="nt">-a</span>, <span class="nt">--absolute</span>             Use absolute position, not applicable to wheel
   <span class="nt">-x</span>, <span class="nt">--xpos</span>                 X position
   <span class="nt">-y</span>, <span class="nt">--ypos</span>                 Y position
   <span class="nt">-h</span>, <span class="nt">--help</span>                 Display this <span class="nb">help </span>and <span class="nb">exit

 </span>You need to disable mouse speed acceleration <span class="k">for </span>correct absolute movement.
</code></pre></div>        </div>
      </li>
    </ol>
  </li>
</ol>

<p>Voila! I’m so happy I got this working! I now have a viable means of getting my <a href="https://askubuntu.com/a/1109490/327339">“show desktop”</a> button to work again once I am forced to use Wayland, and once it <a href="https://askubuntu.com/a/1470563/327339">is less buggy</a> and actually functions right.</p>

<h2 id="what-do-keycodes-1251-321-320-1250-mean-and-how-do-i-find-key-codes-i-want-to-use">What do keycodes <code class="language-plaintext highlighter-rouge">125:1 32:1 32:0 125:0</code> mean, and how do I find key codes I want to use?</h2>

<p>See the <code class="language-plaintext highlighter-rouge">YDOTOOL_SOCKET="$HOME/.ydotool_socket" ydotool key -h</code> help menu above. It states:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Syntax: &lt;keycode&gt;:&lt;pressed&gt;
e.g. 28:1 28:0 means pressing on the Enter button on a standard US keyboard.
     <span class="o">(</span>where :1 <span class="k">for </span>pressed means the key is down and <span class="k">then</span> :0 means the key is released<span class="o">)</span>     
     42:1 38:1 38:0 24:1 24:0 38:1 38:0 42:0 - <span class="s2">"LOL"</span>

Non-interpretable values, such as 0, aaa, l0l, will only cause a delay.

See <span class="sb">`</span>/usr/include/linux/input-event-codes.h<span class="s1">' for available key codes (KEY_*).
</span></code></pre></div></div>

<p>So, open up your Linux <code class="language-plaintext highlighter-rouge">/usr/include/linux/input-event-codes.h</code> file to see all key codes:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># (my preference) open in the Sublime Text GUI</span>
subl /usr/include/linux/input-event-codes.h
<span class="c"># open in the gedit GUI</span>
gedit /usr/include/linux/input-event-codes.h
<span class="c"># open in nano (Ctrl + x to exit)</span>
nano /usr/include/linux/input-event-codes.h
<span class="c"># open in vim (:q then Enter to exit)</span>
vi /usr/include/linux/input-event-codes.h
</code></pre></div></div>

<p>You can see what appears to be a <em>more-recent version</em> of this file online in the Linux kernel here: <a href="https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h">https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h</a>.</p>

<p>As you can see <a href="https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h#L202">in that file here</a>, key code <code class="language-plaintext highlighter-rouge">125</code> is the <code class="language-plaintext highlighter-rouge">KEY_LEFTMETA</code>:</p>

<blockquote>
  <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#define KEY_LEFTMETA      125
</code></pre></div>  </div>
</blockquote>

<p>Key code <code class="language-plaintext highlighter-rouge">32</code> is <code class="language-plaintext highlighter-rouge">KEY_D</code>:</p>

<blockquote>
  <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#define KEY_D         32
</code></pre></div>  </div>
</blockquote>

<p>A <a href="https://www.google.com/search?q=input-event-codes.h+which+is+the+super+or+windows+key%3F&amp;oq=input-event-codes.h+which+is+the+super+or+windows+key%3F&amp;aqs=chrome..69i57.477j0j9&amp;sourceid=chrome&amp;ie=UTF-8">Google search for “input-event-codes.h which is the super or windows key?”</a> helped me find <a href="https://github.com/PixlOne/logiops/issues/201#issuecomment-773745032">this</a>, where @hasimsait said it was <code class="language-plaintext highlighter-rouge">KEY_LEFTMETA</code>. That’s how I knew to use that key for the <kbd>Windows</kbd> (<kbd>Super</kbd>) key.</p>

<p>From the <code class="language-plaintext highlighter-rouge">ydotool key -h</code> help menu above, we learned: <code class="language-plaintext highlighter-rouge">Syntax: &lt;keycode&gt;:&lt;pressed&gt;</code>, where <code class="language-plaintext highlighter-rouge">1</code> is pressed, and <code class="language-plaintext highlighter-rouge">0</code> is not pressed. So, <code class="language-plaintext highlighter-rouge">32:1 32:0</code> means to press and then release the <kbd>D</kbd> key. That would type a lower-case <code class="language-plaintext highlighter-rouge">d</code> if your cursor was in a text editor.</p>

<p>Therefore, <code class="language-plaintext highlighter-rouge">125:1 32:1 32:0 125:0</code> means, in this order: “press the Windows key, press the D key, release the D key, release the Windows key”.</p>

<p>Notice that to activate the <kbd>Windows</kbd> + <kbd>D</kbd> shortcut key, you must <em>release the buttons in the opposite order they were pressed!</em> You hold the <kbd>Windows</kbd> key while pressing and releasing the <kbd>D</kbd> key. So, be sure to release the buttons with <code class="language-plaintext highlighter-rouge">&lt;keycode&gt;:0</code> in the <em>opposite order</em> that you pressed them.</p>

<h2 id="warnings-in-wayland">Warnings in Wayland</h2>

<p>Note that I see this in the output when I run my <code class="language-plaintext highlighter-rouge">ydotoold</code> command above <em>in Wayland</em> only:</p>

<blockquote>
  <p>WARNING: running xinput against an Xwayland server. See the xinput man page for details.<br />
unable to find device pointer:ydotoold virtual device</p>
</blockquote>

<p>I don’t really know what it means, but it seems important. Here is the full run and output:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>ydotoold <span class="nt">--socket-path</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.ydotool_socket"</span> <span class="nt">--socket-own</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nb">id</span> <span class="nt">-u</span><span class="si">)</span><span class="s2">:</span><span class="si">$(</span><span class="nb">id</span> <span class="nt">-g</span><span class="si">)</span><span class="s2">"</span>
<span class="o">[</span><span class="nb">sudo</span><span class="o">]</span> password <span class="k">for </span>gabriel: 
Socket path: /home/gabriel/.ydotool_socket
Removing old stale socket
Socket permission: 0600
Socket ownership: <span class="nv">UID</span><span class="o">=</span>1000, <span class="nv">GID</span><span class="o">=</span>1000
READY
WARNING: running xinput against an Xwayland server. See the xinput man page <span class="k">for </span>details.
unable to find device pointer:ydotoold virtual device
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">man xinput</code> shows the following, but again, I don’t really understand what this means either:</p>

<blockquote>
  <p>XWAYLAND</p>

  <p>Xwayland is an X server that uses  a  Wayland  Compositor  as  backend.
Xwayland  acts as translation layer between the X protocol and the Way‐
land protocol but does not have direct access to the  hardware.  The  X
Input Extension devices created by Xwayland (“xwayland-pointer”, “xway‐
land-keyboard”, etc.) map to the Wayland protocol devices, not to phys‐
ical devices.</p>

  <p>These  X  Input  Extension  devices are only visible to other X clients
connected to the same Xwayland process. Changing properties on Xwayland
devices  only  affects the behavior of those clients. For example, dis‐
abling an Xwayland device with xinput does not disable  the  device  in
Wayland-native  applications.  Other changes may not have any effect at
all.</p>

  <p>In most instances, using xinput with an Xwayland device  is  indicative
of  a  bug  in  a shell script and xinput will print a warning. Use the
Wayland Compositor’s native device configuration methods instead.</p>
</blockquote>

<p>In X11, I don’t get the warning. Here is the output when I run the daemon:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>ydotoold <span class="nt">--socket-path</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.ydotool_socket"</span> <span class="nt">--socket-own</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nb">id</span> <span class="nt">-u</span><span class="si">)</span><span class="s2">:</span><span class="si">$(</span><span class="nb">id</span> <span class="nt">-g</span><span class="si">)</span><span class="s2">"</span>
<span class="o">[</span><span class="nb">sudo</span><span class="o">]</span> password <span class="k">for </span>gabriel: 
Socket path: /home/gabriel/.ydotool_socket
Removing old stale socket
Socket permission: 0600
Socket ownership: <span class="nv">UID</span><span class="o">=</span>1000, <span class="nv">GID</span><span class="o">=</span>1000
READY
</code></pre></div></div>

<h2 id="references">References:</h2>

<ol>
  <li><a href="https://askubuntu.com/questions/1470593/how-can-i-write-a-program-to-press-keys-such-as-windows-d-in-wayland-repla?noredirect=1#comment2577739_1470593">The comment from @Rinzwind
 where I first found out about <code class="language-plaintext highlighter-rouge">ydotool</code></a></li>
  <li>https://github.com/ReimuNotMoe/ydotool - official repository for <code class="language-plaintext highlighter-rouge">ydotool</code></li>
  <li><a href="https://askubuntu.com/a/1299008/327339">Ask Ubuntu Equivalent to xdotool for Wayland</a> - mentions <code class="language-plaintext highlighter-rouge">ydotool</code></li>
  <li>[my answer] Bugs in Wayland, and how to change between the X11 and Wayland window managers in Ubuntu: <a href="https://askubuntu.com/a/1470563/327339">Ask Ubuntu: “failed to enable link training” when resuming from suspend</a></li>
  <li>[my answer] <a href="https://askubuntu.com/a/1109490/327339">Ask Ubuntu: How can I add “Show desktop” to the GNOME dash or Ubuntu Dock?</a></li>
  <li>[my answer] <a href="https://askubuntu.com/a/1472531/327339">Get the User ID (UID) and Group ID (GID) for the running user</a></li>
  <li>Where I learned about <code class="language-plaintext highlighter-rouge">sudo -b</code>: <a href="https://stackoverflow.com/a/67472605/4561887">Running a program in the background as sudo</a></li>
  <li>Where I learned about the <code class="language-plaintext highlighter-rouge">-f</code> part of <code class="language-plaintext highlighter-rouge">ps auxf</code> to graphically show the process hierarchy (previously I just used <code class="language-plaintext highlighter-rouge">ps aux</code>): <a href="https://unix.stackexchange.com/a/453654/114401">Unix &amp; Linux: How to view the names all child processes spawned by a program</a></li>
</ol>]]></content><author><name>Gabriel Staples</name></author><category term="linux" /><category term="ubuntu" /><category term="bash" /><category term="programming" /><category term="computers" /><category term="x11" /><category term="wayland" /><category term="tutorial" /><summary type="html"><![CDATA[## `ydotool` works well in both the X11 _and_ Wayland window managers to press any keys, including Windows + D, for example Here is a full tutorial.]]></summary></entry></feed>