<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Martin Fitzpatrick - tutorials</title><link href="https://www.martinfitzpatrick.com/" rel="alternate"/><link href="https://www.martinfitzpatrick.com/feeds/tutorials.atom.xml" rel="self"/><id>https://www.martinfitzpatrick.com/</id><updated>2023-03-20T06:00:00+00:00</updated><subtitle>Python tutorials, projects and books</subtitle><entry><title>Getting Started With Git and GitHub in Your Python Projects — Version-Controlling Your Python Projects With Git and GitHub</title><link href="https://www.martinfitzpatrick.com/git-github-python/" rel="alternate"/><published>2023-03-20T06:00:00+00:00</published><updated>2023-03-20T06:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2023-03-20:/git-github-python/</id><summary type="html">Using a &lt;a href="https://en.wikipedia.org/wiki/Version_control"&gt;version control system (VCS)&lt;/a&gt; is crucial for any software development project. These systems allow developers to track changes to the project's codebase over time, removing the need to keep multiple copies of the project folder.</summary><content type="html">&lt;p&gt;Using a &lt;a href="https://en.wikipedia.org/wiki/Version_control"&gt;version control system (VCS)&lt;/a&gt; is crucial for any software development project. These systems allow developers to track changes to the project's codebase over time, removing the need to keep multiple copies of the project folder.&lt;/p&gt;
&lt;p&gt;VCSs also facilitate experimenting with new features and ideas without breaking existing functionality in a given project. They also enable collaboration with other developers that can contribute code, documentation, and more.&lt;/p&gt;
&lt;p&gt;In this article, we'll learn about &lt;a href="https://git-scm.com/"&gt;Git&lt;/a&gt;, the most popular VCS out there. We'll learn everything we need to get started with this VCS and start creating our own repositories. We'll also learn how to publish those repositories to &lt;a href="https://github.com/about"&gt;GitHub&lt;/a&gt;, another popular tool among developers nowadays.&lt;/p&gt;
&lt;h2 id="installing-and-setting-up-git"&gt;Installing and Setting Up Git&lt;/h2&gt;
&lt;p&gt;To use Git in our coding projects, we first need to install it on our computer. To do this, we need to navigate to Git's &lt;a href="https://git-scm.com/downloads"&gt;download page&lt;/a&gt; and choose the appropriate installer for our operating system. Once we've downloaded the installer, we need to run it and follow the on-screen instructions.&lt;/p&gt;
&lt;p&gt;We can check if everything is working correctly by opening a terminal or command-line window and running &lt;code&gt;git --version&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once we've confirmed the successful installation, we should provide Git with some personal information. You'll only need to do this once for every computer. Now go ahead and run the following commands with your own information:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git config --global user.name &amp;lt;"YOUR NAME"&amp;gt;
$ git config --global user.email &amp;lt;name@email.com&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The first command adds your full name to Git's config file. The second command adds your email. Git will use this information in all your repositories.&lt;/p&gt;
&lt;p class="admonition admonition-warning"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation-circle"&gt;&lt;/i&gt;&lt;/span&gt;  If you publish your projects to a remote server like GitHub, then your email address will be visible to anyone with access to that repository. If you don't want to expose your email address this way, then you should create a separate email address to use with Git.&lt;/p&gt;
&lt;p&gt;As you'll learn in a moment, Git uses the concept of &lt;strong&gt;branches&lt;/strong&gt; to manage its repositories. A branch is a copy of your project's folder at a given time in the development cycle. The default branch of new repositories is named either &lt;code&gt;master&lt;/code&gt; or &lt;code&gt;main&lt;/code&gt;, depending on your current version of Git.&lt;/p&gt;
&lt;p&gt;You can change the name of the default branch by running the following command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git config --global init.defaultBranch &amp;lt;branch_name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This command will set the name of Git's default branch to &lt;code&gt;branch_name&lt;/code&gt;. Remember that this is just a placeholder name. You need to provide a suitable name for your installation.&lt;/p&gt;
&lt;p&gt;Another useful setting is the default text editor Git will use to type in commit messages and other messages in your repo. For example, if you use an editor like Visual Studio Code, then you can configure Git to use it:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;# Visual Studio Code
$ git config --global core.editor "code --wait"
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With this command, we tell Git to use VS Code to process commit messages and any other message we need to enter through Git.&lt;/p&gt;
&lt;p&gt;Finally, to inspect the changes we've made to Git's configuration files, we can run the following command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git config --global -e
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This command will open the global &lt;code&gt;.gitconfig&lt;/code&gt; file in our default editor. There, we can fix any error we have made or add new settings. Then we just need to save the file and close it.&lt;/p&gt;
&lt;h2 id="understanding-how-git-works"&gt;Understanding How Git Works&lt;/h2&gt;
&lt;p&gt;Git works by allowing us to take a &lt;em&gt;snapshot&lt;/em&gt; of the current state of all the files in our project's folder. Each time we save one of those snapshots, we make a Git &lt;strong&gt;commit&lt;/strong&gt;. Then the cycle starts again, and Git creates new snapshots, showing how our project looked like at any moment.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  Git was created in 2005 by &lt;a href="https://en.wikipedia.org/wiki/Linus_Torvalds"&gt;Linus Torvalds&lt;/a&gt;, the creator of the &lt;a href="https://en.wikipedia.org/wiki/Linux_kernel"&gt;Linux kernel&lt;/a&gt;. Git is an &lt;a href="https://www.pythonguis.com/faq/charge-for-open-source-software/"&gt;open-source&lt;/a&gt; project that is licensed under the &lt;a href="https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html"&gt;GNU General Public License (GPL) v2&lt;/a&gt;. It was initially made to facilitate kernel development due to the lack of a suitable alternative.&lt;/p&gt;
&lt;p&gt;The general workflow for making a Git commit to saving different snapshots goes through the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Change&lt;/strong&gt; the content of our project's folder.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stage&lt;/strong&gt; or mark the changes we want to save in our next commit.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Commit&lt;/strong&gt; or save the changes permanently in our project's Git database.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As the third step mentions, Git uses a special database called a &lt;strong&gt;repository&lt;/strong&gt;. This database is kept inside your project's directory under a folder called &lt;code&gt;.git&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="version-controlling-a-project-with-git-the-basics"&gt;Version-Controlling a Project With Git: The Basics&lt;/h2&gt;
&lt;p&gt;In this section, we'll create a local repository and learn how to manage it using the Git &lt;a href="https://en.wikipedia.org/wiki/Command-line_interface"&gt;command-line interface (CLI)&lt;/a&gt;. On macOS and Linux, we can use the default terminal application to follow along with this tutorial.&lt;/p&gt;
&lt;p&gt;On Windows, we recommend using Git Bash, which is part of the &lt;a href="https://gitforwindows.org/"&gt;Git For Windows&lt;/a&gt; package. Go to the Git Bash download page, get the installer, run it, and follow the on-screen instruction. Make sure to check the &lt;em&gt;Additional Icons&lt;/em&gt; -&amp;gt; &lt;em&gt;On the Desktop&lt;/em&gt; to get direct access to Git Bash on your desktop so that you can quickly find and launch the app.&lt;/p&gt;
&lt;p&gt;Alternatively, you can also use either Windows' Command Prompt or &lt;a href="https://learn.microsoft.com/en-us/powershell/"&gt;PowerShell&lt;/a&gt;. However, some commands may differ from the commands used in this tutorial.&lt;/p&gt;
&lt;h3&gt;Initializing a Git Repository&lt;/h3&gt;
&lt;p&gt;To start version-controlling a project, we need to initialize a new Git repository in the project's root folder or directory. In this tutorial, we'll use a sample project to facilitate the explanation. Go ahead and create a new folder in your file system. Then navigate to that folder in your terminal by running these commands:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ mkdir sample_project
$ cd sample_project
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The first command creates the project's root folder or directory, while the second command allows you to navigate into that folder. Don't close your terminal window. You'll be using it throughout the next sections.&lt;/p&gt;
&lt;p&gt;To initialize a Git repository in this folder, we need to use the &lt;a href="https://git-scm.com/docs/git-init"&gt;&lt;code&gt;git init&lt;/code&gt;&lt;/a&gt; command like in the example below:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git init
Initialized empty Git repository in /.../sample_project/.git/
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This command creates a subfolder called &lt;code&gt;.git&lt;/code&gt; inside the project's folder. The leading dot in the folder's name means that this is a hidden directory. So, you may not see anything on your file manager. You can check the existence of &lt;code&gt;.git&lt;/code&gt; with the &lt;code&gt;ls -a&lt;/code&gt;, which lists all files in a given folder, including the hidden ones.&lt;/p&gt;
&lt;h3&gt;Checking the Status of Our Project&lt;/h3&gt;
&lt;p&gt;Git provides the &lt;a href="https://git-scm.com/docs/git-status"&gt;&lt;code&gt;git status&lt;/code&gt;&lt;/a&gt; command to allow us to identify the current state of a Git repository. Because our &lt;code&gt;sample_project&lt;/code&gt; folder is still empty, running &lt;code&gt;git status&lt;/code&gt; will display something like this:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git status
On branch main

No commits yet

nothing to commit (create/copy files and use "git add" to track)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;When we run &lt;code&gt;git status&lt;/code&gt;, we get detailed information about the current state of our Git repository. This command is pretty useful, and we'll turn back to it in multiple moments.&lt;/p&gt;
&lt;p&gt;As an example of how useful the &lt;code&gt;git status&lt;/code&gt; command is, go ahead and create a file called &lt;code&gt;main.py&lt;/code&gt; inside the project's folder using the following commands:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ touch main.py

$ git status
On branch main

No commits yet

Untracked files:
  (use "git add &amp;lt;file&amp;gt;..." to include in what will be committed)
    main.py

nothing added to commit but untracked files present (use "git add" to track)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With the &lt;a href="https://en.wikipedia.org/wiki/Touch_(command)"&gt;&lt;code&gt;touch&lt;/code&gt;&lt;/a&gt; command, we create a new &lt;code&gt;main.py&lt;/code&gt; file under our project's folder. Then we run &lt;code&gt;git status&lt;/code&gt; again. This time, we get information about the presence of an untracked file called &lt;code&gt;main.py&lt;/code&gt;. We also get some basic instructions on how to add this file to our Git repo. Providing these guidelines or instructions is one of the neatest features of &lt;code&gt;git status&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now, what is all that about untracked files? In the following section, we'll learn more about this topic.&lt;/p&gt;
&lt;h3&gt;Tracking and Committing Changes&lt;/h3&gt;
&lt;p&gt;A file in a Git repository can be either &lt;strong&gt;tracked&lt;/strong&gt; or &lt;strong&gt;untracked&lt;/strong&gt;. Any file that wasn't present in the last commit is considered an untracked file. Git doesn't keep a history of changes for untracked files in your project's folder.&lt;/p&gt;
&lt;p&gt;In our example, we haven't made any commits to our Git repo, so &lt;code&gt;main.py&lt;/code&gt; is naturally untracked. To start tracking it, run the &lt;a href="https://git-scm.com/docs/git-add"&gt;&lt;code&gt;git add&lt;/code&gt;&lt;/a&gt; command as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git add main.py

$ git status
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached &amp;lt;file&amp;gt;..." to unstage)
    new file:   main.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This &lt;code&gt;git add&lt;/code&gt; command has added &lt;code&gt;main.py&lt;/code&gt; to the list of tracked files. Now it's time to save the file permanently using the &lt;a href="https://git-scm.com/docs/git-commit"&gt;&lt;code&gt;git commit&lt;/code&gt;&lt;/a&gt; command with an appropriate commit message provided with the &lt;code&gt;-m&lt;/code&gt; option:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git commit -m "Add main.py"
[main (root-commit) 5ac6586] Add main.py
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 main.py

$ git status
On branch master
nothing to commit, working tree clean
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We have successfully made our first commit, saving &lt;code&gt;main.py&lt;/code&gt; to our Git repository. The &lt;code&gt;git commit&lt;/code&gt; command requires a commit message, which we can provide through the &lt;code&gt;-m&lt;/code&gt; option. Commit messages should clearly describe what we have changed in our project.&lt;/p&gt;
&lt;p&gt;After the commit, our &lt;code&gt;main&lt;/code&gt; branch is completely clean, as you can conclude from the &lt;code&gt;git status&lt;/code&gt; output.&lt;/p&gt;
&lt;p&gt;Now let's start the cycle again by modifying &lt;code&gt;main.py&lt;/code&gt;, staging the changes, and creating a new commit. Go ahead and run the following commands:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ echo "print('Hello, World!')" &amp;gt; main.py
$ cat main.py
print('Hello, World!')

$ git add main.py

$ git commit -m "Create a 'Hello, World!' script  on  main.py"
[main 2f33f7e] Create a 'Hello, World!' script  on  main.py
 1 file changed, 1 insertion(+)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/Echo_(command)"&gt;&lt;code&gt;echo&lt;/code&gt;&lt;/a&gt; command adds the statement &lt;code&gt;"print('Hello, World!')"&lt;/code&gt; to our &lt;code&gt;main.py&lt;/code&gt; file. You can confirm this addition with the &lt;a href="https://en.wikipedia.org/wiki/Cat_(Unix)"&gt;&lt;code&gt;cat&lt;/code&gt;&lt;/a&gt; command, which lists the content of one or more target files. You can also open &lt;code&gt;main.py&lt;/code&gt; in your favorite editor and update the file there if you prefer.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  We can also use the &lt;a href="https://git-scm.com/docs/git-stage"&gt;&lt;code&gt;git stage&lt;/code&gt;&lt;/a&gt; command to stage or add files to a Git repository and include them in our next commit.&lt;/p&gt;
&lt;p&gt;We've made two commits to our Git repo. We can list our &lt;strong&gt;commit history&lt;/strong&gt; using the &lt;a href="https://git-scm.com/docs/git-log"&gt;&lt;code&gt;git log&lt;/code&gt;&lt;/a&gt; command as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git log --oneline
2f33f7e (HEAD -&amp;gt; main) Create a 'Hello, World!' script  on  main.py
5ac6586 Add main.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;git log&lt;/code&gt; command allows us to list all our previous commits. In this example, we've used the &lt;code&gt;--oneline&lt;/code&gt; option to list commits in a single line each. This command takes us to a dedicated output space. To leave that space, we can press the letter &lt;code&gt;Q&lt;/code&gt; on our keyboard.&lt;/p&gt;
&lt;h3&gt;Using a &lt;code&gt;.gitignore&lt;/code&gt; File to Skip Unneeded Files&lt;/h3&gt;
&lt;p&gt;While working with Git, we will often have files and folders that we must not save to our Git repo. For example, most Python projects include a &lt;code&gt;venv/&lt;/code&gt; folder with a virtual environment for that project. Go ahead and create one with the following command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ python -m venv venv
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Once we've added a Python virtual environment to our project's folder, we can run &lt;code&gt;git status&lt;/code&gt; again to check the repo state:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git status
On branch main
Untracked files:
  (use "git add &amp;lt;file&amp;gt;..." to include in what will be committed)
    venv/

nothing added to commit but untracked files present (use "git add" to track)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now the &lt;code&gt;venv/&lt;/code&gt; folder appears as an untracked file in our Git repository. We don't need to keep track of this folder because it's not part of our project's codebase. It's only a tool for working on the project. So, we need to ignore this folder. To do that, we can add the folder to a &lt;code&gt;.gitignore&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;Go ahead and create a &lt;code&gt;.gitignore&lt;/code&gt; file in the project's folder. Add the &lt;code&gt;venv/&lt;/code&gt; folders to it and run &lt;code&gt;git status&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ touch .gitignore
$ echo "venv/" &amp;gt; .gitignore
$ git status
On branch main
Untracked files:
  (use "git add &amp;lt;file&amp;gt;..." to include in what will be committed)
    .gitignore

nothing added to commit but untracked files present (use "git add" to track)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now &lt;code&gt;git status&lt;/code&gt; doesn't list &lt;code&gt;venv/&lt;/code&gt; as an untracked file. This means that Git is ignoring that folder. If we take a look at the output, then we'll see that &lt;code&gt;.gitignore&lt;/code&gt; is now listed as an untracked file. We must commit our &lt;code&gt;.gitignore&lt;/code&gt; files to the Git repository. This will prevent other developers working with us from having to create their own local &lt;code&gt;.gitignore&lt;/code&gt; files.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;   We can also list multiple files and folders in our &lt;code&gt;.gitignore&lt;/code&gt; file one per line. The file even accepts &lt;a href="https://en.wikipedia.org/wiki/Glob_(programming)"&gt;glob patterns&lt;/a&gt; to match specific types of files, such as &lt;code&gt;*.txt&lt;/code&gt;. If you want to save yourself some work, then you can take advantage of GitHub's &lt;a href="https://github.com/github/gitignore"&gt;gitignore&lt;/a&gt; repository, which provides a rich list of predefined &lt;code&gt;.gitignore&lt;/code&gt; files for different programming languages and development environments.&lt;/p&gt;
&lt;p&gt;We can also set up a global &lt;code&gt;.gitignore&lt;/code&gt; file on our computer. This global file will apply to all our Git repositories. If you decide to use this option, then go ahead and create a &lt;code&gt;.gitignore_global&lt;/code&gt; in your home folder.&lt;/p&gt;
&lt;h2 id="working-with-branches-in-git"&gt;Working With Branches in Git&lt;/h2&gt;
&lt;p&gt;One of the most powerful features of Git is that it allows us to create multiple branches. A &lt;strong&gt;branch&lt;/strong&gt; is a copy of our project's current status and commits history. Having the option to create and handle branches allows us to make changes to our project without messing up the main line of development.&lt;/p&gt;
&lt;p&gt;We'll often find that software projects maintain several independent branches to facilitate the development process. A common branch model distinguishes between four different types of branches:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A &lt;code&gt;main&lt;/code&gt; or &lt;code&gt;master&lt;/code&gt; branch that holds the main line of development&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;develop&lt;/code&gt; branch that holds the last developments&lt;/li&gt;
&lt;li&gt;One or more &lt;code&gt;feature&lt;/code&gt; branches that hold changes intended to add new features&lt;/li&gt;
&lt;li&gt;One or more &lt;code&gt;bugfix&lt;/code&gt; branches that hold changes intended to fix critical bugs&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;However, the branching model to use is up to you. In the following sections, we'll learn how to manage branches using Git.&lt;/p&gt;
&lt;h3&gt;Creating New Branches&lt;/h3&gt;
&lt;p&gt;Working all the time on the &lt;code&gt;main&lt;/code&gt; or &lt;code&gt;master&lt;/code&gt; branch isn't a good idea. We can end up creating a mess and breaking the code. So, whenever we want to experiment with a new idea, implement a new feature, fix a bug, or just refactor a piece of code, we should create a new branch.&lt;/p&gt;
&lt;p&gt;To kick things off, let's create a new branch called &lt;code&gt;hello&lt;/code&gt; on our Git repo under the &lt;code&gt;sample_project&lt;/code&gt; folder. To do that, we can use the &lt;a href="https://git-scm.com/docs/git-branch"&gt;&lt;code&gt;git branch&lt;/code&gt;&lt;/a&gt; command followed by the branch's name:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git branch hello
$ git branch --list
* main
  hello
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The first command creates a new branch in our Git repo. The second command allows us to list all the branches that currently exist in our repository. Again, we can press the letter &lt;code&gt;Q&lt;/code&gt; on our keyboard to get back to the terminal prompt.&lt;/p&gt;
&lt;p&gt;The star symbol denotes the currently active branch, which is &lt;code&gt;main&lt;/code&gt; in the example. We want to work on &lt;code&gt;hello&lt;/code&gt;, so we need to activate that branch. In Git's terminology, we need to check out to &lt;code&gt;hello&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Checking Out to a New Branch&lt;/h3&gt;
&lt;p&gt;Although we have just created a new branch, in order to start working on it, we need to &lt;em&gt;switch&lt;/em&gt; to or check out to it by using the &lt;a href="https://git-scm.com/docs/git-checkout"&gt;&lt;code&gt;git checkout&lt;/code&gt;&lt;/a&gt; command as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git checkout hello
Switched to branch 'hello'

$ git branch --list
  main
* hello

$ git log --oneline
2f33f7e (HEAD -&amp;gt; hello, main) Create a 'Hello, World!' script  on  main.py
5ac6586 Add main.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;git checkout&lt;/code&gt; command takes the name of an existing branch as an argument. Once we run the command, Git takes us to the target branch.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  We can derive a new branch from whatever branch we need.&lt;/p&gt;
&lt;p&gt;As you can see, &lt;code&gt;git branch --list&lt;/code&gt; indicates which branch we are currently on by placing a &lt;code&gt;*&lt;/code&gt; symbol in front of the relevant branch name. If we check the commit history with &lt;code&gt;git log --oneline&lt;/code&gt;, then we'll get the same as we get from &lt;code&gt;main&lt;/code&gt; because &lt;code&gt;hello&lt;/code&gt; is a copy of it.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  The &lt;code&gt;git checkout&lt;/code&gt; can take a &lt;code&gt;-b&lt;/code&gt; flag that we can use to create a new branch and immediately check out to it in a single step. That's what most developers use while working with Git repositories. In our example, we could have run &lt;code&gt;git checkout -b hello&lt;/code&gt; to create the &lt;code&gt;hello&lt;/code&gt; branch and check out to it with one command.&lt;/p&gt;
&lt;p&gt;Let's make some changes to our project and create another commit. Go ahead and run the following commands:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ echo "print('Welcome to PythonGUIs!')" &amp;gt;&amp;gt; main.py
$ cat main.py
print('Hello, World!')
print('Welcome to PythonGUIs!')

$ git add main.py
$ git commit -m "Extend our 'Hello, World' program with a welcome message."
[hello be62476] Extend our 'Hello, World' program with a welcome message.
 1 file changed, 1 insertion(+)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The final command committed our changes to the &lt;code&gt;hello&lt;/code&gt; branch. If we compare the commit history of both branches, then we'll see the difference:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git log --oneline -1
be62476 (HEAD -&amp;gt; hello) Extend our 'Hello, World' program with a welcome message.

$ git checkout main
Switched to branch 'main'

$ git log --oneline -1
2f33f7e (HEAD -&amp;gt; main) Create a 'Hello, World!' script  on  main.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we first run &lt;code&gt;git log --oneline&lt;/code&gt; with &lt;code&gt;-1&lt;/code&gt; as an argument. This argument tells Git to give us only the last commit in the active branch's commit history. To inspect the commit history of &lt;code&gt;main&lt;/code&gt;, we first need to check out to that branch. Then we can run the same &lt;code&gt;git log&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;Now say that we're happy with the changes we've made to our project in the &lt;code&gt;hello&lt;/code&gt; branch, and we want to update &lt;code&gt;main&lt;/code&gt; with those changes. How can we do this? We need to merge &lt;code&gt;hello&lt;/code&gt; into &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Merging Two Branches Together&lt;/h3&gt;
&lt;p&gt;To add the commits we've made in a separate branch back to another branch, we can run what is known as a &lt;strong&gt;merge&lt;/strong&gt;. For example, say we want to merge the new commits in &lt;code&gt;hello&lt;/code&gt; into &lt;code&gt;main&lt;/code&gt;. In this case, we first need to switch back to &lt;code&gt;main&lt;/code&gt; and then run the &lt;a href="https://git-scm.com/docs/git-merge"&gt;&lt;code&gt;git merge&lt;/code&gt;&lt;/a&gt; command using &lt;code&gt;hello&lt;/code&gt; as an argument:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git checkout main
Already on 'main'

$ git merge hello
Updating 2f33f7e..be62476
Fast-forward
 main.py | 1 +
 1 file changed, 1 insertion(+)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;To merge a branch into another branch, we first need to check out the branch we want to update. Then we can run &lt;code&gt;git merge&lt;/code&gt;. In the example above, we first check out to &lt;code&gt;main&lt;/code&gt;. Once there, we can merge &lt;code&gt;hello&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Deleting Unused Branches&lt;/h3&gt;
&lt;p&gt;Once we've finished working in a given branch, we can delete the entire branch to keep our repo as clean as possible. Following our example, now that we've merged &lt;code&gt;hello&lt;/code&gt; into &lt;code&gt;main&lt;/code&gt;, we can remove &lt;code&gt;hello&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To remove a branch from a Git repo, we use the &lt;code&gt;git branch&lt;/code&gt; command with the &lt;code&gt;--delete&lt;/code&gt; option. To successfully run this command, make sure to switch to another branch before:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git checkout main
Already on 'main'

$ git branch --delete hello
Deleted branch hello (was be62476).

$ git branch --list
* main
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Deleting unused branches is a good way to keep our Git repositories clean, organized, and up to date. Also, deleting them as soon as we finish the work is even better because having old branches around may be confusing for other developers collaborating with our project. They might end up wondering why these branches are still alive.&lt;/p&gt;
&lt;h2 id="using-a-gui-client-for-git"&gt;Using a GUI Client for Git&lt;/h2&gt;
&lt;p&gt;In the previous sections, we've learned to use the &lt;code&gt;git&lt;/code&gt; command-line tool to manage Git repositories. If you prefer to use GUI tools, then you'll find a bunch of third-party GUI frontends for Git. While they won't completely replace the need for using the command-line tool, they can simplify your day-to-day workflow.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  You can get a complete list of standalone GUI clients available on the Git &lt;a href="https://git-scm.com/downloads/guis"&gt;official documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Most popular IDEs and code editors, including &lt;a href="https://www.jetbrains.com/pycharm/"&gt;PyCharm&lt;/a&gt; and &lt;a href="https://www.pythonguis.com/tutorials/getting-started-vs-code-python/"&gt;Visual Studio Code&lt;/a&gt;, come with basic Git integration out-of-the-box. Some developers will prefer this approach as it is directly integrated with their development environment of choice.&lt;/p&gt;
&lt;p&gt;If you need something more advanced, then &lt;a href="https://www.gitkraken.com"&gt;GitKraken&lt;/a&gt; is probably a good choice. This tool provides a standalone, cross-platform GUI client for Git that comes with many additional features that can boost your productivity.&lt;/p&gt;
&lt;h2 id="managing-a-project-with-github"&gt;Managing a Project With GitHub&lt;/h2&gt;
&lt;p&gt;If we publish a project on a remote server with support for Git repositories, then anyone with appropriate permissions can &lt;a href="https://git-scm.com/docs/git-clone"&gt;clone&lt;/a&gt; our project, creating a local copy on their computer. Then, they can make changes to our project, commit them to their local copy, and finally push the changes back to the remote server. This workflow provides a straightforward way to allow other developers to contribute code to your projects.&lt;/p&gt;
&lt;p&gt;In the following sections, we'll learn how to create a remote repository on GitHub and then push our existing local repository to it. Before we do that, though, head over to &lt;a href="https://github.com/"&gt;GitHub.com&lt;/a&gt; and create an account there if you don't have one yet. Once you have a GitHub account, you can set up the connection to that account so that you can use it with Git.&lt;/p&gt;
&lt;h3&gt;Setting Up a Secure Connection to GitHub&lt;/h3&gt;
&lt;p&gt;In order to work with GitHub via the &lt;code&gt;git&lt;/code&gt; command, we need to be able to authenticate ourselves. There are a few ways of doing that. However, using &lt;a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/about-ssh"&gt;SSH&lt;/a&gt; is the recommended way. The first step in the process is to generate an SSH key, which you can do with the following command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ ssh-keygen -t ed25519 -C "GitHub - name@email.com"
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Replace the placeholder email address with the address you've associated with your GitHub account. Once you run this command, you'll get three different prompts in a row. You can respond to them by pressing Enter to accept the default option. Alternatively, you can provide custom responses.&lt;/p&gt;
&lt;p&gt;Next, we need to copy the contents of our &lt;code&gt;id_ed25519.pub&lt;/code&gt; file. To do this, you can run the following command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ cat ~/.ssh/id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Select the command's output and copy it. Then go to your GitHub &lt;em&gt;Settings&lt;/em&gt; page and click the &lt;em&gt;SSH and GPG keys&lt;/em&gt; option. There, select &lt;em&gt;New SSH key&lt;/em&gt;, set a descriptive title for the key, make sure that the &lt;em&gt;Key Type&lt;/em&gt; is set to &lt;em&gt;Authentication Key&lt;/em&gt;, and finally, paste the contents of &lt;code&gt;id_ed25519.pub&lt;/code&gt; in the &lt;em&gt;Key&lt;/em&gt; field. Finally, click the &lt;em&gt;Add SSH key&lt;/em&gt; button.&lt;/p&gt;
&lt;p class="admonition admonition-warning"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation-circle"&gt;&lt;/i&gt;&lt;/span&gt;  At this point, you may be asked to provide some kind of &lt;a href="https://en.wikipedia.org/wiki/Multi-factor_authentication"&gt;Two-Factor Authentication (2FA)&lt;/a&gt; code. So, be ready for that extra security step.&lt;/p&gt;
&lt;p&gt;Now we can test our connection by running the following command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ ssh -T git@github.com
The authenticity of host 'github.com (IP ADDRESS)' can not be established.
ECDSA key fingerprint is SHA256:p2QAMXNIC1TJYWeIOttrVc98/R1BUFWu3/LiyKgUfQM.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Make sure to check whether the key fingerprint shown on your output matches &lt;a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints"&gt;GitHub's public key fingerprint&lt;/a&gt;. If it matches, then enter &lt;em&gt;yes&lt;/em&gt; and press Enter to connect. Otherwise, don't connect.&lt;/p&gt;
&lt;p&gt;If the connection is successful, we will get a message like this:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;Hi USERNAME! You have successfully authenticated, but GitHub does not provide shell access.
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Congrats! You've successfully connected to GitHub via SSH using a secure SSH key. Now it's time to start working with GitHub.&lt;/p&gt;
&lt;h3&gt;Creating and Setting Up a GitHub Repository&lt;/h3&gt;
&lt;p&gt;Now that you have a GitHub account with a proper SSH connection, let's create a remote repository on GitHub using its web interface. Head over to the GitHub page and click the &lt;code&gt;+&lt;/code&gt; icon next to your avatar in the top-right corner. Then select &lt;em&gt;New repository&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Give your new repo a unique name and choose who can see this repository. To continue with our example, we can give this repository the same name as our local project, &lt;code&gt;sample_project&lt;/code&gt;.&lt;/p&gt;
&lt;p class="admonition admonition-warning"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation-circle"&gt;&lt;/i&gt;&lt;/span&gt;  To avoid conflicts with your existing local repository, don't add &lt;code&gt;.gitignore&lt;/code&gt;, &lt;code&gt;README&lt;/code&gt;, or &lt;code&gt;LICENSE&lt;/code&gt; files to your remote repository.&lt;/p&gt;
&lt;p&gt;Next, set the repo's visibility to &lt;em&gt;Private&lt;/em&gt; so that no one else can access the code. Finally, click the &lt;em&gt;Create repository&lt;/em&gt; button at the end of the page.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  If you create a &lt;em&gt;Public&lt;/em&gt; repository, make sure also to choose an &lt;a href="https://choosealicense.com"&gt;open-source license&lt;/a&gt; for your project to tell people what they can and can't do with your code.&lt;/p&gt;
&lt;p&gt;You'll get a &lt;em&gt;Quick setup&lt;/em&gt; page as your remote repository has no content yet. Right at the top, you'll have the choice to connect this repository via HTTPS or SSH. Copy the SSH link and run the following command to tell Git where the remote repository is hosted:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git remote add origin git@github.com:USERNAME/sample_project.git
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This command adds a new remote repository called &lt;code&gt;origin&lt;/code&gt; to our local Git repo.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  The name &lt;code&gt;origin&lt;/code&gt; is commonly used to denote the main remote repository associated with a given project. This is the default name Git uses to identify the main remote repo.&lt;/p&gt;
&lt;p&gt;Git allows us to add several remote repositories to a single local one using the &lt;code&gt;git remote add&lt;/code&gt; command. This allows us to have several remote copies of your local Git repo.&lt;/p&gt;
&lt;h3&gt;Pushing a Local Git Repository to GitHub&lt;/h3&gt;
&lt;p&gt;With a new and empty GitHub repository in place, we can go ahead and push the content of our local repo to its remote copy. To do this, we use the &lt;code&gt;git push&lt;/code&gt; command providing the target remote repo and the local branch as arguments:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git push -u origin main
Enumerating objects: 9, done.
Counting objects: 100% (9/9), done.
Delta compression using up to 8 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (9/9), 790 bytes | 790.00 KiB/s, done.
Total 9 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:USERNAME/sample_project.git
 * [new branch]      main -&amp;gt; main
branch 'main' set up to track 'origin/main'.
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This is the first time we push something to the remote repo &lt;code&gt;sample_project&lt;/code&gt;, so we use the &lt;code&gt;-u&lt;/code&gt; option to tell Git that we want to set the local &lt;code&gt;main&lt;/code&gt; branch to track the remote &lt;code&gt;main&lt;/code&gt; branch. The command's output provides a pretty detailed summary of the process.&lt;/p&gt;
&lt;p&gt;Note that if you don't add the &lt;code&gt;-u&lt;/code&gt; option, then Git will ask what you want to do. A safe workaround is to copy and paste the commands GitHub suggests, so that you don't forget &lt;code&gt;-u&lt;/code&gt;.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  Using the same command, we can push any local branch to any remote copy of our project's repo. Just change the repo and branch name: &lt;code&gt;git push -u remote_name branch_name&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now let's head over to our browser and refresh the GitHub page. We will see all of our project files and commit history there.&lt;/p&gt;
&lt;p&gt;Now we can continue developing our project and making new commits locally. To push our commits to the remote &lt;code&gt;main&lt;/code&gt; branch, we just need to run &lt;code&gt;git push&lt;/code&gt;. This time, we don't have to use the remote or branch name because we've already set &lt;code&gt;main&lt;/code&gt; to track &lt;code&gt;origin/main&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Pulling Content From a GitHub Repository&lt;/h3&gt;
&lt;p&gt;We can do basic file editing and make commits within GitHub itself. For example, if we click the &lt;code&gt;main.py&lt;/code&gt; file and then click the &lt;em&gt;pencil&lt;/em&gt; icon at the top of the file, we can add another line of code and commit those changes to the remote &lt;code&gt;main&lt;/code&gt; branch directly on GitHub.&lt;/p&gt;
&lt;p&gt;Go ahead and add the statement &lt;code&gt;print("Your Git Tutorial is Here...")&lt;/code&gt; to the end of &lt;code&gt;main.py&lt;/code&gt;. Then go to the end of the page and click the &lt;em&gt;Commit changes&lt;/em&gt; button. This makes a new commit on your remote repository.&lt;/p&gt;
&lt;p&gt;This remote commit won't appear in your local commit history. To download it and update your local &lt;code&gt;main&lt;/code&gt; branch, use the &lt;a href="https://git-scm.com/docs/git-pull"&gt;&lt;code&gt;git pull&lt;/code&gt;&lt;/a&gt; command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git pull
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 696 bytes | 174.00 KiB/s, done.
From github.com:USERNAME/sample_project
   be62476..605b6a7  main       -&amp;gt; origin/main
Updating be62476..605b6a7
Fast-forward
 main.py | 1 +
 1 file changed, 1 insertion(+)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Again, the command's output provides all the details about the operation. Note that &lt;code&gt;git pull&lt;/code&gt; will download the remote branch and update the local branch in a single step.&lt;/p&gt;
&lt;p&gt;If we want to download the remote branch without updating the local one, then we can use the &lt;code&gt;[git fetch](https://git-scm.com/docs/git-fetch)&lt;/code&gt; command. This practice gives us the chance to review the changes and commit them to our local repo only if they look right.&lt;/p&gt;
&lt;p&gt;For example, go ahead and update the remote copy of &lt;code&gt;main.py&lt;/code&gt; by adding another statement like &lt;code&gt;print("Let's go!!")&lt;/code&gt;. Commit the changes. Then get back to your local repo and run the following command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git fetch
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 731 bytes | 243.00 KiB/s, done.
From github.com:USERNAME/sample_project
   605b6a7..ba489df  main       -&amp;gt; origin/main
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This command downloaded the latest changes from &lt;code&gt;origin/main&lt;/code&gt; to our local repo. Now we can compare the remote copy of &lt;code&gt;main.py&lt;/code&gt; to the local copy. To do this, we can use the &lt;a href="https://git-scm.com/docs/git-diff"&gt;&lt;code&gt;git diff&lt;/code&gt;&lt;/a&gt; command as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git diff main origin/main
diff --git a/main.py b/main.py
index be2aa66..4f0e7cf 100644
--- a/main.py
+++ b/main.py
@@ -1,3 +1,4 @@
 print('Hello, World!')
 print('Welcome to PythonGUIs!')
 print("Your Git Tutorial is Here...")
+print("Let's go!!")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In the command's output, you can see that the remote branch adds a line containing &lt;code&gt;print("Let's go!!")&lt;/code&gt; to the end of &lt;code&gt;main.py&lt;/code&gt;. This change looks good, so we can use &lt;code&gt;git pull&lt;/code&gt; to commit the change automatically.&lt;/p&gt;
&lt;h3&gt;Exploring Alternatives to GitHub&lt;/h3&gt;
&lt;p&gt;While GitHub is the most popular public Git server and collaboration platform in use, it is far from being the only one. &lt;a href="https://about.gitlab.com"&gt;GitLab.com&lt;/a&gt; and &lt;a href="https://bitbucket.org"&gt;BitBucket&lt;/a&gt; are popular commercial alternatives similar to GitHub. While they have paid plans, both offer free plans, with some restrictions, for individual users.&lt;/p&gt;
&lt;p&gt;Although, if you would like to use a completely open-source platform instead, &lt;a href="https://codeberg.org"&gt;Codeberg&lt;/a&gt; might be a good option. It's a community-driven alternative with a focus on supporting &lt;a href="https://en.wikipedia.org/wiki/Free_software"&gt;Free Software&lt;/a&gt;. Therefore, in order to use Codeberg, your project needs to use a &lt;a href="https://docs.codeberg.org/getting-started/faq/#is-it-allowed-to-host-non-free-software%3F"&gt;compatible open-source license&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Optionally, you can also run your own Git server. While you could just use barebones &lt;code&gt;git&lt;/code&gt; for this, software such as &lt;a href="https://gitlab.com/rluna-gitlab/gitlab-ce"&gt;GitLab Community Edition (CE)&lt;/a&gt; and &lt;a href="https://forgejo.org/"&gt;Forgejo&lt;/a&gt; provide you with both the benefits of running your own server and the experience of using a service like GitHub.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;By now, you're able to use Git for version-controlling your projects. Git is a powerful tool that will make you much more efficient and productive, especially as the scale of your project grows over time.&lt;/p&gt;
&lt;p&gt;While this guide introduced you to most of its basic concepts and common commands, Git has many more commands and options that you can use to be even more productive. Now, you know enough to get up to speed with Git.&lt;/p&gt;</content><category term="python"/></entry><entry><title>Working With Classes in Python — Understanding the Intricacies of Python Classes</title><link href="https://www.martinfitzpatrick.com/python-classes/" rel="alternate"/><published>2023-03-06T06:00:00+00:00</published><updated>2023-03-06T06:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2023-03-06:/python-classes/</id><summary type="html">Python supports &lt;a href="https://en.wikipedia.org/wiki/Object-oriented_programming"&gt;object-oriented programming (OOP)&lt;/a&gt; through classes, which allow you to bundle data and behavior in a single entity. Python classes allow you to quickly model concepts by creating representations of real objects that you can then use to organize your code.</summary><content type="html">&lt;p&gt;Python supports &lt;a href="https://en.wikipedia.org/wiki/Object-oriented_programming"&gt;object-oriented programming (OOP)&lt;/a&gt; through classes, which allow you to bundle data and behavior in a single entity. Python classes allow you to quickly model concepts by creating representations of real objects that you can then use to organize your code.&lt;/p&gt;
&lt;p&gt;In this tutorial, you'll learn how OOP and classes work in Python. This knowledge will allow you to quickly grasp how you can use their classes and APIs to create robust Python applications.&lt;/p&gt;
&lt;h2 id="defining-classes-in-python"&gt;Defining Classes in Python&lt;/h2&gt;
&lt;p&gt;Python classes are templates or blueprints that allow us to create objects through &lt;strong&gt;instantiation&lt;/strong&gt;. These objects will contain data representing the object's state, and methods that will act on the data providing the object's behavior.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  Instantiation is the process of creating instances of a class by calling the &lt;strong&gt;class constructor&lt;/strong&gt; with appropriate arguments.&lt;/p&gt;
&lt;p&gt;Attributes and methods make up what is known as the class &lt;strong&gt;interface&lt;/strong&gt; or &lt;a href="https://en.wikipedia.org/wiki/API"&gt;API&lt;/a&gt;. This interface allows us to operate on the objects without needing to understand their internal implementation and structure.&lt;/p&gt;
&lt;p&gt;Alright, it is time to start creating our own classes. We'll start by defining a &lt;code&gt;Color&lt;/code&gt; class with minimal functionality. To do that in Python, you'll use the &lt;code&gt;class&lt;/code&gt; keyword followed by the class name. Then you provide the class body in the next &lt;a href="https://en.wikipedia.org/wiki/Indentation_(typesetting)"&gt;indentation&lt;/a&gt; level:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; class Color:
...     pass
...

&amp;gt;&amp;gt;&amp;gt; red = Color()

&amp;gt;&amp;gt;&amp;gt; type(red)
&amp;lt;class '__main__.Color'&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we defined our &lt;code&gt;Color&lt;/code&gt; class using the &lt;code&gt;class&lt;/code&gt; keyword. This class is empty. It doesn't have attributes or methods. Its body only contains a &lt;code&gt;pass&lt;/code&gt; statement, which is Python's way to do nothing.&lt;/p&gt;
&lt;p&gt;Even though the class is minimal, it allows us to create instances by calling its constructor, &lt;code&gt;Colo()&lt;/code&gt;. So, &lt;code&gt;red&lt;/code&gt; is an instance of &lt;code&gt;Color&lt;/code&gt;. Now let's make our &lt;code&gt;Color&lt;/code&gt; class more fun by adding some attributes.&lt;/p&gt;
&lt;h3&gt;Adding Class and Instance &amp;Acy;ttributes&lt;/h3&gt;
&lt;p&gt;Python classes allow you to add two types of attributes. You can have &lt;strong&gt;class&lt;/strong&gt; and &lt;strong&gt;instance attributes&lt;/strong&gt;. A class attribute belongs to its containing class. Its data is common to the class and all its instances. To access a class attribute, we can use either the class or any of its instances.&lt;/p&gt;
&lt;p&gt;Let's now add a class attribute to our &lt;code&gt;Color&lt;/code&gt; class. For example, let's say we need to keep note of how many instance of &lt;code&gt;Color&lt;/code&gt; your code creates. Then you can have a &lt;code&gt;color_count&lt;/code&gt; attribute:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; class Color:
...     color_count = 0
...     def __init__(self):
...         Color.color_count += 1
...

&amp;gt;&amp;gt;&amp;gt; red = Color()
&amp;gt;&amp;gt;&amp;gt; green = Color()

&amp;gt;&amp;gt;&amp;gt; Color.color_count
2
&amp;gt;&amp;gt;&amp;gt; red.color_count
2
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now &lt;code&gt;Color&lt;/code&gt; has a class attribute called &lt;code&gt;color_count&lt;/code&gt; that gets incremented every time we create a new instance. We can quickly access that attribute using either the class directly or one of its instances, like &lt;code&gt;red&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To follow up with this example, say that we want to represent our &lt;code&gt;Color&lt;/code&gt; objects using red, green, and blue attributes as part of the &lt;a href="https://en.wikipedia.org/wiki/RGB_color_model"&gt;RGB color model&lt;/a&gt;. These attributes should have specific values for specific instances of the class. So, they should be instance attributes.&lt;/p&gt;
&lt;p&gt;To add an instance attribute to a Python class, you must use the &lt;code&gt;.__init__()&lt;/code&gt; special method, which we introduced in the previous code but didn't explain. This method works as the instance initializer because it allows you to provide initial values for instance attributes:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; class Color:
...     color_count = 0
...     def __init__(self, red, green, blue):
...         Color.color_count += 1
...         self.red = red
...         self.green = green
...         self.blue = blue
...

&amp;gt;&amp;gt;&amp;gt; red = Color(255, 0, 0)

&amp;gt;&amp;gt;&amp;gt; red.red
255
&amp;gt;&amp;gt;&amp;gt; red.green
0
&amp;gt;&amp;gt;&amp;gt; red.blue
0

&amp;gt;&amp;gt;&amp;gt; Color.red
Traceback (most recent call last):
    ...
AttributeError: type object 'Color' has no attribute 'red'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Cool! Now our &lt;code&gt;Color&lt;/code&gt; class looks more useful. It has the usual class attributes and also three new instance attributes. Note that, unlike class attributes, instance attributes can't be accessed through the class itself. They're specific to a concrete instance.&lt;/p&gt;
&lt;p&gt;There's something that jumps into sight in this new version of &lt;code&gt;Color&lt;/code&gt;. What is the &lt;code&gt;self&lt;/code&gt; argument in the definition of &lt;code&gt;.__init__()&lt;/code&gt;? This attribute holds a reference to the current instance. Using the name &lt;code&gt;self&lt;/code&gt; to identify the current instance is a strong convention in Python.&lt;/p&gt;
&lt;p&gt;We'll use &lt;code&gt;self&lt;/code&gt; as the first or even the only argument to instance methods like &lt;code&gt;.__init__()&lt;/code&gt;. Inside an instance method, we'll use &lt;code&gt;self&lt;/code&gt; to access other methods and attributes defined in the class. To do that, we must prepend &lt;code&gt;self&lt;/code&gt; to the name of the target attribute or method instance of the class.&lt;/p&gt;
&lt;p&gt;For example, our class has an attribute &lt;code&gt;.red&lt;/code&gt; that we can access using the syntax &lt;code&gt;self.red&lt;/code&gt; inside the class. This will return the number stored under that name. From outside the class, you need to use a concrete instance instead of &lt;code&gt;self&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Providing Behavior With Methods&lt;/h3&gt;
&lt;p&gt;A class bundles data (attributes) and behavior (methods) together in an object. You'll use the data to set the object's state and the methods to operate on that data or state.&lt;/p&gt;
&lt;p&gt;Methods are just functions that we define inside a class. Like functions, methods can take arguments, return values, and perform different computations on an object's attributes. They allow us to make our objects usable.&lt;/p&gt;
&lt;p&gt;In Python, we can define three types of methods in our classes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Instance method&lt;/strong&gt;s, which need the instance (&lt;code&gt;self&lt;/code&gt;) as their first argument&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Class methods&lt;/strong&gt;, which take the class (&lt;code&gt;cls&lt;/code&gt;) as their first argument&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Static methods&lt;/strong&gt;, which take neither the class nor the instance&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let's now talk about instance methods. Say that we need to get the attributes of our &lt;code&gt;Color&lt;/code&gt; class as a tuple of numbers. In this case, we can add an &lt;code&gt;.as_tuple()&lt;/code&gt; method like the following:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Color:
    representation = "RGB"

    def __init__(self, red, green, blue):
        self.red = red
        self.green = green
        self.blue = blue

    def as_tuple(self):
        return self.red, self.green, self.blue
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This new method is pretty straightforward. Since it's an instance method, it takes &lt;code&gt;self&lt;/code&gt; as its first argument. Then it returns a tuple containing the attributes &lt;code&gt;.red&lt;/code&gt;, &lt;code&gt;.green&lt;/code&gt;, and &lt;code&gt;.blue&lt;/code&gt;. Note how you need to use &lt;code&gt;self&lt;/code&gt; to access the attributes of the current instance inside the class.&lt;/p&gt;
&lt;p&gt;This method may be useful if you need to iterate over the RGB components of your color objects:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; red = Color(255, 0, 0)
&amp;gt;&amp;gt;&amp;gt; red.as_tuple()
(255, 0, 0)

&amp;gt;&amp;gt;&amp;gt; for level in red.as_tuple():
...     print(level)
...
255
0
0
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Our &lt;code&gt;as_tuple()&lt;/code&gt; method works great! It returns a tuple containing the RGB components of our color objects.&lt;/p&gt;
&lt;p&gt;We can also add class methods to our Python classes. To do this, we need to use the &lt;a href="https://docs.python.org/3/library/functions.html#classmethod"&gt;&lt;code&gt;@classmethod&lt;/code&gt;&lt;/a&gt; decorator as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Color:
    representation = "RGB"

    def __init__(self, red, green, blue):
        self.red = red
        self.green = green
        self.blue = blue

    def as_tuple(self):
        return self.red, self.green, self.blue

    @classmethod
    def from_tuple(cls, rbg):
        return cls(*rbg)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;from_tuple()&lt;/code&gt; method takes a tuple object containing the RGB components of a desired color as an argument, creates a valid color object from it, and returns the object back to the caller:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; blue = Color.from_tuple((0, 0, 255))
&amp;gt;&amp;gt;&amp;gt; blue.as_tuple()
(0, 0, 255)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we use the &lt;code&gt;Color&lt;/code&gt; class to access the class method &lt;code&gt;from_tuple()&lt;/code&gt;. We can also access the method using a concrete instance of this class. However, in both cases, we'll get a completely new object.&lt;/p&gt;
&lt;p&gt;Finally, Python classes can also have static methods that we can define with the &lt;a href="https://docs.python.org/3/library/functions.html#staticmethod"&gt;&lt;code&gt;@staticmethod&lt;/code&gt;&lt;/a&gt; decorator:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Color:
    representation = "RGB"

    def __init__(self, red, green, blue):
        self.red = red
        self.green = green
        self.blue = blue

    def as_tuple(self):
        return self.red, self.green, self.blue

    @classmethod
    def from_tuple(cls, rbg):
        return cls(*rbg)

    @staticmethod
    def color_says(message):
        print(message)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Static methods don't operate either on the current instance self or the current class &lt;code&gt;cls&lt;/code&gt;. These methods can work as independent functions. However, we typically put them inside a class when they are related to the class, and we need to have them accessible from the class and its instances.&lt;/p&gt;
&lt;p&gt;Here's how the method works:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; Color.color_says("Hello from the Color class!")
Hello from the Color class!

&amp;gt;&amp;gt;&amp;gt; red = Color(255, 0, 0)
&amp;gt;&amp;gt;&amp;gt; red.color_says("Hello from the red instance!")
Hello from the red instance!
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This method accepts a message and prints it on your screen. It works independently from the class or instance attributes. Note that you can call the method using the class or any of its instances.&lt;/p&gt;
&lt;h3&gt;Writing Getter &amp;amp; Setter Methods&lt;/h3&gt;
&lt;p&gt;Programming languages like Java and C++ rely heavily on setter and getter methods to retrieve and update the attributes of a class and its instances. These methods encapsulate an attribute allowing us to get and change its value without directly accessing the attribute itself.&lt;/p&gt;
&lt;p&gt;For example, say that we have a &lt;code&gt;Label&lt;/code&gt; class with a &lt;code&gt;text&lt;/code&gt; attribute. We can make &lt;code&gt;text&lt;/code&gt; a non-public attribute and provide getter and setter methods to manipulate the attributes according to our needs:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Label:
    def __init__(self, text):
        self.set_text(text)

    def text(self):
        return self._text

    def set_text(self, value):
        self._text = str(value)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this class, the &lt;code&gt;text()&lt;/code&gt; method is the getter associated with the &lt;code&gt;._text&lt;/code&gt; attribute, while the &lt;code&gt;set_text()&lt;/code&gt; method is the setter for &lt;code&gt;._text&lt;/code&gt;. Note how &lt;code&gt;._text&lt;/code&gt; is a non-public attribute. We know this because it has a leading underscore on its name.&lt;/p&gt;
&lt;p&gt;The setter method calls &lt;code&gt;str()&lt;/code&gt; to convert any input value into a string. Therefore, we can call this method with any type of object. It will convert any input argument into a string, as you will see in a moment.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  If you come from programming languages like Java or C++, you need to know Python doesn't have the notion of &lt;strong&gt;private&lt;/strong&gt;, &lt;strong&gt;protected&lt;/strong&gt;, and &lt;strong&gt;public&lt;/strong&gt; attributes. In Python, you'll use a naming convention to signal that an attribute is non-public. This convention consists of adding a leading underscore to the attribute's name. Note that this naming pattern only indicates that the attribute isn't intended to be used directly. It doesn't prevent direct access, though.&lt;/p&gt;
&lt;p&gt;This class works as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; label = Label("Python!")

&amp;gt;&amp;gt;&amp;gt; label.text()
'Python!'

&amp;gt;&amp;gt;&amp;gt; label.set_text("Classes!")
&amp;gt;&amp;gt;&amp;gt; label.text()
'Classes!'

&amp;gt;&amp;gt;&amp;gt; label.set_text(123)
&amp;gt;&amp;gt;&amp;gt; label.text()
'123'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we create an instance of &lt;code&gt;Label&lt;/code&gt;. The original text is passed to the class constructor, &lt;code&gt;Label()&lt;/code&gt;, which automatically calls &lt;code&gt;__init__()&lt;/code&gt; to set the value of &lt;code&gt;._text&lt;/code&gt; by calling the setter method &lt;code&gt;text()&lt;/code&gt;. You can use &lt;code&gt;text()&lt;/code&gt; to access the label's text and &lt;code&gt;set_text()&lt;/code&gt; to update it. Remember that any input will be converted into a string, as we can see in the final example above.&lt;/p&gt;
&lt;p&gt;The getter and setter pattern is pretty common in languages like Java and C++.  However, this pattern is less popular among Python developers. Instead, they use the &lt;code&gt;@property&lt;/code&gt; decorator to hide attributes behind properties.&lt;/p&gt;
&lt;p&gt;Here's how most Python developer will write their &lt;code&gt;Label&lt;/code&gt; class:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Label:
    def __init__(self, text):
        self.text = text

    @property
    def text(self):
        return self._text

    @text.setter
    def text(self, value):
        self._text = str(value)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This class defines &lt;code&gt;.text&lt;/code&gt; as a property. This property has getter and setter methods. Python calls them automatically when we access the attribute or update its value in an assignment:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; label = Label("Python!")

&amp;gt;&amp;gt;&amp;gt; label.text
'Python!'

&amp;gt;&amp;gt;&amp;gt; label.text = "Class"
&amp;gt;&amp;gt;&amp;gt; label.text
'Class'

&amp;gt;&amp;gt;&amp;gt; label.text = 123
&amp;gt;&amp;gt;&amp;gt; label.text
'123'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Python properties allow you to add function behavior to your attributes while permitting you to use them as normal attributes instead of as methods.&lt;/p&gt;
&lt;h3&gt;Writing Special Methods&lt;/h3&gt;
&lt;p&gt;Python supports many &lt;a href="https://docs.python.org/3/glossary.html#term-special-method"&gt;special methods&lt;/a&gt;, also known as &lt;strong&gt;dunder&lt;/strong&gt; or &lt;strong&gt;magic&lt;/strong&gt; methods, that are part of its class mechanism. We can identify these methods because their names start and end with a double underscore, which is the origin of their other name: dunder methods.&lt;/p&gt;
&lt;p&gt;These methods accomplish different tasks in Python's class mechanism. They all have a common feature: &lt;em&gt;Python calls them automatically depending on the operation we run.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For example, all Python objects are printable. We can print them to the screen using the &lt;code&gt;print()&lt;/code&gt; function. Calling &lt;code&gt;print()&lt;/code&gt; internally falls back to calling the target object's &lt;code&gt;__str__()&lt;/code&gt; special method:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; label = Label("Python!")

&amp;gt;&amp;gt;&amp;gt; print(label)
&amp;lt;__main__.Label object at 0x10354efd0&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we've printed our &lt;code&gt;label&lt;/code&gt; object. This action provides some information about the object and the memory address where it lives. However, the actual output is not very useful from the user's perspective.&lt;/p&gt;
&lt;p&gt;Fortunately, we can improve this by providing our &lt;code&gt;Label&lt;/code&gt; class with an appropriate &lt;code&gt;__str__()&lt;/code&gt; method:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Label:
    def __init__(self, text):
        self.text = text

    @property
    def text(self):
        return self._text

    @text.setter
    def text(self, value):
        self._text = str(value)

    def __str__(self):
        return self.text
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;__str__()&lt;/code&gt; method must return a user-friendly string representation for our objects. In this case, when we print an instance of &lt;code&gt;Label&lt;/code&gt; to the screen, the label's text will be displayed:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; label = Label("Python!")

&amp;gt;&amp;gt;&amp;gt; print(label)
Python!
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As you can see, Python takes care of calling &lt;code&gt;__str__()&lt;/code&gt; automatically when we use the &lt;code&gt;print()&lt;/code&gt; function to display our instances of &lt;code&gt;Label&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Another special method that belongs to Python's class mechanism is &lt;code&gt;__repr__()&lt;/code&gt;. This method returns a developer-friendly string representation of a given object. Here, developer-friendly implies that the representation should allow a developer to recreate the object itself.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Label:
    def __init__(self, text):
        self.text = text

    @property
    def text(self):
        return self._text

    @text.setter
    def text(self, value):
        self._text = str(value)

    def __str__(self):
        return self.text

    def __repr__(self):
        return f"{type(self).__name__}(text='{self.text}')"
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;__repr__()&lt;/code&gt; method returns a string representation of the current objects. This string differs from what &lt;code&gt;__str__()&lt;/code&gt; returns:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; label = Label("Python!")
&amp;gt;&amp;gt;&amp;gt; label
Label(text='Python!')
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now when you access the instance on your REPL session, you get a string representation of the current object. You can copy and paste this representation to recreate the object in an appropriate environment.&lt;/p&gt;
&lt;h2 id="reusing-code-with-inheritance"&gt;Reusing Code With Inheritance&lt;/h2&gt;
&lt;p&gt;Inheritance is an advanced topic in object-oriented programming. It allows you to create hierarchies of classes where each subclass inherits all the attributes and behaviors from its parent class or classes. Arguably, &lt;strong&gt;code reuse&lt;/strong&gt; is the primary use case of inheritance.&lt;/p&gt;
&lt;p&gt;Yes, we code a base class with a given functionality and make that functionality available to its subclass through inheritance. This way, we implement the functionality only once and reuse it in every subclass.&lt;/p&gt;
&lt;p&gt;Python classes support &lt;strong&gt;single&lt;/strong&gt; and &lt;strong&gt;multiple&lt;/strong&gt; inheritance. For example, let's say we need to create a button class. This class needs &lt;code&gt;.width&lt;/code&gt; and &lt;code&gt;.height&lt;/code&gt; attributes that define its rectangular shape. The class also needs a label for displaying some informative text.&lt;/p&gt;
&lt;p&gt;We can code this class from scratch, or we can use inheritance and reuse the code of our current &lt;code&gt;Label&lt;/code&gt; class. Here's how to do this:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Button(Label):
    def __init__(self, text, width, height):
        super().__init__(text)
        self.width = width
        self.height = height

    def __repr__(self):
        return (
            f"{type(self).__name__}"
            f"(text='{self.text}', "
            f"width={self.width}, "
            f"height={self.height})"
        )
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;To inherit from a parent class in Python, we need to list the parent class or classes in the subclass definition. To do this, we use a pair of parentheses and a comma-separated list of parent classes. If we use several parent classes, then we're using multiple inheritance, which can be challenging to reason about.&lt;/p&gt;
&lt;p&gt;The first line in &lt;code&gt;__init__()&lt;/code&gt; calls the &lt;code&gt;__init__()&lt;/code&gt; method on the parent class to properly initialize its &lt;code&gt;.text&lt;/code&gt; attribute. To do this, we use the built-in &lt;code&gt;super()&lt;/code&gt; function. Then we define the &lt;code&gt;.width&lt;/code&gt; and &lt;code&gt;.height&lt;/code&gt; attributes, which are specific to our &lt;code&gt;Button&lt;/code&gt; class. Finally, we provide a custom implementation of &lt;code&gt;__repr__()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here's how our &lt;code&gt;Button&lt;/code&gt; class works:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; button = Button("Ok", 10, 5)

&amp;gt;&amp;gt;&amp;gt; button.text
'Ok'
&amp;gt;&amp;gt;&amp;gt; button.text = "Click Me!"
&amp;gt;&amp;gt;&amp;gt; button.text
'Click Me!'

&amp;gt;&amp;gt;&amp;gt; button.width
10
&amp;gt;&amp;gt;&amp;gt; button.height
5

&amp;gt;&amp;gt;&amp;gt; button
Button(text='Ok', width=10, height=5)
&amp;gt;&amp;gt;&amp;gt; print(button)
Click Me!
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As you can conclude from this code, &lt;code&gt;Button&lt;/code&gt; has inherited the &lt;code&gt;.text&lt;/code&gt; attribute from &lt;code&gt;Label&lt;/code&gt;. This attribute is completely functional. Our class has also inherited the &lt;code&gt;__str__()&lt;/code&gt; method from &lt;code&gt;Label&lt;/code&gt;. That's why we get the button's text when we print the instance.&lt;/p&gt;
&lt;h2 id="wrapping-up-classes-related-concepts"&gt;Wrapping Up Classes-Related Concepts&lt;/h2&gt;
&lt;p&gt;As we've seen, Python allows us to write classes that work as templates that you can use to create concrete objects that bundle together data and behavior. The building blocks of Python classes are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Attributes&lt;/strong&gt;, which hold the data in a class&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Methods&lt;/strong&gt;, which provide the behaviors of a class&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The attributes of a class define the class's data, while the methods provide the class's behaviors, which typically act on that data.&lt;/p&gt;
&lt;p&gt;To better understand OOP and classes in Python, we should first discuss some terms that are commonly used in this aspect of Python development:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Classes&lt;/strong&gt; are blueprints or templates for creating objects -- just like a blueprint for creating a car, plane, house, or anything else. In programming, this blueprint will define the data (attributes) and behavior (methods) of the object and will allow us to create multiple objects of the same kind.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Objects&lt;/strong&gt; or &lt;strong&gt;Instances&lt;/strong&gt; are the realizations of a class. We can create objects from the blueprint provided by the class. For example, you can create John's car from a &lt;code&gt;Car&lt;/code&gt; class.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Methods&lt;/strong&gt; are functions defined within a class. They provide the behavior of an object of that class. For example, our &lt;code&gt;Car&lt;/code&gt; class can have methods to start the engine, turn right and left, stop, and so on.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Attributes&lt;/strong&gt; are properties of an object or class. We can think of attributes as variables defined in a class or object. Therefore, we can have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;class attributes&lt;/strong&gt;, which are specific to a concrete class and common to all the instances of that class. You can access them either through the class or an object of that class. For example, if we're dealing with a single car manufacturer, then our &lt;code&gt;Car&lt;/code&gt; class can have a manufacturer attribute that identifies it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;instance attributes&lt;/strong&gt;, which are specific to a concrete instance. You can access them through the specific instance. For example, our &lt;code&gt;Car&lt;/code&gt; class can have attributes to store properties such as the maximum speed, the number of passengers, the car's weight, and so on.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Instantiation&lt;/strong&gt; is the process of creating an individual &lt;em&gt;instance&lt;/em&gt; from a class. For example, we can create John's car, Jane's car, and Linda's car from our &lt;code&gt;Car&lt;/code&gt; class through instantiation. In Python, this process runs through two steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Instance creation&lt;/strong&gt;: Creates a new object and allocates memory for storing it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Instance initialization&lt;/strong&gt;: Initializes all the attributes of the current object with appropriate values.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Inheritance&lt;/strong&gt; is a mechanism of code reuse that allows us to inherit attributes and methods from one or multiple existing classes. In this context, we'll hear terms like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Parent class&lt;/strong&gt;: The class we're inheriting from. This class is also known as the &lt;strong&gt;superclass&lt;/strong&gt; or &lt;strong&gt;base class&lt;/strong&gt;. If we have one parent class, then we're using &lt;strong&gt;single inheritance&lt;/strong&gt;. If we have more than one parent class, then we're using &lt;strong&gt;multiple inheritance&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Child class&lt;/strong&gt;: The class that inherits from a given parent. This class is also known as the &lt;strong&gt;subclass&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Don't feel frustrated or bad if you don't understand all these terms immediately. They'll become more familiar with use as you use them in your own Python code.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Now you know the basics of Python classes. You also learned fundamental concepts of object-oriented programming, such as inheritance.&lt;/p&gt;</content><category term="python"/></entry><entry><title>Getting started with VS Code for Python — Setting up a Development Environment for Python programming</title><link href="https://www.martinfitzpatrick.com/getting-started-vs-code-python/" rel="alternate"/><published>2022-09-21T09:00:00+00:00</published><updated>2022-09-21T09:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2022-09-21:/getting-started-vs-code-python/</id><summary type="html">Setting up a working development environment is the first step for any project. Your development environment setup will determine how easy it is to develop and maintain your projects over time. That makes it important to choose the &lt;em&gt;right tools for your project&lt;/em&gt;. This article will guide you through how to set up Visual Studio Code, which is a popular free-to-use, cross-platform code editor developed by Microsoft, in order to develop Python applications.</summary><content type="html">
            &lt;p&gt;Setting up a working development environment is the first step for any project. Your development environment setup will determine how easy it is to develop and maintain your projects over time. That makes it important to choose the &lt;em&gt;right tools for your project&lt;/em&gt;. This article will guide you through how to set up Visual Studio Code, which is a popular free-to-use, cross-platform code editor developed by Microsoft, in order to develop Python applications.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt; &lt;em&gt;Visual Studio Code&lt;/em&gt; is not to be confused with &lt;em&gt;Visual Studio&lt;/em&gt;, which is a separate product also offered by Microsoft. Visual Studio is a fully-fledged IDE that is mainly geared towards Windows application development using C# and the .NET Framework.&lt;/p&gt;
&lt;h2 id="setup-a-python-environment"&gt;Setup a Python environment&lt;/h2&gt;
&lt;p&gt;In case you haven't already done this, Python needs to be installed on the development machine. You can do this by going to &lt;a href="https://www.python.org/downloads/"&gt;python.org&lt;/a&gt; and grabbing the specific installer for either Windows or macOS. Python is also available for installation via Microsoft Store on Windows devices.&lt;/p&gt;
&lt;p class="admonition admonition-warning"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation-circle"&gt;&lt;/i&gt;&lt;/span&gt;  Make sure that you select the option to &lt;em&gt;Add Python to PATH&lt;/em&gt; during installation (via the installer).&lt;/p&gt;
&lt;p&gt;If you are on Linux, you can check if Python is already installed on your machine by typing &lt;code&gt;python3 --version&lt;/code&gt; in a terminal. If it returns an error, you need to install it from your distribution's repository. On Ubuntu/Debian, this can be done by typing &lt;code&gt;sudo apt install python3&lt;/code&gt;. Both &lt;code&gt;pip&lt;/code&gt; (or &lt;code&gt;pip3&lt;/code&gt;) and &lt;code&gt;venv&lt;/code&gt; are distributed as separate packages on Ubuntu/Debian and can also be installed by typing &lt;code&gt;sudo apt install python3-pip python3-venv&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="setup-visual-studio-code"&gt;Setup Visual Studio Code&lt;/h2&gt;
&lt;p&gt;First, head over to to &lt;a href="https://code.visualstudio.com/"&gt;code.visualstudio.com&lt;/a&gt; and grab the installer for your specific platform.&lt;/p&gt;
&lt;p&gt;If you are on a Raspberry Pi (with Raspberry Pi OS), you can also install VS Code by simply typing &lt;code&gt;sudo apt install code&lt;/code&gt;. On &lt;a href="https://snapcraft.io/docs/installing-snapd"&gt;Linux distributions that support Snaps&lt;/a&gt;, you can do it by typing &lt;code&gt;sudo snap install code --classic&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once VS Code is installed, head over to the &lt;em&gt;Extensions&lt;/em&gt; tab in the sidebar on the left by clicking on it or by pressing &lt;code&gt;CTRL+SHIFT+X&lt;/code&gt;. Search for the 'Python' extension published by Microsoft and click on &lt;em&gt;Install&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The Extensions tab in the left-hand sidebar" src="https://www.martinfitzpatrick.com/static/tutorials/developer/vscode/extensions.png"  loading="lazy" width="550" height="938"/&gt;
&lt;em&gt;The Extensions tab in the left-hand sidebar.&lt;/em&gt;&lt;/p&gt;
&lt;!-- Screenshot, A Python file opened in VS Code showing autocomplete suggestions and the program running in the in-built terminal. --&gt;
&lt;h2 id="usage-and-configuration"&gt;Usage and Configuration&lt;/h2&gt;
&lt;p&gt;Now that you have finished setting up VS Code, you can go ahead and create a new Python file. Remember that the Python extension only works if you open a &lt;code&gt;.py&lt;/code&gt; file or have selected the language mode for the active file as Python.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  To change the language mode for the active file, simply press &lt;code&gt;CTRL+K&lt;/code&gt; once and then press &lt;code&gt;M&lt;/code&gt; after releasing the previous keys. This kind of keyboard shortcut is called a &lt;em&gt;chord&lt;/em&gt; in VS Code. You can see more of them by pressing &lt;code&gt;CTRL+K CTRL+S&lt;/code&gt; (another chord).&lt;/p&gt;
&lt;p&gt;The Python extension in VS Code allows you to directly run a Python file by clicking on the 'Play' button on the top-right corner of the editor (without having to type &lt;code&gt;python file.py&lt;/code&gt; in the terminal).&lt;/p&gt;
&lt;p&gt;You can also do it by pressing &lt;code&gt;CTRL+SHIFT+P&lt;/code&gt; to open the &lt;em&gt;Command Palette&lt;/em&gt; and running the &lt;code&gt;&amp;gt; Python: Run File in Terminal&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;Finally, you can configure VS Code's settings by going to &lt;code&gt;File &amp;gt; Preferences &amp;gt; Settings&lt;/code&gt; or by pressing &lt;code&gt;CTRL+COMMA&lt;/code&gt;. In VS Code, each individual setting has an unique &lt;em&gt;identifier&lt;/em&gt; which you can see by clicking on the cog wheel that appears to the left of each setting and clicking on 'Copy Setting ID'. This ID is what will be referred to while talking about a specific setting. You can also search for this ID in the search bar under &lt;em&gt;Settings&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="linting-and-formatting-support-optional"&gt;Linting and Formatting Support (Optional)&lt;/h2&gt;
&lt;p&gt;Linters make it easier to find errors and check the quality of your code. On the other hand, code formatters help keep the source code of your application compliant with PEP (Python Enhancement Proposal) standards, which make it easier for other developers to read your code and collaborate with you.&lt;/p&gt;
&lt;p&gt;For VS Code to provide linting support for your projects, you must first install a preferred linter like &lt;code&gt;flake8&lt;/code&gt; or &lt;code&gt;pylint&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;pip install flake8
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Then, go to &lt;em&gt;Settings&lt;/em&gt; in VS Code and toggle the relevant setting (e.g. &lt;code&gt;python.linting.flake8Enabled&lt;/code&gt;) for the &lt;em&gt;Python&lt;/em&gt; extension depending on what you installed. You also need to make sure that &lt;code&gt;python.linting.enabled&lt;/code&gt; is toggled on.&lt;/p&gt;
&lt;p&gt;A similar process must be followed for code formatting. First, install something like &lt;code&gt;autopep8&lt;/code&gt; or &lt;code&gt;black&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;pip install autopep8
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You then need to tell VS Code which formatter to use by modifying &lt;code&gt;python.formatting.provider&lt;/code&gt; and toggle on &lt;code&gt;editor.formatOnSave&lt;/code&gt; so that it works without manual intervention.&lt;/p&gt;
&lt;p class="admonition admonition-warning"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation-circle"&gt;&lt;/i&gt;&lt;/span&gt;  If &lt;code&gt;pip&lt;/code&gt; warns that the installed modules aren't in your PATH, you may have to specify the path to their location in VS Code (under &lt;em&gt;Settings&lt;/em&gt;). Follow the method described under &lt;em&gt;Working With Virtual Environments&lt;/em&gt; to do that.&lt;/p&gt;
&lt;p&gt;Now, when you create a new Python file, VS Code automatically gives you a list of &lt;em&gt;Problems&lt;/em&gt; (&lt;code&gt;CTRL+SHIFT+M&lt;/code&gt;) in your program and formats the code on saving the file.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Identified problems in the source code." src="https://www.martinfitzpatrick.com/static/tutorials/developer/vscode/problems.png"  loading="lazy" width="2072" height="277"/&gt;
&lt;em&gt;Identified problems in the source code, along with a description and line/column numbers.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You can also find the location of identified problems from the source overview on the right hand, inside the scrollbar.&lt;/p&gt;
&lt;h2 id="working-with-virtual-environments"&gt;Working With Virtual Environments&lt;/h2&gt;
&lt;p&gt;Virtual environments are a way of life for Python developers. Most Python projects require the installation of external packages and modules (via &lt;code&gt;pip&lt;/code&gt;). Virtual environments allow you to separate one project's packages from your other projects, which may require a different version of those same packages. Hence, it allows all those projects to have the specific dependencies they require to work.&lt;/p&gt;
&lt;p&gt;The Python extension makes it easier for you by automatically activating the desired virtual environment for the in-built terminal and &lt;em&gt;Run Python File&lt;/em&gt; command after you set the path to the Python interpreter. By default, the path is set to use the system's Python installation (without a virtual environment).&lt;/p&gt;
&lt;p&gt;To use a virtual environment for your project/workspace, you need to first make a new one by opening a terminal (&lt;code&gt;View &amp;gt; Terminal&lt;/code&gt;) and typing &lt;code&gt;python -m venv .venv&lt;/code&gt;. Then, you can set the default interpreter for that project by opening the &lt;em&gt;Command Palette&lt;/em&gt; (&lt;code&gt;CTRL+SHIFT+P&lt;/code&gt;) and selecting &lt;code&gt;&amp;gt; Python: Select Interpreter&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You should now either close the terminal pane in VS Code and open a new one or type &lt;code&gt;source .venv/bin/activate&lt;/code&gt; into the existing one to start using the virtual environment. Then, install the required packages for your project by typing &lt;code&gt;pip install &amp;lt;package_name&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;VS Code, by default, looks for tools like linters and code formatters in the current Python environment. If you don't want to keep installing them over and over again for each new virtual environment you make (unless your project requires a specific version of that tool), you can specify the path to their location under &lt;em&gt;Settings&lt;/em&gt; in VS Code.
- &lt;code&gt;flake8&lt;/code&gt; - &lt;code&gt;python.linting.flake8Path&lt;/code&gt;
- &lt;code&gt;autopep8&lt;/code&gt; - &lt;code&gt;python.formatting.autopep8Path&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;To find the global location of these packages on macOS and Linux, type &lt;code&gt;which flake8&lt;/code&gt; and &lt;code&gt;which autopep8&lt;/code&gt; in a terminal. If you are on Windows, you can use &lt;code&gt;where &amp;lt;command_name&amp;gt;&lt;/code&gt;. Both these commands assume that &lt;code&gt;flake8&lt;/code&gt; and &lt;code&gt;autopep8&lt;/code&gt; are in your PATH.&lt;/p&gt;
&lt;h2 id="understanding-workspaces-in-vs-code"&gt;Understanding Workspaces in VS Code&lt;/h2&gt;
&lt;p&gt;VS Code has a concept of &lt;em&gt;Workspaces&lt;/em&gt;. Each 'project folder' (or the root/top folder) is treated as a separate workspace. This allows you to have project-specific settings and enable/disable certain extensions for that workspace. It is also what allows VS Code to quickly recover the UI state (e.g. files that were previously kept open) when you open that workspace again.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  In VS Code, each workspace (or folder) has to be 'trusted' before certain features like linters, autocomplete suggestions and the in-built terminal are allowed to work.&lt;/p&gt;
&lt;p&gt;In the context of Python projects, if you tend to keep your virtual environments outside the workspace (where VS Code is unable to detect it), you can use this feature to set the default path to the Python interpreter for that workspace. To do that, first &lt;em&gt;Open a Folder&lt;/em&gt; (&lt;code&gt;CTRL+K CTRL+O&lt;/code&gt;) and then go to &lt;code&gt;File &amp;gt; Preferences &amp;gt; Settings &amp;gt; Workspace&lt;/code&gt; to modify &lt;code&gt;python.defaultInterpreterPath&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Setting the default interpreter path for the workspace." src="https://www.martinfitzpatrick.com/static/tutorials/developer/vscode/defaultinterpreterpath.png"  loading="lazy" width="1493" height="300"/&gt;
&lt;em&gt;Setting the default interpreter path for the workspace.&lt;/em&gt;&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  In VS Code settings you can search for settings by name using the bar at the top.&lt;/p&gt;
&lt;p&gt;You can also use this approach to do things like use a different linter for that workspace or disable the code formatter for it. The workspace-specific settings you change are saved in a &lt;code&gt;.vscode&lt;/code&gt; folder inside that workspace, which you can share with others.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  If your VS Code is not recognizing libraries you are using in your code, double check the correct interpreter is being used. You can find which Python version you're using on the command line by running &lt;code&gt;which python&lt;/code&gt; or &lt;code&gt;which python3&lt;/code&gt; on macOS/Linux, or &lt;code&gt;where python&lt;/code&gt; or &lt;code&gt;where python3&lt;/code&gt; on Windows.&lt;/p&gt;
&lt;h2 id="working-with-git-in-vs-code-optional"&gt;Working With Git in VS Code (Optional)&lt;/h2&gt;
&lt;p&gt;Using &lt;em&gt;Version Control&lt;/em&gt; is required for developing applications. VS Code does have in-built support for Git but it is pretty barebones, not allowing much more than tracking changes that you have currently made and committing/pushing those changes once you are done.&lt;/p&gt;
&lt;p&gt;For the best experience, it is recommended to use the &lt;em&gt;GitLens&lt;/em&gt; extension. It lets you view your commit history, check who made the changes and much more. To set it up, you first need to have Git set up on your machine (&lt;a href="https://git-scm.com/"&gt;go here&lt;/a&gt;) and then install &lt;em&gt;GitLens&lt;/em&gt; from the &lt;em&gt;Extensions&lt;/em&gt; tab in the sidebar on the left. You can now use those Git-related features by going to the &lt;em&gt;Git&lt;/em&gt; tab in the sidebar (&lt;code&gt;CTRL+SHIFT+G&lt;/code&gt;).&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  There are more Git-related extensions you could try as well, like &lt;em&gt;Git History&lt;/em&gt; and &lt;em&gt;GitLab Workflow&lt;/em&gt;. Give them a whirl too!&lt;/p&gt;
&lt;h2 id="community-driven-open-source-alternatives"&gt;Community-driven &amp;amp; open source alternatives&lt;/h2&gt;
&lt;p&gt;While VS Code is open source (MIT-licensed), the distributed versions include some Microsoft-specific proprietary modifications, such as telemetry (app tracking). If you would like to avoid this, there is also a community-driven distribution of Visual Studio Code called &lt;a href="https://vscodium.com/"&gt;VSCodium&lt;/a&gt; that provides freely-licensed binaries without telemetry.&lt;/p&gt;
&lt;p&gt;Due to legal restrictions, VSCodium is unable to use the official Visual Studio Marketplace for extensions. Instead, it uses a separate vendor neutral, open source marketplace called &lt;a href="https://open-vsx.org/"&gt;Open VSX Registry&lt;/a&gt;. It doesn't have every extension, especially proprietary ones, and some are not kept up-to-date but both the &lt;em&gt;Python&lt;/em&gt; and &lt;em&gt;GitLens&lt;/em&gt; extensions are available on it.&lt;/p&gt;
&lt;p&gt;You can also use the open source &lt;em&gt;Jedi&lt;/em&gt; language server for the Python extension, rather than the bundled &lt;em&gt;Pylance&lt;/em&gt; language server/extension, by configuring the &lt;code&gt;python.languageServer&lt;/code&gt; setting. You can then completely disable Pylance by going to the &lt;em&gt;Extensions&lt;/em&gt; tab. Note that, if you are on VSCodium, Jedi is used by default (as Pylance is not available on Open VSX Registry) when you install the Python extension.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Having the right tools and making sure they're set up correctly will greatly simplify your development process. While Visual Studio starts as a simple tool, it is flexible and extendable
with plugins to suit your own preferred workflow. In this tutorial we've covered the basics of setting up your environment, and you should now be ready to start &lt;a href="https://www.pythonguis.com/topics/pyqt6-foundation/"&gt;developing your own applications with Python&lt;/a&gt;!&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PyQt6 see my book, &lt;a href="https://www.mfitzp.com/pyqt6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="python"/></entry><entry><title>Using MicroPython and uploading libraries on Raspberry Pi Pico — Using rshell to upload custom code</title><link href="https://www.martinfitzpatrick.com/using-micropython-raspberry-pico/" rel="alternate"/><published>2021-02-22T08:00:00+00:00</published><updated>2021-02-22T08:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2021-02-22:/using-micropython-raspberry-pico/</id><summary type="html">MicroPython is an implementation of the Python 3 programming language, optimized to run microcontrollers. It's one of the options available for programming your Raspberry Pi Pico and a nice friendly way to get started with microcontrollers.</summary><content type="html">&lt;p&gt;MicroPython is an implementation of the Python 3 programming language, optimized to run microcontrollers. It's one of the options available for programming your Raspberry Pi Pico and a nice friendly way to get started with microcontrollers.&lt;/p&gt;
&lt;p&gt;MicroPython can be installed easily on your Pico, by following &lt;a href="https://www.raspberrypi.org/documentation/pico/getting-started/"&gt;the instructions on the Raspberry Pi website&lt;/a&gt; (click the &lt;em&gt;Getting Started with MicroPython&lt;/em&gt; tab and follow the instructions).&lt;/p&gt;
&lt;p&gt;After that point you might get a bit stuck. The &lt;a href="https://datasheets.raspberrypi.org/pico/raspberry-pi-pico-python-sdk.pdf"&gt;Pico documentation&lt;/a&gt; covers connecting to the Pico from a Pi, so if you're wanting to code from your own computer you'll need something else. One option is the &lt;a href="https://projects.raspberrypi.org/en/projects/getting-started-with-the-pico/2"&gt;Thonny IDE&lt;/a&gt; which you use to write and upload code to your Pico. It's got a nice friendly interface for working with Python.&lt;/p&gt;
&lt;p&gt;But if you don't want to change your IDE, or want a way to communicate with your Pico from the command line? You're in luck: there is a simple tool for accessing the MicroPython REPL on your Pico and uploading custom Python scripts or libraries you may wish to use: &lt;strong&gt;rshell&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In this tutorial I'll take you through working with MicroPython in &lt;strong&gt;rshell&lt;/strong&gt;, coding live and uploading custom scripts to your Pico.&lt;/p&gt;
&lt;h2 id="installing-rshell"&gt;Installing rshell&lt;/h2&gt;
&lt;p&gt;Rshell itself is built on Python (not MicroPython) and can be installed and run locally on your main machine. You can install it like any other Python library.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;python -m pip install rshell
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Unfortunately, the current version of &lt;code&gt;rshell&lt;/code&gt; does not always play nicely with the Raspberry Pico. If you have problems you can install a fixed version in the pico branch from &lt;a href="https://github.com/dhylands/rshell/tree/pico"&gt;the rshell repository&lt;/a&gt;. You can install this directly from Github with the following --&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;python -m pip install https://github.com/dhylands/rshell/archive/pico.zip
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This will download the latest version of the &lt;code&gt;pico&lt;/code&gt; branch (as a &lt;code&gt;.zip&lt;/code&gt;) and install this in your Python environment.&lt;/p&gt;
&lt;p&gt;Once installed, you will have access to a new command line tool &lt;code&gt;rshell&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="the-rshell-interface"&gt;The rshell interface&lt;/h2&gt;
&lt;p&gt;To use rshell from the command line, enter &lt;code&gt;rshell&lt;/code&gt; at your command prompt. You will see a welcome message and the prompt will turn green, to indicate you're in &lt;strong&gt;rshell&lt;/strong&gt; mode.&lt;/p&gt;
&lt;p&gt;&lt;img alt="rshell on Windows 10" src="https://www.martinfitzpatrick.com/using-micropython-raspberry-pico/rshell-windows.png"/&gt;
&lt;em&gt;The rshell interface on Windows 10&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="rshell on macOS" src="https://www.martinfitzpatrick.com/using-micropython-raspberry-pico/rshell-mac.png"/&gt;
&lt;em&gt;The rshell interface on macOS&lt;/em&gt;&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  If previous &lt;code&gt;pip install&lt;/code&gt; worked but the &lt;code&gt;rshell&lt;/code&gt; command &lt;em&gt;doesn't&lt;/em&gt; work, then you may have a problem with your Python paths.&lt;/p&gt;
&lt;p&gt;To see the commands available in rshell, enter &lt;code&gt;help&lt;/code&gt; and press Enter.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;help

Documented commands (type help &amp;lt;topic&amp;gt;):
========================================
args    cat  connect  date  edit  filesize  help  mkdir  rm     shell
boards  cd   cp       echo  exit  filetype  ls    repl   rsync

Use the exit command to exit rshell.
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  You can exit &lt;code&gt;rshell&lt;/code&gt; at any time by entering &lt;code&gt;exit&lt;/code&gt; or pressing &lt;code&gt;Ctrl-C&lt;/code&gt;. Once exited the prompt will turn white.&lt;/p&gt;
&lt;p&gt;The basic file operation commands are shown below.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cd &amp;lt;dirname&amp;gt;&lt;/code&gt; change directory&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cp &amp;lt;from&amp;gt; &amp;lt;to&amp;gt;&lt;/code&gt; copy a file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ls&lt;/code&gt; list current directory&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rm &amp;lt;filename&amp;gt;&lt;/code&gt; remove (delete) a file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;filesize &amp;lt;filename&amp;gt;&lt;/code&gt; give the size of a file in bytes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you type &lt;code&gt;ls&lt;/code&gt; and press enter you will see a listing of your current folder &lt;em&gt;on your host computer&lt;/em&gt;. The same goes
for any of the other file operations, until we've connect a board and opened it's file storage -- so be careful!
We'll look at how to connect to a MicroPython board and work with the files on it next.&lt;/p&gt;
&lt;h2 id="connecting-to-your-pico-with-rshell"&gt;Connecting to your Pico with rshell&lt;/h2&gt;
&lt;p&gt;Enter &lt;code&gt;boards&lt;/code&gt; to see a list of MicroPython boards connected to your computer. If you don't
have any connected boards, you'll see the message &lt;code&gt;No boards connected&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If your board isn't connected&lt;/strong&gt; plug your Pico in now. You &lt;em&gt;can&lt;/em&gt; use the &lt;code&gt;connect&lt;/code&gt; command to connect to the board,
but for that you'll need to know which port it is on. Save yourself some effort and just restart &lt;strong&gt;rshell&lt;/strong&gt; to connect
automatically. To do this, type &lt;code&gt;exit&lt;/code&gt; and press Enter (or press &lt;code&gt;Ctrl-C&lt;/code&gt;) to exit and then restart &lt;strong&gt;rshell&lt;/strong&gt; by entering &lt;code&gt;rshell&lt;/code&gt; again at the prompt.&lt;/p&gt;
&lt;p&gt;If a board is connected when you start &lt;strong&gt;rshell&lt;/strong&gt; you will see something like the following...&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;C:\Users\Gebruiker&amp;gt;rshell
Connecting to COM4 (buffer-size 128)...
Trying to connect to REPL  connected
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Or an equivalent on macOS...&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;Martins-Mac: ~ mfitzp$ rshell
Connecting to /dev/cu.usbmodem0000000000001 (buffer-size 128)
Trying to connect to REPL  connected
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;...which shows you've connected to the MicroPython REPL on the Raspberry Pi Pico. Once connected the &lt;code&gt;boards&lt;/code&gt; command
will return some information about the connected board, like the following.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;pyboard @ COM4 connected Epoch: 1970 Dirs:
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The name on the left is the &lt;em&gt;type&lt;/em&gt; of board (Pico appears as &lt;code&gt;pyboard&lt;/code&gt;) and connected port (here COM4). The label at the end &lt;code&gt;Dirs:&lt;/code&gt; will list any files
on the Pico -- currently none.&lt;/p&gt;
&lt;h2 id="starting-a-repl"&gt;Starting a REPL&lt;/h2&gt;
&lt;p&gt;With the board connected, you can enter the Pico's REPL by entering the &lt;code&gt;repl&lt;/code&gt; command. This will return something like the following&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;repl
Entering REPL. Use Control-X to exit.
&amp;gt;
MicroPython v1.14 on 2021-02-14; Raspberry Pi Pico with RP2040
Type "help()" for more information.
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You are now writing Python on the Pico! Try entering &lt;code&gt;print("Hello!")&lt;/code&gt; at the REPL prompt.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;MicroPython v1.14 on 2021-02-14; Raspberry Pi Pico with RP2040
Type "help()" for more information.
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; print("Hello!")
Hello!
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As you can see, MicroPython works just like normal Python. If you enter &lt;code&gt;help()&lt;/code&gt; and press Enter,
you'll get some basic help information about MicroPython on the Pico. Helpfully, you also get a
small reference to how the pins on the Pico are numbered and the different ways you have to control them.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;Type "help()" for more information.
&amp;gt;&amp;gt;&amp;gt; help()
Welcome to MicroPython!

For online help please visit https://micropython.org/help/.

For access to the hardware use the 'machine' module.  RP2 specific commands
are in the 'rp2' module.

Quick overview of some objects:
  machine.Pin(pin) -- get a pin, eg machine.Pin(0)
  machine.Pin(pin, m, [p]) -- get a pin and configure it for IO mode m, pull mode p
    methods: init(..), value([v]), high(), low(), irq(handler)
  machine.ADC(pin) -- make an analog object from a pin
    methods: read_u16()
  machine.PWM(pin) -- make a PWM object from a pin
    methods: deinit(), freq([f]), duty_u16([d]), duty_ns([d])
  machine.I2C(id) -- create an I2C object (id=0,1)
    methods: readfrom(addr, buf, stop=True), writeto(addr, buf, stop=True)
             readfrom_mem(addr, memaddr, arg), writeto_mem(addr, memaddr, arg)
  machine.SPI(id, baudrate=1000000) -- create an SPI object (id=0,1)
    methods: read(nbytes, write=0x00), write(buf), write_readinto(wr_buf, rd_buf)
  machine.Timer(freq, callback) -- create a software timer object
    eg: machine.Timer(freq=1, callback=lambda t:print(t))

Pins are numbered 0-29, and 26-29 have ADC capabilities
Pin IO modes are: Pin.IN, Pin.OUT, Pin.ALT
Pin pull modes are: Pin.PULL_UP, Pin.PULL_DOWN

Useful control commands:
  CTRL-C -- interrupt a running program
  CTRL-D -- on a blank line, do a soft reset of the board
  CTRL-E -- on a blank line, enter paste mode

For further help on a specific object, type help(obj)
For a list of available modules, type help('modules')
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  You can run &lt;code&gt;help()&lt;/code&gt; in the REPL any time you need a reminder.&lt;/p&gt;
&lt;p&gt;While we're here, lets flash the LED on the Pico board.&lt;/p&gt;
&lt;p&gt;Enter the following at the REPL prompt...&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from machine import Pin
led = Pin(25, Pin.OUT)

led.toggle()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Every time you call &lt;code&gt;led.toggle()&lt;/code&gt; the LED will toggle from ON to OFF or OFF to ON.&lt;/p&gt;
&lt;p class="admonition admonition-important"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation"&gt;&lt;/i&gt;&lt;/span&gt;  To exit the REPL at any time press &lt;code&gt;Ctrl-X&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="uploading-a-file"&gt;Uploading a file&lt;/h2&gt;
&lt;p&gt;MicroPython comes with a lot of built-in support for simple devices and communication protocols -- enough to build some quite fun things just by hacking in the REPL. But there are also a lot of libraries available for working with more complex hardware. To use these, you need to be able to upload them to your Pico! Once you can upload files, you can also edit your own code locally on your own computer and upload it from there.&lt;/p&gt;
&lt;p&gt;To keep things simple, lets create our own "library" that adjusts the brightness of the LED on the Pico board -- exciting I know. This library contains a single function &lt;code&gt;ledon&lt;/code&gt; which accepts a single parameter &lt;code&gt;brightness&lt;/code&gt; between 0 and 65535.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from machine import Pin, PWM

led = PWM(Pin(25))

def ledon(brightness=65535):
    led.duty_u16(brightness)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  Don't worry if you don't understand it, we'll cover how this works later. The important bit now is getting this on your Pico.&lt;/p&gt;
&lt;p&gt;Take the code above and save it in a file named &lt;code&gt;picoled.py&lt;/code&gt; on your main computer, in the same folder you're executing &lt;strong&gt;rshell&lt;/strong&gt; from. We'll upload this file to the Pico next.&lt;/p&gt;
&lt;!--
TIP: If you want to try with something more exciting, try [this max7219 library](https://github.com/mcauser/micropython-max7219/blob/master/max7219.py) which can be used to control 8x8 LED matrixes with the MAX7219 chipset.
--&gt;
&lt;p&gt;Start &lt;strong&gt;rshell&lt;/strong&gt; if you are not already in it -- look for the green prompt. Enter &lt;code&gt;boards&lt;/code&gt; at the prompt to get a list of connected boards.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;pyboard @ COM4 connected Epoch: 1970 Dirs:
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;To see the directory contents of the &lt;code&gt;pyboard&lt;/code&gt; device, you can enter:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;ls /pyboard
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You &lt;em&gt;should&lt;/em&gt; see nothing listed. The path &lt;code&gt;/pyboard&lt;/code&gt; works like a &lt;em&gt;virtual folder&lt;/em&gt; meaning you can copy files to this
location to have the uploaded to your Pico. It is only available by a &lt;em&gt;pyboard&lt;/em&gt; is connected. To upload a file, we
copy it to this location. Enter the following at the prompt.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;cp picoled.py /pyboard/picoled.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;After you press Enter you'll see a message confirming the copy is taking place&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;C:\Users\Gebruiker&amp;gt; cp picoled.py /pyboard
Copying 'C:\Users\Gebruiker/picoled.py' to '/pyboard/picoled.py' ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Once the copy is complete, run &lt;code&gt;boards&lt;/code&gt; again at the prompt and you'll see the file listed after the &lt;code&gt;Dirs:&lt;/code&gt; section, showing that it's on the board.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;C:\Users\Gebruiker&amp;gt; boards
pyboard @ COM4 connected Epoch: 1970 Dirs: /picoled.py /pyboard/picoled.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can also enter &lt;code&gt;ls /pyboard&lt;/code&gt; to see the listing directly.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;C:\Users\Gebruiker&amp;gt; ls /pyboard
picoled.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  If you ever need to upload multiple files, just repeat the upload steps until everything is where it needs to be. You can always drop in and out of the REPL to make sure things work.&lt;/p&gt;
&lt;h2 id="using-uploaded-libraries"&gt;Using uploaded libraries&lt;/h2&gt;
&lt;p&gt;Now we've uploaded our library, we can use it from the REPL. To get to the MicroPython REPL enter the &lt;code&gt;repl&lt;/code&gt; command in &lt;code&gt;rshell&lt;/code&gt; as before.
To use the library we uploaded, we can import it, just like any other Python library.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;MicroPython v1.14 on 2021-02-14; Raspberry Pi Pico with RP2040
Type "help()" for more information.
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; import picoled
&amp;gt;&amp;gt;&amp;gt; picoled.ledon(65535)
&amp;gt;&amp;gt;&amp;gt; picoled.ledon(30000)
&amp;gt;&amp;gt;&amp;gt; picoled.ledon(20000)
&amp;gt;&amp;gt;&amp;gt; picoled.ledon(10000)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Or to pulse the brightness of the LED...&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; import picoled
&amp;gt;&amp;gt;&amp;gt; import time
&amp;gt;&amp;gt;&amp;gt; while True:
...     for a in range(0, 65536, 10000):
...         picoled.ledon(a)
...         time.sleep(0.1)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;video class="" controls="true" loop="" style="max-width: 100%; min-height: 546px;"&gt;&lt;source src="//i.imgur.com/vXiiuwM.mp4" type="video/mp4"/&gt;&lt;/video&gt;
&lt;h2 id="auto-running-python"&gt;Auto-running Python&lt;/h2&gt;
&lt;p&gt;So far we've been uploading code and running it manually, but once you start building projects you'll want your code
to run automatically.&lt;/p&gt;
&lt;p&gt;When it starts, MicroPython runs two scripts by default: &lt;code&gt;boot.py&lt;/code&gt; and &lt;code&gt;main.py&lt;/code&gt;, in that order. By uploading your own
script with the name &lt;code&gt;main.py&lt;/code&gt; it will run automatically every time the Raspberry Pico starts.&lt;/p&gt;
&lt;p&gt;Let's update our "library" to become an auto script that runs at startup. Save the following code to a script named &lt;code&gt;main.py&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from machine import Pin, PWM
from time import sleep

led = PWM(Pin(25))

def ledon(brightness=65535):
    led.duty_u16(brightness)


while True:
    for a in range(0, 65536, 10000):
        ledon(a)
        sleep(0.1)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In &lt;strong&gt;rshell&lt;/strong&gt; run the command to copy the file to &lt;code&gt;main.py&lt;/code&gt; on the board.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;cp main.py /pyboard/main.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-important"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation"&gt;&lt;/i&gt;&lt;/span&gt;  Don't copy this file to &lt;code&gt;boot.py&lt;/code&gt; -- the loop will block the REPL startup and you won't be able to connect to your Pico to delete it again!
If you &lt;em&gt;do&lt;/em&gt; this, use the &lt;a href="https://www.raspberrypi.org/documentation/pico/getting-started/"&gt;Resetting Flash memory&lt;/a&gt; instructions to clear your Pico. You will need to re-install MicroPython afterwards.&lt;/p&gt;
&lt;p&gt;Once the &lt;code&gt;main.py&lt;/code&gt; file is uploaded, restart your Pico -- either unplug and re-plug it, or press &lt;code&gt;Ctrl-D&lt;/code&gt; in the REPL -- and the LED will start pulsing automatically.
The script will continue running until it finishes, or the Pico is reset. You can replace the &lt;code&gt;main.py&lt;/code&gt; script at any time to
change the behavior, or delete it with.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;rm /pyboard/main.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="whats-next"&gt;What's next?&lt;/h2&gt;
&lt;p&gt;Now you can upload libraries to your Pico you can get experimenting with the many MicroPython libraries that are available.&lt;/p&gt;
&lt;p&gt;If you're looking for some more things to do with MicroPython on your Pico, there are &lt;a href="https://github.com/raspberrypi/pico-micropython-examples"&gt;some MicroPython examples&lt;/a&gt; available from Raspberry Pi themselves, and also &lt;a href="http://docs.micropython.org/en/latest/index.html"&gt;the MicroPython documentation&lt;/a&gt; for language/API references.&lt;/p&gt;</content><category term="python"/><category term="micropython"/><category term="electronics"/></entry><entry><title>3-axis Accelerometer-Gyro — Measuring acceleration and orientation with an MPU6050</title><link href="https://www.martinfitzpatrick.com/3-axis-gyro-micropython/" rel="alternate"/><published>2019-01-01T08:00:00+00:00</published><updated>2019-01-01T08:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2019-01-01:/3-axis-gyro-micropython/</id><summary type="html">Measuring acceleration and rotation has a lot of useful applications, from drone or rocket stablisation to making physically interactive handheld games.</summary><content type="html">&lt;p&gt;Measuring acceleration and rotation has a lot of useful applications, from drone or rocket stablisation to making physically interactive handheld games.&lt;/p&gt;
&lt;p&gt;An accelerometer measures &lt;em&gt;proper&lt;/em&gt; acceleration, meaning the rate of change of velocity relative to it's own &lt;em&gt;rest frame&lt;/em&gt;. This is in contrast to &lt;em&gt;coordinate&lt;/em&gt; acceleration, which is relative to a fixed coordinate system. The practical upshot of this is that at rest on Earth an accelerometer will measure acceleration due to the Earth's gravity, of &lt;em&gt;g&lt;/em&gt; &amp;asymp; 9.81 m/s. An accelerometer in freefall will measure zero. This can be adjusted for with calibration.&lt;/p&gt;
&lt;p&gt;A gyroscope (from Ancient Greek &amp;gamma;ῦ&amp;rho;&amp;omicron;&amp;sigmaf; "circle" and &amp;sigma;&amp;kappa;&amp;omicron;&amp;pi;έ&amp;omega; "to look") in contrast measures orientation and &lt;em&gt;angular&lt;/em&gt; velocity, or rotation around an an axis. Angular velocity will always be zero at rest.&lt;/p&gt;
&lt;p&gt;The availability of cheap single-chip accelerometer-gyroscope packages makes them practical for any project.&lt;/p&gt;
&lt;h2 id="mpu6050"&gt;MPU6050&lt;/h2&gt;
&lt;p&gt;The MPU6050 is a nifty little 3-axis accelerometer and gyro package, providing measurements for acceleration along and rotation around 3 axes.
It also contains an inbuilt temperature sensor. There are 4 configurable ranges for the gyro and accelerometer, meaning it can be used for both micro and macro measurements. Communication is via a simple I2C interface.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align="right"&gt;Gyro Full Scale Range (&amp;deg;/sec)&lt;/th&gt;
&lt;th align="right"&gt;Gyro Sensitivity (LSB/&amp;deg;/sec)&lt;/th&gt;
&lt;th align="right"&gt;Gyro Rate Noise (dps/&amp;radic;Hz)&lt;/th&gt;
&lt;th align="right"&gt;Accel Full Scale Range (g)&lt;/th&gt;
&lt;th align="right"&gt;Accel Sensitivity (LSB/g)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align="right"&gt;&amp;plusmn;250&lt;/td&gt;
&lt;td align="right"&gt;131&lt;/td&gt;
&lt;td align="right"&gt;0.005&lt;/td&gt;
&lt;td align="right"&gt;&amp;plusmn;2&lt;/td&gt;
&lt;td align="right"&gt;16384&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;&amp;plusmn;500&lt;/td&gt;
&lt;td align="right"&gt;65.5&lt;/td&gt;
&lt;td align="right"&gt;0.005&lt;/td&gt;
&lt;td align="right"&gt;&amp;plusmn;4&lt;/td&gt;
&lt;td align="right"&gt;8192&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;&amp;plusmn;1000&lt;/td&gt;
&lt;td align="right"&gt;32.8&lt;/td&gt;
&lt;td align="right"&gt;0.005&lt;/td&gt;
&lt;td align="right"&gt;&amp;plusmn;8&lt;/td&gt;
&lt;td align="right"&gt;4096&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;&amp;plusmn;2000&lt;/td&gt;
&lt;td align="right"&gt;16.4&lt;/td&gt;
&lt;td align="right"&gt;0.005&lt;/td&gt;
&lt;td align="right"&gt;&amp;plusmn;16&lt;/td&gt;
&lt;td align="right"&gt;2048&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;See the &lt;a href="https://www.invensense.com/products/motion-tracking/6-axis/mpu-6050/"&gt;full MPU-6050 Product Specificiation&lt;/a&gt;.&lt;/p&gt;
&lt;div class="requirements"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th colspan="2"&gt;Requirements&lt;/th&gt;
&lt;th colspan="2"&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wemos D1 &lt;span&gt;v2.2+ or good imitations.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/wemosd1" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3-axis Gyroscope  &lt;span&gt;Based on MPU6050 chip&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/3axisgyrosmpu6050" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Breadboard &lt;span&gt;Any size will do.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/oledi2c128x32" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wires &lt;span&gt;Loose ends, or jumper leads.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2 id="setting-up"&gt;Setting up&lt;/h2&gt;
&lt;p&gt;The MPU-6050 provides an I2C interface for communication. There are Python libraries available which simplify the communication further and return the measurements in a simple format. The examples here are using &lt;a href="https://github.com/adamjezek98/MPU6050-ESP8266-MicroPython"&gt;this MPU6050 library&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can download the &lt;a href="https://github.com/adamjezek98/MPU6050-ESP8266-MicroPython/blob/master/mpu6050.py"&gt;mpu6050.py&lt;/a&gt; file directly.
Click &lt;em&gt;Raw&lt;/em&gt; format and save the file with a &lt;code&gt;.py&lt;/code&gt; extension.
Upload the file to your device using &lt;a href="https://github.com/adafruit/ampy"&gt;ampy&lt;/a&gt; tool (or the WebREPL):&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;ampy --port /dev/tty.wchusbserial141120 put mpu6050.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With the &lt;code&gt;mpu6050.py&lt;/code&gt; file on your Wemos D1, you can import it as any other Python module. Connect to your device,
and then in the REPL enter:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from machine import I2C, Pin
import mpu6050
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If the &lt;code&gt;import mpu6050&lt;/code&gt; succeeds, the package is correctly uploaded and you're good to go.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Wiring for 3-axis Gyro with Wemos D1" src="https://www.martinfitzpatrick.com/static/tutorials/3-axis-gyro-micropython/gyro-wemos-d1.jpg"  loading="lazy" width="1064" height="752"/&gt;&lt;/p&gt;
&lt;p&gt;Wire up the MPU6050, connecting pins &lt;code&gt;D1&lt;/code&gt; to &lt;code&gt;SCL&lt;/code&gt; and &lt;code&gt;D2&lt;/code&gt; to &lt;code&gt;SDA&lt;/code&gt;. Provide power from &lt;code&gt;G&lt;/code&gt; and &lt;code&gt;5V&lt;/code&gt;. The light on the MPU6050 should light up once it's active.&lt;/p&gt;
&lt;h2 id="reading-values"&gt;Reading values&lt;/h2&gt;
&lt;p&gt;With the &lt;code&gt;mpu6050&lt;/code&gt; Python library on your device, and the MPU6050 module wired up, you can connect to the shell and start talking to it.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from machine import I2C, Pin
import mpu6050

i2c = I2C(scl=Pin(5), sda=Pin(4))
accel = mpu6050.accel(i2c)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With the &lt;code&gt;accel&lt;/code&gt; object, we can read values from the sensor with &lt;code&gt;.get_values()&lt;/code&gt;. This will return a dictionary of measurements from the sensor.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; accel.get_values()
{'GyZ': -46, 'GyY': -135, 'GyX': -1942, 'Tmp': 26.7888, 'AcZ': 24144, 'AcY': 68, 'AcX': -1004}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;There are 3 sets of measurements returned from the sensor &amp;mdash;&amp;nbsp;&lt;em&gt;acceleration&lt;/em&gt;, &lt;em&gt;rotation&lt;/em&gt; (gyration) and &lt;em&gt;temperature&lt;/em&gt;. The &lt;em&gt;acceleration&lt;/em&gt; and &lt;em&gt;rotation&lt;/em&gt; measurements provide 3 values each, one for each of the axes (X, Y, Z).&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align="left"&gt;Measurement&lt;/th&gt;
&lt;th align="left"&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align="left"&gt;AcX&lt;/td&gt;
&lt;td align="left"&gt;Acceleration &lt;em&gt;along&lt;/em&gt; X axis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;AcY&lt;/td&gt;
&lt;td align="left"&gt;Acceleration &lt;em&gt;along&lt;/em&gt; Y axis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;AcZ&lt;/td&gt;
&lt;td align="left"&gt;Acceleration &lt;em&gt;along&lt;/em&gt; Z axis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;GyX&lt;/td&gt;
&lt;td align="left"&gt;Rotation &lt;em&gt;around&lt;/em&gt; X axis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;GyY&lt;/td&gt;
&lt;td align="left"&gt;Rotation &lt;em&gt;around&lt;/em&gt; Y axis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;GyZ&lt;/td&gt;
&lt;td align="left"&gt;Rotation &lt;em&gt;around&lt;/em&gt; Z axis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;Tmp&lt;/td&gt;
&lt;td align="left"&gt;Temperature &amp;deg;C&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  The direction of the X and Y axes relative to the sensor are shown on the module itself. But you can always just adjust your code by trial and error.&lt;/p&gt;
&lt;h2 id="smoothing"&gt;Smoothing&lt;/h2&gt;
&lt;p&gt;If you repeatedly read measurements from the sensor in this way you'll notice that they're bouncing all over the place. This is normal for analog sensors. Before we can use the measurements from the sensor, we need to &lt;em&gt;smooth out&lt;/em&gt; these random fluctuations to leave us with real representative data.&lt;/p&gt;
&lt;p&gt;A simple way to do this is to read multiple values and take the mean (or median) of all the values. The sensor returns multiple values, so we need to average all of these individually.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;def get_smoothed_values(n_samples=10):
    """
    Get smoothed values from the sensor by sampling
    the sensor `n_samples` times and returning the mean.
    """
    result = {}
    for _ in range(n_samples):
        data = accel.get_values()

        for k in data.keys():
            # Add on value / n_samples (to generate an average)
            # with default of 0 for first loop.
            result[k] = result.get(k, 0) + (data[k] / n_samples)

    return result
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Running the above function will sample the sensor ten times, and return the averaged values.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; accel.get_values()
{'GyZ': -46, 'GyY': -135, 'GyX': -1942, 'Tmp': 26.7888, 'AcZ': 24144, 'AcY': 68, 'AcX': -1004}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you repeatedly run this you should find the samples have stabilised quite a bit. As the values are in the range &amp;plusmn;32768 the above measurements are actually &lt;em&gt;pretty close&lt;/em&gt; to zero,
though &lt;em&gt;Z acceleration&lt;/em&gt; is notably higher. &lt;code&gt;AcZ&lt;/code&gt; is the acceleration measurement in the Z (straight-up) axis and this value
is the acceleration due to gravity of &lt;em&gt;g&lt;/em&gt; &amp;asymp; 9.81 m/s.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  The measured value 24144/16384 (at lowest measurement sensitivity) gives 1.47&lt;em&gt;g&lt;/em&gt; so it's still off a bit.&lt;/p&gt;
&lt;p&gt;The other offsets at rest are just inherent errors in the chip (individual, not the model), they're not interesting. To get our measurements centred around zero we can must identify this bias and adjust for it through &lt;em&gt;calibration&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="calibration"&gt;Calibration&lt;/h2&gt;
&lt;p&gt;If we take a number of repeated sensor measurements over time we can determine the standard, or average, deviation
from zero over time. This offset can then be subtracted from future measurements to correct them. The device must
be at rest and not changing for this to work reliably.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;def calibrate(threshold=50, n_samples=100):
    """
    Get calibration date for the sensor, by repeatedly measuring
    while the sensor is stable. The resulting calibration
    dictionary contains offsets for this sensor in its
    current position.
    """
    while True:
        v1 = get_accel(n_samples)
        v2 = get_accel(n_samples)
        # Check all consecutive measurements are within
        # the threshold. We use abs() so all calculated
        # differences are positive.
        if all(abs(v1[k] - v2[k]) &amp;lt; threshold for k in v1.keys()):
            return v1  # Calibrated.
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  The &lt;code&gt;all(abs(v1[k] - v2[k]) &amp;lt; threshold for key in v1.keys())&lt;/code&gt; line is a bit of a beast. It iterates all the keys in our &lt;code&gt;v1&lt;/code&gt; dictionary, testing &lt;code&gt;abs(v1[k] - v2[k])&lt;/code&gt; for each. Here &lt;code&gt;abs()&lt;/code&gt; gives us the &lt;em&gt;absolute&lt;/em&gt; or positive difference, so we don't need to compare against negative &lt;code&gt;threshold&lt;/code&gt;. Finally, &lt;code&gt;all()&lt;/code&gt; tests that this is true for every key we've iterated.&lt;/p&gt;
&lt;p&gt;Run this &lt;code&gt;calibrate()&lt;/code&gt; function and wiggle the sensor around. You will see the device remain in the calibrating state, with the light flashing, while you wiggle it.&lt;/p&gt;
&lt;p&gt;This is because while the device is moving, the difference between consecutive measurements will be &lt;em&gt;greater&lt;/em&gt; than the defined threshold.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  In the above calibration method we're testing all measurements from the sensor. You could of course only test some of them &amp;mdash;&amp;nbsp;e.g. only gyro or acceleration &amp;mdash; depending on what you're using.&lt;/p&gt;
&lt;p&gt;If you place your sensor onto the table, the calibration test will pass and the function will return values in the same format as for &lt;code&gt;.get_values()&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; calibrate()
{'GyZ': -46, 'GyY': -115, 'GyX': -1937, 'Tmp': 26.8359, 'AcZ': 23960, 'AcY': 44, 'AcX': -872}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The output dictionary of base measurements can be used to adjust subsequent measurements to remove this offset and recalibrate to zero at rest.&lt;/p&gt;
&lt;p&gt;Below is an updated &lt;code&gt;get_smoothed_values&lt;/code&gt; function which removes the calibrated offset before returning the smoothed data.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;def get_smoothed_values(n_samples=10, calibration=None):
    """
    Get smoothed values from the sensor by sampling
    the sensor `n_samples` times and returning the mean.

    If passed a `calibration` dictionary, subtract these
    values from the final sensor value before returning.
    """
    result = {}
    for _ in range(n_samples):
        data = accel.get_values()

        for k in data.keys():
            # Add on value / n_samples to produce an average
            # over n_samples, with default of 0 for first loop.
            result[k] = result.get(k, 0) + (data[k] / n_samples)

    if calibration:
        # Remove calibration adjustment.
        for k in calibration.keys():
            result[k] -= calibration[k]

    return result
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The following short snippet will allow you to see a table of the gyro and acceleration measurements in (very smoothed) real-time. The numbers are padded to stop them bouncing around as they change, and it uses control-characters to clear the terminal.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;calibration = calibrate()
while True:
    data = get_smoothed_values(n_samples=100, calibration=calibration)
    print(
        '\t'.join('{0}:{1:&amp;gt;10.1f}'.format(k, data[k])
        for k in sorted(data.keys())),
    end='\r')
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Running this you should see something like the following at rest:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;AcX:     -17.7  AcY:      -3.2  AcZ:      -4.2  GyX:      -1.3  GyY:       1.9  GyZ:       1.8  Tmp:       0.0
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you pick up the sensor, you should see the &lt;code&gt;Z&lt;/code&gt; acceleration increase, or decrease as you drop it. The &lt;code&gt;X&lt;/code&gt; and &lt;code&gt;Y&lt;/code&gt; acceleration should increase/decrease if you tilt the device in any direction. The acceleration measured here is acceleration due to gravity &amp;mdash; if you tilt the device so the X axis is pointing straight down, all of &lt;em&gt;g&lt;/em&gt; (&amp;asymp; 9.81 m/s) will be acting through X, and none through &lt;code&gt;Z&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;AcX:   17679.9  AcY:     233.8  AcZ:  -16332.6  GyX:       3.9  GyY:      32.5  GyZ:       1.0  Tmp:      -0.2
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Gyroscopic measurements show rotation around the relevant axis, so will always be zero at rest, but increase with rotational speed in each axis. For example if you rotate the device away from you, you should see a spike in the &lt;code&gt;Y&lt;/code&gt; gyroscopic value, which returns to zero as the unit comes to a rest.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;AcX:   -6540.4  AcY:      22.4  AcZ:   -1930.1  GyX:    -116.7  GyY:     748.9  GyZ:     211.3  Tmp:       0.0
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you pop your finger on the chip, you should also see the temperature raise very slightly.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;AcX:    -547.8  AcY:      29.8  AcZ:     -36.0  GyX:      -6.9  GyY:       8.9  GyZ:      -3.8  Tmp:       0.3
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This covers the basic work of interfacing with an MPU6050 from MicroPython. I'll be adding some projects using this
chip shortly.&lt;/p&gt;</content><category term="python"/><category term="micropython"/></entry><entry><title>Dictionary Views &amp; Set Operations — Working with dictionary view objects</title><link href="https://www.martinfitzpatrick.com/python-dictionary-sets/" rel="alternate"/><published>2018-09-30T06:00:00+00:00</published><updated>2018-09-30T06:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2018-09-30:/python-dictionary-sets/</id><summary type="html">The &lt;em&gt;keys&lt;/em&gt;, &lt;em&gt;values&lt;/em&gt; and &lt;em&gt;items&lt;/em&gt; from a dictionary can be accessed using the &lt;code&gt;.keys()&lt;/code&gt;, &lt;code&gt;.values()&lt;/code&gt; and &lt;code&gt;.items()&lt;/code&gt; methods. These methods return &lt;em&gt;view objects&lt;/em&gt; which provide a view on the source dictionary.</summary><content type="html">&lt;p&gt;The &lt;em&gt;keys&lt;/em&gt;, &lt;em&gt;values&lt;/em&gt; and &lt;em&gt;items&lt;/em&gt; from a dictionary can be accessed using the &lt;code&gt;.keys()&lt;/code&gt;, &lt;code&gt;.values()&lt;/code&gt; and &lt;code&gt;.items()&lt;/code&gt; methods. These methods return &lt;em&gt;view objects&lt;/em&gt; which provide a view on the source dictionary.&lt;/p&gt;
&lt;p&gt;The view objects &lt;code&gt;dict_keys&lt;/code&gt; and &lt;code&gt;dict_items&lt;/code&gt; support &lt;code&gt;set&lt;/code&gt;-like operations (the latter only when all values are hashable) which can be used to combine and filter dictionary elements.&lt;/p&gt;
&lt;h2 id="keys"&gt;Keys&lt;/h2&gt;
&lt;p&gt;Dictionary keys are &lt;em&gt;always&lt;/em&gt; hashable, so &lt;code&gt;set&lt;/code&gt; operations are always available on the &lt;code&gt;dict_keys&lt;/code&gt; view object.&lt;/p&gt;
&lt;h3&gt;All keys (&lt;code&gt;set&lt;/code&gt; union)&lt;/h3&gt;
&lt;p&gt;To get all &lt;em&gt;keys&lt;/em&gt; from multiple dictionaries, you can use the &lt;code&gt;set&lt;/code&gt; union.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d1.keys() | d2.keys()
{'key5', 'key3', 'key2', 'key1'}  #&amp;nbsp;this is a set, not a dict
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  You can use the same approach to combine &lt;code&gt;dict_items&lt;/code&gt; and merge dictionaries.&lt;/p&gt;
&lt;h3&gt;Keys in common (&lt;code&gt;set&lt;/code&gt; intersection)&lt;/h3&gt;
&lt;p&gt;The &lt;em&gt;keys&lt;/em&gt; common to two dictionaries can be determined using &lt;code&gt;set&lt;/code&gt; intersection (&lt;code&gt;&amp;amp;&lt;/code&gt;).&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d1.keys() &amp;amp; d2.keys()
{'key3'}    #&amp;nbsp;this is a set, not a dictionary
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You could use the resulting &lt;code&gt;set&lt;/code&gt; to filter your dictionary using a dictionary comprehension.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; {k:d1[k] for k in keys}
{'key2':'value2', 'key1':'value1'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Unique keys (&lt;code&gt;set&lt;/code&gt; difference)&lt;/h3&gt;
&lt;p&gt;To retrieve keys unique to a given dictionary, you can use &lt;code&gt;set&lt;/code&gt; difference (&lt;code&gt;-&lt;/code&gt;). Keys from the right hand &lt;code&gt;dict_keys&lt;/code&gt; are removed from the left, resulting in a &lt;code&gt;set&lt;/code&gt; of the remaining keys.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d1.keys() - d2.keys()
{'key1', 'key2'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Unique keys from both (&lt;code&gt;set&lt;/code&gt; symmetric difference)&lt;/h3&gt;
&lt;p&gt;If you want items unique to &lt;em&gt;both&lt;/em&gt; dictionaries, the &lt;code&gt;set&lt;/code&gt; symmetric difference (&lt;code&gt;^&lt;/code&gt;) returns this. The result is items unique to both the left and right hand of the comparison.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d1.keys() ^ d2.keys()
{'key5', 'key2', 'key1'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="items"&gt;Items&lt;/h2&gt;
&lt;p&gt;If both the keys &lt;em&gt;and&lt;/em&gt; values of a dictionary are hashable, the &lt;code&gt;dict_items&lt;/code&gt; view will support &lt;code&gt;set&lt;/code&gt;-like operations.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  If the values are &lt;em&gt;not&lt;/em&gt; hashable all of these2 operations will all raise a &lt;code&gt;TypeError&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Merge (&lt;code&gt;set&lt;/code&gt; union)&lt;/h3&gt;
&lt;p&gt;You can use &lt;code&gt;set&lt;/code&gt; &lt;em&gt;union&lt;/em&gt; operations to merge two dictionaries.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}
&amp;gt;&amp;gt;&amp;gt; d3 = {'key4':'value4', 'key6':'value6'}

&amp;gt;&amp;gt;&amp;gt; d = dict(d1.items() | d2.items() | d3.items())
&amp;gt;&amp;gt;&amp;gt; d
{'key1':'value1', 'key2':'value2', 'key3':'value3-new', 'key5':'value5', 'key4':'value4', 'key6':'value6'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Since it is quite common for dictionary values to &lt;em&gt;not&lt;/em&gt; be hashable, you will probably want to use &lt;a href="/article/python-dictionaries"&gt;one of the other approaches for merging dictionaries&lt;/a&gt; instead.&lt;/p&gt;
&lt;h3&gt;Common entries (&lt;code&gt;set&lt;/code&gt; intersection)&lt;/h3&gt;
&lt;p&gt;The &lt;em&gt;items&lt;/em&gt; common to two dictionaries can be determined using &lt;code&gt;set&lt;/code&gt; intersection (&lt;code&gt;&amp;amp;&lt;/code&gt;). Both the key &lt;em&gt;and&lt;/em&gt; value must match &amp;mdash; items are compared as &lt;code&gt;(key, value)&lt;/code&gt; tuples.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key1':'value1', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d1.items() &amp;amp; d2.items()
{('key1', 'value1')}    #&amp;nbsp;this is a set, not a dict
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Unique entries (&lt;code&gt;set&lt;/code&gt; difference)&lt;/h3&gt;
&lt;p&gt;To retrieve items unique to a given dictionary, you can use &lt;code&gt;set&lt;/code&gt; difference (&lt;code&gt;-&lt;/code&gt;). Items from the right hand &lt;code&gt;dict_keys&lt;/code&gt; are removed from the left, resulting in a &lt;code&gt;set&lt;/code&gt; of the remaining item &lt;code&gt;(key, value)&lt;/code&gt; tuples.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d1.items() - d2.items()
{('key3', 3), ('key2', 'value2'), ('key1', 'value1')}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Unique entries from both (&lt;code&gt;set&lt;/code&gt; symmetric difference)&lt;/h3&gt;
&lt;p&gt;If you want items unique to &lt;em&gt;both&lt;/em&gt; dictionaries, the &lt;code&gt;set&lt;/code&gt; symmetric difference (&lt;code&gt;^&lt;/code&gt;) returns this. The result is item &lt;code&gt;(key, value)&lt;/code&gt; tuples unique to both the left and right hand of the comparison.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d1.items() ^ d2.items()
{('key2', 'value2'), ('key5', 'value5'), ('key1', 'value1'), ('key3', 3), ('key3', 'value3-new')}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</content><category term="python"/></entry><entry><title>Displaying images on OLED screens — Using 1-bpp images in MicroPython</title><link href="https://www.martinfitzpatrick.com/displaying-images-oled-displays/" rel="alternate"/><published>2018-08-27T16:00:00+00:00</published><updated>2018-08-27T16:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2018-08-27:/displaying-images-oled-displays/</id><summary type="html">We've previously covered the &lt;a href="/oled-displays-i2c-micropython/"&gt;basics of driving OLED I2C displays from MicroPython&lt;/a&gt;, including simple graphics commands and text. Here we look at displaying monochrome 1 bit-per-pixel images and animations using MicroPython on a Wemos D1.</summary><content type="html">&lt;p&gt;We've previously covered the &lt;a href="/oled-displays-i2c-micropython/"&gt;basics of driving OLED I2C displays from MicroPython&lt;/a&gt;, including simple graphics commands and text. Here we look at displaying monochrome 1 bit-per-pixel images and animations using MicroPython on a Wemos D1.&lt;/p&gt;
&lt;p&gt;Processing the images and correct choice of image-formats is important to get the most detail, and to not run out of memory.&lt;/p&gt;
&lt;div class="requirements"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th colspan="2"&gt;Requirements&lt;/th&gt;
&lt;th colspan="2"&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wemos D1 &lt;span&gt;v2.2+ or good imitations.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/wemosd1" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.96in OLED Screen  &lt;span&gt;128x64 pixels, I2c interface.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/oledi2c128x64" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Breadboard &lt;span&gt;Any size will do.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://amzn.to/2HB39F1" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wires &lt;span&gt;Loose ends, or jumper leads.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2 id="setting-up"&gt;Setting up&lt;/h2&gt;
&lt;p&gt;The display communicates over I2C, but we need a driver to interface with it. You can import &lt;code&gt;ssd1306&lt;/code&gt; as any other Python module. Connect to your device,
and then in the REPL enter:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from machine import I2C, Pin
import ssd1306
import framebuf
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If the &lt;code&gt;import ssd1306&lt;/code&gt; succeeds, the package is correctly uploaded and you're good to go.&lt;/p&gt;
&lt;p&gt;Wire up the OLED display, connecting pins &lt;code&gt;D1&lt;/code&gt; to &lt;code&gt;SCL&lt;/code&gt; and &lt;code&gt;D2&lt;/code&gt; to &lt;code&gt;SDA&lt;/code&gt;.
Provide power from &lt;code&gt;G&lt;/code&gt; and &lt;code&gt;5V&lt;/code&gt;. The display below is a 2-colour version, where the top 1/4 of the pixels are yellow, while the rest is blue. They're intended for mobile screens, but it looks kind of neat with Scatman.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The circuit" src="https://www.martinfitzpatrick.com/static/tutorials/displaying-images-oled-displays/image-wemos-circuit.jpg"  loading="lazy" width="961" height="516"/&gt;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;i2c = I2C(-1, Pin(5), Pin(4))
display = ssd1306.SSD1306_I2C(128, 64, i2c)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  If your display is a different size just fiddle the numbers above. You'll need to change some parameters on loops later too.&lt;/p&gt;
&lt;p&gt;To test the display is working, let's set all the pixels to on and show it.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;display.fill(1)
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The screen should light up completely. If it doesn't, something is wrong.&lt;/p&gt;
&lt;h2 id="image-processing"&gt;Image Processing&lt;/h2&gt;
&lt;p&gt;To display an image on a 1-bit per pixel monochrome display we need to get our image into the same format. The best way to do this is using image manipulation software, such as Photoshop or GIMP. These allow you to down-sample the image to monochrome while maintaining detail by adding dither or other adjustments.&lt;/p&gt;
&lt;p&gt;The first step is to crop the image down to the correct dimensions &amp;mdash; the display used here is 128x64 pixels. To preserve as much of the image as possible you might find it useful to &lt;em&gt;resize&lt;/em&gt; the larger axis to the max (e.g. if the image is wider than high, resize the width to 128 pixels). Then &lt;em&gt;crop&lt;/em&gt; the remaining axis.&lt;/p&gt;
&lt;p&gt;You can convert images to 1-bit-per-pixel in GIMP through the Image -&amp;gt; Mode -&amp;gt; Indexed... dialog.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Convert image to Indexed 1bpp" src="https://www.martinfitzpatrick.com/static/tutorials/displaying-images-oled-displays/convert-image-to-indexed.png"  loading="lazy" width="498" height="506"/&gt;&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  If you're image is already in an indexed format this won't be available. So convert back to RGB/Grayscale first, then re-select Image -&amp;gt; Mode -&amp;gt; Indexed.&lt;/p&gt;
&lt;p&gt;Select "Use black and white (1-bit) palette" to enable 1bpp mode. The colour dithering settings are best chosen by trial and error depending on the image being converted although turning off dithering entirely is often best for images of solid colour blocks (e.g. logos).&lt;/p&gt;
&lt;p&gt;Once the imagine is converted to black &amp;amp; white you can save to file. There are two good options for saving 1bpp images &amp;mdash; PBM and PGM. PBM is a 1 bit-per-pixel format, while PGM is grayscale 1 byte per pixel.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align="left"&gt;Type&lt;/th&gt;
&lt;th align="left"&gt;Magic number (ASCII)&lt;/th&gt;
&lt;th align="left"&gt;Magic number (Binary)&lt;/th&gt;
&lt;th align="left"&gt;Extension&lt;/th&gt;
&lt;th align="left"&gt;Colors&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align="left"&gt;Portable BitMap&lt;/td&gt;
&lt;td align="left"&gt;P1&lt;/td&gt;
&lt;td align="left"&gt;P4&lt;/td&gt;
&lt;td align="left"&gt;.pbm&lt;/td&gt;
&lt;td align="left"&gt;0&amp;ndash;1 (white &amp;amp; black)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;Portable GrayMap&lt;/td&gt;
&lt;td align="left"&gt;P2&lt;/td&gt;
&lt;td align="left"&gt;P5&lt;/td&gt;
&lt;td align="left"&gt;.pgm&lt;/td&gt;
&lt;td align="left"&gt;0&amp;ndash;255 (gray scale)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;Portable PixMap&lt;/td&gt;
&lt;td align="left"&gt;P3&lt;/td&gt;
&lt;td align="left"&gt;P6&lt;/td&gt;
&lt;td align="left"&gt;.ppm&lt;/td&gt;
&lt;td align="left"&gt;0&amp;ndash;255 (RGB)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;While PBM is clearly better suited, we can pre-process PGM down to an equivalent bit stream. Both approaches are included here, in case your software can only produce one or the other.&lt;/p&gt;
&lt;p&gt;Save as either PBM (recommended) or PGM, and select Raw mode, not ASCII.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Raw mode dialog" src="https://www.martinfitzpatrick.com/static/tutorials/displaying-images-oled-displays/export-pbm.png"  loading="lazy" width="403" height="259"/&gt;&lt;/p&gt;
&lt;h3&gt;Example images&lt;/h3&gt;
&lt;p&gt;Some example images (128x64 pixels) are shown below, in PNG format. Each of the images &lt;a href="https://download.martinfitzpatrick.com/oled-sample-images.zip"&gt;is available in this zip&lt;/a&gt; which contains PBM, PGM and PNG formats.&lt;/p&gt;
&lt;p&gt;&lt;img alt="pyMadeTHis" src="https://www.martinfitzpatrick.com/static/tutorials/displaying-images-oled-displays/pymadethis.png"  loading="lazy" width="128" height="64"/&gt;
&lt;img alt="Alan Partridge" src="https://www.martinfitzpatrick.com/static/tutorials/displaying-images-oled-displays/alan.png"  loading="lazy" width="128" height="64"/&gt;
&lt;img alt="Blackadder" src="https://www.martinfitzpatrick.com/static/tutorials/displaying-images-oled-displays/blackadder.png"  loading="lazy" width="128" height="64"/&gt;
&lt;img alt="Scatman" src="https://www.martinfitzpatrick.com/static/tutorials/displaying-images-oled-displays/scatman.png"  loading="lazy" width="128" height="64"/&gt;&lt;/p&gt;
&lt;h2 id="portable-bitmap-format"&gt;Portable Bitmap Format&lt;/h2&gt;
&lt;p&gt;Portable Bitmap Format (PBM) format consists of a regular header, separated by newlines, then the image data.  The header starts with a magic number indicating the image format and whether the format is ASCII for binary. In all examples here we're using binary since it's more compact. The second line is a comment, which is usually the program used to create it. Third are the image dimensions. Then, following a final newline, you get the image binary blob.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;P4
# CREATOR: GIMP PNM Filter Version 1.1
128 64
&amp;lt;data&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The data is stored as a 1-bit-per-pixel stream, with pixel on as &lt;code&gt;1&lt;/code&gt; pixel off as &lt;code&gt;0&lt;/code&gt;. On a normal display screen an &lt;em&gt;on&lt;/em&gt; pixel appears as black &amp;mdash;&amp;nbsp;this is different on the OLED, which we need to account for later.&lt;/p&gt;
&lt;p&gt;To upload your PBM file to the controller &amp;mdash;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;ampy --port /dev/tty.wchusbserial141120 put alan.pbm
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Loading images&lt;/h3&gt;
&lt;p&gt;The PBM data stream is already in the correct format for use. We can wrap the data in &lt;code&gt;bytearray&lt;/code&gt;, use this to create a &lt;code&gt;FrameBuffer&lt;/code&gt; and blit it immediately. However, we need to skip the header region (3x &lt;code&gt;readline&lt;/code&gt;) before reading the subsequent data block.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;with open('scatman.pbm', 'rb') as f:
    f.readline() #&amp;nbsp;Magic number
    f.readline() #&amp;nbsp;Creator comment
    f.readline() #&amp;nbsp;Dimensions
    data = bytearray(f.read())
fbuf = framebuf.FrameBuffer(data, 128, 64, framebuf.MONO_HLSB)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  We can't use &lt;code&gt;readlines()&lt;/code&gt; since the binary image data may contain ASCII code 13 (newline).&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;framebuf.MONO_HLSB&lt;/code&gt; format &lt;a href="https://docs.micropython.org/en/latest/pyboard/library/framebuf.html#framebuf.framebuf.MONO_HLSB"&gt;is desribed in the MicroPython docs as&lt;/a&gt; &amp;mdash;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Monochrome (1-bit) color format This defines a mapping where the bits in a byte are horizontally mapped. Each byte occupies 8 horizontal pixels with bit 0 being the leftmost. Subsequent bytes appear at successive horizontal locations until the rightmost edge is reached. Further bytes are rendered on the next row, one pixel lower.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This matches exactly with the format of our PBM data.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  This framebuffer format &lt;code&gt;framebuf.MONO_HLSB&lt;/code&gt; used is different to that used by the ssd1306 screen (&lt;code&gt;framebuf.MONO_VLSB&lt;/code&gt;). This is handled transparently by the framebuffer when blitting.&lt;/p&gt;
&lt;h3&gt;Displaying an image&lt;/h3&gt;
&lt;p&gt;We have the image data in &lt;code&gt;fbuf&lt;/code&gt;, which can be &lt;em&gt;blitted&lt;/em&gt; directly to our display framebuffer, using &lt;code&gt;.blit&lt;/code&gt;. This accepts coordinates at which to blit. Because the OLED screen displays inverse (on = light, off = black) we need to switch &lt;code&gt;.invert(1)&lt;/code&gt; on the display.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;display.invert(1)
display.blit(fbuf, 0, 0)
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="portable-graymap-format"&gt;Portable Graymap Format&lt;/h2&gt;
&lt;p&gt;Portable Graymap Format (PGM) format shares a similar header to PBM, again newline separated. However, there is an additional 4th header line which contains the max value &amp;mdash;&amp;nbsp;indicating the number of values between black and white. Black is again zero, max (255 here) is white.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;P5
# CREATOR: GIMP PNM Filter Version 1.1
128 64
255
&amp;lt;data&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The format uses 1 byte per pixel. This is 8x too many for our purposes, but we can process it down to 1bpp. Since we're saving a mono image each pixel will contain either &lt;code&gt;0&lt;/code&gt; (fully off) or &lt;code&gt;255&lt;/code&gt; (fully on).&lt;/p&gt;
&lt;p&gt;To upload your PGM file to the controller &amp;mdash;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;ampy --port /dev/tty.wchusbserial141120 put alan.pgm
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Loading images&lt;/h3&gt;
&lt;p&gt;Since each pixel is a single byte it is easy to iterate, though slow as hell. We opt here to turn on bright pixels, which gives us the correct output without switching the display invert on.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;with open('alan.pgm', 'rb') as f:
    f.readline() #&amp;nbsp;Magic number
    f.readline() #&amp;nbsp;Creator comment
    f.readline() #&amp;nbsp;Dimensions
    data = bytearray(f.read())

for x in range(128):
    for y in range(32):
        c = data[x + y*128]
        display.pixel(x, y, 1 if c == 255 else 0)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Packing bits&lt;/h3&gt;
&lt;p&gt;Using 1 byte per pixel wastes 7 bits which is not great, and iterating to draw the pixels is slow. If we pack the bits we can blit as we did with PBM. To do this we simply iterate over the PGM image data in blocks of 8 (8 bits=1 byte).&lt;/p&gt;
&lt;p&gt;Each iteration we create our zero'd-byte (an &lt;code&gt;int&lt;/code&gt; of 0). As we iterate over the 8 bits, we add &lt;code&gt;2**(7-n)&lt;/code&gt; if that bit should be set to &lt;em&gt;on&lt;/em&gt;. The first byte we hit sets the topmost bit, which has a value of &lt;code&gt;2**(7-0) = 2**7 = 128&lt;/code&gt;, the second &lt;code&gt;2**(7-1) = 2**6 = 64&lt;/code&gt;. The table below shows the values for each bit in a byte.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;|7| 6|5|4|3|2|1|0
|:--|:--|:--|:--|:--|:--|:--|:--|
|2^7|   2^6|2^5|2^4|2^3|2^2|2^1|2^0|
|128|   64|32|16|8|4|2|1|
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The result is a single byte with a single bit set in turn for each byte we iterated over.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;p = []
for i in range(0, len(d), 8):
    byte = 0
    for n, bit in enumerate(d[i:i+8]):
        byte += 2**(7-n) if bit == 255 else 0

    p.append(byte)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We choose to interpret the 255 values as on (the opposite as in PBM where black = on, giving an inverted image). You could of course reverse it.&lt;/p&gt;
&lt;p&gt;The variable &lt;code&gt;p&lt;/code&gt; now contains a list of &lt;code&gt;int&lt;/code&gt; values in the range 0-255 (bytes). We can cast this to a &lt;code&gt;bytearray&lt;/code&gt; and then use this create our &lt;code&gt;FrameBuffer&lt;/code&gt; object.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Create a framebuffer object
fbuf = framebuf.FrameBuffer(bytearray(p), 128, 64, framebuf.MONO_HLSB)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;framebuf.MONO_HLSB&lt;/code&gt; format &lt;a href="https://docs.micropython.org/en/latest/pyboard/library/framebuf.html#framebuf.framebuf.MONO_HLSB"&gt;is desribed in the MicroPython docs as&lt;/a&gt; &amp;mdash;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Monochrome (1-bit) color format This defines a mapping where the bits in a byte are horizontally mapped. Each byte occupies 8 horizontal pixels with bit 0 being the leftmost. Subsequent bytes appear at successive horizontal locations until the rightmost edge is reached. Further bytes are rendered on the next row, one pixel lower.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This matches exactly with the format of our PGM (and bit-packed) data.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  This framebuffer format &lt;code&gt;framebuf.MONO_HLSB&lt;/code&gt; used is different to that used by the ssd1306 screen (&lt;code&gt;framebuf.MONO_VLSB&lt;/code&gt;). This is handled transparently by the framebuffer when blitting.&lt;/p&gt;
&lt;h3&gt;Packing script&lt;/h3&gt;
&lt;p&gt;A command-line packing script is given below (and &lt;a href="https://download.martinfitzpatrick.com/oled-sample-images.zip"&gt;you can download it here&lt;/a&gt;), which can be used to pack a PGM into a 1bpp bitstream. The script accepts a single filename of a PGM file to process, and outputs the resulting packed bit data as &lt;code&gt;&amp;lt;filename&amp;gt;.bin&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import os
import sys

fn = sys.argv[1]

with open(fn, 'rb') as f:
    f.readline() # Magic number
    f.readline() # Creator comment
    f.readline() # Dimensions
    f.readline() # Max value, 255
    data = bytearray(f.read())

p = []
for i in range(0, len(data), 8):
    byte = 0
    for n, bit in enumerate(data[i:i+8]):
        byte += 2**(7-n) if bit == 255 else 0

    p.append(byte)

b = bytearray(p)

basename, _ = os.path.splitext(fn)
with open('%s.bin' % basename, 'wb') as f:
    f.write(b)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The resulting file is 1KB in size, and identical to a &lt;code&gt;.pbm&lt;/code&gt; format file, minus the header and with colours inverted (this makes display simpler).&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;python pack.py scatman.1.pgm

ls -l

-rw-r--r--  1 martin  staff  1024 26 Aug 18:11 scatman.bin
-rw-r--r--  1 martin  staff  8245 26 Aug 18:02 scatman.pgm
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;To upload your BIN file to the controller &amp;mdash;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;ampy --port /dev/tty.wchusbserial141120 put scatman.bin
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Loading images&lt;/h3&gt;
&lt;p&gt;Since we've stripped off the PGM header, the resulting file can be read directly into a &lt;code&gt;bytearray&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;with open('scatman.bin', 'rb') as f:
    data = bytearray(f.read())

fbuf = framebuf.FrameBuffer(data, 128, 64, framebuf.MONO_HLSB)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The colours were inverted in our bit packer so we can just blit the framebuffer directly without inverting the display.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;display.blit(fbuf, 0, 0)
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="animation"&gt;Animation&lt;/h2&gt;
&lt;p&gt;Both the PBM and PGM images are 1KB in memory once loaded, leaving us plenty of space to load multiple images and animate them. The following loads a series of &lt;a href="https://www.youtube.com/watch?v=Hy8kmNEo1i8"&gt;Scatman John&lt;/a&gt; PBM images and animates them in a loop.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from machine import I2C, Pin
import ssd1306
import time
import framebuf

i2c = I2C(-1, Pin(5), Pin(4))
display = ssd1306.SSD1306_I2C(128, 64, i2c)

images = []
for n in range(1,7):
    with open('scatman.%s.pbm' % n, 'rb') as f:
        f.readline() #&amp;nbsp;Magic number
        f.readline() #&amp;nbsp;Creator comment
        f.readline() #&amp;nbsp;Dimensions
        data = bytearray(f.read())
    fbuf = framebuf.FrameBuffer(data, 128, 64, framebuf.MONO_HLSB)
    images.append(fbuf)

display.invert(1)
while True:
    for i in images:
        display.blit(i, 0, 0)
        display.show()
        time.sleep(0.1)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The resulting animation &amp;mdash;&lt;/p&gt;
&lt;p&gt;&lt;img alt="I'm the Scatman" src="https://i.imgur.com/z4BB9i4.gif"/&gt;&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  The image distortion is due to frame rate mismatch with the camera and won't be visible in person.&lt;/p&gt;
&lt;h2 id="optimization"&gt;Optimization&lt;/h2&gt;
&lt;p&gt;There is still plenty of room left for optimization. For static images there are often multiple consecutive blocks of bits of the same colour (think backround regions) or regular patterns (dithering). By setting aside a few bits as repeat markers we could compress these regions down to a single pattern, at the cost of random larger files for very random images and unpacking time.&lt;/p&gt;
&lt;p&gt;We could get away with a lot less data for the animation (particularly the example above) by storing only frame deltas (changes), and using key frames. But we'd also need masking, and that takes memory... and yeah. Let's not, for now.&lt;/p&gt;</content><category term="python"/><category term="micropython"/><category term="electronics"/></entry><entry><title>Dictionaries — A rather long guide to Python's key:value hash type</title><link href="https://www.martinfitzpatrick.com/python-dictionaries/" rel="alternate"/><published>2018-08-26T12:00:00+00:00</published><updated>2018-08-26T12:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2018-08-26:/python-dictionaries/</id><summary type="html">Dictionaries are key-value stores, meaning they store, and allow retrieval of data (or &lt;em&gt;values&lt;/em&gt;) through a unique key. This is analogous with a real dictionary where you look up &lt;em&gt;definitions&lt;/em&gt; (data) using a given &lt;em&gt;key&lt;/em&gt; &amp;mdash; the word. Unlike a language dictionary however, keys in Python dictionaries are &lt;em&gt;not&lt;/em&gt; alphabetically sorted.</summary><content type="html">&lt;p&gt;Dictionaries are key-value stores, meaning they store, and allow retrieval of data (or &lt;em&gt;values&lt;/em&gt;) through a unique key. This is analogous with a real dictionary where you look up &lt;em&gt;definitions&lt;/em&gt; (data) using a given &lt;em&gt;key&lt;/em&gt; &amp;mdash; the word. Unlike a language dictionary however, keys in Python dictionaries are &lt;em&gt;not&lt;/em&gt; alphabetically sorted.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  From Python 3.6 onwards dictionaries are &lt;em&gt;ordered&lt;/em&gt; in that elements are stored and retrieved in the order in which they are added. This usually only has consequences for iterating (see later).&lt;/p&gt;
&lt;p&gt;Anything which can be stored in a Python variable can be stored in a dictionary &lt;em&gt;value&lt;/em&gt;. That includes mutable types including &lt;code&gt;list&lt;/code&gt; and even &lt;code&gt;dict&lt;/code&gt; &amp;mdash; meaning you can nest dictionaries inside on another. In contrast &lt;em&gt;keys&lt;/em&gt; must be &lt;em&gt;hashable&lt;/em&gt; and immutable &amp;mdash;&amp;nbsp;the object hash must not change once calculated. This means &lt;code&gt;list&lt;/code&gt; or &lt;code&gt;dict&lt;/code&gt; objects cannot be used for dictionary &lt;em&gt;keys&lt;/em&gt;, however a &lt;code&gt;tuple&lt;/code&gt; is fine.&lt;/p&gt;
&lt;p&gt;A &lt;em&gt;hash&lt;/em&gt; is a reproducible, compact, representation of an original value. &lt;em&gt;Reproducible&lt;/em&gt; means that hashing the same input will always produce the same output. This is essential for dictionary keys where hashes are used to store and look up values: if the hash changed each time we hashed the key, we'd never find anything!&lt;/p&gt;
&lt;h2 id="creating"&gt;Creating&lt;/h2&gt;
&lt;p&gt;Dictionaries can be defined using both literal or constructor syntax. Literal syntax is a bit cleaner, but there are situations where &lt;code&gt;dict()&lt;/code&gt; is useful.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;d = {}        # An empty dictionary, using literal syntax
d = dict()    # An empty dictionary, using object syntax
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can add initial items to a dictionary by passing the key-value pairs at creation time.
The following two syntaxes are equivalent, and will produce an identical dictionary.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d = {'key1': 'value1', 'key2': 'value2', 'key3': 3}
&amp;gt;&amp;gt;&amp;gt; d
{'key1': 'value1', 'key2': 'value2', 'key3': 3}

&amp;gt;&amp;gt;&amp;gt; d = dict(key1='value1', key2='value2', key3=3)
&amp;gt;&amp;gt;&amp;gt; d
{'key1': 'value1', 'key2': 'value2', 'key3': 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;However, note that keys in the &lt;code&gt;dict&lt;/code&gt; syntax are limited to valid keyword parameter names only &amp;mdash;&amp;nbsp;for example, you cannot use anything which would not be a valid variable name (including numbers, number-initial alphanumeric names or punctuation).&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; dict(1='hello')
SyntaxError: invalid syntax

&amp;gt;&amp;gt;&amp;gt; dict(1a='hello')
SyntaxError: invalid syntax
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As always in Python, keyword parameters are interpreted as string names, ignoring any variables defined with the same name.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; a = 12345
&amp;gt;&amp;gt;&amp;gt; {a:'test'}
{12345: 'test'}

&amp;gt;&amp;gt;&amp;gt; dict(a='test')
{'a': 'test'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;For this reason &lt;code&gt;dict()&lt;/code&gt; is only really useful where you have very restricted key names. This is often the case, but you can avoid these annoyances completely by sticking with the literal &lt;code&gt;{}&lt;/code&gt; syntax.&lt;/p&gt;
&lt;h2 id="adding"&gt;Adding&lt;/h2&gt;
&lt;p&gt;You can add items to a dictionary by assigning a value to a key, using the square bracket &lt;code&gt;[]&lt;/code&gt; syntax.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d = {}
&amp;gt;&amp;gt;&amp;gt; d['this'] = 'that'
&amp;gt;&amp;gt;&amp;gt; d
{'this':'that'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Assigning to keys which already exist will replace the existing value for that key.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d = {}
&amp;gt;&amp;gt;&amp;gt; d['this'] = 'that'
&amp;gt;&amp;gt;&amp;gt; d['this'] = 'the other'
&amp;gt;&amp;gt;&amp;gt; d
{'this':'the other'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="retrieving"&gt;Retrieving&lt;/h2&gt;
&lt;p&gt;Values for a given key can be retrieved by key, using the square bracket &lt;code&gt;[]&lt;/code&gt; syntax.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d = {'key1': 'value1', 'key2': 'value2', 'key3': 3}
&amp;gt;&amp;gt;&amp;gt; d['key1']
'value1'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Retrieving an item does not remove it from the dictionary.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d
{'key1': 'value1', 'key2': 'value2', 'key3': 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The value returned is the same object stored in the dictionary, not a copy. This is important to bear in mind when using &lt;em&gt;mutable&lt;/em&gt; objects such as lists as values.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d = {'key1': [1,2,3,4]}
&amp;gt;&amp;gt;&amp;gt; l = d['key1']
&amp;gt;&amp;gt;&amp;gt; l
[1,2,3,4]

&amp;gt;&amp;gt;&amp;gt; l.pop()
4

&amp;gt;&amp;gt;&amp;gt; d
d = {'key1': [1,2,3]}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Notice that changes made to the returned &lt;code&gt;list&lt;/code&gt; continue to be reflected in the dictionary. The retrieved list and the value in the dictionary are the same object.&lt;/p&gt;
&lt;h2 id="removing"&gt;Removing&lt;/h2&gt;
&lt;p&gt;To remove an item from a dictionary you can use &lt;code&gt;del&lt;/code&gt; using square bracket syntax with the key to access the element.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d = {'key1': 'value1', 'key2': 'value2', 'key3': 3}

&amp;gt;&amp;gt;&amp;gt; del d['key1]
&amp;gt;&amp;gt;&amp;gt; d
{'key2':'value2', 'key3': 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can also remove items from a dictionary by using &lt;code&gt;.pop(&amp;lt;key&amp;gt;)&lt;/code&gt;. This removes the given key from the dictionary, and returns the value.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d = {'key1': 'value1', 'key2': 'value2', 'key3': 3}

&amp;gt;&amp;gt;&amp;gt; d.pop('key1)
'value1'

&amp;gt;&amp;gt;&amp;gt; d
{'key2':'value2', 'key3': 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="counting"&gt;Counting&lt;/h2&gt;
&lt;p&gt;The number of elements in a dictionary can be found by using &lt;code&gt;len()&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d = {'key1': 'value1', 'key2': 'value2', 'key3': 3}
&amp;gt;&amp;gt;&amp;gt; len(d)
3
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The length of a dictionaries &lt;code&gt;.keys()&lt;/code&gt;, &lt;code&gt;.values()&lt;/code&gt; and &lt;code&gt;.items()&lt;/code&gt; are always equal.&lt;/p&gt;
&lt;h2 id="view-objects"&gt;View objects&lt;/h2&gt;
&lt;p&gt;The &lt;em&gt;keys&lt;/em&gt;, &lt;em&gt;values&lt;/em&gt; and &lt;em&gt;items&lt;/em&gt; from a dictionary can be accessed using the &lt;code&gt;.keys()&lt;/code&gt;, &lt;code&gt;.values()&lt;/code&gt; and &lt;code&gt;.items()&lt;/code&gt; methods. These methods return &lt;em&gt;view objects&lt;/em&gt; which provide a view on the source dictionary.&lt;/p&gt;
&lt;p&gt;There are separate view objects for each of &lt;em&gt;keys&lt;/em&gt;, &lt;em&gt;values&lt;/em&gt; and &lt;em&gt;items&lt;/em&gt; &amp;mdash; &lt;code&gt;dict_keys&lt;/code&gt;, &lt;code&gt;dict_values&lt;/code&gt; and &lt;code&gt;dict_items&lt;/code&gt; respectively.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d = {'key1': 'value1', 'key2': 'value2', 'key3': 3}
&amp;gt;&amp;gt;&amp;gt; d.keys()
dict_keys(['key1', 'key2', 'key3'])

&amp;gt;&amp;gt;&amp;gt; d.values()
dict_values(['value1', 'value2', 3])
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;dict_items&lt;/code&gt; provides a view over tuples of &lt;code&gt;(key, value)&lt;/code&gt; pairs.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d.items()
dict_items([('key1', 'value1'), ('key2', 'value2'), ('key3', 3)])
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;These view objects are all iterable. They are also &lt;em&gt;dynamic&lt;/em&gt; &amp;mdash;&amp;nbsp;changes to the original dictionary continue to be reflected in the view after it is created.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; k = d.keys()
&amp;gt;&amp;gt;&amp;gt; k
dict_keys(['key1', 'key2', 'key3'])

&amp;gt;&amp;gt;&amp;gt; d['key4'] = 'value4'
&amp;gt;&amp;gt;&amp;gt; k
dict_keys(['key1', 'key2', 'key3', 'key4'])
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  This is different to Python 2.7, where &lt;code&gt;.keys()&lt;/code&gt;, &lt;code&gt;.values()&lt;/code&gt; and &lt;code&gt;.items()&lt;/code&gt; returned a static list.&lt;/p&gt;
&lt;h2 id="membership"&gt;Membership&lt;/h2&gt;
&lt;p&gt;To determine if a given &lt;em&gt;key&lt;/em&gt; is present in a dictionary, you can use the &lt;code&gt;in&lt;/code&gt; keyword. This will return &lt;code&gt;True&lt;/code&gt; if the give key is found, &lt;code&gt;False&lt;/code&gt; if it is not.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d = {'key1': 'value1', 'key2': 'value2', 'key3': 3}

&amp;gt;&amp;gt;&amp;gt; 'key2' in d
True

&amp;gt;&amp;gt;&amp;gt; 'key5' in d
False
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can also check whether a given &lt;em&gt;value&lt;/em&gt; or key-value pair is in a dictionary by using the &lt;code&gt;.values()&lt;/code&gt; and &lt;code&gt;.items()&lt;/code&gt; views.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; 'value1' in d.values()
True

&amp;gt;&amp;gt;&amp;gt; 'value5' in d.values()
False

&amp;gt;&amp;gt;&amp;gt; ('key1', 'value1') in d.items()
True

&amp;gt;&amp;gt;&amp;gt; ('key3', 'value5') in d.items()
False
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  These lookups are less efficient that key-based lookups on dictionaries, and needing to lookup &lt;em&gt;values&lt;/em&gt; or &lt;em&gt;items&lt;/em&gt; is often an indication that a &lt;code&gt;dict&lt;/code&gt; is not a good store for your data.&lt;/p&gt;
&lt;h2 id="lists-from-dictionaries"&gt;Lists from dictionaries&lt;/h2&gt;
&lt;p&gt;To get a &lt;code&gt;list&lt;/code&gt; of a dictionary's &lt;em&gt;keys&lt;/em&gt;, &lt;em&gt;values&lt;/em&gt; or &lt;em&gt;items&lt;/em&gt; of a dictionary to lists, we can
take the &lt;code&gt;dict_keys&lt;/code&gt;, &lt;code&gt;dict_values&lt;/code&gt; or &lt;code&gt;dict_items&lt;/code&gt; view objects and pass them to &lt;code&gt;list()&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d = {'key1': 'value1', 'key2': 'value2', 'key3': 3}

&amp;gt;&amp;gt;&amp;gt; list(d.keys())
['key1', 'key2', 'key3']

&amp;gt;&amp;gt;&amp;gt; list(d.values())
['value1', 'value2', 3]

&amp;gt;&amp;gt;&amp;gt; list(d.items())
[('key1', 'value1'), ('key2', 'value2'), ('key3', 3)]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  Converting the view objects to lists breaks the link to the original dictionary, so further updates to the dictionary will not be reflected in the list.&lt;/p&gt;
&lt;h2 id="dictionaries-from-lists"&gt;Dictionaries from lists&lt;/h2&gt;
&lt;p&gt;Similarly lists can be used to generate dictionaries. The simplest approach is using a &lt;code&gt;list&lt;/code&gt; of &lt;code&gt;2-tuple&lt;/code&gt; where the first element in the &lt;code&gt;tuple&lt;/code&gt; is used for the &lt;em&gt;key&lt;/em&gt; and the second for the &lt;em&gt;value&lt;/em&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; l = [('key1', 'value1'), ('key2', 'value2'), ('key3', 3)]
&amp;gt;&amp;gt;&amp;gt; d = dict(l) # Pass the list as to the dict constructor

&amp;gt;&amp;gt;&amp;gt; d
{'key1': 'value1', 'key2': 'value2', 'key3': 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  You can pass in other iterators, not just lists. The only restriction is that the iterator needs to return 2 items per iteration.&lt;/p&gt;
&lt;p&gt;If you have your &lt;em&gt;key&lt;/em&gt; and &lt;em&gt;value&lt;/em&gt; elements in seperate lists, you can use &lt;code&gt;zip&lt;/code&gt; to combine them together into tuples before creating the dictionary.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; keys = ['key1', 'key2', 'key3']
&amp;gt;&amp;gt;&amp;gt; vals = ['value1', 'value2', 3]

&amp;gt;&amp;gt;&amp;gt; l = zip(keys, vals)
&amp;gt;&amp;gt;&amp;gt; l
&amp;lt;zip object&amp;gt;

&amp;gt;&amp;gt;&amp;gt; dict(l)
{'key1': 'value1', 'key2': 'value2', 'key3': 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If &lt;em&gt;key&lt;/em&gt; and &lt;em&gt;value&lt;/em&gt; lists are not of the same length, the behaviour of &lt;code&gt;zip&lt;/code&gt; is to silently drop any extra items from the longer list.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; keys = ['key1', 'key2', 'oops']
&amp;gt;&amp;gt;&amp;gt; vals = ['value1', 'value2']

&amp;gt;&amp;gt;&amp;gt; dict(zip(keys, vals))
{'key1': 'value1', 'key2': 'value2'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="iterating"&gt;Iterating&lt;/h2&gt;
&lt;p&gt;By default iterating over a dictionary iterates over the &lt;em&gt;keys&lt;/em&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d = {'key1': 'value1', 'key2': 'value2', 'key3': 3}

&amp;gt;&amp;gt;&amp;gt; for k in d:
...     print(k)
key1
key2
key3
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This is functionally equivalent to iterating over the &lt;code&gt;.keys()&lt;/code&gt; view.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d = {'key1': 'value1', 'key2': 'value2', 'key3': 3}

&amp;gt;&amp;gt;&amp;gt; for k in d.keys():
...     print(k)
key1
key2
key3
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The dictionary is unaffected by iterating over it, and you can use the key within your loop to access the value from the dictionary.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d = {'key1': 'value1', 'key2': 'value2', 'key3': 3}

&amp;gt;&amp;gt;&amp;gt; for k in d:
...     print(k, d[k])  #&amp;nbsp;Access value by key.
key1 value1
key2 value2
key3 3
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you want access to dictionary values within your loop, you can iterate over &lt;em&gt;items&lt;/em&gt; to have them returned in the &lt;code&gt;for&lt;/code&gt; loop. The &lt;em&gt;keys&lt;/em&gt; vand &lt;em&gt;values&lt;/em&gt; are returned as a 2-tuple.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d = {'key1':'value1', 'key2':'value2', 'key3':3}

&amp;gt;&amp;gt;&amp;gt; for kv in d.items():
...     print(kv)
('key1', 'value1')
('key2', 'value2')
('key3', 3)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can unpack the &lt;em&gt;key&lt;/em&gt; and &lt;em&gt;value&lt;/em&gt; to seperate variables in the loop, making them available without indexing. This is the most common loop structure used with dictionaries.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d = {'key1':'value1', 'key2':'value2', 'key3':3}

&amp;gt;&amp;gt;&amp;gt; for k, v in d.items():
...     print(k, v)
key1 value1
key2 value2
key3 3
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you are only interested in the dictionary &lt;em&gt;values&lt;/em&gt; you can also iterate over these directly.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d = {'key1':'value1', 'key2':'value2', 'key3':3}

&amp;gt;&amp;gt;&amp;gt; for v in d.values():
...     print(v)
value1
value2
3
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you want to count as you iterate you can use &lt;code&gt;enumerate&lt;/code&gt; as with any iterator, but you must nest the unpacking.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d = {'key1':'value1', 'key2':'value2', 'key3':3}

&amp;gt;&amp;gt;&amp;gt; for n, (k, v) in enumerate(d.items()):
...     print(n, k, v)
0 key1 value1
1 key2 value2
2 key3 3
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="dictionary-comprehensions"&gt;Dictionary comprehensions&lt;/h2&gt;
&lt;p&gt;Dictionary comprehensions are shorthand iterations which can be used to construct dictionaries, while filtering or altering &lt;em&gt;keys&lt;/em&gt; or &lt;em&gt;values&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Iterating over a list of &lt;code&gt;(key, value)&lt;/code&gt; tuples and assigning to &lt;em&gt;keys&lt;/em&gt; and &lt;em&gt;values&lt;/em&gt; will create a new dictionary.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; l = [('key1','value1'), ('key2','value2'), ('key3',3)]

&amp;gt;&amp;gt;&amp;gt; {k:v for k,v in l}
{'key1': 'value1', 'key2': 'value2', 'key3': 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can filter elements by using a trailing &lt;code&gt;if&lt;/code&gt; clause. If this expression evaluates to &lt;code&gt;False&lt;/code&gt; the element will be skipped (if it evaluates &lt;code&gt;True&lt;/code&gt; it will be added).&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; l = [('key1','value1'), ('key2','value2'), ('key3',3)]

&amp;gt;&amp;gt;&amp;gt; {k:v for k,v in l if isinstance(v, str)}  #&amp;nbsp;Only add strings.
{'key1': 'value1', 'key2': 'value2'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Any valid expression can be used for the comparison, as long as it returns &lt;em&gt;thruthy&lt;/em&gt; or &lt;em&gt;falsey&lt;/em&gt; values.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; l = [('key1','value1'), ('key2','value2'), ('key3',3)]

&amp;gt;&amp;gt;&amp;gt; {k:v for k,v in l if v != 'value1'}
{'key2': 'value2', 'key3': 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Comparisons can be performed against keys, values, or both.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; l = [('key1','value1'), ('key2','value2'), ('key3',3)]

&amp;gt;&amp;gt;&amp;gt; {k:v for k,v in l if v != 'value1' and k != 'key3'}
{'key2': 'value2'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Since empty string evaluates as &lt;code&gt;False&lt;/code&gt; in Python testing the value alone can be used to strip empty string values from a dictionary.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d = {'key1':'value1', 'key2':'value2', 'key3':'', 'another-empty':''}

&amp;gt;&amp;gt;&amp;gt; {k:v for k,v in d.items() if v}
{'key1': 'value1', 'key2': 'value2'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Separate lists of &lt;em&gt;keys&lt;/em&gt; and &lt;em&gt;values&lt;/em&gt; can be zipped, and filtered using a dictionary comprehension.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt; k = ['key1', 'key2', 'key3']
&amp;gt;&amp;gt; v = ['value1', 'value2', 3]

&amp;gt;&amp;gt;&amp;gt; {k:v for k,v in zip(k,v) if k != 'key1'}
{'key2': 'value2', 'key3': 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Expressions can also be used in the &lt;code&gt;k:v&lt;/code&gt; construct to alter keys or values that are generated for the dictionary.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; l = [('key1', 1), ('key2', 2), ('key3', 3)]

&amp;gt;&amp;gt;&amp;gt; {k:v**2 for k,v in l}
{'key1': 1, 'key2': 4, 'key3': 9}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Any expressions are valid, for both &lt;em&gt;keys&lt;/em&gt; and &lt;em&gt;values&lt;/em&gt;, including calling functions.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; l = [('key1', 1), ('key2', 2), ('key3', 3)]

&amp;gt;&amp;gt;&amp;gt; def cube(v):
...     return v**3

&amp;gt;&amp;gt;&amp;gt; def reverse(k):
...     return k[::-1]

&amp;gt;&amp;gt;&amp;gt; {reverse(k):cube(v) for k,v in l}
{'1yek': 1, '2yek': 8, '3yek': 27}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can use a ternary &lt;code&gt;if-else&lt;/code&gt; in the &lt;code&gt;k:v&lt;/code&gt; to selectively replace keys. In the following example &lt;em&gt;values&lt;/em&gt; are replaced if they don't match &lt;code&gt;'value1'&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; l = [('key1','value1'), ('key2','value2'), ('key3',3)]

&amp;gt;&amp;gt;&amp;gt; {k:v if v=='value1' else None for k,v in l}
{'key1': 'value1', 'key2': None, 'key3': None}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can also use ternary syntax to process keys. Any expressions are valid here, in the follow example we replace missing keys with the current iteration number (1-indexed).&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; l = [(None,'value1'), (None,'value2'), ('key3',3)]

&amp;gt;&amp;gt;&amp;gt; {k if k else n:v for n,(k,v) in enumerate(l, 1)}
{1: 'value1', 2: 'value2', 'key3': 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If your expressions generate duplicate keys, the later value will take precedence for that key.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; l = [(None,'value1'), (None,'value2'), ('key3',3)]

&amp;gt;&amp;gt;&amp;gt; {k if k else 0:v for n,(k,v) in enumerate(l)}
{0: 'value2', 'key3': 3} #&amp;nbsp;0:value1 has been overwritten by 0:value1
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can use nested loops within dictionary comprehensions although &lt;em&gt;you often won't want to&lt;/em&gt; since it can get pretty confusing. One useful application of this however is for flattening nested dictionaries. The follow example unnestes 2-deep dictionaries, discarding the outer keys.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d = {'a': {'naa':1, 'nab':2, 'nac':3}, 'b': {'nba':4, 'nbb':5, 'nbc':6}}

&amp;gt;&amp;gt;&amp;gt; {k:v for di in d.values() for k,v in di.items()}
{'naa': 1, 'nab': 2, 'nac': 3, 'nba': 4, 'nbb': 5, 'nbc': 6}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The left hand loops it the &lt;em&gt;outer&lt;/em&gt; loop, which iterates the &lt;code&gt;d&lt;/code&gt; dictionary producing the values in &lt;code&gt;di&lt;/code&gt;. The &lt;em&gt;inner&lt;/em&gt; loop on the right iterates this dictionary &lt;em&gt;keys&lt;/em&gt; and &lt;em&gt;values&lt;/em&gt; as &lt;code&gt;k&lt;/code&gt; and &lt;code&gt;v&lt;/code&gt;, which are used to construct the new dictionary on the far left &lt;code&gt;k:v&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="merging"&gt;Merging&lt;/h2&gt;
&lt;p&gt;There are a number of ways to merge dictionaries. The major difference between the approaches is in how (or whether) they handle duplicate keys.&lt;/p&gt;
&lt;h3&gt;Update&lt;/h3&gt;
&lt;p&gt;Each dictionary object has an &lt;code&gt;.update()&lt;/code&gt; method, which can be used to add a set of &lt;em&gt;keys&lt;/em&gt; and &lt;em&gt;values&lt;/em&gt; to an existing dictionary, using another dictionary as the source.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key4':'value4', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d1.update(d2)
&amp;gt;&amp;gt;&amp;gt; d1
{'key1':'value1', 'key2':'value2', 'key3': 3, 'key4':'value4', 'key5':'value5'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  This updates the original dictionary, and does not return a copy.&lt;/p&gt;
&lt;p&gt;If there are duplicate keys in the dictionary being updated from, the values from that dictionary will replace those in the dictionary being updated.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d1.update(d2)
&amp;gt;&amp;gt;&amp;gt; d1
{'key1':'value1', 'key2':'value2', 'key3':'value3-new', 'key5':'value5'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you do not want to replace already existing keys, you can use a dictionary comprehension to pre-filter.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d1.update({k:v for k, v in d2.items() if k not in d1})
&amp;gt;&amp;gt;&amp;gt; d1
{'key1':'value1', 'key2':'value2', 'key3': 3, 'key5':'value5'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Unpacking&lt;/h3&gt;
&lt;p&gt;Dictionaries can be &lt;em&gt;unpacked&lt;/em&gt; to &lt;code&gt;key=value&lt;/code&gt; keyword pairs, which is used to pass parameters to functions or constructors. This can be used to combine multiple dictionaries by unpacking them consecutively.&lt;/p&gt;
&lt;p class="admonition admonition-warning"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation-circle"&gt;&lt;/i&gt;&lt;/span&gt;  This requires Python 3.6 and above.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key4':'value4', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d = {**d1, **d2}
&amp;gt;&amp;gt;&amp;gt; d
{'key1': 'value1', 'key2': 'value2', 'key3': 3, 'key4': 'value4', 'key5': 'value5'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Unpacking using this syntax handles duplicate keys, with the later dictionary taking precedence of the earlier.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d = {**d1, **d2}
&amp;gt;&amp;gt;&amp;gt; d
{'key1': 'value1', 'key2': 'value2', 'key3': 'value3-new', 'key5': 'value5'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can use this same syntax to merge multiple dictionaries together.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}
&amp;gt;&amp;gt;&amp;gt; d3 = {'key4':'value4', 'key6':'value6'}

&amp;gt;&amp;gt;&amp;gt; d = {**d1, **d2, **d3}
&amp;gt;&amp;gt;&amp;gt; d
{'key1': 'value1', 'key2': 'value2', 'key3': 'value3-new', 'key5': 'value5', 'key4': 'value4', 'key6': 'value6'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can also unpack to a &lt;code&gt;dict()&lt;/code&gt;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; dict(**d1, **d3)
{'key1': 'value1', 'key2': 'value2', 'key3': 3, 'key4': 'value4', 'key6': 'value6'}

&amp;gt;&amp;gt;&amp;gt; dict(**d1, **d2)
TypeError: type object got multiple values for keyword argument 'key3'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;However, in this case duplicate keys are &lt;em&gt;not&lt;/em&gt; supported, and you are limited by the keyword naming restrictions described earlier.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; dict(**d1, **d2)
TypeError: type object got multiple values for keyword argument 'key3'

&amp;gt;&amp;gt;&amp;gt; dict(**{3:'value3'})
TypeError: keyword arguments must be strings
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;There is no such restriction for &lt;code&gt;{}&lt;/code&gt; unpacking.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; {**{3:'value3'}}
{3:'value3'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Addition (Python 2.7 only)&lt;/h3&gt;
&lt;p&gt;In Python 2.7 &lt;code&gt;dict.items()&lt;/code&gt; returns a list of &lt;code&gt;(key, value)&lt;/code&gt; tuples. Lists can be concatenated using the &lt;code&gt;+&lt;/code&gt; operator, and the resulting list can be converted back to a new dictionary by passing to the &lt;code&gt;dict&lt;/code&gt; constructor.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; l = dict(d1.items() + d2.items())
&amp;gt;&amp;gt;&amp;gt; l
[('key3', 3), ('key2', 'value2'), ('key1', 'value1'), ('key3', 'value3-new'), ('key5', 'value5')]

&amp;gt;&amp;gt;&amp;gt; dict(l)
{'key3': 'value3-new', 'key2': 'value2', 'key1': 'value1', 'key5': 'value5'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can add together multiple dictionaries using this method. The later dictionary keys take precedence over the former.&lt;/p&gt;
&lt;h3&gt;Union (set merge)&lt;/h3&gt;
&lt;p&gt;If both the keys &lt;em&gt;and&lt;/em&gt; values of a dictionary are hashable, the &lt;code&gt;dict_items&lt;/code&gt; view supports &lt;code&gt;set&lt;/code&gt;-like operations.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}
&amp;gt;&amp;gt;&amp;gt; d3 = {'key4':'value4', 'key6':'value6'}

&amp;gt;&amp;gt;&amp;gt; dict(d1.items() | d2.items() | d3.items())
{'key4': 'value4', 'key5': 'value5', 'key2': 'value2', 'key6': 'value6', 'key3': 3, 'key1': 'value1'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  The merging occurs right-to left.&lt;/p&gt;
&lt;p&gt;If the values are &lt;em&gt;not&lt;/em&gt; hashable this will raise a &lt;code&gt;TypeError&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5': []}  # list is unhashable

&amp;gt;&amp;gt;&amp;gt; d1.items() | d2.items()
TypeError: unhashable type:'list'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;All standard &lt;code&gt;set&lt;/code&gt; operations are possible on &lt;code&gt;dict_keys&lt;/code&gt; and &lt;code&gt;dict_items&lt;/code&gt;.&lt;/p&gt;
&lt;!--
For more examples of using dictionary view objects to filter keys and items see [dictionary view objects set operations](/article/python-dictionary-sets).--&gt;
&lt;h2 id="copying"&gt;Copying&lt;/h2&gt;
&lt;p&gt;To make a copy of an existing dictionary you can use &lt;code&gt;.copy()&lt;/code&gt;. This results in an identical dictionary which is a distinct object.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}

&amp;gt;&amp;gt;&amp;gt; d2 = d1.copy()
&amp;gt;&amp;gt;&amp;gt; d2
{'key1':'value1', 'key2':'value2', 'key3':3}

&amp;gt;&amp;gt;&amp;gt; id(d1) == id(d2)
False
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can also make a copy of a dictionary by passing an existing dictionary to the &lt;code&gt;dict&lt;/code&gt; constructor. This is functionally equivalent to &lt;code&gt;.copy()&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}

&amp;gt;&amp;gt;&amp;gt; d2 = dict(d1)
&amp;gt;&amp;gt;&amp;gt; d2
{'key1':'value1', 'key2':'value2', 'key3':3}

&amp;gt;&amp;gt;&amp;gt; id(d1) == id(d2)
False
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In both cases these are &lt;em&gt;shallow copies&lt;/em&gt; meaning nested objects within the dictionary are not also copied. Changes to this nested objects will also be reflected in the original dictionary.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':{'nested':'dictionary'}}

&amp;gt;&amp;gt;&amp;gt; d2 = d1.copy()
&amp;gt;&amp;gt;&amp;gt; d2
{'key1': 'value1', 'key2': 'value2', 'key3': {'nested': 'dictionary'}}

&amp;gt;&amp;gt;&amp;gt; id(d1) == id(d2)
False

&amp;gt;&amp;gt;&amp;gt; id(d1['key3']) == id(d2['key3'])
True

&amp;gt;&amp;gt;&amp;gt; d2['key3']['nested'] = 'I changed in d1'
&amp;gt;&amp;gt;&amp;gt; d1
{'key1': 'value1', 'key2': 'value2', 'key3': {'nested': 'I changed in d1'}}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you want nested objects to &lt;em&gt;also&lt;/em&gt; be copied, you need to create a &lt;code&gt;deepcopy&lt;/code&gt; of your dictionary.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':{'nested':'dictionary'}}

&amp;gt;&amp;gt;&amp;gt; from copy import deepcopy
&amp;gt;&amp;gt;&amp;gt; d2 = deepcopy(d1)
&amp;gt;&amp;gt;&amp;gt; d2
{'key1': 'value1', 'key2': 'value2', 'key3': {'nested': 'dictionary'}}

&amp;gt;&amp;gt;&amp;gt; id(d1) == id(d2)
False

&amp;gt;&amp;gt;&amp;gt; id(d1['key3']) == id(d2['key3'])
False

&amp;gt;&amp;gt;&amp;gt; d2['key3']['nested'] = ['I did not change in d1']
&amp;gt;&amp;gt;&amp;gt; d1
{'key1': 'value1', 'key2': 'value2', 'key3': {'nested': 'dictionary'}}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  Since a &lt;code&gt;deepcopy&lt;/code&gt; copies all nested objects it is slower and uses more memory. Only use it when it's actually neccessary.&lt;/p&gt;</content><category term="python"/></entry><entry><title>Driving I2C OLED displays with MicroPython — I2C monochrome displays with SSD1306</title><link href="https://www.martinfitzpatrick.com/oled-displays-i2c-micropython/" rel="alternate"/><published>2018-08-25T08:00:00+00:00</published><updated>2018-08-25T08:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2018-08-25:/oled-displays-i2c-micropython/</id><summary type="html">These mini monochrome OLED screens make great displays for projects &amp;mdash;&amp;nbsp;perfect for data readout, simple UIs or monochrome games.</summary><content type="html">&lt;p&gt;These mini monochrome OLED screens make great displays for projects &amp;mdash;&amp;nbsp;perfect for data readout, simple UIs or monochrome games.&lt;/p&gt;
&lt;div class="requirements"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th colspan="2"&gt;Requirements&lt;/th&gt;
&lt;th colspan="2"&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wemos D1 &lt;span&gt;v2.2+ or good imitations.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/wemosd1" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.91in OLED Screen  &lt;span&gt;128x32 pixels, I2c interface.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/oledi2c128x32" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Breadboard &lt;span&gt;Any size will do.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://amzn.to/2HB39F1" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wires &lt;span&gt;Loose ends, or jumper leads.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2 id="setting-up"&gt;Setting up&lt;/h2&gt;
&lt;p&gt;MicroPython provides some built-in support for these displays, and a simple framebuffer which can be used as a drawing surface. However, we still need a driver to interface the buffer to the display.&lt;/p&gt;
&lt;p&gt;There is a Python &lt;em&gt;ssd1306&lt;/em&gt; module for OLED displays available
&lt;a href="https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py"&gt;in the MicroPython repository&lt;/a&gt;.
Click &lt;em&gt;Raw&lt;/em&gt; format and save the file with a &lt;code&gt;.py&lt;/code&gt; extension.&lt;/p&gt;
&lt;p&gt;You can then use the &lt;a href="https://github.com/adafruit/ampy"&gt;ampy&lt;/a&gt; tool (or the WebREPL) to upload
the file to your device's filesystem:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;ampy --port /dev/tty.wchusbserial141120 put ssd1306.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With the &lt;code&gt;ssd1306.py&lt;/code&gt; file on your Wemos D1, you can import it as any other Python module. Connect to your device,
and then in the REPL enter:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from machine import I2C, Pin
import ssd1306
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-warning"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation-circle"&gt;&lt;/i&gt;&lt;/span&gt;  Both I2C and SPI-controlled displays are available&amp;nbsp;&amp;mdash; these instructions will only work for I2C displays.&lt;/p&gt;
&lt;p&gt;If the &lt;code&gt;import ssd1306&lt;/code&gt; succeeds, the package is correctly uploaded and you're good to go.
Wire up the OLED display, connecting pins &lt;code&gt;D1&lt;/code&gt; to &lt;code&gt;SCL&lt;/code&gt; and &lt;code&gt;D2&lt;/code&gt; to &lt;code&gt;SDA&lt;/code&gt;.
Provide power from &lt;code&gt;G&lt;/code&gt; and &lt;code&gt;5V&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The circuit" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/display-wemos-circuit.jpg"  loading="lazy" width="622" height="731"/&gt;&lt;/p&gt;
&lt;h2 id="using-the-interface"&gt;Using the interface&lt;/h2&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  The following instructions are based on a 0.91in monochrome 128x32 OLED, but other
displays using the same chipset can be used with this library.&lt;/p&gt;
&lt;p&gt;To use the display you first need to create an I2C interface. In MicroPython I2C
is &lt;a href="/wemos-d1-pins-micropython/"&gt;via a software implementation&lt;/a&gt;
so you can put it on any GPIO pins you like.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;i2c = I2C(-1, Pin(5), Pin(4))
display = ssd1306.SSD1306_I2C(128, 32, i2c)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The SSD1306 module makes use of the MicroPython &lt;code&gt;framebuf&lt;/code&gt; frame buffer, an efficient in-memory buffer for working with
a simple graphics view. The methods for drawing text and primitives are from this framebuffer implementation.
For a complete overview of what's available, &lt;a href="http://docs.micropython.org/en/latest/wipy/library/framebuf.html"&gt;check the MicroPython documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  The setup of the framebuffer format (monochrome, bit ordering, etc.) is also handled by the SSD1306 library. Check
the &lt;a href="http://docs.micropython.org/en/latest/wipy/library/framebuf.html"&gt;framebuf documentation&lt;/a&gt; for more info on
available options for other displays.&lt;/p&gt;
&lt;p&gt;To test your I2C connection to the display, fill the display in solid colour.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;display.fill(1)  # Fill the entire display with 1="on"
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  You need to call &lt;code&gt;display.show()&lt;/code&gt; to actually send the current &lt;code&gt;framebuf&lt;/code&gt; to the device.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The display, filled" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0004.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;h2 id="drawing-primitives"&gt;Drawing primitives&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;.fill()&lt;/code&gt; method can be used to fill the entire display with a specified colour.
Note that this changes all pixels to the given colour, and does not perform a flood
fill of matching regions.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Fill the entire display with colour 0
display.fill(0)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="The display, filled with blank" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0005.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  For filling specific regions of the display see &lt;code&gt;.fill_rect()&lt;/code&gt; below.&lt;/p&gt;
&lt;p&gt;Setting individual pixels can be accomplished using &lt;code&gt;.pixel()&lt;/code&gt;. This is only a good idea
when you area setting relatively few pixels as it is much slower than using other methods.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Set the pixel at 3, 4 (x, y) to 1
# .pixel(x, y, c)
display.pixel(3, 4, 1)  # 3rd param is the colour
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="A single pixel" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0006.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;p&gt;If you don't provide a color via parameter &lt;em&gt;c&lt;/em&gt;, this method &lt;em&gt;returns&lt;/em&gt; the
colour of the pixel at the specified coordinates.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Return the value at 3, 4 (x, y)
# .pixel(x, y)
c = display.pixel(3, 4)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  This is not a particularly nice API. In other cases (e.g. text) omitting
the optional &lt;code&gt;c&lt;/code&gt; will use 1 as a default.&lt;/p&gt;
&lt;p&gt;Horizontal and vertical lines can be drawn with &lt;code&gt;.hline()&lt;/code&gt; and &lt;code&gt;.vline()&lt;/code&gt;
respectively, providing a starting &lt;em&gt;x,y&lt;/em&gt; location and line &lt;em&gt;length&lt;/em&gt; and &lt;em&gt;colour&lt;/em&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Draw a horizontal line, starting from 2, 3 (x, y), 4 pixels wide
# .hline(x, y, w, c)
display.hline(2, 3, 25, 1)
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="A horizontal line" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0007.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Draw a vertical line, starting from 5, 0 (x, y), 6 pixels high
# .vline(x, y, h, c)
display.vline(5, 0, 15, 1)
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="A vertical line" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0008.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;p&gt;For diagonal lines, the &lt;code&gt;.line()&lt;/code&gt; method can be used to draw lines
between between two sets of points &lt;em&gt;x1,y1&lt;/em&gt; and &lt;em&gt;x2,y2&lt;/em&gt; specified in order.
The parameter &lt;em&gt;c&lt;/em&gt; controls the colour of the line drawn.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Draw a short diagonal line down to the right.
# .line(x1, y1, x2, y2, c)
display.line(0, 0, 50, 25, 1)
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="A diagonal line" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0009.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  There is no &lt;em&gt;antialiasing&lt;/em&gt;, so diagonal lines will probably look pretty jaggy.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;.draw_rect()&lt;/code&gt; method allows you to draw a &lt;em&gt;unfilled&lt;/em&gt; rectangle, starting at &lt;em&gt;x,y&lt;/em&gt; and
with a specified width &lt;em&gt;w&lt;/em&gt; and height &lt;em&gt;h&lt;/em&gt;. The specified colour &lt;em&gt;c&lt;/em&gt; is used to draw
the boundary of the rectangle, but it is not filled.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Draw an unfilled rectangle of 8, 5 pixels, starting at 1,1 in colour 1.
# .rect(x, y, w, h, c)
display.rect(5, 5, 100, 20, 1)
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="An empty rectangle" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0010.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;p&gt;You can also draw filled rectangles, using &lt;code&gt;.fill_rect()&lt;/code&gt;. The parameters are the same
as for &lt;code&gt;.rect()&lt;/code&gt; but all pixels within the boundary will be set.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Draw a filled rectangle of 10x5 pixels, starting at 3,3 in colour 1
# .fill_rect(x, y, w, h, c)
display.fill_rect(9, 9, 25, 25, 1)
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="An filled rectangle" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0011.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;h2 id="writing-text"&gt;Writing text&lt;/h2&gt;
&lt;p&gt;The framebuffer class provides support for writing text using a simple 8x8 bitmap
font. The pixel &lt;em&gt;x,y&lt;/em&gt; positions are relative to the top-left of the 8x8 character, so
positioning at &lt;em&gt;0,0&lt;/em&gt; will give the absolute top left of the screen.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Print "Hello world!" 1 pixel from top left, in colour 1 (on)
# .text(text, x, y, c)
display.text("Hello world!", 1, 1, 1)
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Hello world" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0014.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;.text()&lt;/code&gt; method takes an optional 4th parameter &lt;em&gt;color&lt;/em&gt;, which gives the colour to draw text in. On
a mono screen this can be either &lt;code&gt;0&lt;/code&gt; (off) or &lt;code&gt;1&lt;/code&gt; (on).&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Print "Hello world!" at 2,2 in colour 0 (off)
display.text("Hello world!", 2, 2, 0)
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Hello world, black over" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0015.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Print "Hello world!" at 3,3 in colour 1 (on)
display.text("Hello world!", 3, 3, 1)
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Hello world, white over black" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0016.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;h2 id="scrolling"&gt;Scrolling&lt;/h2&gt;
&lt;p&gt;You can shift the contents of the entire framebuffer around using &lt;code&gt;.scroll&lt;/code&gt;, which takes &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; parameters to specify
the scroll (positive/negative) in each dimension.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;#&amp;nbsp;.scroll(x, y)
display.scroll(-10, 0)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Scrolled left" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0018.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  There is no support for scrolling a portion of the framebuffer. You can however keep a separate framebuffer which you
scroll and then blit to your main display &amp;mdash; see the next section.&lt;/p&gt;
&lt;h2 id="pixel-graphics"&gt;Pixel graphics&lt;/h2&gt;
&lt;p&gt;For very simple graphics that do not update often you can get away with writing bitmap graphics to the framebuffer
pixel by pixel. For example, in the following code block we draw an icon from a list-of-lists of binary colour data,
iterating over with simple loops:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;ICON = [
    [ 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [ 0, 1, 1, 0, 0, 0, 1, 1, 0],
    [ 1, 1, 1, 1, 0, 1, 1, 1, 1],
    [ 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [ 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [ 0, 1, 1, 1, 1, 1, 1, 1, 0],
    [ 0, 0, 1, 1, 1, 1, 1, 0, 0],
    [ 0, 0, 0, 1, 1, 1, 0, 0, 0],
    [ 0, 0, 0, 0, 1, 0, 0, 0, 0],
]

display.fill(0) # Clear the display
for y, row in enumerate(ICON):
    for x, c in enumerate(row):
        display.pixel(x, y, c)

display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Bitmap heart graphic" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0019.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;urandom&lt;/code&gt; we can scatter images over the display, like in the following example:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import urandom

def random_heart():
    xofs = urandom.getrandbits(8)
    yofs = urandom.getrandbits(5)
    for y, row in enumerate(ICON):
        for x, c in enumerate(row):
            display.pixel(x + xofs, y + yofs, c)

for n in range(100):
    random_heart()

display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Scattered heart graphics" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0022.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  If you can add an &lt;code&gt;if c&lt;/code&gt; around the &lt;code&gt;display.pixel&lt;/code&gt; call to only output
pixels which are on &amp;mdash; effectively masking, and avoiding the black square.&lt;/p&gt;
&lt;p&gt;For bigger graphics or where you need faster updates, you can instead &lt;em&gt;blit&lt;/em&gt; your bitmap images from one framebuffer
to another. Pass the &lt;code&gt;framebuf&lt;/code&gt; to blit, and the coordinates &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; to blit at.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Blit a framebuffer at the pixel position 1, 1.
# display.blit(fbuf, x, y)
display.blit(fbuf, 1, 1)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;blit&lt;/code&gt; method takes an optional 4th parameter &lt;em&gt;key&lt;/em&gt; which is a colour to
considered 'transparent' when blitting. Pixels with this value won't be copied over
onto the target display.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Blit except pixels with value 0 (i.e. only pixels with value 1)
# .blit(fbuf, x, y, key)
display.blit(fbuf, 1, 1, key=0)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;See &lt;a href="/displaying-images-oled-displays/"&gt;this follow up&lt;/a&gt; for details on how to display images on OLED screens, including animated graphics.&lt;/p&gt;
&lt;h2 id="display-control"&gt;Display control&lt;/h2&gt;
&lt;p&gt;The drawing methods so far are inherited from the the MicroPython &lt;code&gt;framebuf&lt;/code&gt;
object, which is written to the display on &lt;code&gt;.update()&lt;/code&gt;. But the &lt;code&gt;ssd1306&lt;/code&gt; object itself
also provides methods for direct control of the display component.&lt;/p&gt;
&lt;p&gt;The display can be turned on and off using &lt;code&gt;display.poweron()&lt;/code&gt;
and &lt;code&gt;display.poweroff()&lt;/code&gt; respectively.&lt;/p&gt;
&lt;p&gt;You can set the contrast for the display using &lt;code&gt;.contrast()&lt;/code&gt; passing in parameter
&lt;em&gt;c&lt;/em&gt; which is a value between &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;255&lt;/code&gt;. This controls the contrast between
the foreground, active colour &lt;code&gt;1&lt;/code&gt; and the background &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Set the display contrast to half (127/255 = 0.5)
# .contrast(c)
display.contrast(50)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Contrast 50" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0023.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;p&gt;To invert the display, switching foreground and background colours, call &lt;code&gt;.invert(1)&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;display.invert(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Inverted" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0025.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;p&gt;Calling &lt;code&gt;.invert(0)&lt;/code&gt; will return the display to how it was originally. You can
use this for display-flashing visual effects:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import time
while True:
    display.invert(0)
    time.sleep(0.01)
    display.invert(1)
    time.sleep(0.01)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</content><category term="python"/><category term="micropython"/><category term="electronics"/></entry><entry><title>Heart rate (HR) sensors — Photoplethysmography, because that's a real word</title><link href="https://www.martinfitzpatrick.com/heartrate-sensor-modules/" rel="alternate"/><published>2018-02-27T00:00:00+00:00</published><updated>2018-02-27T00:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2018-02-27:/heartrate-sensor-modules/</id><summary type="html">&lt;p&gt;Pulse sensors are a common feature of fitness monitors, used to track your activity and cardiac fitness over time. These external monitors use the reflection and absorption of bright green/infra-red light to detect the pulse wave travelling down the artery &amp;mdash;&amp;nbsp;a technique called &lt;em&gt;photoplethysmography&lt;/em&gt; (PPG). This same techique is used in hospital finger-clip monitors. Wrist-worn devices like the Fitbit typically use green-light sensors, while the IPhone monitor uses a combination of green and infra-red light. The use of infra-red light for pulse monitoring has a longer history, and is the type of choice used in hospital monitors, because (in combination with a red LED) allows both heart rate and oxygen saturation sensing.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Pulse sensors are a common feature of fitness monitors, used to track your activity and cardiac fitness over time. These external monitors use the reflection and absorption of bright green/infra-red light to detect the pulse wave travelling down the artery &amp;mdash;&amp;nbsp;a technique called &lt;em&gt;photoplethysmography&lt;/em&gt; (PPG). This same techique is used in hospital finger-clip monitors. Wrist-worn devices like the Fitbit typically use green-light sensors, while the IPhone monitor uses a combination of green and infra-red light. The use of infra-red light for pulse monitoring has a longer history, and is the type of choice used in hospital monitors, because (in combination with a red LED) allows both heart rate and oxygen saturation sensing.&lt;/p&gt;

&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  In healthy individuals the oxygen saturation level shouldn't fall, so it's not particularly useful in a fitness trcker.&lt;/p&gt;
&lt;h2 id="how-it-works"&gt;How it works&lt;/h2&gt;
&lt;p&gt;Haemoglobin, the oxygen-carrying component of red blood cells, is a strong &lt;em&gt;reflector&lt;/em&gt; of red light, and a strong &lt;em&gt;absorber&lt;/em&gt; of green light. Together these characteristics give oxygenated blood its red colour.&lt;/p&gt;
&lt;p&gt;The image below (&lt;a href="https://commons.wikimedia.org/wiki/File:Fig_1_-_The_molar_extinction_coefficient_of_HbO2_and_Hb.png"&gt;adapted from Wikimedia&lt;/a&gt;) shows the relative absorption by blood of different wavelengths of light. Green, red and infra-red regions are highlighted.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Red and green light absorbance by blood." src="https://www.martinfitzpatrick.com/static/tutorials/heartrate-sensor-modules/pulsesensor_wavelength.png"  loading="lazy" width="165" height="133"  loading="lazy" width="165" height="133"/&gt;&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  The differences in absorption between oxygenated (red) and deoxygenated (blue) blood can be used with paired red-IR LEDs to determine blood oxygenation percentage. See &lt;a href="#digital-sensors-max30100rcwl-0530"&gt;the MAX30100 sensor&lt;/a&gt; for an example.&lt;/p&gt;
&lt;p&gt;During a pulse there is a wave of increase blood pressure through the arteries, slightly stretching the elastic arterial walls and bringing a pulse of highly oxygenated blood. This change in arterial size and increase concentration of blood is what is exploited to detect the pulse with PPG.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Green light&lt;/strong&gt; is absorbed by red blood cells, but scattered by the tissues. Between pulses, scattered light will be reflected back out towards the incident light and can be detected. However, during a pulse, the small increase in blood volume leads to an increased absorption of the green light. This results in a reduction of reflected signal. We cab detect this increased absorbance by the &lt;em&gt;reduction&lt;/em&gt; in &lt;em&gt;reflection&lt;/em&gt; of green light.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Red/infra-red light&lt;/strong&gt; is reflected by red blood cells. Between pulses most light is transmitted and scattered into the tissues. During a pulse the small increase in blood volume leads to an increased reflection of light by red blood cells, and a reduction in transmission. We can detect this increased scattering of light either by the &lt;em&gt;reduction&lt;/em&gt; in IR &lt;em&gt;transmission&lt;/em&gt;, or alternatively an &lt;em&gt;increase&lt;/em&gt; in &lt;em&gt;reflection&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Red and green light interactions with blood." src="https://www.martinfitzpatrick.com/static/tutorials/heartrate-sensor-modules/pulsesensor_theory.png"  loading="lazy" width="800" height="227"/&gt;&lt;/p&gt;
&lt;p&gt;Below we look at the main types and how to interface with them from different microcontrollers. You can also use one of these sensors &lt;a href="/calculating-heart-rate-from-sensor-data/"&gt;to build a working heart monitor&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="analogue-sensors-ky-039pulsesensorcom"&gt;Analogue Sensors (KY-039/Pulsesensor.com)&lt;/h2&gt;
&lt;h3&gt;Sensor types&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;IR-phototransistor sensors (KY-039)&lt;/strong&gt; like the &lt;a href="http://geni.us/ky039" target="_blank" class="link-affiliate"&gt;Keyes KY-039&lt;/a&gt; use a bright infrared (IR) LED and a photo transistor to
detect the &lt;em&gt;reduced IR transmission&lt;/em&gt; or &lt;em&gt;increased IR reflection&lt;/em&gt; during the pulse. These sensors are more suited to transmission use because of their construction.&lt;/p&gt;
&lt;p&gt;&lt;img alt="KY-039 Heart beat sensor" src="https://www.martinfitzpatrick.com/static/tutorials/heartrate-sensor-modules/keyes-039_pulse_sensor.jpg"  loading="lazy" width="800" height="587"/&gt;
&lt;img alt="KY-039 Reverse" src="https://www.martinfitzpatrick.com/static/tutorials/heartrate-sensor-modules/keyes-039_pulse_sensor_reverse.jpg"  loading="lazy" width="800" height="626"/&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Green-light phototransistors&lt;/strong&gt; like the &lt;a href="http://geni.us/pulsesensor" target="_blank" class="link-affiliate"&gt;PulseSensor.com&lt;/a&gt; function in reverse usign reflected, rather than transmitted light to detect the pulse. This is done using a rather pleasant green LED, which when wired up looks a bit like a tripod from the War of the Worlds. By using green light these sensors detect the &lt;em&gt;reduction in reflection&lt;/em&gt; due to blood absorbing the green light. The circuitry for these sensors is a little more sophisticated than a rawlight sensor, with automatic amplification and noise cancellation features. This makes the subsequent HR calculation a little simpler. The sensors are similar to those found in wearable heart rate/fitness devices.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Pulsesensor.com Active" src="https://www.martinfitzpatrick.com/static/tutorials/heartrate-sensor-modules/pulsesensor_d1.jpg"  loading="lazy" width="516" height="600"/&gt;
&lt;img alt="Pulsesensor.com Reverse" src="https://www.martinfitzpatrick.com/static/tutorials/heartrate-sensor-modules/pulsesensor_rear.jpg"  loading="lazy" width="800" height="600"/&gt;&lt;/p&gt;
&lt;p&gt;See &lt;a href="/articles/calculating-heart-rate-from-sensor-data/"&gt;this tutorial&lt;/a&gt; for an example of building a heart rate monitor using a Pulsesensor.com sensor.&lt;/p&gt;
&lt;h3&gt;Reading analogue sensors&lt;/h3&gt;
&lt;p&gt;The principal for reading both types of sensor is the same. With the sensor + connected to Vref
(3.3V or 5V; depending on controller), and GND to GND, the measured value is readable via the S pin.
The analogue output from this pin varies between GND (light completely blocked) and Vref (light completely passing).&lt;/p&gt;
&lt;p&gt;The variation caused by the pulse is a tiny fraction of that caused by putting your finger in front of
the sensor, so calculating the pulse value requires a bit of code (see later).&lt;/p&gt;
&lt;h4&gt;Raspberry Pi&lt;/h4&gt;
&lt;p class="admonition admonition-warning"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation-circle"&gt;&lt;/i&gt;&lt;/span&gt;  Wire the + on the sensor to &lt;em&gt;+3.3v&lt;/em&gt; on your Pi. While the sensor can handle 5V itself, the input
voltage sets the max output from the S pin. More than 3.3V will damage your Pi's GPIO.&lt;/p&gt;
&lt;p&gt;As the readings are analogue you will need a Analogue to Digital Converter (ADC) such as the MCP3008
between the sensor and your Pi. The Pi then communicates with the chip via SPI.&lt;/p&gt;
&lt;!-- A longer [introduction to using SPI with the Pi] is also available. --&gt;
&lt;p&gt;Add the MCP3008 to a breadboard, with the notch facing up towards your
breakout board. Using hardware SPI we can wire the MCP3008 up as follows:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align="left"&gt;MCP3008&lt;/th&gt;
&lt;th align="left"&gt;Raspberry Pi&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align="left"&gt;&lt;code&gt;VDD&lt;/code&gt; (16)&lt;/td&gt;
&lt;td align="left"&gt;&lt;code&gt;3.3V&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;&lt;code&gt;VREF&lt;/code&gt; (15)&lt;/td&gt;
&lt;td align="left"&gt;&lt;code&gt;3.3V&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;&lt;code&gt;AGND&lt;/code&gt; (14)&lt;/td&gt;
&lt;td align="left"&gt;&lt;code&gt;GND&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;&lt;code&gt;DGND&lt;/code&gt; (13)&lt;/td&gt;
&lt;td align="left"&gt;&lt;code&gt;GND&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;&lt;code&gt;CLK&lt;/code&gt; (12)&lt;/td&gt;
&lt;td align="left"&gt;&lt;code&gt;SCLK&lt;/code&gt; (11)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;&lt;code&gt;DOUT&lt;/code&gt; (11)&lt;/td&gt;
&lt;td align="left"&gt;&lt;code&gt;MISO&lt;/code&gt; (9)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;&lt;code&gt;DIN&lt;/code&gt; (10)&lt;/td&gt;
&lt;td align="left"&gt;&lt;code&gt;MOSI&lt;/code&gt; (10)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;&lt;code&gt;CS/SHDN&lt;/code&gt; (9)&lt;/td&gt;
&lt;td align="left"&gt;&lt;code&gt;CE0&lt;/code&gt; (8)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  The analogue and digital ground pins are wired together here.
This is fine since we aren't looking for highly accurate readings.&lt;/p&gt;
&lt;p&gt;Wire the 3.3V and GND pins up first, leaving the SCLK, MISO, MOSI and CEO pins to wire to your Pi. They connect
to pins 11, 9, 10 and 8 respectively, with the first 3 in order up one side of the GPIO, and the
last on the other side, level with the first pin you connected.&lt;/p&gt;
&lt;p&gt;Once connected you can use the &lt;code&gt;gpiozero.MCP3008()&lt;/code&gt; interface to read values out directly.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import gpiozero
hr = gpiozero.MCP3008(channel=0)  #&amp;nbsp;Set the channel if you're using a different one

&amp;gt;&amp;gt;&amp;gt; hr.value
    0.7663
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;To check that the sensor is responding correctly, try writing the values out in a loop (using &lt;code&gt;end="\r"&lt;/code&gt;
keeps our output on a single line).&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;while True:
    print("%4f" % hr.value, end="\r")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you put your finger between the emitter and sensor while running this you should see the number
increase, indicating &lt;em&gt;increased&lt;/em&gt; resistance as the light has to travel through your finger. Somewhere
buried in the noisy variation you see is your heart beating.&lt;/p&gt;
&lt;h4&gt;MicroPython (ESP8266 inc. Wemos D1)&lt;/h4&gt;
&lt;p&gt;If you're running on a ESP8266 device you have access to an ADC pin, labelled as A0 on the Wemos D1.
Once your sensor is wired to this pin you can use following MicroPython code to read the value from the sensor:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import machine
adc = machine.ADC(0)

&amp;gt;&amp;gt;&amp;gt; adc.read()
550
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  The values output by this ADC are integers in the range 0-1023. A reading of 550 is equivalent to 0.54 on a 0-1 scale.&lt;/p&gt;
&lt;p&gt;Below is a plot of data from the Pulsesensor.com sensor, as read using a Wemos D1. Notice that the Pulsesensor.com sensor auto-calibrates the reading range.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Complete set of data from the sensor." src="https://www.martinfitzpatrick.com/static/tutorials/heartrate-sensor-modules/pulsesensor_data.png"  loading="lazy" width="730" height="214"/&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Zoomed in region" src="https://www.martinfitzpatrick.com/static/tutorials/heartrate-sensor-modules/pulsesensor_sample.png"  loading="lazy" width="730" height="214"  loading="lazy" width="730" height="214"/&gt;&lt;/p&gt;
&lt;h4&gt;BBC micro:bit&lt;/h4&gt;
&lt;p&gt;The micro:bit can read analog values on pins 0, 1, 2, 3, 4 and 10.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from microbit import *
&amp;gt;&amp;gt;&amp;gt; pin0.read_analog()
550
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  The values output by this ADC are integers in the range 0-1023. A reading of 550 is equivalent to 0.54 on a 0-1 scale.&lt;/p&gt;
&lt;h2 id="digital-sensors-max30100rcwl-0530"&gt;Digital sensors (MAX30100/RCWL-0530)&lt;/h2&gt;
&lt;p&gt;Digital sensors based on MAX30100 chips offer both heart rate sending and oximetry (oxygen saturation measurements).
These include 2 LEDs (IR and visible red light) to detect pulse and oxygen saturation levels respectively.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A digital heart rate sensor using the MAX30100 chip." src="https://www.martinfitzpatrick.com/static/tutorials/heartrate-sensor-modules/max30100.jpg"  loading="lazy" width="360" height="302"/&gt;&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  Oximeters pair two light emitters and receivers working at slightly different wavelengths. Using the different light absorbance profiles of oxygenated and deoxygenated blood it is possible to determine the level of O&lt;sub&gt;2&lt;/sub&gt; in the blood in addition to the pulse.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Red and green light absorbance by blood." src="https://www.martinfitzpatrick.com/static/tutorials/heartrate-sensor-modules/pulsesensor_wavelength.png"  loading="lazy" width="165" height="133"  loading="lazy" width="165" height="133"/&gt;&lt;/p&gt;
&lt;h3&gt;Reading digital sensors&lt;/h3&gt;
&lt;p&gt;The MAX30100 chip &lt;a href="https://datasheets.maximintegrated.com/en/ds/MAX30100.pdf"&gt;datasheet&lt;/a&gt; provides an I2C interface to
read the resulting data, which can be interfaced directly to any I2C supporting micro controller.
It is available at I2C channel 0x57 (87).&lt;/p&gt;
&lt;p&gt;There was no simple interface available for connecting with a MAX30100 based sensor from a Raspberry Pi (the Intel
IoT UMP project looks promising, but doesn't build on Pi as of September 2017). As a stop-gap
I re-implemented a C library in Python. The &lt;a href="https://github.com/mfitzp/max30100"&gt;source code&lt;/a&gt; is available.
This currently works with Raspberry Pi only, although adapting to MicroPython should be as simple as
writing an adaptor for the I2C API.&lt;/p&gt;
&lt;p&gt;You can create an interface to your device/chip by creating an instance of the MAX30100 class.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import max30100
mx30 = max30100.MAX30100()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;To read the sensor use &lt;code&gt;.read_sensor()&lt;/code&gt;. This stores the current measurement, making this value available
under two properties. Historic values are in a queue.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;mx30.read_sensor()

# The latest value is now available by .ir
mx30.ir
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can switch on O&lt;sub&gt;2&lt;/sub&gt; sensing as follows.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;ms30.enable_spo2()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Subsequent calls to &lt;code&gt;.read_sensor()&lt;/code&gt; will now populate the &lt;code&gt;.red&lt;/code&gt; attribute.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# The latest value is now available by .ir and .red
mx30.ir, mx30.red
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;For more information on using the max30100 package, including the O&lt;sub&gt;2&lt;/sub&gt; saturation sensor,
see the &lt;a href="https://github.com/mfitzp/max30100"&gt;documentation on Github&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="calculating-a-heart-rate"&gt;Calculating a heart rate&lt;/h2&gt;
&lt;p&gt;Regardless of what sensor you use, you will end up with the same output &amp;mdash; a stream of values, indicating transmission or reflection of a particular wavelength of light.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Pulse sensor data, zoomed on a subset of the data" src="https://www.martinfitzpatrick.com/static/tutorials/heartrate-sensor-modules/pulsesensor_sample.png"  loading="lazy" width="730" height="214"  loading="lazy" width="730" height="214"/&gt;&lt;/p&gt;
&lt;p&gt;There are a number of ways to calculate the heart rate from this data, but peak-counting is a simple and effective method. For a working example of how to do this &lt;a href="/wemos-heart-rate-sensor-display-micropython/"&gt;see this Wemos D1 heart rate monitor&lt;/a&gt;.&lt;/p&gt;</content><category term="raspberry-pi"/><category term="electronics"/><category term="python"/><category term="science"/></entry><entry><title>Partial Least Squares Discriminant Analysis (PLS-DA) with Python</title><link href="https://www.martinfitzpatrick.com/partial-least-squares-discriminant-analysis-plsda/" rel="alternate"/><published>2015-10-18T09:00:00+00:00</published><updated>2015-10-18T09:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2015-10-18:/partial-least-squares-discriminant-analysis-plsda/</id><summary type="html">Partial least squares discriminant analysis (PLS-DA) is an adaptation of PLS
regression methods to the problem of supervised clustering. It has seen
extensive use in the analysis of multivariate datasets, such as that derived
from NMR-based metabolomics.</summary><content type="html">&lt;p&gt;Partial least squares discriminant analysis (PLS-DA) is an adaptation of PLS
regression methods to the problem of supervised clustering. It has seen
extensive use in the analysis of multivariate datasets, such as that derived
from NMR-based metabolomics.&lt;/p&gt;
&lt;p&gt;In this method the groups within the samples are already known (e.g. experimental groups) and the
goal therefore is to determine two things &amp;mdash;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Are the groups &lt;em&gt;actually&lt;/em&gt; different?&lt;/li&gt;
&lt;li&gt;What are the &lt;em&gt;features&lt;/em&gt; that best describe the differences between the groups?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In an experimental context this means determining whether control and test
samples are different, and identifying the (known, quantified) experimental
variables that contribute to that difference.&lt;/p&gt;
&lt;p&gt;PLS-DA is based on PLS regression (PLS-R) with the Y variable generated from
experimental group membership, mapped into a linear space. In a 2-group experiment
this can be as simple as 0 and 1.&lt;/p&gt;
&lt;h2 id="setting-up"&gt;Setting up&lt;/h2&gt;
&lt;p&gt;The implementation of PLS we will be using is provided by the &lt;code&gt;scikit-learn&lt;/code&gt;
library. We will also be making use of &lt;code&gt;matplotlib&lt;/code&gt; for plotting our outputs
and &lt;code&gt;pandas&lt;/code&gt; for some basic data handling.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;pip install scikit-learn matplotlib pandas
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The sample data for this example is &lt;a href="https://download.martinfitzpatrick.com/data/plsda.zip"&gt;available for download&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Download and unzip the file into your data folder.&lt;/p&gt;
&lt;p&gt;For this demo we will start with 1D &lt;sup&gt;1&lt;/sup&gt;H NMR data as it makes explanation and
visualization of the PLS models easy to understand. However, later we will
also generate PLS-DA models for other data types, to demonstrate how you
can easily apply these same methods to any type of multivariate data set.&lt;/p&gt;
&lt;h2 id="loading-the-data"&gt;Loading the data&lt;/h2&gt;
&lt;p&gt;Before starting, let's take a look at the data we are working with.
Create a new Jupyter notebook using the Python 3 kernel, and in the first cell
enter and run the following. This will import all the neccessary libraries, as
well as using the &lt;code&gt;%matplotlib&lt;/code&gt; magic to display output figures in the notebook.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import sklearn
import os
plt.style.use('ggplot')
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We will start by loading the data. The source data is provided in CSV format
with experimental samples along the horizontal axis and spectral variables (ppm) along the
vertical axis. In addition to the a sample number, there is also a sample group
(class) from the experiment).&lt;/p&gt;
&lt;p&gt;There are many ways to load this data, but using &lt;code&gt;pandas&lt;/code&gt; allows
us to keep the elements of the data together nicely.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;df = pd.read_csv('data.csv'), index_col=0, header=[0,1])
df
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="visualising-the-dataset"&gt;Visualising the dataset&lt;/h2&gt;
&lt;p&gt;Let's plot the raw data to begin with to get an idea of what we're working with.
We can use the build in &lt;code&gt;pandas&lt;/code&gt; plot functions to do this quickly.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;df.plot(kind='line',legend=False, figsize=(12,4))
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="The processed NMR data for our analysis" src="/static/images/tutorials/partial-least-squares-discriminant-analysis-plsda/nmr-data.png"/&gt;&lt;/p&gt;
&lt;p&gt;If you look closely you'll see most of the samples look very alike, but there is
one (in red) that looks very unusual. This will become important later.&lt;/p&gt;
&lt;p&gt;You might be interested to see experimental groups plotted the same color. The
information on experimental groups is stored in the Pandas column &lt;code&gt;MultiIndex&lt;/code&gt;
'Group', and can be retrieved using:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;df.columns.get_level_values('Group')

Index([u'H', u'N', u'N', u'N', u'N', u'N', u'N', u'N', u'N', u'N', u'H', u'H',
        u'H', u'H', u'H', u'H', u'H'],
        dtype='object', name=u'Group')
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As this is basically a list of values ("N" or "H", one for each sample) we
can use this, together with a dictionary colormap, to plot samples from each
group in a single colour.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;colormap = {
    'N': '#ff0000',  # Red
    'H': '#0000ff',  # Blue
}

colorlist = [colormap[c] for c in df.columns.get_level_values('Group')]

df.plot(kind='line', legend=False, figsize=(12,4), color=colorlist)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="The processed NMR data with sample group colours" src="/static/images/tutorials/partial-least-squares-discriminant-analysis-plsda/nmr-data-coloured.png"/&gt;&lt;/p&gt;
&lt;p&gt;Now we can see that the dodgy sample is from the "H" (blue) group.&lt;/p&gt;
&lt;h2 id="building-the-model"&gt;Building the model&lt;/h2&gt;
&lt;p&gt;Let's now perform the PLS-DA. As already described, to do this we need to create a
pseudolinear Y value against which to correlate the samples. Since we have only
two sample groups we can do this very easily.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;y = [g == 'N' for g in df.columns.get_level_values('Group')]
y


[False,
    True,
    True,
    True,
    True,
    True,
    True,
    True,
    True,
    True,
    False,
    False,
    False,
    False,
    False,
    False,
    False]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Next we convert the boolean values into 0 and 1 by setting the &lt;code&gt;dtype&lt;/code&gt; on the array.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;y = np.array(y, dtype=int)
y


array([0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0])
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you have more than 2 groups, but want to perform a similar 2-group analysis,
you can filter the DataFrame using Pandas indexing.&lt;/p&gt;
&lt;p&gt;Next we apply fit a PLS-DA model to our data. We must specify the number of
components, or &lt;em&gt;latent variables&lt;/em&gt; (LVs), to use for our data.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from sklearn.cross_decomposition import PLSRegression
plsr = PLSRegression(n_components=2, scale=False) # (1)
plsr.fit(df.values.T, y) # (2)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;We select 2 components, with autoscaling off.&lt;/li&gt;
&lt;li&gt;The algorithm expects data to be in the transpose, with samples in the
   horizontal axis and features in the vertical.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="scores-and-weights"&gt;Scores and weights&lt;/h2&gt;
&lt;p&gt;The key elements of a PLS-DA are the scores and weights of the model. The &lt;em&gt;scores&lt;/em&gt;
describe the position of each sample in each determined latent variable (LV). Notice
how there are two columns, one for each LV, and 17 rows, one for each sample.
You can see the scores as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;plsr.x_scores_

array([
    [-3.89611177,  0.63670615],
    [ 0.45130405,  0.2670518 ],
    [ 0.57231162,  0.21282613],
    [ 0.49461256,  0.14515445],
    [ 0.56241956,  0.09096893],
    [ 0.6650397 ,  0.27074594],
    [ 0.49613275,  0.35219217],
    [ 0.10264215,  0.51124086],
    [ 0.58655054,  0.27135712],
    [ 0.73518884,  0.11557932],
    [ 0.58786011,  0.08883542],
    [-0.64436773, -0.88135558],
    [-0.5939848 , -0.83145755],
    [-0.5002382 , -0.81607976],
    [ 0.13732602, -0.17700316],
    [ 0.17014101, -0.15633072],
    [ 0.07317358, -0.10043153]
])
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In contrast, the &lt;em&gt;weights&lt;/em&gt; describe the contribution of each variable to each LV.
You can view the shape of the weights as follows. Again there are 2 columns, one
for each LV and 383 rows, one for each variable.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;plsr.x_weights_.shape
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  You can remove the &lt;code&gt;.shape&lt;/code&gt; to see the full list of data, but it's rather long.&lt;/p&gt;
&lt;p&gt;This isn't very enlightening in itself so lets plot these values. Again,
we'll generate a DataFrame to simplify the plotting. As samples are in the rows
in the scores array, we assign an index from &lt;code&gt;df.columns&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;scores = pd.DataFrame(plsr.x_scores_)
scores.index=df.columns

ax = scores.plot(x=0, y=1, kind='scatter', s=50, alpha=0.7,
                    c=colorlist, figsize=(6,6))
ax.set_xlabel('Scores on LV 1')
ax.set_ylabel('Scores on LV 2')
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="PLS-DA Scores plot for Latent variable 1 vs. Latent variable 2" src="/static/images/tutorials/partial-least-squares-discriminant-analysis-plsda/scores-lv1-lv2.png"/&gt;&lt;/p&gt;
&lt;p&gt;So we have good separation between our sample groups, however it is in LV2. While
practically this has little effect, the fact that the major variation in our data
is not between our sample groups is a little alarming. Remember, PLS-DA is a
&lt;em&gt;supervised&lt;/em&gt; approach, whereby the algorithm is specifically tuned to find the
difference beteen the groups. So, what is happening?&lt;/p&gt;
&lt;p&gt;If you think back to when we first plotted the data, there was an ugly looking
sample in the dataset &amp;mdash; a blue spectra that was very different to all the others,
with artefacts and bumps. The variation in this sample is so large that it is
swamping the difference between our samples.&lt;/p&gt;
&lt;p&gt;So, which sample is it. Let's look at the scores DataFrame.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;scores

0         1
Sample Group
101    H     -3.896112  0.636706
103    N      0.451304  0.267052
105    N      0.572312  0.212826
107    N      0.494613  0.145154
109    N      0.562420  0.090969
111    N      0.665040  0.270746
113    N      0.496133  0.352192
115    N      0.102642  0.511241
117    N      0.586551  0.271357
119    N      0.735189  0.115579
85     H      0.587860  0.088835
89     H     -0.644368 -0.881356
91     H     -0.593985 -0.831458
93     H     -0.500238 -0.816080
95     H      0.137326 -0.177003
97     H      0.170141 -0.156331
99     H      0.073174 -0.100432
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We're looking for a sample with a score in LV1 (column 0) of ~ -4.0. The only
sample in the scores with that sort of value is Sample=101, in group "H".
We could also plot sample numbers on the figure as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;scores = pd.DataFrame(plsr.x_scores_)
scores.index=df.columns

ax = scores.plot(x=0, y=1, kind='scatter', s=50, alpha=0.7,
                    c=colorlist, figsize=(6,6))
ax.set_xlabel('Scores on LV 1')
ax.set_ylabel('Scores on LV 2')

for n, (x, y) in enumerate(scores.values):
    label = scores.index.values[n][0]
    ax.text(x,y,label)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Iterating over &lt;code&gt;scores.values&lt;/code&gt; would return a tuple of values, one for each column.
This means we can do &lt;code&gt;for x,y in scores.values:&lt;/code&gt; to get the &lt;code&gt;x,y&lt;/code&gt; values.
However, we also need the index (row) in order to get the label for the sample. To get
an index, we wrap the &lt;code&gt;scores.values&lt;/code&gt; in &lt;code&gt;enumerate&lt;/code&gt;. Each loop generates a tuple, containing
the index and the tuple of &lt;code&gt;x,y&lt;/code&gt; which is then unpacked by &lt;code&gt;(n, (x, y)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="PLS-DA Scores plot for Latent variable 1 vs. Latent variable 2 with sample labels" src="/static/images/tutorials/partial-least-squares-discriminant-analysis-plsda/scores-lv1-lv2-labelled.png"/&gt;&lt;/p&gt;
&lt;p&gt;Now we know which sample is causing the problems, let's remove it.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;f_df = df.iloc[:, df.columns.get_level_values('Sample') != '101']
f_df
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We can do repeat the analysis so far, with the excluded value. Note that we
also need to update the colormap.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;f_colorlist = [colormap[c] for c in f_df.columns.get_level_values('Group')]
f_y = np.array([g == 'N' for g in f_df.columns.get_level_values('Group')], dtype=int)

from sklearn.cross_decomposition import PLSRegression
f_plsr = PLSRegression(n_components=2, scale=False)
f_plsr.fit(f_df.values.T, f_y)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Let's plot the PLS-DA model built using filtered values.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;f_scores = pd.DataFrame(f_plsr.x_scores_)
f_scores.index=f_df.columns

ax = f_scores.plot(x=0, y=1, kind='scatter', s=50, alpha=0.7,
                    c=f_colorlist, figsize=(6,6))
ax.set_xlabel('Scores on LV 1')
ax.set_ylabel('Scores on LV 2')

for n, (x, y) in enumerate(f_scores.values):
    label = f_scores.index.values[n][0]
    ax.text(x,y,label)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="PLS-DA Scores plot for filtered data, LV1 and LV2 with sample labels" src="/static/images/tutorials/partial-least-squares-discriminant-analysis-plsda/scores-lv1-lv2-filtered-labelled.png"/&gt;&lt;/p&gt;
&lt;p&gt;This looks a lot better, the majority of our variation is now in the first latent
variable. You'll notice that one of our samples is still mis-classified, but that's
just science. As there is nothing &lt;em&gt;objectively&lt;/em&gt; wrong with this sample, we just
have to accept that our experiment wasn't perfect.&lt;/p&gt;
&lt;p&gt;However, if we &lt;em&gt;did&lt;/em&gt; want to filter that sample from the DataFrame we could
update our filter code as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;samples_to_filter = ['85','101']
filter_ = [s not in samples_to_filter for s in df.columns.get_level_values('Sample')]
ff_df = df.iloc[:, filter_ ]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</content><category term="nmr"/><category term="python"/></entry><entry><title>1D 1H NMR data processing</title><link href="https://www.martinfitzpatrick.com/1d-1h-nmr-data-processing/" rel="alternate"/><published>2015-10-17T09:00:00+00:00</published><updated>2015-10-17T09:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2015-10-17:/1d-1h-nmr-data-processing/</id><summary type="html">1D &lt;sup&gt;1&lt;/sup&gt;H NMR is a commonly applied to metabolomic studies, being well suited
to untargeted analysis of complex biofluids. It has been successfully applied to
the classification and diagnosis of a number of diseases including [ref].</summary><content type="html">&lt;p&gt;1D &lt;sup&gt;1&lt;/sup&gt;H NMR is a commonly applied to metabolomic studies, being well suited
to untargeted analysis of complex biofluids. It has been successfully applied to
the classification and diagnosis of a number of diseases including [ref].&lt;/p&gt;
&lt;p&gt;There are a number of important steps that must be applied prior 1D &lt;sup&gt;1&lt;/sup&gt;H NMR data
to statistical analysis, all gearded towards ensuring that the variation observed
in the data is representative of real biological variation and not a artifact of
sample handling, metabolite extraction or NMR acquisition. These steps are
described briefly below and approached in turn in the following method. It is
important to familiarise yourself with both the purpose of these steps, and
any associated caveats or limits, so that you can correctly apply them to your
own data.&lt;/p&gt;
&lt;h2 id="setting-up"&gt;Setting up&lt;/h2&gt;
&lt;p&gt;To process raw NMR data we will be making use of of the &lt;code&gt;nmrglue&lt;/code&gt; library. In
addition we will make use of features from the &lt;code&gt;numpy&lt;/code&gt;, &lt;code&gt;scipy&lt;/code&gt; and &lt;code&gt;matplotlib&lt;/code&gt;
libraries, which you should already have installed.
To make sure you can run the following command on the terminal:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;pip install numpy scipy nmrglue matplotlib
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The demo data for this example is &lt;a href="https://download.martinfitzpatrick.com/1d-1h-nmr-data-processing.zip"&gt;available for download&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Download and unzip the file into your data folder.&lt;/p&gt;
&lt;h2 id="loading-bruker-fid-spectra"&gt;Loading Bruker FID spectra&lt;/h2&gt;
&lt;p&gt;Create a new Jupyter notebook using the Python 3 kernel, and in the first cell
enter and run the following. This will import all the neccessary libraries, as
well as using the &lt;code&gt;%matplotlib&lt;/code&gt; magic to display output figures in the notebook.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;%matplotlib inline
import matplotlib.pyplot as plt
import nmrglue as ng
import numpy as np
import scipy as sp
import os
plt.style.use('ggplot')
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Bruker-format data is processed using a digital filter before saving as FID, this
filter must be removed for subsequent analysis to work. To open a given spectra,
pass the parent folder of the &lt;code&gt;fid&lt;/code&gt; file.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;dic, data = ng.fileio.bruker.read('87')
data = ng.bruker.remove_digital_filter(dic, data)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We can plot the spectra using &lt;code&gt;matplotlib&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;fig = plt.figure(figsize=(12,4))
ax = fig.add_subplot(1,1,1)
ax.plot(np.arange(0, data.shape[0]), data) # &amp;lt;1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;We don't have the x-axis &lt;em&gt;ppm&lt;/em&gt; scale yet, so we generate a linear
   scale of the same size as the data. Here &lt;code&gt;.shape[0]&lt;/code&gt; is the length of the fid along
   the first (and only) axis of the 1D spectra.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt="The raw FID data for spectra 87" src="/images/tutorials/1d-1h-nmr-data-processing/single-spectra-raw.png"/&gt;&lt;/p&gt;
&lt;h2 id="fourier-transform-ft"&gt;Fourier transform (FT)&lt;/h2&gt;
&lt;p&gt;A &lt;em&gt;fourier transform&lt;/em&gt; is a signal transformation that decomposes a signal
into it's constituent frequencies. In the case of NMR data, this decomposition
is to a series of peaks, that represent the resonance of chemical subgroups. These
peaks contain both our identification (frequency) and quantification (amplitude)
information.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;nmrglue&lt;/code&gt; package provides a &lt;em&gt;fast fourier transform&lt;/em&gt; (FFT) for this purpose&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;data_fft = ng.proc_base.fft(data)

fig = plt.figure(figsize=(12,4))
ax = fig.add_subplot(1,1,1)
ax.plot(np.arange(0, data_fft.shape[0]), data_fft)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="The fourier transformed FID data for spectra 87" src="/images/tutorials/1d-1h-nmr-data-processing/single-spectra-fft.png"/&gt;&lt;/p&gt;
&lt;p&gt;You'll notice that the spectra is out of &lt;em&gt;phase&lt;/em&gt;, i.e. the peaks are not
symmetrical and well defined. We will fix this shortly, but first lets load
a complete set of spectra so we can view them all at once.&lt;/p&gt;
&lt;h2 id="batch-loading-fft-for-multiple-spectra"&gt;Batch loading + FFT for multiple spectra&lt;/h2&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;root_folder = '.'
zero_fill_size = 32768
data = [] # &amp;lt;1&amp;gt;
for folder in os.listdir(root_folder):
    dic, fid = ng.fileio.bruker.read(os.path.join(root_folder,folder))
    fid = ng.bruker.remove_digital_filter(dic, fid)
    fid = ng.proc_base.zf_size(fid, zero_fill_size) # &amp;lt;2&amp;gt;
    fid = ng.proc_base.rev(fid) # &amp;lt;3&amp;gt;
    fid = ng.proc_base.fft(fid)
    data.append(fid)
data = np.array(data)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;We build as a list, then transform to an array for speed.&lt;/li&gt;
&lt;li&gt;Zero-filling to a power of 2 speeds up FFT.&lt;/li&gt;
&lt;li&gt;Reverse the data to match common representation (for convenience only).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The data is output in a 2D array, with samples along the first axis.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;data.shape

(17, 32768)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We want to be able to plot multiple spectra on the same plot, so we'll write
a simple function to do this.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;def plotspectra(ppms, data, start=None, stop=None):

    if start: # &amp;lt;1&amp;gt;
        ixs = list(ppms).index(start)
        ppms = ppms[ixs:]
        data = data[:,ixs:]
    if stop:
        ixs = list(ppms).index(stop)
        ppms = ppms[:ixs]
        data = data[:,:ixs]


    fig = plt.figure(figsize=(12, 4))
    ax = fig.add_subplot(1,1,1)
    for n in range(data.shape[0]):
        ax.plot(ppms, data[n,:])

    ax.set_xlabel('ppm')
    ax.invert_xaxis()

    return fig
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;This block allows us to select regions of the spectra to view by filtering
   on the supplied ppms.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We can use this function to look at all spectra following fourier transform:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;ppms = np.arange(0, data.shape[1])
fig = plotspectra(ppms, data)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="All spectra following fourier transformation" src="/images/tutorials/1d-1h-nmr-data-processing/all-spectra-fft.png"/&gt;&lt;/p&gt;
&lt;p&gt;We can take a close up view of the TMSP peak, which is currently on the left
hand side of the spectra in the region 28000-30000.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;ppms = np.arange(0, data.shape[1])
fig = plotspectra(ppms, data, start=28000, stop=30000)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="The cropped TMSP region of all spectra following fourier transformation" src="/images/tutorials/1d-1h-nmr-data-processing/all-spectra-crop-fft.png"/&gt;&lt;/p&gt;
&lt;p&gt;Clearly the spectra are all out of phase and poorly aligned. We will fix the
phasing problem first, but lets start off by getting the correct ppm values for
the spectra.&lt;/p&gt;
&lt;h2 id="calculating-ppm-values"&gt;Calculating ppm values&lt;/h2&gt;
&lt;p&gt;The method for calculating ppm values is rather complicted for Bruker format
files. However, the following will give the correct output:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;offset = (float(dic['acqus']['SW']) / 2) - (float(dic['acqus']['O1']) / float(dic['acqus']['BF1']))
start = float(dic['acqus']['SW']) - offset
end = -offset
step = float(dic['acqus']['SW']) / zero_fill_size

ppms = np.arange(start, end, -step)[:zero_fill_size]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We can now plot the spectra with the correct ppms.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;fig = plotspectra(ppms, data)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="All spectra following fourier transformation with correct ppm values" src="/images/tutorials/1d-1h-nmr-data-processing/all-spectra-fft-ppm.png"/&gt;&lt;/p&gt;
&lt;h2 id="phase-correction"&gt;Phase correction&lt;/h2&gt;
&lt;p&gt;The next step is to phase correct all the spectra. The package &lt;code&gt;nmrglue&lt;/code&gt; provides
a few automated algorithms that do a reasonable job with most normal, good quality
spectra. Thankfully, that's what we have here.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;for n in range(0, data.shape[0]):
    data[n, :] = ng.proc_autophase.autops(data[n,:], 'acme')
fig = plotspectra(ppms, data)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Phase corrected spectra" src="/images/tutorials/1d-1h-nmr-data-processing/all-spectra-phase-correct.png"/&gt;&lt;/p&gt;</content><category term="science"/><category term="nmr"/><category term="python"/></entry><entry><title>Automatic phase correction of NMR spectra</title><link href="https://www.martinfitzpatrick.com/automatic-phase-correction-of-nmr-spectra/" rel="alternate"/><published>2014-06-01T15:36:00+00:00</published><updated>2014-06-01T15:36:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2014-06-01:/automatic-phase-correction-of-nmr-spectra/</id><summary type="html">This notebook demonstrates automatic phase correction algorithms implemented for nmrglue. Two standard algorithms are implemented:</summary><content type="html">&lt;p&gt;This notebook demonstrates automatic phase correction algorithms implemented for nmrglue. Two standard algorithms are implemented:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;ACME algorithm by Chen Li et al.
   Journal of Magnetic Resonance 158 (2002) 164-168&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Naive peak minima minimisation&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The outputs for the two algorithms are shown below. Automatic phase correction can be used through the addition of an &lt;code&gt;autops&lt;/code&gt; function to the &lt;code&gt;proc_base&lt;/code&gt; set alongside the algorithm name to employ for scoring of phase. Custom algorithms can be provided via the same parameter.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import nmrglue as ng

dic, data = ng.bruker.read("/Users/mxf793/Data/THPNH/Extract/1d/103/")
data = ng.bruker.remove_digital_filter(dic, data)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;%matplotlib inline
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import matplotlib.pyplot as plt
plt.figure(figsize=(10,4))
plt.plot(data)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;/usr/local/lib/python2.7/site-packages/numpy/core/numeric.py:462: ComplexWarning: Casting complex values to real discards the imaginary part
  return array(a, dtype, copy=False, order=order)





[&amp;lt;matplotlib.lines.Line2D at 0x10cc7d090&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="png" src="https://www.martinfitzpatrick.com/static/tutorials/legacy/automatic-phase-correction-nmr-spectra/automatic-phase-correction-nmr-spectra_3_2.png"  loading="lazy" width="616" height="260"/&gt;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;data_fft = ng.proc_base.fft(data)
plt.figure(figsize=(10,4))
plt.plot(data_fft)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;[&amp;lt;matplotlib.lines.Line2D at 0x10d0588d0&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="png" src="https://www.martinfitzpatrick.com/static/tutorials/legacy/automatic-phase-correction-nmr-spectra/automatic-phase-correction-nmr-spectra_4_1.png"  loading="lazy" width="616" height="260"/&gt;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;data_pc_pm = ng.proc_base.autops(data_fft,'peak_minima')
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;Optimization terminated successfully.
         Current function value: 0.000037
         Iterations: 90
         Function evaluations: 165
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;plt.figure(figsize=(10,4))
plt.plot(data_pc_pm)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;[&amp;lt;matplotlib.lines.Line2D at 0x10d2e6710&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="png" src="https://www.martinfitzpatrick.com/static/tutorials/legacy/automatic-phase-correction-nmr-spectra/automatic-phase-correction-nmr-spectra_6_1.png"  loading="lazy" width="616" height="260"/&gt;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;data_pc_acme = ng.proc_base.autops(data_fft,'acme')
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;Warning: Maximum number of function evaluations has been exceeded.
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;plt.figure(figsize=(10,4))
plt.plot(data_pc_acme)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;[&amp;lt;matplotlib.lines.Line2D at 0x10d4db850&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="png" src="https://www.martinfitzpatrick.com/static/tutorials/legacy/automatic-phase-correction-nmr-spectra/automatic-phase-correction-nmr-spectra_8_1.png"  loading="lazy" width="616" height="260"/&gt;&lt;/p&gt;</content><category term="science"/><category term="python"/></entry><entry><title>NMRLab 1D NMR processing (MATLAB)</title><link href="https://www.martinfitzpatrick.com/nmrlab-1d-nmr-processing-matlab/" rel="alternate"/><published>2014-05-28T11:14:00+00:00</published><updated>2014-05-28T11:14:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2014-05-28:/nmrlab-1d-nmr-processing-matlab/</id><summary type="html">This notebook uses a subset of the available processing features in NMRLab (+Metabolab) to process 1D NMR spectra. The output is saved as a CSV file that can be imported into &lt;code&gt;pandas&lt;/code&gt;, PLS_Toolbox or any other package for subsequent analysis.</summary><content type="html">&lt;p&gt;This notebook uses a subset of the available processing features in NMRLab (+Metabolab) to process 1D NMR spectra. The output is saved as a CSV file that can be imported into &lt;code&gt;pandas&lt;/code&gt;, PLS_Toolbox or any other package for subsequent analysis.&lt;/p&gt;
&lt;p&gt;To use this workbook you will need an installation of &lt;a href="http://www.mathworks.co.uk/products/matlab/"&gt;MATLAB&lt;/a&gt; and a license for the &lt;a href="http://beregond.bham.ac.uk/nmrlab/"&gt;NMRLab&lt;/a&gt; software (available free). To use segmental alignment (recommended) you will also need to install the &lt;a href="http://www.models.life.ku.dk/icoshift"&gt;icoshift&lt;/a&gt; package for MATLAB.&lt;/p&gt;
&lt;p&gt;For more information on NMRLab/Metabolab see the following references:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;U.L. G&amp;uuml;nther, C. Ludwig, H. R&amp;uuml;terjans &lt;a href="http://dx.doi.org/10.1006/jmre.2000.2071"&gt;NMRLAB - Advanced NMR Data Processing in Matlab&lt;/a&gt;. J Magn Reson, 145(2), 201-208 (2000)&lt;/li&gt;
&lt;li&gt;C. Ludwig and U. G&amp;uuml;nther &lt;a href="http://dx.doi.org/10.1186/1471-2105-12-366"&gt;MetaboLab - advanced NMR data processing and analysis for metabolomics&lt;/a&gt;. BMC Bioinformatics, 12, 366, (2011)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="configure-variables"&gt;Configure variables&lt;/h2&gt;
&lt;p&gt;Edit paths before for the data to process. The path should be the folder containing the experiment folders (1,2,3,4 etc.).&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;base_path = '/Users/mxf793/Data/THPNH/Extract/1d'
output_file = '/Users/mxf793/Data/THPNH/Extract/1d/processed.csv'
# include_match = []
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="setting-up-widget-interaction"&gt;Setting up widget interaction&lt;/h2&gt;
&lt;p&gt;We define a quick callback to pass widget values into global variables, allowing us to then pass these into &lt;code&gt;%%matlab&lt;/code&gt; magic cells. We also define all the default config options used by widgets later, changes overwrite these defaults so widgets will not reset.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from IPython.html.widgets import interact, interactive, fixed
from IPython.html import widgets

def widget_callback(**kwargs):
    for k,v in kwargs.items():
        globals()[k] = v

spa_ref_spec_no=1
spa_max_shift=22

bc_algo=1
bc_npts=20
bc_tau=20
bc_par=6
bc_bas_noise=0

rea_ref_spec_no=1
rea_max_shift=22
rea_algo=1

bi_bin_size=0.05
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="set-up-matlab-connection"&gt;Set up MATLAB connection&lt;/h2&gt;
&lt;p&gt;First we start up an instance of MATLAB server and connect to it - this will take a few moments. If it succeeds you will get a message returned &lt;code&gt;MATLAB started and connected!&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;%load_ext pymatbridge
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;Starting MATLAB on http://localhost:50397
 visit http://localhost:50397/exit.m to shut down same
....MATLAB started and connected!
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="initialise-nmrlab-and-global-variables"&gt;Initialise NMRLab and global variables&lt;/h2&gt;
&lt;p&gt;We initialise MATLAB paths, and call the init to set up the environment for subsequent calls. We also pass in a number of variables from the Python environment, including the base_path for the data files, and a list of folders condtaining data ('fids') for processing.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;%%matlab
NMRPAR.CURSET=[1,1];
nmrlab_init;
nmrlab_globals;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;{Warning: The value of local variables may have been changed to match the
         globals.  Future versions of MATLAB will require that you declare
         a variable to be global before you use that variable.}
&amp;gt; In /Users/mxf793/MATLAB/Toolbox/nmrlab/nmrlab_init.p&amp;gt;nmrlab_init at 14
  In web_eval at 44
  In webserver at 241
*********************************************
*****    Welcome to  NMRLAB 3.5.0.0        ****
*********************************************

Setting global variables.
This computer is a MACI64
0.635
Running MATLAB version 7.1.
New swappath: /private/tmp/mxf793/15
Defining Paths for NMRLAB ...
Loading NMRDAT_EMPTY.

*********************************************

WAVLABPATH /Applications/MATLAB_R2011a_Student.app/wavelab does not exist.
The default path is MATLABHOME /wavelab
If you have installed wavelab elsewhere, set the WAVELETPATH
variable in nmrlab.m
WaveLab v .802 Setup Complete
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="png" src="https://www.martinfitzpatrick.com/static/tutorials/legacy/nmrlab-1d-nmr-processing/nmrlab-1d-nmr-processing_8_1.png"  loading="lazy" width="576" height="432"/&gt;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import os

base_path = '/Users/mxf793/Data/THPNH/Extract/1d'
exclude = ['99999','9999','999']
folders = set(os.path.basename(folder) for folder, subfolders, files in os.walk(base_path) for file_ in files if file_=='fid' and os.path.basename(folder) not in exclude)
folders = list(folders)

# Sort folders (numerically)
folders.sort(key=int)

expfolders = folders
pathname_separator ='/'
nspc = len(expfolders)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;%%matlab -i base_path,output_file,pathname_separator,expfolders
expfolders = str2cell(expfolders)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;expfolders =
    '87'
    '89'
    '91'
    '93'
    '95'
    '97'
    '99'
    '101'
    '103'
    '105'
    '107'
    '109'
    '111'
    '113'
    '115'
    '117'
    '119'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;%%matlab
%Dim 1
global h_DR

NMRPAR = struct();
NMRPAR.PATHNAMESEPARATOR = pathname_separator;

procSet = 1;
if(ishandle(h_DR))
    if(isfield(handles,'t_procSet'))
        set(handles.t_procSet,'visible','on');
        procSet = 1;
    end
end
pathBase{1} = base_path;
dataSet = [1 ];
dataType = 'B';
NMRPAR.CURSET = [dataSet(1),1];
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;%%matlab
expName = [];
for k = 1:length(expfolders)
    expName{1}{k} = expfolders{k};
end
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="loading-spectra-fids"&gt;Loading spectra FIDs&lt;/h2&gt;
&lt;p&gt;The next step is to load all the spectra FIDs into MATLAB for processing. This may take a moment.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;%%matlab
expCounter = ones(length(dataSet),1);
for k = 1:length(expName)
    dataSet2 = find(dataSet(k)==dataSet);
    for l = 1:length(expName{k})
        pathname = [pathBase{k} NMRPAR.PATHNAMESEPARATOR expName{k}{l} NMRPAR.PATHNAMESEPARATOR];
        re1d2(pathname,dataSet(k),expCounter(k),pathname,dataType);
        expCounter(dataSet2) = expCounter(dataSet2) + 1;
        fclose('all');
    end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;re(/Users/mxf793/Data/THPNH/Extract/1d/87/,1,1,/Users/mxf793/Data/THPNH/Extract/1d/87/,B)
0D dataset identified.
Reading acqus.
ans =
     0
ans =
     0
Parameters from BRUKER acqus files:

Update PROC and DISP
TD1 = 32768
SFO1 = 600.1328
SWH1 = 7194.2446
Reading FID ...
Real-&amp;gt;complex conversion.
tline2 =

tline2 =
sy0207noesy H2O+D2O /opt/topspin3.0/data/youngsp/nmr youngsp {3  D6 - 344}
re(/Users/mxf793/Data/THPNH/Extract/1d/89/,1,2,/Users/mxf793/Data/THPNH/Extract/1d/89/,B)
0D dataset identified.
Reading acqus.
ans =
     0
ans =
     0
Parameters from BRUKER acqus files:

Update PROC and DISP
TD1 = 32768
SFO1 = 600.1328
SWH1 = 7194.2446
Reading FID ...
Real-&amp;gt;complex conversion.
tline2 =

tline2 =
sy0207noesy H2O+D2O /opt/topspin3.0/data/youngsp/nmr youngsp {3  E6 - 345}
tline2 =
TopShimResult: 0.36
re(/Users/mxf793/Data/THPNH/Extract/1d/91/,1,3,/Users/mxf793/Data/THPNH/Extract/1d/91/,B)
0D dataset identified.
Reading acqus.
ans =
     0
ans =
     0
Parameters from BRUKER acqus files:

Update PROC and DISP
TD1 = 32768
SFO1 = 600.1328
SWH1 = 7194.2446
Reading FID ...
Real-&amp;gt;complex conversion.
tline2 =

tline2 =
sy0207noesy H2O+D2O /opt/topspin3.0/data/youngsp/nmr youngsp {3  F6 - 346}
tline2 =
TopShimResult: 0.29
re(/Users/mxf793/Data/THPNH/Extract/1d/93/,1,4,/Users/mxf793/Data/THPNH/Extract/1d/93/,B)
0D dataset identified.
Reading acqus.
ans =
     0
ans =
     0
Parameters from BRUKER acqus files:

Update PROC and DISP
TD1 = 32768
SFO1 = 600.1328
SWH1 = 7194.2446
Reading FID ...
Real-&amp;gt;complex conversion.
tline2 =

tline2 =
sy0207noesy H2O+D2O /opt/topspin3.0/data/youngsp/nmr youngsp {3  G6 - 347}
tline2 =
TopShimResult: 0.33
re(/Users/mxf793/Data/THPNH/Extract/1d/95/,1,5,/Users/mxf793/Data/THPNH/Extract/1d/95/,B)
0D dataset identified.
Reading acqus.
ans =
     0
ans =
     0
Parameters from BRUKER acqus files:

Update PROC and DISP
TD1 = 32768
SFO1 = 600.1328
SWH1 = 7194.2446
Reading FID ...
Real-&amp;gt;complex conversion.
tline2 =

tline2 =
sy0207noesy H2O+D2O /opt/topspin3.0/data/youngsp/nmr youngsp {3  H6 - 348}
tline2 =
TopShimResult: 0.39
re(/Users/mxf793/Data/THPNH/Extract/1d/97/,1,6,/Users/mxf793/Data/THPNH/Extract/1d/97/,B)
0D dataset identified.
Reading acqus.
ans =
     0
ans =
     0
Parameters from BRUKER acqus files:

Update PROC and DISP
TD1 = 32768
SFO1 = 600.1328
SWH1 = 7194.2446
Reading FID ...
Real-&amp;gt;complex conversion.
tline2 =

tline2 =
sy0207noesy H2O+D2O /opt/topspin3.0/data/youngsp/nmr youngsp {3  A7 - 349}
tline2 =
TopShimResult: 0.37
re(/Users/mxf793/Data/THPNH/Extract/1d/99/,1,7,/Users/mxf793/Data/THPNH/Extract/1d/99/,B)
0D dataset identified.
Reading acqus.
ans =
     0
ans =
     0
Parameters from BRUKER acqus files:

Update PROC and DISP
TD1 = 32768
SFO1 = 600.1328
SWH1 = 7194.2446
Reading FID ...
Real-&amp;gt;complex conversion.
tline2 =

tline2 =
sy0207noesy H2O+D2O /opt/topspin3.0/data/youngsp/nmr youngsp {3  B7 - 350}
tline2 =
TopShimResult: 0.38
re(/Users/mxf793/Data/THPNH/Extract/1d/101/,1,8,/Users/mxf793/Data/THPNH/Extract/1d/101/,B)
0D dataset identified.
Reading acqus.
ans =
     0
ans =
     0
Parameters from BRUKER acqus files:

Update PROC and DISP
TD1 = 32768
SFO1 = 600.1328
SWH1 = 7194.2446
Reading FID ...
Real-&amp;gt;complex conversion.
tline2 =

tline2 =
sy0207noesy H2O+D2O /opt/topspin3.0/data/youngsp/nmr youngsp {3  C7 - 351}
tline2 =
TopShimResult: 0.33
re(/Users/mxf793/Data/THPNH/Extract/1d/103/,1,9,/Users/mxf793/Data/THPNH/Extract/1d/103/,B)
0D dataset identified.
Reading acqus.
ans =
     0
ans =
     0
Parameters from BRUKER acqus files:

Update PROC and DISP
TD1 = 32768
SFO1 = 600.1328
SWH1 = 7194.2446
Reading FID ...
Real-&amp;gt;complex conversion.
tline2 =

tline2 =
sy0207noesy H2O+D2O /opt/topspin3.0/data/youngsp/nmr youngsp {3  D7 - 352}
tline2 =
TopShimResult: 0.37
re(/Users/mxf793/Data/THPNH/Extract/1d/105/,1,10,/Users/mxf793/Data/THPNH/Extract/1d/105/,B)
0D dataset identified.
Reading acqus.
ans =
     0
ans =
     0
Parameters from BRUKER acqus files:

Update PROC and DISP
TD1 = 32768
SFO1 = 600.1328
SWH1 = 7194.2446
Reading FID ...
Real-&amp;gt;complex conversion.
tline2 =

tline2 =
sy0207noesy H2O+D2O /opt/topspin3.0/data/youngsp/nmr youngsp {3  E7 - 353}
tline2 =
TopShimResult: 0.35
re(/Users/mxf793/Data/THPNH/Extract/1d/107/,1,11,/Users/mxf793/Data/THPNH/Extract/1d/107/,B)
0D dataset identified.
Reading acqus.
ans =
     0
ans =
     0
Parameters from BRUKER acqus files:

Update PROC and DISP
TD1 = 32768
SFO1 = 600.1328
SWH1 = 7194.2446
Reading FID ...
Real-&amp;gt;complex conversion.
tline2 =

tline2 =
sy0207noesy H2O+D2O /opt/topspin3.0/data/youngsp/nmr youngsp {3  F7 - 354}
tline2 =
TopShimResult: 0.39
re(/Users/mxf793/Data/THPNH/Extract/1d/109/,1,12,/Users/mxf793/Data/THPNH/Extract/1d/109/,B)
0D dataset identified.
Reading acqus.
ans =
     0
ans =
     0
Parameters from BRUKER acqus files:

Update PROC and DISP
TD1 = 32768
SFO1 = 600.1328
SWH1 = 7194.2446
Reading FID ...
Real-&amp;gt;complex conversion.
tline2 =

tline2 =
sy0207noesy H2O+D2O /opt/topspin3.0/data/youngsp/nmr youngsp {3  G7 - 355}
tline2 =
TopShimResult: 0.49
re(/Users/mxf793/Data/THPNH/Extract/1d/111/,1,13,/Users/mxf793/Data/THPNH/Extract/1d/111/,B)
0D dataset identified.
Reading acqus.
ans =
     0
ans =
     0
Parameters from BRUKER acqus files:

Update PROC and DISP
TD1 = 32768
SFO1 = 600.1328
SWH1 = 7194.2446
Reading FID ...
Real-&amp;gt;complex conversion.
tline2 =

tline2 =
sy0207noesy H2O+D2O /opt/topspin3.0/data/youngsp/nmr youngsp {3  H7 - 356}
tline2 =
TopShimResult: 0.35
re(/Users/mxf793/Data/THPNH/Extract/1d/113/,1,14,/Users/mxf793/Data/THPNH/Extract/1d/113/,B)
0D dataset identified.
Reading acqus.
ans =
     0
ans =
     0
Parameters from BRUKER acqus files:

Update PROC and DISP
TD1 = 32768
SFO1 = 600.1328
SWH1 = 7194.2446
Reading FID ...
Real-&amp;gt;complex conversion.
tline2 =

tline2 =
sy0207noesy H2O+D2O /opt/topspin3.0/data/youngsp/nmr youngsp {3  A8 - 357}
re(/Users/mxf793/Data/THPNH/Extract/1d/115/,1,15,/Users/mxf793/Data/THPNH/Extract/1d/115/,B)
0D dataset identified.
Reading acqus.
ans =
     0
ans =
     0
Parameters from BRUKER acqus files:

Update PROC and DISP
TD1 = 32768
SFO1 = 600.1328
SWH1 = 7194.2446
Reading FID ...
Real-&amp;gt;complex conversion.
tline2 =

tline2 =
sy0207noesy H2O+D2O /opt/topspin3.0/data/youngsp/nmr youngsp {3  B8 - 358}
tline2 =
TopShimResult: 0.63
re(/Users/mxf793/Data/THPNH/Extract/1d/117/,1,16,/Users/mxf793/Data/THPNH/Extract/1d/117/,B)
0D dataset identified.
Reading acqus.
ans =
     0
ans =
     0
Parameters from BRUKER acqus files:

Update PROC and DISP
TD1 = 32768
SFO1 = 600.1328
SWH1 = 7194.2446
Reading FID ...
Real-&amp;gt;complex conversion.
tline2 =

tline2 =
sy0207noesy H2O+D2O /opt/topspin3.0/data/youngsp/nmr youngsp {3  C8 - 359}
tline2 =
TopShimResult: 0.52
re(/Users/mxf793/Data/THPNH/Extract/1d/119/,1,17,/Users/mxf793/Data/THPNH/Extract/1d/119/,B)
0D dataset identified.
Reading acqus.
ans =
     0
ans =
     0
Parameters from BRUKER acqus files:

Update PROC and DISP
TD1 = 32768
SFO1 = 600.1328
SWH1 = 7194.2446
Reading FID ...
Real-&amp;gt;complex conversion.
tline2 =

tline2 =
sy0207noesy H2O+D2O /opt/topspin3.0/data/youngsp/nmr youngsp {3  D8 - 360}
tline2 =
TopShimResult: 0.33
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="importing-spectra-and-pre-processing"&gt;Importing Spectra and Pre-processing&lt;/h2&gt;
&lt;p&gt;The next step processes the input data automatically. Note that this will take quite some time if you're running with a large number of spectra at the same time. Run the cell and wait. Once the processing is completed a plot figure will be shown of all imported spectra.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;%%matlab
phc0 = 0;
phc1 = 0;
dataSet2a = unique(dataSet);
for l = 1:length(dataSet2a)
    dataSet2 = dataSet2a(l);
    nspc = NMRDAT(dataSet2,1).ACQUS(1).NE;
    for k = 1:nspc
        fid_npts                        = length(NMRDAT(dataSet2,k).SER);
        zf                              = 2*2^nextpow2(fid_npts);
        swh                             = NMRDAT(dataSet2,k).ACQUS(1).SW_h;
        sfo1                            = NMRDAT(dataSet2,k).ACQUS(1).SFO1;
        NMRPAR.CURSET                   = [dataSet2,k];
        NMRDAT(dataSet2,k).ACQUS(1).DIM  = 1;
        NMRDAT(dataSet2,k).PROC(1).GIBBS = 1;
        NMRDAT(dataSet2,k).PROC(1).WDWF  = 'EM';
        NMRDAT(dataSet2,k).PROC(1).LB    = 0.3;
        NMRDAT(dataSet2,k).PROC(1).BC    = 32;
        NMRDAT(dataSet2,k).PROC(1).PH0   = phc0;
        NMRDAT(dataSet2,k).PROC(1).PH1   = phc1;
        NMRDAT(dataSet2,k).PROC(1).SMO   = 2;
        NMRDAT(dataSet2,k).PROC(1).SOL   = [32 32 0];
        NMRDAT(dataSet2,k).PROC(1).AUTOPHASE    = 1;
        NMRDAT(dataSet2,k).PROC(1).ZF    = zf;
        NMRDAT(dataSet2,k).PROC(1).REF   = [4.7458, zf/2, zf, swh, sfo1];
        NMRDAT(dataSet2,k).DISP.ax_typ   = 1;
        NMRDAT(dataSet2,k).DISP.PLOT     = 0;
        xfb(1,1);
        ref_tmsp;
        apk4;
    end
end
NMRPAR.CURSET = [dataSet(1),1];

% Set up some global vars
ppm = linspace(11,-1,32768);
nppm = size(ppm,2)
spc = [];

pppm = ppm;
pspc = spc;

% Output a figure showing all plots
clf;
hold on;

for k = 1:nspc
    spci = real( NMRDAT(1,k).MAT );
    spc = [spc; spci'];
    plot(ppm, spci);
end

spc1 = spc';

set(gca,'XDir','Reverse');

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,1).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,1).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
tEnd =
    2.3581
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,1).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 444 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,1).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 444 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
range =
    0.4000
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,1).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 431.928 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,1).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 432.928 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,1).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 92.5369 -21.7569 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,1).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 80.1763 -8.0423 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,1).
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 80.1763 -8.0423 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,2).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,2).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
tEnd =
    1.8321
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,2).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 109 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,2).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 109 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
range =
    0.4000
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,2).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 77.77 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,2).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 78.97 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,2).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 165.1121 -95.6301 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 78.97 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,2).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 106.7156 -30.8016 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,2).
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 106.7156 -30.8016 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,3).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,3).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
tEnd =
    1.9090
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,3).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 67 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,3).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 67 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
range =
    0.4000
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,3).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 61.47 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,3).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 63.07 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,3).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 63.07 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,3).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 67.7947 -5.2467 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,3).
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 67.7947 -5.2467 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,4).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,4).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
tEnd =
    2.0172
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,4).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 75 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,4).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 75 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
range =
    0.4000
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,4).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 69.312 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,4).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 70.312 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,4).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 81.6118 -12.5478 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,4).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 169.2485 -109.8635 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 81.6118 -12.5478 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,4).
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 81.6118 -12.5478 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,5).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,5).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
tEnd =
    1.7971
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,5).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 91 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,5).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 91 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
range =
    0.4000
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,5).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 74.928 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,5).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 75.728 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,5).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 94.2839 -20.6039 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,5).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 270.909 -216.723 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 94.2839 -20.6039 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,5).
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 94.2839 -20.6039 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,6).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,6).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
tEnd =
    2.0382
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,6).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 88 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,6).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 88 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
range =
    0.4000
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,6).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 83.596 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,6).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 84.796 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,6).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 95.7437 -12.1597 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,6).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 277.3022 -213.8182 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 95.7437 -12.1597 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,6).
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 95.7437 -12.1597 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,7).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,7).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
tEnd =
    1.8126
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,7).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 82 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,7).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 82 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
range =
    0.4000
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,7).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 84.638 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,7).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 85.838 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,7).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 85.838 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,7).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 235.0558 -165.7318 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 85.838 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,7).
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 85.838 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,8).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,8).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
tEnd =
    2.5376
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,8).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 429 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,8).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 429 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
range =
    0.4000
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,8).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 432.508 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,8).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 433.508 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,8).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 433.508 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,8).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 165.3203 -101.9283 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 433.508 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,8).
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 433.508 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,9).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,9).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
tEnd =
    1.8255
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,9).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 71 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,9).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 71 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
range =
    0.4000
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,9).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 71.328 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,9).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 72.328 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,9).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 72.328 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,9).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 253.4552 -201.2272 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 72.328 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,9).
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 72.328 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,10).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,10).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
tEnd =
    2.2389
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,10).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 69 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,10).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 69 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
range =
    0.4000
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,10).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 80.132 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,10).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 81.332 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,10).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 94.9834 -15.1694 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,10).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 213.8976 -147.3066 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 94.9834 -15.1694 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,10).
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 94.9834 -15.1694 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,11).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,11).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
tEnd =
    2.0196
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,11).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 79 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,11).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 79 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
range =
    0.4000
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,11).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 79.508 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,11).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 80.708 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,11).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 96.8476 -17.9276 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,11).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 241.1291 -178.1931 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 96.8476 -17.9276 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,11).
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 96.8476 -17.9276 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,12).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,12).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
tEnd =
    3.1141
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,12).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 434 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,12).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 434 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
range =
    0.4000
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,12).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 429.902 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,12).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 431.102 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,12).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 82.2845 -12.4125 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,12).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 204.9921 -148.6171 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 82.2845 -12.4125 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,12).
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 82.2845 -12.4125 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,13).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,13).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
tEnd =
    1.7759
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,13).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 71 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,13).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 71 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
range =
    0.4000
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,13).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 59.218 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,13).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 60.418 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,13).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 60.418 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,13).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 92.2217 -35.3007 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 60.418 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,13).
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 60.418 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,14).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,14).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
tEnd =
    1.7551
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,14).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 434 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,14).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 434 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
range =
    0.4000
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,14).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 421.032 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,14).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 421.832 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 421.032 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,14).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 77.6853 -18.4863 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,14).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 78.4757 -19.3637 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,14).
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 78.4757 -19.3637 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,15).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,15).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
tEnd =
    2.4110
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,15).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 445 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,15).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 445 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
range =
    0.4000
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,15).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 442.384 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,15).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 442.784 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 442.384 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,15).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 97.8035 -17.1225 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,15).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 191.2078 -120.8428 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 97.8035 -17.1225 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,15).
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 97.8035 -17.1225 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,16).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,16).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
tEnd =
    2.0406
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,16).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 89 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,16).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 89 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
range =
    0.4000
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,16).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 82.416 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,16).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 82.816 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,16).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 94.2529 -12.7009 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,16).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 221.6779 -154.2089 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 94.2529 -12.7009 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,16).
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 94.2529 -12.7009 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,17).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,17).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 0 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
tEnd =
    2.7352
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,17).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 436 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,17).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 436 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
range =
    0.4000
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,17).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 442.222 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,17).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 444.022 0 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,17).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 96.4833 -13.8343 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,17).
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 220.3428 -151.3408 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
Avance 3 data.
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 96.4833 -13.8343 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
==============================================
XFB - Two-dimensional data processing.
XFB will be run for data set (1,17).
Avance 3 data.
SOL in DIM 1. SOL(32,32,0,76).
BC(32).
DIM 1 window function: EM, 0.3, 7194.2446, 76
GIBBS in DIM 1
ZF 32768 in DIM 1.
Avance 3 data.
DFT in DIM 1
Phase 96.4833 -13.8343 in DIM 1
Matrix transposed.
Processing DIM 1 done.
----------------------------------------------
You may want to delete your SER file now.
nppm =
       32768
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="png" src="https://www.martinfitzpatrick.com/static/tutorials/legacy/nmrlab-1d-nmr-processing/nmrlab-1d-nmr-processing_16_1.png"  loading="lazy" width="576" height="432"/&gt;&lt;/p&gt;
&lt;h2 id="processing"&gt;Processing&lt;/h2&gt;
&lt;p&gt;To display the result of each step we define a simple MATLAB plot of mean and median spectra. This is usually enough to spot major issues in the spectra and see the improvements during processing. The output is captured and displayed using the ImageOut class we defined earlier.&lt;/p&gt;
&lt;p&gt;The plot below shows the starting state of the spectra before processing.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;%matlab pspc = spc1; pppm = ppm; spcmedian = median(pspc, 2); spcmean = mean(pspc, 2); clf; hold on; plot(pppm, spcmedian, 'k'); plot(pppm, spcmean, 'r'); set(gca,'XDir','Reverse');
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="png" src="https://www.martinfitzpatrick.com/static/tutorials/legacy/nmrlab-1d-nmr-processing/nmrlab-1d-nmr-processing_18_0.png"  loading="lazy" width="576" height="432"/&gt;&lt;/p&gt;
&lt;h2 id="spectra-alignment-tmsp"&gt;Spectra alignment (TMSP)&lt;/h2&gt;
&lt;p&gt;We next apply an alignment on the TMSP standard to account for variation in the position of peaks arising during acquisition. Note that this does not account for differences in peak positions arising from pH (where peaks shift independently), this will be accounted for later using segmental shift (icoshift algorithm).&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;i = interact(widget_callback,
         spa_ref_spec_no=widgets.IntSliderWidget(min=1, max=nspc, step=1, value=spa_ref_spec_no, description="Reference spc:"),
         spa_max_shift=widgets.IntSliderWidget(min=1, max=100, step=1, value=spa_max_shift, description="Max shift"),
         )
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;%%matlab -i spa_ref_spec_no,spa_max_shift
[spc2, nul] = spcalign_tmsp(spc1, 1, 22, [150:155], 1);

pspc = spc2; pppm = ppm;
spcmedian = median(pspc, 2); spcmean = mean(pspc, 2); clf; hold on; plot(pppm, spcmedian, 'k'); plot(pppm, spcmean, 'r'); set(gca,'XDir','Reverse');
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="png" src="https://www.martinfitzpatrick.com/static/tutorials/legacy/nmrlab-1d-nmr-processing/nmrlab-1d-nmr-processing_21_0.png"  loading="lazy" width="576" height="432"/&gt;&lt;/p&gt;
&lt;h2 id="baseline-correction"&gt;Baseline correction&lt;/h2&gt;
&lt;p&gt;Remove variability in the baseline using one of the pre-defined NMRLab methods. The defaults here use a spline baseline of 20 points along the spectra.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;i = interact(widget_callback,
         bc_algo=widgets.DropdownWidget(values=[1,2,3,4,5,6,7,8], value=bc_algo, description="Algorithm"),
         bc_npts=widgets.IntSliderWidget(min=1, max=50, step=1, value=bc_npts, description="Reference spc:"),

         bc_tau=widgets.IntSliderWidget(min=1, max=50, step=1, value=bc_tau, description="Tau:"),
         bc_par=widgets.IntSliderWidget(min=1, max=10, step=1, value=bc_par, description="Poly order"),
         bc_bas_noise=widgets.FloatSliderWidget(min=1, max=10, step=0.01, value=bc_bas_noise, description="Baseline noise"),
         )
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)

&amp;lt;ipython-input-1-cb62b35ad0d7&amp;gt; in &amp;lt;module&amp;gt;()
----&amp;gt; 1 i = interact(widget_callback,
      2          bc_algo=widgets.DropdownWidget(values=[1,2,3,4,5,6,7,8], value=bc_algo, description="Algorithm"),
      3          bc_npts=widgets.IntSliderWidget(min=1, max=50, step=1, value=bc_npts, description="Reference spc:"),
      4
      5          bc_tau=widgets.IntSliderWidget(min=1, max=50, step=1, value=bc_tau, description="Tau:"),


NameError: name 'interact' is not defined
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;%%matlab -i bc_algo,bc_npts,bc_tau,bc_par,bc_bas_noise
[spc3, baseline, is_baseline] = baselinenl(spc2, bc_npts, double(bc_tau), double(bc_par), 0, bc_algo, bc_bas_noise);

spc3 = reshape(spc3, size(ppm,2), nspc);

pspc = spc3; pppm = ppm;
spcmedian = median(pspc, 2); spcmean = mean(pspc, 2); clf; hold on; plot(pppm, spcmedian, 'k'); plot(pppm, spcmean, 'r'); set(gca,'XDir','Reverse');
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="png" src="https://www.martinfitzpatrick.com/static/tutorials/legacy/nmrlab-1d-nmr-processing/nmrlab-1d-nmr-processing_24_0.png"  loading="lazy" width="576" height="432"/&gt;&lt;/p&gt;
&lt;h2 id="scale-spectra-tmsp"&gt;Scale spectra (TMSP)&lt;/h2&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;%%matlab
spc4 = spcscale(spc3,1,1);

pspc = spc4; pppm = ppm;
spcmedian = median(pspc, 2); spcmean = mean(pspc, 2); clf; hold on; plot(pppm, spcmedian, 'k'); plot(pppm, spcmean, 'r'); set(gca,'XDir','Reverse');
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="png" src="https://www.martinfitzpatrick.com/static/tutorials/legacy/nmrlab-1d-nmr-processing/nmrlab-1d-nmr-processing_26_0.png"  loading="lazy" width="576" height="432"/&gt;&lt;/p&gt;
&lt;h2 id="exclude-regions-water-tmsp-ppm10"&gt;Exclude regions (Water, TMSP, &amp;lt;ppm10)&lt;/h2&gt;
&lt;p&gt;We next remove regions from the spectra that are detrimental to subsequent analysis: first the water region in the middle of the spectra, then TMSP and finally &amp;lt;10ppm (extreme ends of spectra). These are removed so that noise variation does not interfere with scaling and multivariate analysis.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;%%matlab -o ex_max,ex_1_end,ex_2_start,ex_2_end,ex_3_start
ex_max = size(ppm,2); ex_1_end = find(ppm&amp;lt;10, 1); ex_2_start = find(ppm&amp;lt;6, 1); ex_2_end = find(ppm&amp;lt;4.5, 1); ex_3_start = find(ppm&amp;lt;0.5, 1);
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;i = interact(widget_callback,
         # Default remove &amp;lt;10 ppm
         ex_1_end=widgets.FloatSliderWidget(min=1, max=int(ex_max), step=1, value=int(ex_1_end), description="Region (1) end"),
         # Default remove roughly ppm 4.5-6
         ex_2_start=widgets.FloatSliderWidget(min=1, max=int(ex_max), step=1, value=int(ex_2_start), description="Region (2) start"),
         ex_2_end=widgets.FloatSliderWidget(min=1, max=int(ex_max), step=1, value=int(ex_2_end), description="Region (2) end"),
         # Default remove TMSP
         ex_3_start=widgets.FloatSliderWidget(min=1, max=int(ex_max), step=1, value=int(ex_3_start), description="Region (3) start"),
         )
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;%%matlab -i ex_1_end,ex_2_start,ex_2_end,ex_3_start
excludes = zeros(size(ppm,2),1);
excludes(1:ex_1_end) = 1; excludes(ex_2_start:ex_2_end) = 1; excludes(ex_3_start:end) = 1;
excludes = logical(excludes);

spc5 = spcexclude(spc4,excludes,1);
ppme = spcexclude(ppm',excludes,1)';

pspc = spc5; pppm = ppme;
spcmedian = median(pspc, 2); spcmean = mean(pspc, 2); clf; hold on; plot(pppm, spcmedian, 'k'); plot(pppm, spcmean, 'r'); set(gca,'XDir','Reverse');
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="png" src="https://www.martinfitzpatrick.com/static/tutorials/legacy/nmrlab-1d-nmr-processing/nmrlab-1d-nmr-processing_30_0.png"  loading="lazy" width="576" height="432"/&gt;&lt;/p&gt;
&lt;h2 id="realign-nmr-spectra"&gt;Realign NMR spectra&lt;/h2&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;i = interact(widget_callback,
         rea_ref_spec_no=widgets.IntSliderWidget(min=1, max=nspc, step=1, value=rea_ref_spec_no, description="Reference spc:"),
         rea_max_shift=widgets.IntSliderWidget(min=1, max=100, step=1, value=rea_max_shift, description="Max shift"),
         rea_algo=widgets.DropdownWidget(values=[1,2], value=rea_algo, description="Algorithm"),
         )
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;%%matlab -i rea_ref_spec_no,rea_max_shift,rea_algo
spc6 = spcalign(spc5, rea_ref_spec_no, rea_max_shift, rea_algo);

pspc = spc6; pppm = ppme;
spcmedian = median(pspc, 2); spcmean = mean(pspc, 2); clf; hold on; plot(pppm, spcmedian, 'k'); plot(pppm, spcmean, 'r'); set(gca,'XDir','Reverse');
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;Shifts applied to spectra:
1: 0.
2: 2.
3: 6.
4: 5.
5: 4.
6: 22.
7: 11.
8: 2.
9: 22.
10: 22.
11: 22.
12: 1.
13: 1.
14: 3.
15: 5.
16: 7.
17: 2.
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="png" src="https://www.martinfitzpatrick.com/static/tutorials/legacy/nmrlab-1d-nmr-processing/nmrlab-1d-nmr-processing_33_1.png"  loading="lazy" width="576" height="432"/&gt;&lt;/p&gt;
&lt;h2 id="segmental-alignment"&gt;Segmental alignment&lt;/h2&gt;
&lt;p&gt;Here we use the &lt;a href="http://www.models.life.ku.dk/icoshift"&gt;icoshift&lt;/a&gt; algorithm for segmental alignment (this is the same as used internally by NMRLab, but with a simpler interface to the function to call).&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;sa_xt = 'average'
sa_intt = 'number'
sa_intn = 50
sa_shiftt = 'f'
sa_shiftn = 50


i = interact(widget_callback,
         sa_xt=widgets.DropdownWidget(values=['average','median','max','average2'], value=sa_xt, description="Target:"),

         sa_intt=widgets.DropdownWidget(values=['whole','number'], value=sa_intt, description="Interval type:"),
         sa_intn=widgets.IntSliderWidget(min=1, max=200, step=1, value=sa_intn, description="Interval number:"),

         sa_shiftt=widgets.DropdownWidget(values=['f','b','n'], value=sa_shiftt, description="Max shift type:"),
         sa_shiftn=widgets.IntSliderWidget(min=1, max=200, step=1, value=sa_shiftn, description="Max shift number:"),
         )
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;%%matlab -i sa_xt,sa_intt,sa_intn,sa_shiftt,sa_shiftn

if strcmp(sa_intt, 'number')
    sa_intt = sa_intn;
end

if strcmp(sa_shiftt, 'n')
    sa_shiftt = sa_shiftn;
end

spc7 = icoshift(sa_xt,spc6',sa_intt, sa_shiftt)';

pspc = spc7; pppm = ppme;
spcmedian = median(pspc, 2); spcmean = mean(pspc, 2); clf; hold on; plot(pppm, spcmedian, 'k'); plot(pppm, spcmean, 'r'); set(gca,'XDir','Reverse');
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt; Fast automatic searching for the best "n" for each interval enabled

 Co-shifting interval no. 1 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 2 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 3 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 4 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 5 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 6 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 7 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 8 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 9 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 10 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 11 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 12 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 13 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 14 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 15 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 16 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 17 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 18 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 19 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 20 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 21 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 22 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 23 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 24 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 25 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 26 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 27 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 28 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 29 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 30 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 31 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 32 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 33 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 34 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 35 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 36 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 37 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 38 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 39 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 40 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 41 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 42 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 43 of 50...
 Best shift allowed for this interval = 438

 Co-shifting interval no. 44 of 50...
 Best shift allowed for this interval = 437

 Co-shifting interval no. 45 of 50...
 Best shift allowed for this interval = 437

 Co-shifting interval no. 46 of 50...
 Best shift allowed for this interval = 437

 Co-shifting interval no. 47 of 50...
 Best shift allowed for this interval = 437

 Co-shifting interval no. 48 of 50...
 Best shift allowed for this interval = 437

 Co-shifting interval no. 49 of 50...
 Best shift allowed for this interval = 437

 Co-shifting interval no. 50 of 50...
 Best shift allowed for this interval = 386
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="png" src="https://www.martinfitzpatrick.com/static/tutorials/legacy/nmrlab-1d-nmr-processing/nmrlab-1d-nmr-processing_36_1.png"  loading="lazy" width="576" height="432"/&gt;&lt;/p&gt;
&lt;h2 id="binning-bucket-spectra"&gt;Binning (Bucket spectra)&lt;/h2&gt;
&lt;p&gt;Binning reduces the size of the dataset by grouping regions of the spectra together. This speeds up subsequent analysis, and reduces the impact of small peak shifts (assuming these occur within a single bin).&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;i = interact(widget_callback,
         bi_bin_size=widgets.FloatSliderWidget(min=0.01, max=1.0, step=0.01, value=bi_bin_size, description="Bin size (ppm)"),
         )
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;%%matlab -i bi_bin_size
step_size = (max(ppme) - min(ppme)) / size(ppme,2);
points = bi_bin_size / step_size;
points = round(points / 2);
spc8 = spcbucket(spc7, points);
ppmb = linspace(max(ppme), min(ppme), size(spc8,1));

pspc = spc8; pppm = ppmb;
spcmedian = median(pspc, 2); spcmean = mean(pspc, 2); clf; hold on; plot(pppm, spcmedian, 'k'); plot(pppm, spcmean, 'r'); set(gca,'XDir','Reverse');
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;{Warning: 12 points left!}
&amp;gt; In /Users/mxf793/MATLAB/Toolbox/nmrlab/metabolab/spcbucket.p&amp;gt;spcbucket at 54
  In web_eval at 44
  In webserver at 241
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="png" src="https://www.martinfitzpatrick.com/static/tutorials/legacy/nmrlab-1d-nmr-processing/nmrlab-1d-nmr-processing_39_1.png"  loading="lazy" width="576" height="432"/&gt;&lt;/p&gt;
&lt;h2 id="scale-spectra-tsapqn"&gt;Scale spectra (TSA/PQN)&lt;/h2&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;%%matlab
spc9 = spcscale(spc8,1,1); % TSA by default; PQN must calculate the scaling vector manually

pspc = spc9; pppm = ppmb;
spcmedian = median(pspc, 2); spcmean = mean(pspc, 2); clf; hold on; plot(pppm, spcmedian, 'k'); plot(pppm, spcmean, 'r'); set(gca,'XDir','Reverse');
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="png" src="https://www.martinfitzpatrick.com/static/tutorials/legacy/nmrlab-1d-nmr-processing/nmrlab-1d-nmr-processing_41_0.png"  loading="lazy" width="576" height="432"/&gt;&lt;/p&gt;
&lt;h2 id="variance-stabilisation-autoparetoglog"&gt;Variance stabilisation (auto/pareto/glog)&lt;/h2&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;i = interact(widget_callback,
         glogexp=widgets.IntSliderWidget(min=1, max=10, step=1, value=4, description="glog(exp)"),
         y0=widgets.FloatSliderWidget(min=0.0, max=10.0, step=0.1, value=0.0, description="y0"),
         )
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;%%matlab -i glogexp,y0
spc10 = glogtrans(spc9, 10.0^-double(glogexp), y0 );
pspc = spc10; pppm = ppmb;
spcmedian = median(pspc, 2); spcmean = mean(pspc, 2); clf; hold on; plot(pppm, spcmedian, 'k'); plot(pppm, spcmean, 'r'); set(gca,'XDir','Reverse');
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="png" src="https://www.martinfitzpatrick.com/static/tutorials/legacy/nmrlab-1d-nmr-processing/nmrlab-1d-nmr-processing_44_0.png"  loading="lazy" width="576" height="432"/&gt;&lt;/p&gt;
&lt;h2 id="export-to-csv"&gt;Export to CSV&lt;/h2&gt;
&lt;p&gt;The data resulting from the analysis is now stored in &lt;code&gt;spc10&lt;/code&gt; as a 2D array (variables down, samples across). We have the current ppms in a seperate variable. So we combine the (append to the left) and then output direct to a CSV file from MATLAB.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;%%matlab
output = [ppmb', spc10]';
writecsv(output,output_file);
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</content><category term="science"/><category term="matlab"/><category term="python"/></entry><entry><title>MetaboHunter 1D NMR Identification (Python Interface) Demo</title><link href="https://www.martinfitzpatrick.com/metabohunter-1d-nmr-identification-python-interface-demo/" rel="alternate"/><published>2014-05-25T21:07:00+00:00</published><updated>2014-05-25T21:07:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2014-05-25:/metabohunter-1d-nmr-identification-python-interface-demo/</id><summary type="html">&lt;a href="http://www.nrcbioinformatics.ca/metabohunter/"&gt;MetaboHunter&lt;/a&gt; is a web service for automated assignment of 1D raw, bucketed or peak picked NMR spectra.
Identification is performed in comparison to two publicly available databases (&lt;a href="http://www.hmdb.ca"&gt;HMDB&lt;/a&gt;, &lt;a href="http://mmcd.nmrfam.wisc.edu/"&gt;MMCD&lt;/a&gt;) of NMR standard measurements. More information about the algorithm is available in the published paper:</summary><content type="html">&lt;p&gt;&lt;a href="http://www.nrcbioinformatics.ca/metabohunter/"&gt;MetaboHunter&lt;/a&gt; is a web service for automated assignment of 1D raw, bucketed or peak picked NMR spectra.
Identification is performed in comparison to two publicly available databases (&lt;a href="http://www.hmdb.ca"&gt;HMDB&lt;/a&gt;, &lt;a href="http://mmcd.nmrfam.wisc.edu/"&gt;MMCD&lt;/a&gt;) of NMR standard measurements. More information about the algorithm is available in the published paper:&lt;/p&gt;
&lt;p&gt;Tulpan, D., Leger, S., Belliveau, L., Culf, A., Cuperlovic-Culf, M. (2011). &lt;a href="http://www.biomedcentral.com/1471-2105/12/400"&gt;MetaboHunter: semi-automatic identification of 1H-NMR metabolite spectra in complex mixtures&lt;/a&gt;. BMC Bioinformatics 2011, 12:400&lt;/p&gt;
&lt;p&gt;I find the service useful to give a first-pass identification of metabolites from 1D spectra, which can subsequently be confirmed or combined with identification via other methods. I originally wrote a Python interface as a standalone script, then as a &lt;a href="http://pathomx.org"&gt;Pathomx&lt;/a&gt; plugin, and have now moved the code into a reusable Python module with some extra IPython goodness. The walkthrough below demonstrates using the service with standard settings, passing a numpy array of ppms and peak heights. There is also a demo of a simple IP[y] Notebook widget set that can be used to configure the request.&lt;/p&gt;
&lt;p&gt;The module and source code is available via &lt;a href="https://pypi.python.org/"&gt;PyPi&lt;/a&gt; and &lt;a href="https://github.com/mfitzp/metabohunter/"&gt;Github&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="setup"&gt;Setup&lt;/h2&gt;
&lt;p&gt;The module is on &lt;a href="https://pypi.python.org/"&gt;PyPi&lt;/a&gt; and has no funky dependencies. You should be able to instal the &lt;code&gt;metabohunter&lt;/code&gt; from the command line:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;pip install metabohunter
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;To use the module simply import it. The main module object provides two useful things: a &lt;code&gt;request&lt;/code&gt; function that performs the request to the MetaboHunter service and a &lt;code&gt;IPyMetaboHunter&lt;/code&gt; which provides nice widgets for IPython Notebooks and a synchronized config dictionary that can be passed to requests.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import metabohunter as mh
import numpy as np
import os
os.environ['http_proxy'] = ''
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="input-format"&gt;Input format&lt;/h2&gt;
&lt;p&gt;To make a request to the MetaboHunter service you need to provide two lists (or 1D numpy arrays) of ppm values (the x axis scale on an NMR spectra) and peak heights (y axis). Here we create some dummy data using an 50-element axis of 0-10 in 0.2 increments, together with a 50-element series of peak heights generated randomly.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;ppms = np.arange(0,10,0.2)
peaks = np.random.random(50)*10
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;ppms
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;array([ 0. ,  0.2,  0.4,  0.6,  0.8,  1. ,  1.2,  1.4,  1.6,  1.8,  2. ,
        2.2,  2.4,  2.6,  2.8,  3. ,  3.2,  3.4,  3.6,  3.8,  4. ,  4.2,
        4.4,  4.6,  4.8,  5. ,  5.2,  5.4,  5.6,  5.8,  6. ,  6.2,  6.4,
        6.6,  6.8,  7. ,  7.2,  7.4,  7.6,  7.8,  8. ,  8.2,  8.4,  8.6,
        8.8,  9. ,  9.2,  9.4,  9.6,  9.8])
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;peaks
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;array([ 8.31680605,  6.04419835,  6.89353176,  6.00962915,  4.41208152,
        3.2333172 ,  1.39946687,  6.4614129 ,  6.20912024,  0.06888817,
        7.42894489,  6.7128017 ,  0.79111548,  8.85208481,  4.9710428 ,
        4.95762437,  9.82106628,  3.3606115 ,  8.71282185,  9.6313281 ,
        5.1396787 ,  6.90228616,  4.12455523,  3.71683751,  1.77995641,
        1.87159547,  5.43813402,  6.26325801,  9.17281811,  2.507874  ,
        0.64188688,  5.03782693,  6.93223808,  8.59120112,  2.95107901,
        9.70824585,  1.30386675,  1.02667654,  2.46923911,  9.02715511,
        2.42110673,  5.2022395 ,  8.79650171,  7.06068795,  9.45386543,
        4.38466017,  0.22570328,  3.25368676,  0.63608104,  6.98335382])
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="performing-a-request"&gt;Performing a request&lt;/h2&gt;
&lt;p&gt;The results are returned back in a list of the same length as the input array. Mapped metabolites are represented by their &lt;a href="http://hmdb.ca"&gt;Human Metabolome Database (HMDB)&lt;/a&gt; identifier whereas unmapped peaks are represented by &lt;code&gt;None&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;hmdbs = mh.request(ppms,peaks)
hmdbs
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;[None,
 None,
 None,
 None,
 None,
 None,
 None,
 None,
 None,
 None,
 'HMDB00766',
 None,
 'HMDB00210',
 'HMDB01919',
 'HMDB01919',
 None,
 None,
 'HMDB00210',
 None,
 None,
 None,
 None,
 None,
 None,
 None,
 None,
 None,
 None,
 None,
 None,
 None,
 None,
 None,
 None,
 'HMDB00763',
 'HMDB00617',
 'HMDB00763',
 'HMDB00259',
 None,
 None,
 None,
 None,
 None,
 None,
 None,
 None,
 None,
 None,
 None,
 None]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;To throw away the None's and get the ppm values for the mapped metabolites you can do something like:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;[(ppm, hmdb) for ppm, hmdb in zip(ppms, hmdbs) if hmdb is not None]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;[(2.0, 'HMDB00766'),
 (2.4000000000000004, 'HMDB00210'),
 (2.6000000000000001, 'HMDB01919'),
 (2.8000000000000003, 'HMDB01919'),
 (3.4000000000000004, 'HMDB00210'),
 (6.8000000000000007, 'HMDB00763'),
 (7.0, 'HMDB00617'),
 (7.2000000000000002, 'HMDB00763'),
 (7.4000000000000004, 'HMDB00259')]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="ipython-candy"&gt;IPython Candy&lt;/h2&gt;
&lt;p&gt;To make the metabohunter module a bit nicer to work with from within IP[y] Notebooks, the module provides a simple class for generating widgets to control settings. The class is initialised with the default settings for the request, however you can pass additional variables (any of the keyword arguments allowed for &lt;code&gt;request&lt;/code&gt;).&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;mhi = mh.IPyMetaboHunter(confidence=0.1, tolerance=0.5)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Once the objet is created you can call &lt;code&gt;.display()&lt;/code&gt; to render the widgets in the current cell. Any changes to the variables are stored back into the &lt;code&gt;IPyMetaboHunter&lt;/code&gt; class object (here &lt;code&gt;mhi&lt;/code&gt;) and available in subsequent calculations.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;mhi.display()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;mhi.settings
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;{'confidence': 0.1,
 'database': 'HMDB',
 'frequency': '600',
 'metabotype': 'All',
 'method': 'HighestNumberNeighbourhood',
 'noise': 0.0,
 'ph': 'ph7',
 'solvent': 'water',
 'tolerance': 0.5}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The widgets manager makes the keyword arguments for the request available via a &lt;code&gt;kwargs&lt;/code&gt; property. To provide these to the request function as keyword arguments we just need to unfurl it into the function call using &lt;code&gt;**&lt;/code&gt;. Try adjusting the parameters above and seeing how they affect the results when re-running the request.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;mh.request(ppms,peaks,**mhi.kwargs)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;[None,
 None,
 None,
 None,
 None,
 'HMDB00172',
 'HMDB00011',
 'HMDB00518',
 'HMDB00510',
 'HMDB00510',
 'HMDB00518',
 'HMDB00510',
 'HMDB01547',
 'HMDB01547',
 'HMDB00101',
 'HMDB00208',
 'HMDB00192',
 'HMDB00162',
 'HMDB00014',
 'HMDB00122',
 'HMDB01401',
 'HMDB00272',
 'HMDB00902',
 'HMDB00085',
 None,
 None,
 'HMDB00215',
 None,
 'HMDB00393',
 None,
 None,
 None,
 None,
 None,
 'HMDB01392',
 'HMDB00617',
 'HMDB00303',
 'HMDB01406',
 None,
 None,
 'HMDB00232',
 'HMDB00902',
 None,
 None,
 None,
 None,
 None,
 None,
 None,
 None]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</content><category term="science"/><category term="python"/></entry><entry><title>Dot Blot Analysis with ImageJ</title><link href="https://www.martinfitzpatrick.com/dot-blot-analysis-imagej/" rel="alternate"/><published>2014-05-18T21:10:00+00:00</published><updated>2014-05-18T21:10:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2014-05-18:/dot-blot-analysis-imagej/</id><summary type="html">Analyzing a dot blot in ImageJ by background subtraction and measuring the integrated density of each dot.
This dot blot image is available in the File/Open Samples menu in ImageJ 1.33s or later.</summary><content type="html">&lt;p&gt;Analyzing a dot blot in ImageJ by background subtraction and measuring the integrated density of each dot.
This dot blot image is available in the File/Open Samples menu in ImageJ 1.33s or later.&lt;/p&gt;
&lt;div class="section" id="method"&gt;
&lt;h2 id="method"&gt;Method&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;This method usually requires background correction of the image, which can be done using the Process/Subtract Background command.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="figure"&gt;
&lt;img alt="step/None/subtraction.gif" src="/images/step/None/subtraction.gif"/&gt;
&lt;/div&gt;
&lt;p&gt;See image for profile plots (Analyze/Plot Profile) of the first row of dots before and after background correction was done using the Subtract Background command with the rolling ball radius set to 25 pixels.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;After correcting the background, enable "Integrated Density" in Analyze/Set Measurements, create a circular selection, drag it over the first dot, press "m" (Analyze/Measure), then repeat for the other 27 dots.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="figure"&gt;
&lt;img alt="step/None/screen-shot.jpg" src="/images/step/None/screen-shot.jpg"/&gt;
&lt;/div&gt;
&lt;p&gt;Notice how the image now has a black background? It was inverted (Edit/Invert) so background pixel values are near zero, which is required for correct calculation of integrated density. You can invert the lookup table (Image/Lookup Tables/Invert LUT) to restore the original appearance of the image. The "Use Inverting Lookup Table" option in Edit/Options/Image will invert the pixel data and invert the lookup table.&lt;/p&gt;
&lt;/div&gt;
</content><category term="science"/></entry><entry><title>Measuring cell fluorescence using ImageJ</title><link href="https://www.martinfitzpatrick.com/measuring-cell-fluorescence-using-imagej/" rel="alternate"/><published>2014-05-18T21:10:00+00:00</published><updated>2014-05-18T21:10:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2014-05-18:/measuring-cell-fluorescence-using-imagej/</id><summary type="html">Determining the level of cellular fluorescence from fluorescence microscopy images in ImageJ</summary><content type="html">&lt;p&gt;Determining the level of cellular fluorescence from fluorescence microscopy images in ImageJ&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/static/images/method/37/pser-imagej-setup.png"/&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open ImageJ. Note: ImageJ may be freely downloaded from &lt;a href="http://rsbweb.nih.gov/ij/download.html"&gt;here&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Select the cell of interest using any of the drawing/selection tools (i.e. rectangle, circle, polygon or freeform)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="" src="/static/images/step/246/pser-imagej-setup.png"/&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;From the Analyze menu select &amp;ldquo;set measurements&amp;rdquo;. Make sure you have &lt;strong&gt;area integrated intensity&lt;/strong&gt; and &lt;strong&gt;mean grey value&lt;/strong&gt; selected (the rest can be ignored).&lt;/li&gt;
&lt;li&gt;Now select &amp;ldquo;Measure&amp;rdquo; from the analyze menu. You should now see a popup box with a stack of values for that first cell.&lt;/li&gt;
&lt;li&gt;Now go and select a region next to your cell that has no fluorescence, this will be your background.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Repeat this step for the other cells in the field of view that you want to measure.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  Size is not important. If you want to be super accurate here take 3+ selections from around the cell.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/static/images/step/249/background-selection.png"/&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Once you have finished, select all the data in the Results window and copy and paste into a new spreadsheet (or similar program)&lt;/li&gt;
&lt;li&gt;Use this formula to calculate the corrected total cell fluorescence (CTCF).&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;CTCF = Integrated Density &amp;ndash; (Area of selected cell  X Mean fluorescence of background readings)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Make a graph and your done.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Notice that rounded up mitotic cells appear to have a much higher level of staining due to its smaller size concentrating the staining in a smaller space. If you used the raw integrated density you would have data suggesting that the flattened cell has less staining then the rounded up one, when in reality they have a similar level of fluorescence.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/static/images/step/252/psercdkexcel.png"/&gt;&lt;/p&gt;</content><category term="science"/></entry><entry><title>Cytoscape v3.0.2 on Mac OS X</title><link href="https://www.martinfitzpatrick.com/cytoscape-302-java-fix/" rel="alternate"/><published>2013-08-08T10:45:00+00:00</published><updated>2013-08-08T10:45:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2013-08-08:/cytoscape-302-java-fix/</id><summary type="html">&lt;a href="http://cytoscape.org/releasenotes.html"&gt;Cytoscape 3.0.2&lt;/a&gt; was released on 1st August, a bugfix release for the 3.x series. However, there is a compatibility issue with the latest Java update from Apple resulting in Cytoscape hanging on startup. I'd followed advice found elsewhere to update to the latest Java version with no success, until I found &lt;a href="https://groups.google.com/d/msg/cytoscape-discuss/3iCDXQ4lOTM/arovMUx0_DAJ"&gt;this post&lt;/a&gt; from Tim Hull, outlining a simple way to check what version you have installed.</summary><content type="html">&lt;p&gt;&lt;a href="http://cytoscape.org/releasenotes.html"&gt;Cytoscape 3.0.2&lt;/a&gt; was released on 1st August, a bugfix release for the 3.x series. However, there is a compatibility issue with the latest Java update from Apple resulting in Cytoscape hanging on startup. I'd followed advice found elsewhere to update to the latest Java version with no success, until I found &lt;a href="https://groups.google.com/d/msg/cytoscape-discuss/3iCDXQ4lOTM/arovMUx0_DAJ"&gt;this post&lt;/a&gt; from Tim Hull, outlining a simple way to check what version you have installed.&lt;/p&gt;
&lt;p&gt;/usr/libexec/java_home -v 1.6 -exec java -version&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If the output contains &amp;ldquo;xM4508&amp;rdquo;, you have the broken
version. If it contains "xM4509", this is probably a
different issue...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you find that you have the bad version, you can download the updated from Apple using the links below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://support.apple.com/kb/DL1572"&gt;Java Update for OS X 10.7, 10.8+&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://support.apple.com/kb/DL1573"&gt;Java Update for OS X 10.6-only&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The updated Java fixed the problem for me.&lt;/p&gt;</content><category term="science"/><category term="bioinformatics"/></entry><entry><title>cx_Freeze and PySide on Mac</title><link href="https://www.martinfitzpatrick.com/cx-freeze-and-pyside-on-mac/" rel="alternate"/><published>2013-04-18T09:00:00+00:00</published><updated>2013-04-18T09:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2013-04-18:/cx-freeze-and-pyside-on-mac/</id><summary type="html">I'd had success using py2app for building Mac binaries for distribution but wanted to give cx_Freeze a go since it's cross platform - allowing builds for Windows, Linux, and more. Unfortunately, attempting to build using cx_Freeze was resulting in errors:</summary><content type="html">
            &lt;p&gt;I'd had success using py2app for building Mac binaries for distribution but wanted to give cx_Freeze a go since it's cross platform - allowing builds for Windows, Linux, and more. Unfortunately, attempting to build using cx_Freeze was resulting in errors:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;libpyside-python2.7.1.1.dylib: No such file or directory
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If found a tip suggesting you can do a &lt;code&gt;touch libpyside-python2.7.1.1.dylib&lt;/code&gt; in you're applications folder to get rid of this error by creating a dummy file. But then you just end up with this instead:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;copying Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/               PyQt4/Qt.so -&amp;gt; build/exe.macosx-10.6-intel-2.7/PyQt4.Qt.so
copying /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/PyQt4/QtCore.so -&amp;gt; build/exe.macosx-10.6-intel-2.7/PyQt4.QtCore.so
copying QtCore.framework/Versions/4/QtCore -&amp;gt; build/exe.macosx-10.6-intel-2.7/QtCore error: QtCore.framework/Versions/4/QtCore: No such file or directory
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;It's looking for the file in the wrong place: QtCore should be prefixed with the same path as PyQt4 before it. There are various version of PySide available - but whether you try to &lt;code&gt;pip install&lt;/code&gt; or &lt;code&gt;git clone https://github.com/PySide/pyside-setup&lt;/code&gt; and then &lt;code&gt;python setup.py install&lt;/code&gt; you'll find that you end up in much the same place. In most cases however the installation simply fails.&lt;/p&gt;
&lt;h2 id="homebrew"&gt;Homebrew&lt;/h2&gt;
&lt;p&gt;So try a different tack: &lt;a href="http://mxcl.github.io/homebrew/"&gt;Homebrew&lt;/a&gt;. Homebrew is a package manager for Mac, that has a large number of successfully buildable software packaged specifically for the Mac. I've successfully used it to install a number of things, but steered clear of it for Python things that could be pip installed. However, there seemed to be little alternative - and actually I think brewed Python has it's advantages (being more up to date being one, being guaranteed to work with other brewed packages the other).&lt;/p&gt;
&lt;p&gt;At this point my default Python install was the Mac default (Darwin, llvm-compat) version, installed under &lt;code&gt;/Library/Python/2.7/&lt;/code&gt; so first that needed to change.&lt;/p&gt;
&lt;h2 id="python"&gt;Python&lt;/h2&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;brew install python
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The above will take quite a long time (particularly the Python bit) but you should now have a successfully brewed Python on which to base your apps. If you've had a different Python (e.g. system python) installed you may need to update your paths to ensure that the brewed one is being used - Homebrew actually gives you the instructions to correctly do this: but just add &lt;code&gt;/usr/local/lib/python2.7/site-packages:/usr/local/share/python&lt;/code&gt; to your &lt;code&gt;PATH&lt;/code&gt; variable in &lt;code&gt;.bash_profile&lt;/code&gt; e.g.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;export PATH=/usr/local/lib/python2.7/site-packages:/usr/local/share/python:$PATH
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="pyside"&gt;Pyside&lt;/h2&gt;
&lt;p&gt;An important fact about brewed Python is that it will continue to look in your system site-packages folder, and do so in preference to your brewed site-packages. If you have attempted to install Pyside (or anything else) before you may wish to remove them to stop conflicts.&lt;/p&gt;
&lt;p&gt;Next install PySide via brew (the pip install does not work).&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;brew install pyside
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The install cx_Freeze via pip (it's not in brew, and the pip works fine).&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;pip install cx_Freeze
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Finally, check PySide are cx_Freeze are set up correctly by starting a Python interactive shell and entering the following (you should get the same output):&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; import PySide
&amp;gt;&amp;gt;&amp;gt; import cx_Freeze
&amp;gt;&amp;gt;&amp;gt; PySide.__version__
'1.1.2'
&amp;gt;&amp;gt;&amp;gt; cx_Freeze.version
'4.3.1'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You're now free to build your apps.&lt;/p&gt;
&lt;h2 id="update-12012014"&gt;Update 12.01.2014&lt;/h2&gt;
&lt;p&gt;There are some more things that now no longer work (as the app I'm building has got more and more complicated). However, there are solutions to both.&lt;/p&gt;
&lt;p&gt;First of all is using PyQt5 with cx_Freeze raises an &lt;a href="https://bitbucket.org/anthony_tuininga/cx_freeze/issue/33/handle-qt-5-platform-plugins#comment-7104993"&gt;issue&lt;/a&gt; with finding the &lt;code&gt;qt-menu-nib&lt;/code&gt; file that cx_Freeze uses to determine if it is packaging Qt - this folder no longer exists on Qt5. However, you can pass any old folder to cx_Freeze on the command line and it won't matter e.g.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;python setup.py bdist_mac --qt-menu-nib=/usr/local/Cellar/qt5/5.2.0-beta1/plugins/platforms/
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Another issue, documented &lt;a href="https://bitbucket.org/anthony_tuininga/cx_freeze/issue/43/import-errors-when-using-cx_freeze-with#comment-7370405"&gt;here&lt;/a&gt; occurs when importing SciPy optimize based modules. That link contains a bit of hack code to fix it.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PyQt5 see my book, &lt;a href="https://www.mfitzp.com/pyqt5-book/"&gt;Create GUI Applications with Python &amp; Qt5.&lt;/a&gt;&lt;/p&gt;
            </content><category term="python"/><category term="mac"/><category term="pyqt"/><category term="pyside"/></entry><entry><title>Pelicans on Webfaction</title><link href="https://www.martinfitzpatrick.com/pelicans-on-webfaction/" rel="alternate"/><published>2013-04-07T09:00:00+00:00</published><updated>2013-04-07T09:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2013-04-07:/pelicans-on-webfaction/</id><summary type="html">&lt;p&gt;As mentioned in the previous post, I recently migrated this site over to the very clever &lt;a href="http://getpelican.com"&gt;Pelican&lt;/a&gt;. Setting it up was relatively straightforward using a combination of the
official docs, &lt;a href="http://www.dominicrodger.com/pelican.html"&gt;this post&lt;/a&gt; and linked github repo from &lt;a href="http://www.dominicrodger.com"&gt;Dominic Rodger&lt;/a&gt;. That said, there were a few things that I stumbled at and non-obvious decisions that I've documented below.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;As mentioned in the previous post, I recently migrated this site over to the very clever &lt;a href="http://getpelican.com"&gt;Pelican&lt;/a&gt;. Setting it up was relatively straightforward using a combination of the
official docs, &lt;a href="http://www.dominicrodger.com/pelican.html"&gt;this post&lt;/a&gt; and linked github repo from &lt;a href="http://www.dominicrodger.com"&gt;Dominic Rodger&lt;/a&gt;. That said, there were a few things that I stumbled at and non-obvious decisions that I've documented below.&lt;/p&gt;

&lt;h2 id="setting-up"&gt;Setting up&lt;/h2&gt;
&lt;p&gt;Before starting you need to install the packages. Thankfully everything you need is available via the python packaging service PIP. Unfortunately PIP isn't installed by default on Webfaction  so you'll need to get that set up as per the &lt;a href="http://docs.webfaction.com/software/python.html"&gt;instructions from Webfaction&lt;/a&gt;. So SSH into your account and do the following:&lt;/p&gt;
&lt;h3&gt;PIP&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Make sure the destination directory exists. Enter &lt;code&gt;mkdir -p $HOME/lib/pythonX.Y&lt;/code&gt;, where X.Y is the Python version, and press Enter.&lt;/li&gt;
&lt;li&gt;Install pip. Enter &lt;code&gt;easy_install-X.Y pip&lt;/code&gt;, where X.Y is the version of Python you wish to use, and press Enter.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;In my case X.Y was 2.7&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Pelican &amp;amp; dependencies&lt;/h3&gt;
&lt;p&gt;I then manually installed these dependencies, I'm not sure if all are required (or already present on Webfaction) - give it a go and let me know in the comments.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;pip install feedgenerator   # to generate the Atom feeds
pip install jinja2          # for templating support
pip install pygments        # for syntax highlighting
pip install docutils        # for supporting reStructuredText as an input format
pip install pytz            # for timezone definitions
pip install blinker         # an object-to-object and broadcast signaling system
pip install unidecode       # for ASCII transliterations of Unicode text
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;If you&amp;rsquo;re not using Python 2.7, you will also need to &lt;code&gt;pip install argparse&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In the Pelican setup it lists the following two as optional, but you'll need the first to use markdown flavoured markup. The second makes your text output prettier, I installed it.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;pip install markdown        # for supporting Markdown as an input format
pip install typogrify       # for typographical enhancements
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now with all that out the way, we can install pelican. As with the above, it's a simple case of entering at the command line:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;pip install pelican
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Webfaction&lt;/h3&gt;
&lt;p&gt;First things first, we need to create a Webfaction webapp to hold our site. In fact, we need 2: one for the source code and settings, one for the output. Only the second of these is actually served publically - and Webfaction allows for this by making it possible to have applications with no association website. You do this through the Webfaction panel.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you want to, you can put the generator code in your home folder instead but I think that is a bit confusing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can also decide here how you want to name the two apps. Initially I had &lt;code&gt;mfitzp&lt;/code&gt; holding the source, and &lt;code&gt;mfitzp_public&lt;/code&gt; holding the generated output. However, it dawned on me that if I wanted more than one Pelican site (quite possible) I was going to end up with multiple dud apps scattered around. To prevent this I created two apps, one &lt;code&gt;pelican&lt;/code&gt; that will hold any Pelican site settings repos in a subfolder, and &lt;code&gt;mfitzp&lt;/code&gt; which is the public served version of this site.&lt;/p&gt;
&lt;p&gt;The first, &lt;code&gt;pelican&lt;/code&gt;, was created as 'Static only (no .htaccess)', the second &lt;code&gt;mfitzp&lt;/code&gt; as 'Static/CGI/PHP-5.4'. This latter decision only really has an impact if you need to use URL rewriting, or other &lt;code&gt;.htaccess&lt;/code&gt; directives further down the line. I did, so I did. If you like this can also be 'Static only (no .htaccess)' and it'll be served by Webfaction's potentially-faster nginx server.&lt;/p&gt;
&lt;p&gt;Remember to associate the public one with an active domain under 'websites'. Make sure it's working (if you've just set it up DNS could hold you up here).&lt;/p&gt;
&lt;h3&gt;Back to black&lt;/h3&gt;
&lt;p&gt;Back in the SSH session you can now cd ~/webapps and see the two new created folders &lt;code&gt;pelican&lt;/code&gt; and &lt;code&gt;yoursitenamehere&lt;/code&gt;. Now change into your pelican folder with&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;cd pelican              # change into your pelican folder
mkdir yoursitenamehere  # create a directory to hold your site config
cd yoursitenamehere     # change into your site's folder
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We can now use the Pelican command to initiate the setup:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;pelican-quickstart
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Answer the questions and Pelican will create your initial folder. The important things to remember are that your output folder, relative to the current location is &lt;code&gt;../../yoursitenamehere&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Github&lt;/h3&gt;
&lt;p&gt;Following in Dominic's footsteps I decided to use &lt;a href="http://www.github.com/"&gt;github&lt;/a&gt; for my content hosting. However, Pelican has a handy tool to generate your setup, so you'll need to initiate the repo on the Webfaction side and re-home it onto github. Bear with me.&lt;/p&gt;
&lt;p&gt;The first step is to log into your Github account and create the new repo, calling it whatever you like. &lt;strong&gt;Do not add a README.md&lt;/strong&gt; as we need the repo empty to do the next thing - if you mess it up, just delete and recreate. Github is fine with that. Once created make note of your repo's read &amp;amp; write git url (SSH).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you haven't set up github with your SSH keys you'll need to do that now. Generate a key on your Webfaction host using &lt;code&gt;ssh-keygen&lt;/code&gt;. At the SSH prompt enter &lt;code&gt;less ~/.ssh/id_rsa.pub&lt;/code&gt; to see the generated public key and copy-paste it into your &lt;a href="https://github.com/settings/ssh"&gt;Github SSH settings&lt;/a&gt;. Hit &lt;code&gt;q&lt;/code&gt; to exit less.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Back in the shell, initiaise the github repo and then re-home it to your github repo.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;git init
git add .
git commit -a -m "Initial commit"
git remote add origin git@github.com:&amp;lt;github-username&amp;gt;/&amp;lt;github-repo-name&amp;gt;.git
git push -u origin master
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Sorted. The repo for your site is setup, and the repo now things Github is it's origin, so it will pull down from it to get updates. This means you can now clone that repo yourself to another machine and push your changes up via github.&lt;/p&gt;
&lt;h2 id="getting-going"&gt;Getting going&lt;/h2&gt;
&lt;p&gt;To all intents and purposes you are now finished and ready to make a beautiful website. But there are a few final things to consider.&lt;/p&gt;
&lt;h3&gt;Folders&lt;/h3&gt;
&lt;p&gt;Firstly, folder structure. Things are 'expected' to be laid out as follows by the system, but it's not immediately clear - meaning it can get a bit confusing why certain content is not showing up.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;:::text
/content
    /images
    /pages
    /extra
    /&amp;lt;category-1&amp;gt;
    /&amp;lt;category-2&amp;gt;
/theme
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The folder &lt;code&gt;images&lt;/code&gt; is automatically copied through to appear at &lt;code&gt;/images&lt;/code&gt; on your live site. Similarly &lt;code&gt;pages&lt;/code&gt; is a special folder that anything contained within will be created as a static page, will not appear in feeds and may appear on link lists in certain forms. The URL structure for articles and pages is different (see later). Finally, &lt;code&gt;extra&lt;/code&gt; is an optional folder, but extremely useful for copying through completely static content to the a particular destination. For example, on this site I'm using the following in my &lt;code&gt;pelicanconf.py&lt;/code&gt; to copy through a static robots.txt and .htaccess:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;FILES_TO_COPY = ( ('extra/robots.txt', 'robots.txt'),
                  ('extra/.htaccess', '.htaccess') )
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;URLs&lt;/h3&gt;
&lt;p&gt;Pretty-urls are a nice thing to have. Unfortunately, by default Pelican gives you a lot of &lt;code&gt;.html&lt;/code&gt; everywhere. The official docs suggest solving this by setting your URL/SAVE_AS directives to put files as &lt;code&gt;index.html&lt;/code&gt; under folders for the slug, so for example &lt;code&gt;posts/your-slug-here/index.html&lt;/code&gt;. This works, as you can now put the URL as &lt;code&gt;posts/your-slug-here/&lt;/code&gt; and hit the file. Pretty URLs no redirects or &lt;code&gt;.htaccess&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;However, it doesn't work if you want to use custom themed error pages generated by Pelican (you can of course manually create them and copy them in the FILES_TO_COPY above, but you will need to maintain them in line with the theme by hand). To do that you either need to access that all pages will have URLs like &lt;code&gt;/about.html&lt;/code&gt; or use &lt;code&gt;.htaccess&lt;/code&gt;. And if you're going to use &lt;code&gt;.htacess&lt;/code&gt; redirects you may as well make it handle the pretty URLs too.&lt;/p&gt;
&lt;p&gt;So to start with add the following to your &lt;code&gt;pelicanconf.py&lt;/code&gt; file.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;ARTICLE_URL = 'posts/{slug}'
ARTICLE_SAVE_AS = 'posts/{slug}.html'
PAGE_URL = '{slug}'
PAGE_SAVE_AS = '{slug}.html'
AUTHOR_URL = 'author/{slug}/'
AUTHOR_SAVE_AS = 'author/{slug}.html'
CATEGORY_URL = 'category/{slug}'
CATEGORY_SAVE_AS = 'category/{slug}.html'
TAG_URL = 'tag/{slug}'
TAG_SAVE_AS = 'tag/{slug}.html'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This creates a set of URL formats where articles are linked to at &lt;code&gt;posts/your-slug-here&lt;/code&gt; (note, no trailing slash, all the cool kids do it) and saved as the same name but with .html extension. Now save the config and create a new file names &lt;code&gt;.htaccess&lt;/code&gt; in your &lt;code&gt;content/extra&lt;/code&gt; folder - this will hold your .htaccess config for the live site.  Add the following:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;:::text
Options +FollowSymLinks
RewriteEngine On

# Remove trailing slashes.
# e.g. example.com/foo/ will redirect to example.com/foo
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)/$ /$1 [R=permanent,QSA]

# Redirect to HTML if it exists.
# e.g. example.com/foo will display the contents of example.com/foo.html
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^(.+)$ $1.html [L,QSA]

ErrorDocument 404 /404.html
ErrorDocument 403 /403.html
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;What it does is explained in the comments for each directive. The first chunk removes trailing slashes. The second chunk checks what is requested is not an actual file (e.g. static file), not a directory, and that &lt;code&gt;&amp;lt;filename&amp;gt;.html&lt;/code&gt; exists, before rewriting the request to return that. The final two are for our error files.&lt;/p&gt;
&lt;p&gt;Save the file and you're good to go.&lt;/p&gt;
&lt;h3&gt;Miscellaneous &lt;code&gt;peliconf.py&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Here are some additional extras for &lt;code&gt;peliconf.py&lt;/code&gt; you might want to try out. Since I installed it I also turned on Typogrify by adding the following:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;TYPOGRIFY = True
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Because of the new feed variables in 3.0 some old themes won't work. Add this to fix:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;FEED_DOMAIN = SITEURL
FEED_ATOM = 'feeds/atom.xml'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="express-yourself-hey-hey"&gt;Express yourself (hey, hey)&lt;/h2&gt;
&lt;p&gt;It's time to write someting (finally). We'll create a test post and our two error document pages. So, first, simply create a new text file and enter the following:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;:::text
Date: 2013-04-07 09:00
Author: Your Name
Email: your.email@your.email.provider
Title: A test post
Slug: a-test-post
Tags: test,post

Hello!
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Save it in the content folder with the name &lt;code&gt;a-test-post.md&lt;/code&gt;. You can save it under a sub-folder (create it) to categorise the post if you like. Then create another empty text file and add the following:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;:::text
Title: 404
Slug: 404
Status: hidden

Not found
=========

Nothing to see here, move along.
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Save it under the &lt;code&gt;content/pages&lt;/code&gt; folder with the name &lt;code&gt;404.md&lt;/code&gt;. Edit a similar file for 403 Forbidden errors and save as &lt;code&gt;403.md&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="pelicans-are-go"&gt;Pelicans are go&lt;/h2&gt;
&lt;p&gt;Let's commit and push this to github incase we lose all our hard work. At the command line, in your pelican content source folder &lt;code&gt;pelican/yoursitenamehere&lt;/code&gt; enter the following:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;git add .
git commit -a -m "Initial commit"
git push
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now lets generate a site. In the same folder enter the following to create the content:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;make html
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can also use:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;make publish
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The only difference is the latter &lt;em&gt;also&lt;/em&gt; uses the config settings in &lt;code&gt;publishconf.py&lt;/code&gt; including the quite useful &lt;code&gt;RELATIVE_URLS = False&lt;/code&gt; directive (uncomment it first).&lt;/p&gt;
&lt;p&gt;If you now access your site from the web, you should see your post in all it's glorious creativitiy. If it hasn't worked, drop a note in the comments and I'll work it out with you.&lt;/p&gt;
&lt;h2 id="automatic-for-the-lazy-people"&gt;Automatic for the (lazy) people&lt;/h2&gt;
&lt;p&gt;I'm lazy, so I don't really like doing the whole commit-push-pull-make thing. Thankfully it's relatively easy to automate the whole thing with a short shell script. Still on your SSH session to webfaction enter:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;nano ~/auto_pelican_publish.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This starts a simple editor to create a shell script. Simply copy and paste the following into the file, editing the paths as appropriate:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;#!/usr/bin/env bash

cd &amp;lt;path-to-your-pelican-content-folder&amp;gt; # e.g. ~/pelican/yoursitenamehere
git pull
make publish
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Save by pressing &lt;code&gt;Ctrl-X&lt;/code&gt; and then &lt;code&gt;Y&lt;/code&gt;. That's it. Back at the command line type:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;chmod +x ~/auto_pelican_publish.sh  # to make the file executable
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Next to add this to cron. Back at the command line enter:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;crontab -e
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Add the following to the top of the file, replacing your username:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;:::text
PATH=/usr/kerberos/bin:/usr/local/bin:/bin:/usr/bin:/opt/dell/srvadmin/bin:/home/&amp;lt;webfaction-username&amp;gt;/bin
*/15 * * * * ~/auto_publish_pelican.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Again &lt;code&gt;Ctrl-X&lt;/code&gt; and &lt;code&gt;Y&lt;/code&gt; to exit and save. This will run the auto-publish script every 15 minutes, pulling from your remote git repository and then publishing the files it finds. All you need to do is make your commits on your local machine, push them to github, and within 15 minutes they'll be on your site. You can adjust the timer by changing the 15 to another number, e.g. 30 means every half an hour.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Pelican is a really neat bit of software, that gets you away from all the faffing of dynamic web stuff to just concentrating on the important thing - writing. Hopefully this guide makes setting yourself up on Webfaction a little easier. If you hit any stumbling blocks in spite of this let me know in the comments! Happy pelicaning - whatever that involves.&lt;/p&gt;</content><category term="web"/><category term="python"/></entry><entry><title>Speed up MATLAB on MacOS X</title><link href="https://www.martinfitzpatrick.com/speed-up-matlab-on-macos-x/" rel="alternate"/><published>2013-03-24T12:03:00+00:00</published><updated>2013-03-24T12:03:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2013-03-24:/speed-up-matlab-on-macos-x/</id><summary type="html">On MacOS X 10.5 there is considerable slow-down in the MATLAB editor and other GUI elements. The issue is related to a change in the default Mac Java 2D rendering engine from Quartz2D (10.4) and Sun2D (10.5). This newer rendering engine improves performance for figure drawing, but other GUI operations are slower.</summary><content type="html">&lt;p&gt;On MacOS X 10.5 there is considerable slow-down in the MATLAB editor and other GUI elements. The issue is related to a change in the default Mac Java 2D rendering engine from Quartz2D (10.4) and Sun2D (10.5). This newer rendering engine improves performance for figure drawing, but other GUI operations are slower.&lt;/p&gt;
&lt;p&gt;The temporary fix is to instruct MATLAB to use the old rendering engine (Quartz2D). This will speed up scrolling and most GUI operations, at the cost of reduced figure drawing speed.&lt;/p&gt;
&lt;p&gt;&lt;img alt="method/1539/Screen Shot 2013-03-24 at 12.20.16.png" src="/static/images/method/1539/image.png"/&gt;&lt;/p&gt;
&lt;p&gt;Open a terminal and browse to to your Matlab app bundle, eg.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;cd /Applications/MATLAB_R2011a_Student.app
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Your MATLAB folder will likely be different to this. A quick way to find it is to use the &lt;code&gt;tab&lt;/code&gt; key. At the command-line enter &lt;code&gt;cd /Applications/MATLAB&lt;/code&gt; then hit &lt;code&gt;&amp;lt;tab&amp;gt;&lt;/code&gt; and the name should autocomplete.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you're on 2008a or later, you need to browse down to the maci folder.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;cd bin/maci
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;or for 64 bit&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;cd bin/maci64
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Again, typing &lt;code&gt;cd bin/maci&amp;lt;tab&amp;gt;&lt;/code&gt; should get you there.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;At the command line type &lt;code&gt;nano java.opts&lt;/code&gt; to create a new empty file and open a command-line text editor. You can copy and paste into this window, so simply copy the text below and paste it onto a new line in the file.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;-Dapple.awt.graphics.UseQuartz=true
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;To save hit &lt;code&gt;Ctrl-X&lt;/code&gt;(exit) then press &lt;code&gt;Y&lt;/code&gt; to save and &lt;code&gt;Return&lt;/code&gt; to accept the filename&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can also optionally add &lt;code&gt;-Dapple.laf.useScreenMenuBar=false&lt;/code&gt; to turn off the Mac top-of-screen menu bar. This appears to also slightly increase speed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Restart MATLAB and you should see a speed improvement.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As this fix may reduce drawing speed of figures, you may want to switch off the workaround while doing a lot of figure drawing. You can do so by exiting MATLAB, renaming the java.opts file e.g. to java.opts.off with &lt;code&gt;mv java.opts java.opts.off&lt;/code&gt; and restarting MATLAB. To re-enable simply quit, rename the file back to java.opts with &lt;code&gt;mv java.opts.off java.opts&lt;/code&gt; and restart.&lt;/p&gt;
&lt;/blockquote&gt;</content><category term="matlab"/><category term="mac"/><category term="computing"/></entry><entry><title>Customize directory colors</title><link href="https://www.martinfitzpatrick.com/customize-directory-colors/" rel="alternate"/><published>2012-03-10T09:03:00+00:00</published><updated>2012-03-10T09:03:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2012-03-10:/customize-directory-colors/</id><summary type="html">You can use the command ls --color (or an alias) to show directories with colours for folders, files, links, etc. However, you may not realise these colours can be easily configured using bashrc and a configuration file.</summary><content type="html">&lt;p&gt;You can use the command ls --color (or an alias) to show directories with colours for folders, files, links, etc. However, you may not realise these colours can be easily configured using bashrc and a configuration file.&lt;/p&gt;
&lt;p&gt;Edit your &lt;code&gt;.bashrc&lt;/code&gt; file (in your home directory) to include the following line:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;alias lc="ls --color=always"
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This will enable coloured listings on all uses of ls (to save you typing &lt;code&gt;--colors&lt;/code&gt;. Save the file and in your terminal window enter &lt;code&gt;source ~/.bashrc&lt;/code&gt; to reload your bash config. Try an &lt;code&gt;ls&lt;/code&gt; to confirm that you have got colors working.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;On some systems (including Mac) the bash configuration is stored in &lt;code&gt;~/.bash_profile&lt;/code&gt; instead.
You have a lot of options for configuring the directory colours. They can be stored in&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Shell variable &lt;code&gt;LS_COLORS&lt;/code&gt; which can be set in .bashrc via &lt;code&gt;export LS_COLORS="COLOR_CONFIG"&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;In the file &lt;code&gt;/etc/DIR_COLORS&lt;/code&gt; (you will need to be root to configure and this is global for all users)&lt;/li&gt;
&lt;li&gt;In the file pointed by the variable &lt;code&gt;COLORS&lt;/code&gt; (can be in your home directory)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Color configuration is done through a special formatted string:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;FILE-TYPE Attribute codes: Text color codes:Background color codes

FILE-TYPE: is file type like DIR (for directories)
Attribute codes:
    00=none
    01=bold
    04=underscore
    05=blink
    07=reverse
    08=concealed
Text color codes:
    30=black
    31=red
    32=green
    33=yellow
    34=blue
    35=magenta
    36=cyan
    37=white
Background color codes:
    40=black
    41=red
    42=green
    43=yellow
    44=blue
    45=magenta
    46=cyan
    47=white
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;For example &lt;code&gt;DIR 01;34&lt;/code&gt; gives you a bold blue directory.&lt;/p&gt;
&lt;p&gt;So to change the configuration globally edit the &lt;code&gt;/etc/DIR_COLORS&lt;/code&gt; file as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;sudo nano /etc/DIR_COLORS
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Look for:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;DIR 01;34 # default is Bold blue with black background
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And change it to:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;DIR 01;34;41 # NEW default is Bold blue with RED background
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Using LS_COLORS (in your own &lt;code&gt;.bashrc&lt;/code&gt; file) the format is slightly different:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;LS_COLORS='di=1:fi=0:ln=31:pi=5:so=5:bd=5:cd=5:or=31:mi=0:ex=35:*.rpm=90'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Here the codes are as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;di = directory
fi = file
ln = symbolic link
pi = fifo file
so = socket file
bd = block (buffered) special file
cd = character (unbuffered) special file
or = symbolic link pointing to a non-existent file (orphan)
mi = non-existent file pointed to by a symbolic link (visible when you type ls -l)
ex = file which is executable (ie. has 'x' set in permissions).

0   = default colour
1   = bold
4   = underlined
5   = flashing text
7   = reverse field
31  = red
32  = green
33  = orange
34  = blue
35  = purple
36  = cyan
37  = grey
40  = black background
41  = red background
42  = green background
43  = orange background
44  = blue background
45  = purple background
46  = cyan background
47  = grey background
90  = dark grey
91  = light red
92  = light green
93  = yellow
94  = light blue
95  = light purple
96  = turquoise
100 = dark grey background
101 = light red background
102 = light green background
103 = yellow background
104 = light blue background
105 = light purple background
106 = turquoise background
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</content><category term="bash"/><category term="cli"/><category term="computing"/></entry><entry><title>Control memcached from the command line</title><link href="https://www.martinfitzpatrick.com/control-memcached-from-the-command-line/" rel="alternate"/><published>2012-02-05T05:02:00+00:00</published><updated>2012-02-05T05:02:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2012-02-05:/control-memcached-from-the-command-line/</id><summary type="html">&lt;code&gt;memcached&lt;/code&gt; is a general-purpose distributed memory caching system
originally developed by Danga Interactive for LiveJournal but now used
by many other sites. It is often used to speed up dynamic
database-driven websites by caching data and objects in RAM to reduce
the number of times an external data source (such as a database or API)
must be read. Here we describe the options available from the command
line to control a memcached instance via unix socket or IP:port.</summary><content type="html">&lt;p&gt;&lt;code&gt;memcached&lt;/code&gt; is a general-purpose distributed memory caching system
originally developed by Danga Interactive for LiveJournal but now used
by many other sites. It is often used to speed up dynamic
database-driven websites by caching data and objects in RAM to reduce
the number of times an external data source (such as a database or API)
must be read. Here we describe the options available from the command
line to control a memcached instance via unix socket or IP:port.&lt;/p&gt;
&lt;p&gt;&lt;img alt="method/1536/screenshot.png" src="https://www.martinfitzpatrick.com/control-memcached-from-the-command-line/screenshot.png"/&gt;&lt;/p&gt;
&lt;p&gt;Memcached can be set up to either take control commands via an IP
address and port or via a unix socket.&lt;/p&gt;
&lt;h2 id="starting-up"&gt;Starting up&lt;/h2&gt;
&lt;p&gt;When listening via IP:port by default memcached listens for connections
on port &lt;code&gt;11211&lt;/code&gt; and accepts connections from &lt;code&gt;INADDR_ANY&lt;/code&gt;. To prevent
possibly malicious access you may want to consider restricting
connections to an internal or firewalled connection. Alternatively you
can set memcached to only listen for connections on &lt;code&gt;localhost&lt;/code&gt; - i.e.
the server running the instance of memcached using  &lt;code&gt;127.0.0.1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To start up an instance of memcached listening on &lt;code&gt;localhost&lt;/code&gt; port
&lt;code&gt;11211&lt;/code&gt; only use the following:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;::sh
memcached -d -m memory -l 127.0.0.1
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;memory&lt;/code&gt; is the maximum number of megabytes of memory you want
memcached to use.&lt;/p&gt;
&lt;p&gt;If you want to specify a different port to listen on use the &lt;code&gt;-p PORT&lt;/code&gt;
option.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Alternatively you can use unix sockets to communicate directly with the
memcached instance. In this case commands can only be sent from a login
shell that has access to the socket file - for example if you ssh into
the server hosting it.&lt;/p&gt;
&lt;p&gt;To start up memcached using unix sockets you would use the following
command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;::sh
memcached -d -m memory -s ~/memcached.sock
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This will create a file named &lt;code&gt;memcached.sock&lt;/code&gt; in your home directory to
communicate through.&lt;/p&gt;
&lt;h2 id="memcache-management"&gt;Memcache management&lt;/h2&gt;
&lt;p&gt;Memcached comes with a number of useful &lt;a href="http://code.google.com/p/memcached/wiki/NewCommands"&gt;management commands&lt;/a&gt;.
Of these the most frequently useful are &lt;code&gt;flush_all&lt;/code&gt; and the stats commands
including &lt;code&gt;stats&lt;/code&gt;, &lt;code&gt;stats items&lt;/code&gt; and &lt;code&gt;stats slabs&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To send a command to memcached you can use &lt;code&gt;nc&lt;/code&gt; (netcat). The general
form for all commands is:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;echo "command" | nc 127.0.0.1 11211
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Or for socket connections:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;echo "command" | nc -U ~/memcached.sock
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;To flush the entire contents of your memcached instance you could issue
the &lt;code&gt;flush_all&lt;/code&gt; command with one of the following:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;echo "flush_all" | nc 127.0.0.1 11211 echo "flush_all" | nc -U ~/memcached.sock
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Results in:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;[user@web37 ~]$ echo "flush_all" | nc -U ~/memcached.sock OK

[user@web37 ~]$
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="step/None/Screen Shot 2012-08-05 at 18.19.20.png" src="https://www.martinfitzpatrick.com/control-memcached-from-the-command-line/Screen Shot 2012-08-05 at 18.19.20.png"/&gt;&lt;/p&gt;
&lt;h2 id="statistics-and-state"&gt;Statistics and state&lt;/h2&gt;
&lt;p&gt;You can use the stats commands to get information about the state of
your memcached instance. For example:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;echo "stats" | nc 127.0.0.1 11211 echo "stats" | nc -U ~/memcached.sock
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Will output a list of stats for the current instance. Additional
information can be found using &lt;code&gt;stats items&lt;/code&gt; to give total stats about
items stored in the cached and &lt;code&gt;stats slabs&lt;/code&gt; which provides more
information including performance hints.&lt;/p&gt;
&lt;p&gt;&lt;img alt="step/None/Screen Shot 2012-08-05 at 17.28.09.png" src="https://www.martinfitzpatrick.com/control-memcached-from-the-command-line/Screen Shot 2012-08-05 at 17.28.09.png"/&gt;&lt;/p&gt;</content><category term="bash"/><category term="linux"/><category term="computing"/></entry><entry><title>Install Haiku under Mac OS X with Virtualbox</title><link href="https://www.martinfitzpatrick.com/install-haiku-under-mac-os-x-with-virtualbox/" rel="alternate"/><published>2012-02-05T03:02:00+00:00</published><updated>2012-02-05T03:02:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2012-02-05:/install-haiku-under-mac-os-x-with-virtualbox/</id><summary type="html">Haiku is an open source operating system currently in development that specifically targets personal computing. Inspired by the Be Operating System, development began in 2001, and the operating system became self-hosting in 2008 with the first alpha release in September 2009, the second in May 2010 and the third in June 2011. Here we describe how to run a Virtualbox hosted Haiku OS on Mac OS X.</summary><content type="html">&lt;p&gt;Haiku is an open source operating system currently in development that specifically targets personal computing. Inspired by the Be Operating System, development began in 2001, and the operating system became self-hosting in 2008 with the first alpha release in September 2009, the second in May 2010 and the third in June 2011. Here we describe how to run a Virtualbox hosted Haiku OS on Mac OS X.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This has been tested on Mac OS X 10.8 (Mountain Lion) MacBook Air. You might need to tweak some settings on other hardware/OS versions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To run Haiku on top of Mac OS X first download and install &lt;a href="https://www.virtualbox.org/wiki/Downloads"&gt;Virtualbox&lt;/a&gt;.
&lt;img alt="step/None/Screen Shot 2012-08-05 at 14.31.31.png" src="/static/images/step/None/Screen%20Shot%202012-08-05%20at%2014.31.31.png"/&gt;&lt;/p&gt;
&lt;p&gt;Download the version of Virtualbox for Mac OS X and open up the .dmg. Double-click on &lt;code&gt;Virtualbox.mpkg&lt;/code&gt; to start the installation.
&lt;img alt="step/None/Screen Shot 2012-08-05 at 14.34.10.png" src="/static/images/step/None/Screen%20Shot%202012-08-05%20at%2014.34.10.png"/&gt;&lt;/p&gt;
&lt;p&gt;While Virtualbox is installing download a copy of Haiku. The simplest way to get Haiku running in Virtualbox is to &lt;a href="https://www.haiku-os.org/get-haiku"&gt;download the pre-built VM image&lt;/a&gt;. Download the .zip file and double-click to unpack to a folder. Put this somewhere safe: this will hold your Haiku install.
&lt;img alt="step/None/Screen Shot 2012-08-05 at 14.33.15.png" src="/static/images/step/None/Screen%20Shot%202012-08-05%20at%2014.33.15.png"/&gt;&lt;/p&gt;
&lt;p&gt;Start up Virtualbox and click to create a new virtual machine. There is a short wizard to set this up. Set the operating system to Other and version to Other/Unknown.
&lt;img alt="step/None/Screen Shot 2012-08-05 at 14.36.07.png" src="/static/images/step/None/Screen%20Shot%202012-08-05%20at%2014.36.07.png"/&gt;&lt;/p&gt;
&lt;p&gt;Set the memory according to your host computer but 512MB should be enough for a comfortable Haiku install.
&lt;img alt="step/None/Screen Shot 2012-08-05 at 15.21.13.png" src="/static/images/step/None/Screen%20Shot%202012-08-05%20at%2015.21.13.png"/&gt;&lt;/p&gt;
&lt;p&gt;On the Virtual Hard Disk ensure that Start-up disk is checked and select "Use existing hard disk". Click the folder icon to the right on the drop down and browse to the location on your downloaded and unpacked Haiku folder.  Select the file named &lt;code&gt;haiku-r1alpha3.vmdk&lt;/code&gt; (or similar on later releases).
&lt;img alt="step/None/Screen Shot 2012-08-05 at 15.21.21.png" src="/static/images/step/None/Screen%20Shot%202012-08-05%20at%2015.21.21.png"/&gt;&lt;/p&gt;
&lt;p&gt;The default settings of Virtualbox work perfectly fine. However, Haiku occasionally locks up on the boot loader screen. A simple fix is to enable dual processor support. To do this right click on the created virtual machine and choose settings. Navigate to System -&amp;gt; Processor and increase the processor(s) to &lt;code&gt;2&lt;/code&gt;. Click OK to save the configuration.
&lt;img alt="step/None/Screen Shot 2012-08-05 at 15.27.47.png" src="/static/images/step/None/Screen%20Shot%202012-08-05%20at%2015.27.47.png"/&gt;&lt;/p&gt;
&lt;p&gt;In order to make networking work from within Haiku you will also need to change the virtualised networking card to any of the Intel ones. In settings navigate to Network -&amp;gt; Adapter 1 -&amp;gt; Advanced (hidden drop down section) and change the adapter type for example to &lt;code&gt;Intel PRO/1000 MT Desktop&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="step/None/Screen Shot 2012-08-05 at 15.46.09.png" src="/static/images/step/None/Screen%20Shot%202012-08-05%20at%2015.46.09.png"/&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can leave the attached to setting as NAT or alternatively bind it to a specific adapter in your host computer by choosing Bridged Adapter and then (for example) wlan0 for Wi-Fi/Airport.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Double click the virtual machine to start Haiku!
&lt;img alt="step/None/Screen Shot 2012-08-05 at 14.37.53.png" src="/static/images/step/None/Screen%20Shot%202012-08-05%20at%2014.37.53.png"/&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Your mouse and keyboard will be captured by Virtualbox/Haiku if you click in the window. To get it back for use in Mac OS X press the &lt;code&gt;Command&lt;/code&gt; (&lt;code&gt;Cmd&lt;/code&gt;) key.&lt;/p&gt;
&lt;/blockquote&gt;</content><category term="computing"/></entry><entry><title>Find all files containing a given string</title><link href="https://www.martinfitzpatrick.com/find-all-files-containing-a-given-string/" rel="alternate"/><published>2012-02-01T01:02:00+00:00</published><updated>2012-02-01T01:02:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2012-02-01:/find-all-files-containing-a-given-string/</id><summary type="html">A quick one-liner to recursively search files for a given text string.</summary><content type="html">&lt;p&gt;A quick one-liner to recursively search files for a given text string.&lt;/p&gt;
&lt;p&gt;Open up a terminal session and enter the following - replacing &lt;code&gt;"foo"&lt;/code&gt; with the text to search for.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;find . -exec grep -l "foo" {} \;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can also limit the search to files with a particular extension (e.g. HTML or CSS). The first form will only return files in the current directory&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;find *.html -exec grep -l "foo" {} \;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Alternatively you can search recursively while matching filenames using the &lt;code&gt;-name&lt;/code&gt; parameter:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;find . -name *.html -exec grep -l "foo" {} \;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;You can pass other options to find including for example &lt;code&gt;-mtime -1&lt;/code&gt; to specify only files modified within the past day.
You can also replace &lt;code&gt;.&lt;/code&gt; with the name of any other folder to search:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;find /var/www -name *.html -exec grep -l "foo" {} \;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This will search &lt;code&gt;/var/www&lt;/code&gt; recursively for files named &lt;code&gt;*.html&lt;/code&gt; and containing &lt;code&gt;foo&lt;/code&gt;.&lt;/p&gt;</content><category term="bash"/><category term="mac"/><category term="cli"/><category term="linux"/><category term="computing"/></entry></feed>