All in One View

Content from What Are Jupyter Notebooks?


Last updated on 2026-08-24 | Edit this page

Estimated time: 20 minutes

Overview

Questions

  • What is a Jupyter Notebook?
  • Why are Jupyter Notebooks useful for computational research?
  • How do Jupyter Notebooks differ from regular Python scripts?

Objectives

  • Understand what Jupyter Notebooks are and their key features
  • Recognize use cases where Jupyter Notebooks are ideal
  • Understand the difference between notebooks and scripts
  • Identify when to use notebooks vs. other tools

What is a Jupyter Notebook?


Jupyter Notebook is an open-source web application that allows you to create and share documents containing:

  • Live code: Executable Python, R, Julia, and 40+ other languages
  • Output: Formatted results, plots, images, tables
  • Narrative text: Written explanation in Markdown, including equations
  • Visualizations: Plots, charts, and interactive graphics

All of these elements live in a single document, in your web browser, making it easy to explore data, test ideas, and communicate your work.

Callout

Where does the name come from?

Jupyter evolved from IPython (Interactive Python), created in 2001 by Fernando Pérez. In 2014, the IPython team created Jupyter Notebooks, extending the interactive concept to include rich output and the notebook format we know today.

The name “Jupyter” comes from the three core languages it originally supported: Julia, Python, and R.

Today, Jupyter supports over 100 languages through different “kernels” (the execution engines that run code), but Python remains the most common.

Anatomy of a Jupyter Notebook


A Notebook is organized into cells, where each cell contains either:

Code cells

PYTHON

import numpy as np
import matplotlib.pyplot as plt

# Create data
x = np.linspace(0, 10, 100)
y = np.sin(x)

# Create plot
plt.plot(x, y)
plt.title('Sine Wave')
plt.xlabel('x')
plt.ylabel('sin(x)')
plt.show()

When you run a code cell, the code executes on the kernel (the Python interpreter) and results appear below the cell.

Markdown cells

These contain formatted text, written in Markdown (a simple markup language):

MARKDOWN

# Analysis of Temperature Data

This notebook analyzes temperature measurements from the Sagehen weather station.

## Methods

We collected data every hour from January to March 2024.

## Results

- Mean temperature: 45F
- Maximum: 82F
- Minimum: 18F

When you run a markdown cell, it displays as formatted text. This lets you combine explanation with code in a single document.

Raw cells (less common)

Raw cells contain unformatted text, useful for specific export formats. Most notebooks use code and markdown cells.

Why Jupyter Notebooks?


Advantages for research

Interactive exploration: Rather than writing a complete script, then running it, then debugging, Jupyter lets you develop code interactively. Run one cell, see results, then modify and run again. This tight feedback loop accelerates discovery.

Reproducible analysis: Your code, results, and explanations live together in one document. This makes it much easier for collaborators to understand what you did and reproduce your work.

Rich output: Plots appear inline below your code. Tables are formatted nicely. Equations can be written in LaTeX. This produces publication-quality results without leaving the notebook.

Easy to learn: Jupyter’s interface is intuitive. You don’t need to know Git, command-line tools, or complex IDEs to get started. For students and researchers new to computing, Jupyter is an excellent entry point.

Shareable: You can save notebooks as .ipynb files and email them to collaborators. They can open the same notebook and see your code, output, and explanations. (The .ipynb format is JSON-based text, so it works with version control.)

Multiple languages: While we focus on Python in this workshop, Jupyter supports R, Julia, and many others. One user can work in their preferred language in a single environment.

When to use Jupyter vs. other tools


Use Jupyter Notebooks for: - Exploratory analysis (understanding new data) - Interactive visualization and data exploration - Teaching and learning (great for tutorials) - Communicating results to non-technical audiences - Data science and statistical analysis workflows - Prototyping analysis pipelines before scaling with SLURM

Use regular Python scripts for: - Long-running analyses (better for batch jobs via SLURM) - Production pipelines that run unattended - Code that needs to be imported into other programs - Performance-critical code where every millisecond counts - Large analyses that generate thousands of files

Use Jupyter + SLURM together for: - Developing and testing code interactively in Jupyter - When satisfied with the approach, convert to a script - Submit the script as a batch job to SLURM for production - Use Jupyter again to analyze the results

Discussion

Choosing the right tool

Ask yourself these questions: - Do I need to explore and understand data interactively? Use Jupyter - Will this run for hours unattended? Use a script + SLURM batch job - Do I need to explain my work to non-programmers? Use a Jupyter notebook - Am I just testing an idea quickly? Start in Jupyter, convert to script if needed - Do I need publication-quality visualizations? Jupyter is great for this

The best practice: Start with Jupyter for exploration, then convert to scripts for production work.

Challenge

Challenge 1: Jupyter Notebook use case evaluation

For each scenario, decide: Is Jupyter Notebook ideal, useful but not perfect, or not recommended? 1. Scenario A: Training a deep neural network on 100 million images that will take 48 hours 2. Scenario B: Exploring a new dataset, creating visualizations, and testing analysis ideas 3. Scenario C: Documenting your lab’s data analysis pipeline for new students 4. Scenario D: Writing production code that multiple applications depend on

For each, write 1-2 sentences explaining your reasoning.

Scenario A: Not recommended. Long-running training jobs are better suited for batch scripts submitted to SLURM (e.g., sbatch). Jupyter notebooks require active browser connections and aren’t designed for unattended 48-hour jobs. Use a Python script with SLURM instead, and use Jupyter afterward to analyze results.

Scenario B: Ideal. This is exactly what Jupyter is designed for. Interactive exploration, immediate feedback, and inline visualizations make it perfect for understanding new data and testing different analysis approaches before committing to a final approach.

Scenario C: Ideal. Jupyter notebooks are excellent for documentation and teaching. A notebook with markdown explanations, working code examples, and output demonstrates the pipeline step-by-step, making it easy for students to learn and adapt the process for their own data.

Scenario D: Not recommended. Production code that other applications depend on should be modular Python files (modules) or packages. Notebooks are harder to import and test, and their state-based execution makes them risky for critical workflows. Convert to .py scripts and use proper software engineering practices.

Key Points
  • Jupyter Notebooks combine code, output, and narrative text in interactive documents
  • They’re ideal for exploration, teaching, and communicating research
  • Notebooks are great for development but scripts are better for long-running production jobs
  • The best practice: Start with Jupyter, convert to scripts when ready to scale

Content from Launching Jupyter on Sagehen HPC


Last updated on 2026-08-27 | Edit this page

Estimated time: 25 minutes

Overview

Questions

  • How do I launch Jupyter on the Sagehen HPC cluster?
  • What is the OnDemand portal and why use it?
  • How do I request the right amount of compute resources?

Objectives

  • Understand how the OnDemand portal works
  • Successfully launch JupyterLab on Sagehen
  • Request appropriate compute resources for your analysis

The OnDemand Portal


Open OnDemand is a web-based interface for accessing HPC clusters. Instead of using SSH and command-line tools, OnDemand provides a graphical interface for:

  • Viewing file systems on the cluster
  • Launching interactive applications (like Jupyter)
  • Monitoring jobs
  • Managing files with a web-based file browser
  • Terminal access (if needed)

Sagehen’s OnDemand portal is at: https://ondemand.hpc.pomona.edu/

Why OnDemand?

Advantages: - No installation needed on your laptop, works with any web browser - No SSH required, log in with your Pomona credentials - Web interface, no command-line knowledge needed - DUO authentication, secure access with your MFA device - Direct cluster access, Jupyter runs on the cluster, not your laptop - Easy resource management, request CPUs and RAM through simple forms

When you launch Jupyter through OnDemand: 1. OnDemand submits a SLURM job on your behalf 2. The job starts a JupyterLab server on a compute node 3. OnDemand creates a secure tunnel from your browser to that server 4. You see JupyterLab in your browser and can start working

All computation happens on the cluster, all results stay on the cluster (no network overhead).

Step 1: Access OnDemand


  1. Open a web browser (Chrome, Firefox, Safari, Edge)
  2. Navigate to: https://ondemand.hpc.pomona.edu/
  3. You’ll see the Pomona College sign-in page
  4. Enter your Pomona username and password
  5. Complete DUO authentication (approve on your phone or enter a code)
Pomona College single sign-on login page showing username and password fields
Pomona College sign-in page for OnDemand access
  1. After authenticating, you’ll see the OnDemand dashboard with your pinned apps
Pomona College OnDemand dashboard showing Interactive Apps sidebar with pinned applications
OnDemand dashboard showing pinned apps and navigation menu

Step 2: Launch JupyterLab


Once logged into OnDemand:

  1. In the top menu bar, click “Interactive Apps”
  2. Select “Jupyter Notebook” from the dropdown menu
OnDemand Interactive Apps dropdown menu with Jupyter Notebook highlighted among other applications
Interactive Apps dropdown showing Jupyter Notebook option
  1. A form appears asking for resource requests

Step 3: Request Resources


The form asks for several parameters. Here’s what each means:

Number of cores

How many CPU cores do you want? Default is 1, which is fine for learning and small datasets.

Recommendations: - 1 core: Development, testing, interactive work - 2-4 cores: Light data analysis, typical notebooks - 8 cores: Medium datasets, faster computation - 16+ cores: Large-scale analysis (rarely needed for notebooks)

For this workshop: Use 1 core to avoid using cluster resources others need.

Memory (RAM)

How much RAM do you want? Specify in GB.

Recommendations: - 2 GB: Development, testing - 4-8 GB: Typical data analysis (default) - 16-32 GB: Large datasets (thousands of columns) - 64+ GB: Very large in-memory analysis

For this workshop: Use 4 GB (or the default).

Wall time

How long should the job run before automatically stopping? Specify in hours.

Common durations: - 1 hour: Tutorials, learning - 4 hours: Interactive work, exploratory analysis - 8 hours: Long analysis sessions

Important: When your time limit is reached, the Jupyter server automatically stops. You can request a new session if you need more time. Your files are saved on the cluster, so no work is lost.

For this workshop: Use 1 hour (plenty of time for learning).

Partition (optional)

Sagehen has different partitions (queues) for different job types:

  • amd (default): Standard CPU partition, 128 cores, 500 GB RAM
  • gpu: GPUs available (A100, L40S, RTX PRO 6000) for deep learning
  • short: Max 2 hours, for quick jobs

For Jupyter notebooks: Use the default (amd) unless you’re using GPUs.

Callout

Workshop settings

For this workshop, use these values:

Number of cores: 1
Memory (GB): 4
Wall time (hours): 1
Partition: amd

Click “Launch” and wait. These are conservative settings that will start quickly and give us plenty of resources for learning.

The launch form lets you select partition, Jupyter version, number of hours, CPU cores, and memory:

OnDemand Jupyter Notebook launch form with fields for partition, Jupyter version, number of hours, CPU cores, and memory
Jupyter Notebook launch form showing resource request options
Challenge

Challenge 1: Launch JupyterLab

  1. Open https://ondemand.hpc.pomona.edu/ in your web browser
  2. Log in with your Pomona credentials + DUO
  3. Click “Interactive Apps” then “JupyterLab”
  4. Fill in the form:
    • Number of cores: 1
    • Memory: 4 GB
    • Wall time: 1 hour
    • Partition: amd (default)
  5. Click “Launch” and wait for the server to start

Once JupyterLab appears in your browser, you’ve successfully launched Jupyter on Sagehen! Leave it running for the next episode.

Success indicators: After clicking “Launch”, you’ll see a blue box showing “Your job is starting…” with SLURM job information. After 30 seconds to 2 minutes, this will redirect and you’ll see the JupyterLab interface with: - A left sidebar with file browser and launcher icons - A main area (likely empty since you haven’t opened anything yet) - A top menu bar with File, Edit, View, Run, Kernel menus - A kernel selector showing “Python 3” in the top right

If this doesn’t appear: - Wait up to 5 minutes (cluster might be busy) - Check your internet connection - Try again with fewer resources (fewer cores may start faster) - Contact if stuck

Key Points
  • OnDemand provides a web-based interface for launching Jupyter at https://ondemand.hpc.pomona.edu/
  • Request compute resources (cores, memory, time) when launching
  • Use conservative settings (1 core, 4 GB, 1 hour) for learning
  • The amd partition is the default for standard CPU work

Content from Notebook Interface Basics


Last updated on 2026-08-27 | Edit this page

Estimated time: 25 minutes

Overview

Questions

  • What does the JupyterLab interface look like?
  • How do I create my first notebook?
  • Where does my data live on Sagehen HPC?
  • How do I stop my session when done?

Objectives

  • Navigate the JupyterLab interface
  • Create a new notebook and select a kernel
  • Understand where data lives on Sagehen and how to access it
  • Stop sessions properly to free cluster resources

Waiting for the Server


OnDemand shows a card with your session status. First you’ll see it in the “Starting” state:

OnDemand session card showing Jupyter Notebook job in Starting state with session details and time remaining
Session starting with status card showing job details

Once the job is running, a green “Connect to Jupyter” button appears:

OnDemand session card showing Jupyter Notebook job in Running state with green Connect to Jupyter button
Session running with Connect to Jupyter button

Click “Connect to Jupyter” to open JupyterLab. This typically takes 30 seconds to 2 minutes from launch to connection.

If it takes longer than 5 minutes

The cluster might be busy. You can: - Wait longer (SLURM may take time to find resources) - Request fewer resources (smaller jobs start faster) - Try later (cluster load varies throughout the day) - Email if it seems stuck

The JupyterLab Interface


Once the server starts, you’ll see the JupyterLab interface with the Launcher tab:

JupyterLab interface showing file browser on the left and Launcher tab with Notebook, Console, and Other sections including Python 3, ipyrad, and Python Data Visualization kernels
JupyterLab interface showing the Launcher with notebook, console, and other options

The interface has:

  • Left sidebar: File browser, running kernels, Git integration
  • Main area: Launcher tab (or notebook/file editor once you open something)
  • Menu bar: File, Edit, View, Run, Kernel menus
  • Right sidebar (optional): Property inspector, Git, comments

JupyterLab is the modern interface for Jupyter. It’s more powerful than the older “Notebook” interface, with better file browsing and tabbed editing.

Creating Your First Notebook


  1. In the left sidebar, look for “Notebook” under “Other”
  2. Click the “+” button or right-click and select “New Notebook”
  3. A dialog asks: “Select a kernel”. Choose “Python 3 (ipykernel)”
  4. A new notebook appears in the main area

Congratulations! You now have a blank notebook running on Sagehen.

Understanding Data Storage


Callout

Sagehen HPC storage paths

Your Jupyter session has access to all Sagehen storage: - /rhome/<myusername>: Your home directory (100 GB, backed up) - /bigdata/lab/<labname>: Lab shared storage (1 TB, backed up) - /scratch/your_username: Fast temporary (SSD, not backed up)

Note: /rhome and /bigdata share a 1 TB lab quota.

In your first notebook cell, you can check your location:

PYTHON

import os
print(os.getcwd())  # Probably /rhome/<myusername>
print(os.listdir('..'))  # See files in your home

You can load data from any of these locations:

PYTHON

import pandas as pd
df = pd.read_csv('/bigdata/lab/<labname>/lab_data/experiments.csv')
# or
df = pd.read_csv(os.path.expanduser('~/data/file.csv'))

Stopping Your Session


When you’re done:

  1. Save your notebook (Ctrl+S or File then Save)
  2. Go back to the OnDemand dashboard
  3. Find your running JupyterLab session
  4. Click “Delete” to stop the server

This frees cluster resources for others. Your notebook file is automatically saved on Sagehen, so you can access it later.

After your session ends (or you delete it), the OnDemand card shows “Completed”:

OnDemand session card showing Jupyter Notebook session in Completed state with message that the card will be retained for 7 more days
OnDemand session card showing completed status
Callout

Don’t forget to stop your session!

Even if you close your browser without clicking Delete, the session continues running (using cluster resources) until the wall time expires. Always delete your session when done so other users can access those resources.

Connecting Again Later


Your notebooks are saved on Sagehen. To access them again:

  1. Log into OnDemand again
  2. Click “Interactive Apps” then “JupyterLab” again
  3. The new JupyterLab session starts fresh, but your files are still there
  4. Open any notebook from the file browser
Challenge

Challenge 1: Explore the file system

In your running JupyterLab session: 1. Look at the left sidebar (the file browser) 2. Click on the folder icon to navigate 3. You should see your home directory 4. Create a new folder called jupyter-workshop (right-click then New Folder) 5. Click on the folder to open it 6. Create a new notebook: Click “+” then select “Notebook” then choose “Python 3”

This is your workspace for the rest of the workshop. Save this first notebook as test.ipynb in your workshop folder.

Step-by-step completion: 1. File browser: In the left sidebar, you’ll see a folder icon at the top. Click it to reveal the file browser showing your current directory (usually /rhome/<myusername>). 2. Create folder: Right-click in the empty file browser area and select “New Folder”. Type jupyter-workshop and press Enter. 3. Navigate to folder: Double-click the jupyter-workshop folder to open it. You should now see “jupyter-workshop” in the file path at the top. 4. Create notebook: In the file browser or main area, click the “+” button or use File then New then Notebook. When prompted, select “Python 3” or “Python 3 (ipykernel)” kernel. 5. Save notebook: A new untitled notebook appears in the main area. Click File then Save Notebook As (or use Ctrl+S). Name it test.ipynb. It will be saved in your jupyter-workshop folder.

Verification: You should see test.ipynb appear in the left file browser under jupyter-workshop, and the tab in the main area should show “test.ipynb” as the active notebook.

Challenge

Challenge 2: Access cluster data

In your notebook, create a code cell and run:

PYTHON

import os

# Where am I?
print("Current directory:", os.getcwd())

# What can I see?
print("\nHome directory contents:")
print(os.listdir(os.path.expanduser("~")))

# Can I see /bigdata?
print("\n/bigdata contents:")
try:
    print(os.listdir("/bigdata"))
except PermissionError:
    print("No access to /bigdata (expected if not in a lab group)")

This verifies you can access the cluster’s storage. The errors are normal if you’re not in a lab group.

Expected output (varies by username and lab membership):

Current directory: /rhome/<myusername>

Home directory contents:
['Documents', 'Desktop', '.local', '.config', 'Downloads', ...]

/bigdata contents:
['lab1_data', 'lab2_data', ...]  # Or PermissionError if not in a lab

What this demonstrates: - Your Jupyter kernel runs as your user (not root) - You’re currently in your home directory (/rhome/<myusername>) - You have full read/write access to your home directory - You can access /bigdata if you’re in a lab group (if not, that’s normal)

Key Points
  • JupyterLab provides a modern interface with file browser, menus, and tabbed editing
  • Create notebooks by clicking “+” and selecting a Python kernel
  • Your Jupyter session has full access to Sagehen storage (/rhome, /bigdata, /scratch)
  • Always stop your session when done to free cluster resources
  • Your notebooks are saved on the cluster, accessible in future sessions

Content from Working with Cells


Last updated on 2026-08-27 | Edit this page

Estimated time: 35 minutes

Overview

Questions

  • How do I create and edit cells in a notebook?
  • What’s the difference between code cells and markdown cells?
  • How do I run cells and understand cell execution?
  • Why does execution order matter?

Objectives

  • Understand the two main cell types (code and markdown)
  • Create, edit, and run cells in different ways
  • Understand notebook state and execution order
  • Use markdown to add rich formatting and documentation
A JupyterLab notebook. The upper cell holds markdown source with a heading, Methods and Results sections and a bullet list. Below it the same cell is shown rendered as formatted text, with Analysis of Temperature Data as a heading, a short paragraph, and bulleted mean, maximum and minimum values.
A markdown cell before and after running it: the source on top, the formatted result below.

Cell Types in Jupyter


A notebook is a sequence of cells. Each cell has a type that determines what happens when you run it.

JupyterLab interface with the file browser on the left showing home directory folders and the Launcher on the right with Python 3 ipykernel notebook option
JupyterLab interface showing file browser and notebook area

Code cells

Code cells contain executable Python code. When you run a code cell:

  1. The code is sent to the Python kernel (interpreter)
  2. Python executes the code
  3. Any output (print statements, plots, results) appears below the cell
  4. The code has access to all variables from previously executed cells

Example code cell:

PYTHON

# This is a code cell
x = 5
y = 10
print(f"x + y = {x + y}")

When run, this produces output:

x + y = 15

Markdown cells

Markdown cells contain formatted text (not code). When you run a markdown cell, it displays as formatted text with headings, bold, italics, lists, links, images, and more.

MARKDOWN

# Main Heading

## Subheading

This is **bold** and this is *italic*.

- Bullet point 1
- Bullet point 2

You can also write equations:
$$E = mc^2$$

Creating Cells


Creating a new cell

Using the menu: 1. Click the “+” button in the toolbar (or use menu: Insert then Insert Cell) 2. By default, new cells are code cells 3. To change to markdown, click the cell and use the dropdown (shows “Code” by default)

Callout

Useful keyboard shortcuts

  • Ctrl+Shift+A: Insert cell above current cell
  • Ctrl+Shift+B: Insert cell below current cell
  • Escape then M: Convert current cell to markdown
  • Escape then Y: Convert current cell to code

Choosing cell type

When you create a cell, it defaults to code. To change to markdown:

  1. Click the cell you want to change
  2. Look for a dropdown in the toolbar that says “Code”
  3. Click it and select “Markdown” (or “Raw” for raw text)
JupyterLab toolbar cell type dropdown menu expanded showing dash, Code, Markdown, and Raw cell type options
Cell type dropdown showing Code, Markdown, and Raw options

Or use the keyboard shortcut: press Escape then M (while the cell is selected)

Running Cells


Run a single cell

Click in a cell and press Ctrl+Enter (Windows/Linux) or Cmd+Enter (Mac).

The cell runs and the cursor stays in that cell.

Run a cell and move to the next

Press Shift+Enter in a cell.

The cell runs and the cursor moves to the next cell (or creates one if you’re at the end).

Run all cells

In the menu, click Run then Run All Cells

JupyterLab Run menu expanded showing options including Run Selected Cell, Run All Cells, and Restart Kernel and Run All Cells with keyboard shortcuts
Run menu showing cell execution options and keyboard shortcuts

This executes every cell in the notebook from top to bottom. Useful when you want to make sure everything works.

Understanding Execution Order


A key concept: Cells execute in the order you run them, not necessarily top-to-bottom.

Example:

Cell 1:  x = 5
Cell 2:  print(x)      # If I run this first, I'll get an error!
Cell 3:  x = 10        # If I run this after Cell 2, x changes to 10

If you run Cell 2, then Cell 3, then Cell 2 again, you’ll get different results.

Cell numbers

Each code cell displays a number like [1], [2], etc., showing the order in which it was run, not its position in the notebook.

JupyterLab notebook showing three code cells with execution numbers: cell 1 sets x equals 5, cell 2 prints x showing output 5, cell 3 sets x equals 10
Code cells showing execution numbers In[1], In[2], In[3] with variable assignments and print output
Callout

Best practice

Arrange cells logically (top-to-bottom) and run them in order. Don’t jump around. When in doubt, restart the kernel and run all cells from the top to make sure everything works in sequence.

Challenge

Challenge 1: Create a multi-cell notebook

In your running JupyterLab session: 1. Create a new notebook 2. In Cell 1 (code): Import libraries python import numpy as np import matplotlib.pyplot as plt 3. Press Shift+Enter (create new cell and move to it) 4. In Cell 2 (markdown): Add a title ``` # My First Analysis

This notebook demonstrates basic cell operations. 5. Run that markdown cell (Ctrl+Enter) 6. Create Cell 3 (code): Create datapython x = np.linspace(0, 2*np.pi, 100) y = np.sin(x) 7. Create Cell 4 (code): Plot the datapython plt.plot(x, y) plt.title(‘Sine Wave’) plt.xlabel(‘x’) plt.ylabel(‘sin(x)’) plt.show() ```

Run all cells. You should see a plot appear below Cell 4.

Expected notebook structure and behavior:

[1] import numpy as np
    import matplotlib.pyplot as plt
    (Cell runs successfully with no output)

# My First Analysis
This notebook demonstrates basic cell operations.
(Displays as formatted heading and text)

[2] x = np.linspace(0, 2*np.pi, 100)
    y = np.sin(x)
    (Cell runs successfully with no output)

[3] plt.plot(x, y)
    plt.title('Sine Wave')
    plt.xlabel('x')
    plt.ylabel('sin(x)')
    plt.show()
    (Cell displays a smooth sine wave plot)

This demonstrates the power of notebooks: code, formatted text, and visualizations all in one document!

Challenge

Challenge 2: Test execution order

In the same notebook: 1. Create a new cell at the end: python print(f"x has {len(x)} points") print(f"y ranges from {y.min():.2f} to {y.max():.2f}") 2. Run this cell. It works because x and y were defined earlier. 3. Now, restart the kernel: Click Kernel then Restart Kernel then Restart 4. Try to run the last cell again without running the previous cells. 5. You’ll get a NameError because x and y no longer exist. 6. Now, click Run then Run All Cells (to run from the beginning) 7. The error is fixed because all cells ran in order.

This demonstrates why execution order and kernel state matter!

Before restarting: Cell prints x has 100 points and y ranges from -1.00 to 1.00.

After restarting, running only the last cell: You see NameError: name 'x' is not defined because restarting the kernel clears all variables from memory.

After Run All Cells: Everything works again because Cell 1 defined numpy, Cell 3 created x and y, so the final cell can access them.

Key lesson: Cells depend on each other through kernel state (shared variables). Always run cells top-to-bottom or use “Run All Cells” to ensure dependencies are met.

Key Points
  • Notebooks contain code cells (executable) and markdown cells (formatted text)
  • Cells run in the order you execute them, not necessarily top-to-bottom
  • The kernel maintains state (variables, imports) across cells
  • Always arrange cells logically and run them in order
  • Each cell should do one thing; keep cells focused and manageable

Content from Python Fundamentals in Notebooks


Last updated on 2026-08-24 | Edit this page

Estimated time: 30 minutes

Overview

Questions

  • How do I write Python code in notebooks?
  • How do I import and use common data science libraries?
  • How do I load data from CSV and other formats?
  • What’s special about IPython magic commands?

Objectives

  • Write and execute Python code in notebook cells
  • Import and use NumPy, pandas, and matplotlib
  • Load data from CSV and other file formats
  • Use IPython magic commands for enhanced functionality

Python in Jupyter


Jupyter notebooks run Python code almost identically to regular Python scripts, but with some enhancements. Let’s explore the main differences and capabilities.

Basic Python syntax

Standard Python works as expected:

PYTHON

# Variables and types
name = "Alice"
age = 30
scores = [85, 92, 78, 95]
data_dict = {"name": "Bob", "age": 25}

# Loops and conditionals
for score in scores:
    if score >= 90:
        print(f"{score} is excellent!")

# Functions
def calculate_average(values):
    return sum(values) / len(values)

avg = calculate_average(scores)
print(f"Average: {avg:.2f}")

This all works in Jupyter exactly as it would in a script.

Importing Libraries


Most data science work relies on libraries. Common ones:

NumPy (numerical computing)

PYTHON

import numpy as np

# Create arrays
arr = np.array([1, 2, 3, 4, 5])
matrix = np.ones((3, 4))

# Array operations
result = arr * 2
print(result)  # [2 4 6 8 10]

# Statistics
mean = np.mean(arr)
std = np.std(arr)

pandas (data manipulation)

PYTHON

import pandas as pd

# Create a DataFrame (like an Excel table)
data = {
    'name': ['Alice', 'Bob', 'Carol'],
    'age': [30, 25, 28],
    'salary': [50000, 45000, 55000]
}
df = pd.DataFrame(data)

# Access columns
print(df['name'])

# Statistics
print(df.describe())

# Filter rows
high_earners = df[df['salary'] > 50000]

matplotlib (visualization)

PYTHON

import matplotlib.pyplot as plt

# Create a line plot
x = [1, 2, 3, 4, 5]
y = [2, 4, 6, 5, 8]
plt.plot(x, y)
plt.xlabel('X axis')
plt.ylabel('Y axis')
plt.title('Simple Plot')
plt.show()  # Important! Displays the plot

Loading Data from Files


A typical workflow: Load data from CSV (or other formats) and explore it.

From CSV files

PYTHON

import pandas as pd

# Load CSV file (use full path for clarity)
df = pd.read_csv('/rhome/<myusername>/data/experiments.csv')

# See first few rows
print(df.head())

# Get information about the DataFrame
print(df.info())

# Summary statistics
print(df.describe())

# Check shape (rows, columns)
print(f"Data has {df.shape[0]} rows and {df.shape[1]} columns")

From other formats

pandas supports many formats:

PYTHON

# Excel file
df = pd.read_excel('data.xlsx')

# JSON
df = pd.read_json('data.json')

# HDF5 (efficient for large datasets)
df = pd.read_hdf('data.h5', key='dataset')

# Pickle (Python-specific, fast)
df = pd.read_pickle('data.pkl')

IPython Magic Commands


Jupyter uses IPython (Interactive Python), which includes special commands called “magic” commands. These start with % (line magic) or %% (cell magic).

Callout

Commonly used magic commands

Timing code:

PYTHON

%timeit sum(range(1000000))    # Time a single line
%time x = sum(range(1000000))  # Time once (not averaged)

Displaying output:

PYTHON

%matplotlib inline             # Show plots inline (usually default)
%whos                          # List all variables in memory

Running external files:

PYTHON

%run my_script.py              # Run a Python script
%load my_code.py               # Load code from file into a cell

Output Handling


By default, Jupyter only displays the last output in a cell. To display multiple outputs:

PYTHON

# Method 1: Use print()
print("First output")
print("Second output")

# Method 2: Use display()
from IPython.display import display
display("First output")
display("Second output")

# Method 3: Semicolon suppresses output
x = 5
y = 10
z = x + y;  # Semicolon suppresses output
Challenge

Challenge 1: Load and explore data

  1. Create a new notebook cell and load some sample data:

    PYTHON

    import pandas as pd
    import numpy as np
    
    # Create sample dataset
    np.random.seed(42)
    df = pd.DataFrame({
        'date': pd.date_range('2024-01-01', periods=100),
        'temperature': np.random.normal(65, 10, 100),
        'humidity': np.random.normal(60, 15, 100),
        'wind_speed': np.random.exponential(5, 100)
    })
  2. In subsequent cells, explore the data:

    • Print df.head() to see first rows
    • Print df.info() to see data types
    • Print df.describe() for statistics
    • Calculate mean temperature and humidity

Document each step with markdown cells explaining what you’re doing.

Cell 1 (Markdown):

MARKDOWN

# Weather Data Analysis

Exploring a simulated dataset of 100 days of weather measurements.

Cell 2 (Code): Create the dataset (code above).

Cell 3 (Code):

PYTHON

print("First 5 rows of the dataset:")
print(df.head())

Cell 4 (Code):

PYTHON

print("\nDataFrame Information:")
df.info()

Cell 5 (Code):

PYTHON

mean_temp = df['temperature'].mean()
mean_humidity = df['humidity'].mean()
print(f"Mean temperature: {mean_temp:.2f} F")
print(f"Mean humidity: {mean_humidity:.2f}%")

Expected output includes a nicely formatted table with dates, temperatures (around 65F), humidity (around 60%), and wind speeds, followed by calculated means.

Key Points
  • Python code runs normally in Jupyter cells, with enhanced display capabilities
  • NumPy, pandas, and matplotlib are the core data science libraries
  • pandas can load data from CSV, Excel, JSON, HDF5, and many other formats
  • Magic commands (% and %%) add special IPython functionality like timing code
  • Use print() or display() to show multiple outputs from a single cell

Content from Data Analysis in Notebooks


Last updated on 2026-08-27 | Edit this page

Estimated time: 45 minutes

Overview

Questions

  • How do I explore and summarize data in a notebook?
  • How do I select, filter, and modify data with pandas?
  • How do I work with data stored on Sagehen HPC?
  • How do I keep a notebook reproducible as analysis grows?

Objectives

  • Explore datasets using pandas summary methods
  • Select, filter, and transform data
  • Modify DataFrames with new columns, sorting, and deduplication
  • Work with data on Sagehen’s storage systems
  • Apply notebook hygiene that keeps analysis reproducible

Why Data Analysis in Notebooks Matters for HPC


Most HPC research starts with a dataset and an investigator who needs to figure out what is in it before deciding how to scale the analysis. Notebooks shine in this exploratory phase because the unit of work is one cell at a time: load, look, plot, decide, adjust. The friction of switching between code and inspection is gone, which means more experiments per hour and faster discovery of the surprises that always live in real data.

The patterns in this episode are equally useful at the small scale (a 100 MB CSV in a notebook on the login node) and the large scale (a 50 GB Parquet dataset that drives a SLURM training job later). Learning them in the notebook context first means you bring the same vocabulary to the cluster.

Exploring Data


Once you have loaded a dataset, the first hour of any analysis is exploration. Pandas exposes a small vocabulary that, used in order, will tell you almost everything you need to know about a new dataset:

PYTHON

import pandas as pd

df = pd.read_csv('data.csv')

# First rows
print(df.head(10))

# Last rows
print(df.tail())

# Data types
print(df.dtypes)

# Missing values
print(df.isnull().sum())

# Unique values in a column
print(df['category'].unique())

# Value counts
print(df['category'].value_counts())

# Statistics by group
print(df.groupby('category')['value'].mean())

# Correlation matrix
print(df.corr())

A useful habit: in your first cell after loading, always print df.shape, df.dtypes, and df.isnull().sum(). The shape tells you whether the load worked. The dtypes catch the most common pandas surprise (numeric column read as object because of one row with stray text). The null count tells you whether to plan for missing-value handling before any analysis.

Callout

When pandas hits its limits on Sagehen HPC

The login node has plenty of memory for most pandas work, but it is shared with every other user. A 20 GB DataFrame load on the login node hurts everyone. Two thresholds to remember:

If the file is under ~2 GB, load it directly in your login-node notebook.

If the file is between 2 and 50 GB, launch your Jupyter session inside an interactive SLURM allocation: srun --partition=short --time=02:00:00 --mem=64G --pty bash, then start jupyter from there.

If the file is over 50 GB, switch to Dask (covered in Workshop 17). Pandas will not fit it in memory.

Selecting Data


PYTHON

# Select a column
ages = df['age']

# Select multiple columns
subset = df[['name', 'age', 'salary']]

# Filter rows (age > 25)
young_workers = df[df['age'] > 25]

# Filter multiple conditions
expensive_experienced = df[(df['salary'] > 50000) & (df['age'] > 30)]

# Select by position
first_row = df.iloc[0]
first_5_rows = df.iloc[:5]

# Select by label
row_by_name = df.loc[df['name'] == 'Alice']

The two indexers .loc and .iloc look similar but mean different things. .loc selects by label (the index value or a boolean mask), .iloc selects by integer position. When you reset an index after filtering, integer positions stay sequential but labels can have gaps; mixing them up is a frequent source of confusing bugs.

For boolean filtering with multiple conditions, parentheses are required around each condition. df[df['a'] > 0 & df['b'] > 0] is a syntax error trap because & binds tighter than >. Write df[(df['a'] > 0) & (df['b'] > 0)] instead.

Modifying Data


PYTHON

# Add a new column
df['years_employed'] = df['age'] - 22

# Rename columns
df.rename(columns={'old_name': 'new_name'}, inplace=True)

# Replace values
df['status'] = df['status'].replace('active', 'employed')

# Remove duplicates
df = df.drop_duplicates()

# Sort by column
df_sorted = df.sort_values('salary', ascending=False)

# Drop columns
df = df.drop(columns=['unwanted_col'])

Notebook hygiene tip: every modification cell should produce output you can verify. After drop_duplicates, print len(df) to confirm the new size. After rename, print df.columns. Silent mutations are how notebook bugs survive into the SLURM job that fails three hours into a long run.

Working with Sagehen HPC Data


Callout

A typical analysis workflow on Sagehen HPC

PYTHON

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

# Load data from /bigdata (lab shared storage)
df = pd.read_csv('/bigdata/lab/<labname>/experiments_2024.csv')

# Explore
print(f"Shape: {df.shape}")
print(df.info())
print(df.describe())

# Analyze
results = df.groupby('condition')['response'].agg(['mean', 'std'])
print(results)

# Visualize
plt.figure(figsize=(10, 6))
results['mean'].plot(kind='bar', yerr=results['std'])
plt.title('Response by Condition')
plt.xlabel('Condition')
plt.ylabel('Mean Response')
plt.tight_layout()
plt.show()

# Save results back to lab storage (NOT /scratch, which gets cleaned)
results.to_csv('/bigdata/lab/<labname>/results/condition_summary.csv')
print("Analysis complete!")

Three small details in that workflow are worth pulling out. Source data lives in /bigdata/lab/<labname>/, which is your lab’s persistent 1 TB allocation. Final results go back to /bigdata, not /scratch, because /scratch is wiped without backups. Plots are produced inline in the notebook so reviewers can re-run cell by cell and reproduce them.

Jupyter Display Options


Notebooks have rich display capabilities beyond simple print output:

PYTHON

from IPython.display import HTML, Markdown, Image

# Display HTML
HTML("<h1>Hello</h1>")

# Display markdown
Markdown("# Heading\n\nSome text")

# Display images
Image(url='https://example.com/image.png')

The most useful of these in practice is display(df) for nicely-formatted DataFrames and Markdown for inline section headers when you want narrative inside a long analysis notebook. For final analyses you plan to share, Markdown cells alongside code make a notebook readable as a written report.

Challenge

Challenge 1: Data manipulation

Using the weather data from the previous episode (or create it fresh):

PYTHON

import pandas as pd
import numpy as np

np.random.seed(42)
df = pd.DataFrame({
    'date': pd.date_range('2024-01-01', periods=100),
    'temperature': np.random.normal(65, 10, 100),
    'humidity': np.random.normal(60, 15, 100),
    'wind_speed': np.random.exponential(5, 100)
})
  1. Filter the data to rows where temperature > 70F
  2. Calculate statistics (mean, std, min, max) for filtered data
  3. Create a new column called ‘wind_category’ that labels wind speeds as:
    • ‘light’ if < 5
    • ‘moderate’ if 5-10
    • ‘strong’ if > 10
  4. Count how many observations fall into each wind category

PYTHON

# Step 1: Filter to rows where temperature > 70F
warm_days = df[df['temperature'] > 70]
print(f"Number of days with temperature > 70F: {len(warm_days)}")

# Step 2: Calculate statistics for filtered data
print("\nStatistics for warm days (temp > 70F):")
print(f"Mean temperature: {warm_days['temperature'].mean():.2f} F")
print(f"Std deviation: {warm_days['temperature'].std():.2f} F")
print(f"Min: {warm_days['temperature'].min():.2f} F")
print(f"Max: {warm_days['temperature'].max():.2f} F")

# Step 3: Create wind_category column
df['wind_category'] = pd.cut(df['wind_speed'],
                              bins=[0, 5, 10, float('inf')],
                              labels=['light', 'moderate', 'strong'],
                              right=False)

# Step 4: Count observations in each category
print("\nWind speed category counts:")
wind_counts = df['wind_category'].value_counts().sort_index()
print(wind_counts)

Key pandas techniques: - df[df['temperature'] > 70]: Boolean filtering to select rows meeting a condition - .mean(), .std(), .min(), .max(): Statistical functions on columns - pd.cut(): Binning continuous values into categories - .value_counts(): Counting occurrences of each category

Challenge

Challenge 2: Group-Then-Plot

Using the weather DataFrame from the previous challenge, group by wind_category and compute the mean temperature in each category. Make a bar chart of the result with error bars (standard deviation). Save the chart to /bigdata/lab/<labname>/figures/temp_by_wind.png.

PYTHON

import matplotlib.pyplot as plt

stats = df.groupby('wind_category')['temperature'].agg(['mean', 'std'])
print(stats)

plt.figure(figsize=(8, 5))
stats['mean'].plot(kind='bar', yerr=stats['std'], capsize=4)
plt.ylabel('Mean temperature (F)')
plt.title('Temperature by wind category')
plt.tight_layout()
plt.savefig('/bigdata/lab/<labname>/figures/temp_by_wind.png', dpi=150)
plt.show()

The capsize=4 adds visible end caps to error bars. dpi=150 is enough for a presentation slide; bump to 300 for print-quality figures.

Key Points
  • Always inspect shape, dtypes, and null counts as the first cell after loading
  • Use .loc for label-based selection and .iloc for position-based; mixing them is a common bug
  • Boolean filters with multiple conditions need parentheses: df[(a) & (b)]
  • For files over 2 GB, do exploratory work in an interactive SLURM allocation, not on the login node
  • Save final results to /bigdata, not /scratch (which is wiped without backups)
  • Notebook hygiene: every modification cell should produce verifiable output

Content from Visualization in Notebooks


Last updated on 2026-08-24 | Edit this page

Estimated time: 25 minutes

Overview

Questions

  • How do I create plots and visualizations in Jupyter?
  • What types of plots are available with matplotlib?
  • How do I create multi-panel figures with subplots?

Objectives

  • Create line plots, scatter plots, histograms, and bar charts
  • Customize plots with titles, labels, colors, and legends
  • Create multi-panel figures using subplots
  • Understand how inline plotting works in notebooks

Inline Visualizations


Jupyter excels at inline visualizations. Plots appear right below code cells. Here’s an example of a matplotlib sine wave plot rendered inline in a notebook:

JupyterLab notebook with Python code importing numpy and matplotlib, creating a sine wave plot that displays inline below the code cell
Notebook showing Python code with matplotlib and a rendered sine wave plot

Line Plots


PYTHON

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
y = np.sin(x)

plt.figure(figsize=(10, 6))  # Width x Height in inches
plt.plot(x, y, label='sin(x)')
plt.plot(x, np.cos(x), label='cos(x)')
plt.xlabel('x')
plt.ylabel('y')
plt.title('Sine and Cosine')
plt.legend()
plt.grid(True)
plt.show()

Scatter Plots


PYTHON

import matplotlib.pyplot as plt
import numpy as np

# Generate random data
np.random.seed(42)
x = np.random.randn(100)
y = 2 * x + np.random.randn(100)

plt.scatter(x, y, alpha=0.6)
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Scatter Plot')
plt.show()

Histograms


PYTHON

import matplotlib.pyplot as plt
import numpy as np

data = np.random.normal(100, 15, 1000)  # mean=100, std=15, 1000 samples

plt.hist(data, bins=30, edgecolor='black')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.title('Distribution')
plt.show()

Bar Plots


PYTHON

import matplotlib.pyplot as plt

categories = ['A', 'B', 'C', 'D']
values = [10, 24, 36, 15]

plt.bar(categories, values, color='steelblue')
plt.xlabel('Category')
plt.ylabel('Value')
plt.title('Bar Chart')
plt.show()

Subplots


Create multi-panel figures for comparing visualizations:

PYTHON

import matplotlib.pyplot as plt
import numpy as np

fig, axes = plt.subplots(2, 2, figsize=(12, 10))

x = np.linspace(0, 10, 100)

axes[0, 0].plot(x, np.sin(x))
axes[0, 0].set_title('Sine')

axes[0, 1].plot(x, np.cos(x))
axes[0, 1].set_title('Cosine')

axes[1, 0].plot(x, x**2)
axes[1, 0].set_title('Quadratic')

axes[1, 1].plot(x, np.exp(-x))
axes[1, 1].set_title('Exponential Decay')

plt.tight_layout()
plt.show()
Challenge

Challenge 1: Create visualizations

Using the weather data (create it fresh if needed):

PYTHON

import pandas as pd
import numpy as np

np.random.seed(42)
df = pd.DataFrame({
    'date': pd.date_range('2024-01-01', periods=100),
    'temperature': np.random.normal(65, 10, 100),
    'humidity': np.random.normal(60, 15, 100),
    'wind_speed': np.random.exponential(5, 100)
})

Create a figure with 3 subplots side by side: 1. A line plot of temperature over time 2. A scatter plot of temperature vs humidity 3. A histogram of wind speeds

Customize each plot with titles, labels, and colors.

PYTHON

import matplotlib.pyplot as plt

fig, axes = plt.subplots(1, 3, figsize=(15, 4))

# Subplot 1: Line plot of temperature over time
axes[0].plot(df['date'], df['temperature'], color='red', linewidth=2)
axes[0].set_title('Temperature Over Time', fontsize=12, fontweight='bold')
axes[0].set_xlabel('Date')
axes[0].set_ylabel('Temperature (F)')
axes[0].grid(True, alpha=0.3)
axes[0].tick_params(axis='x', rotation=45)

# Subplot 2: Scatter plot of temperature vs humidity
axes[1].scatter(df['temperature'], df['humidity'], alpha=0.6, color='blue', s=50)
axes[1].set_title('Temperature vs Humidity', fontsize=12, fontweight='bold')
axes[1].set_xlabel('Temperature (F)')
axes[1].set_ylabel('Humidity (%)')
axes[1].grid(True, alpha=0.3)

# Subplot 3: Histogram of wind speeds
axes[2].hist(df['wind_speed'], bins=20, color='green', edgecolor='black', alpha=0.7)
axes[2].set_title('Distribution of Wind Speeds', fontsize=12, fontweight='bold')
axes[2].set_xlabel('Wind Speed')
axes[2].set_ylabel('Frequency')
axes[2].grid(True, alpha=0.3, axis='y')

plt.tight_layout()
plt.show()

Key visualization techniques: - figsize=(15, 4) creates a wide figure suitable for 3 subplots - alpha=0.6 adds transparency so overlapping points are visible - grid=True adds reference lines for easier reading - tight_layout() prevents subplot overlap - Each subplot has descriptive titles and axis labels

Key Points
  • Jupyter displays matplotlib plots inline below code cells
  • Line plots, scatter plots, histograms, and bar charts cover most visualization needs
  • Use figsize to control plot dimensions and tight_layout() to prevent overlap
  • Subplots let you compare multiple visualizations in a single figure
  • Always add titles, axis labels, and legends for clarity

Content from Managing Kernels and Environments


Last updated on 2026-08-27 | Edit this page

Estimated time: 35 minutes

Overview

Questions

  • What is a kernel and how does it affect my notebook?
  • How do I manage and restart kernels?
  • What is a Python environment and why do I need one?
  • How do I create a conda environment for my project?

Objectives

  • Understand what the kernel is and how it maintains state
  • Restart and manage kernels effectively
  • Understand what Python environments are and why they matter
  • Create and activate conda environments
  • Install packages and register Jupyter kernels

The Kernel: Python’s Memory


The kernel is the Python interpreter running behind the scenes. It maintains:

  • All variables you’ve created
  • All imported libraries
  • All function definitions
  • All state from executed cells

When you run a code cell:

PYTHON

name = "Alice"
age = 30

Those variables exist in the kernel’s memory. In later cells, you can use them:

PYTHON

print(f"{name} is {age} years old")  # This works!

Restarting the Kernel


Sometimes you want to clear all variables and start fresh (e.g., to debug issues or verify your notebook works from scratch).

To restart:

  1. Click Kernel then Restart Kernel
  2. Or press the “restart” button in the toolbar
  3. You’ll see a confirmation dialog
JupyterLab Restart Kernel dialog asking Do you want to restart the kernel of Untitled.ipynb with Cancel and red Restart buttons
Restart Kernel confirmation dialog
  1. Click “Restart”

After restarting, all variables are gone. If you run cells again, you’re starting fresh.

Callout

When to restart the kernel

  • You want to verify your notebook works from the beginning
  • You have mysterious errors that might be due to out-of-order execution
  • You want to clear memory (e.g., if you loaded a huge dataset)
  • You modified imported code and want to reload it

Interrupting a long-running cell

If a cell is taking too long (stuck in an infinite loop or processing huge data):

  1. Press the “stop” button in the toolbar
  2. Or click Kernel then Interrupt Kernel
  3. The cell stops executing

What Are Python Environments?


A Python environment is an isolated installation of Python with a specific set of packages. Think of it as a container holding Python and libraries.

Why environments matter

Imagine you have two projects:

  • Project A: Needs numpy 1.20, matplotlib 3.3
  • Project B: Needs numpy 1.25, matplotlib 3.8

If you install all packages in Python’s default location, they conflict. Environments solve this by keeping packages isolated.

Without environments:

System Python
  numpy 1.25
  matplotlib 3.8
  pandas 2.0
  ... (all packages mixed together)

With environments:

Python installation
  project_a_env
    numpy 1.20
    matplotlib 3.3
    pandas 1.5

  project_b_env
    numpy 1.25
    matplotlib 3.8
    pandas 2.0

Creating conda Environments


conda is Pomona’s recommended tool for managing Python environments. On Sagehen HPC, load conda first:

BASH

module load miniconda3
conda --version  # Verify it works

Creating an environment

BASH

# Create environment with a name and Python version
conda create --name myproject python=3.11

# Or with initial packages
conda create --name myproject python=3.11 numpy pandas matplotlib

Activating and using an environment

BASH

# Activate
conda activate myproject

# Install packages
conda install numpy scipy pandas

# Install with pip (if not available in conda)
pip install scikit-learn

# Deactivate when done
conda deactivate

Listing environments

BASH

conda env list

# Output:
# base                     /opt/miniconda3
# myproject             *  /opt/miniconda3/envs/myproject
# (the * shows your current environment)

Using Environments in Jupyter


To use a conda environment in Jupyter, you must install a kernel:

BASH

# Activate your environment
conda activate myproject

# Install jupyter and ipykernel
conda install jupyter ipykernel

# Install the kernel
python -m ipykernel install --user --name myproject --display-name "My Project (Python 3.11)"

Selecting kernel in notebook

When you create a new notebook in JupyterLab:

  1. Click “Select Kernel” when creating the notebook
  2. Or click the kernel name in the top-right of JupyterLab
  3. Choose your environment’s kernel from the list
JupyterLab Select Kernel dialog showing available kernels including ipyrad, Python Data Visualization, and Python 3 ipykernel under Start python Kernel section
Select Kernel dialog showing available Python kernels

On Sagehen, you’ll see multiple kernels available in the Launcher:

JupyterLab Launcher showing three notebook kernel options: Python 3 ipykernel, ipyrad, and Python Data Visualization
JupyterLab Launcher showing available notebook kernels

Verifying your environment

In a notebook using your environment:

PYTHON

import sys
print(sys.executable)  # Shows the Python in your environment
print(sys.version)     # Shows Python version

import numpy
print(numpy.__version__)  # Verify packages are installed
Challenge

Challenge 1: Create a conda environment

  1. Open a terminal (SSH to Sagehen or use OnDemand terminal):

  2. Load conda and create an environment:

    BASH

    module load miniconda3
    conda create --name workshop python=3.11 numpy pandas matplotlib jupyter ipykernel
  3. Activate it and install the kernel:

    BASH

    conda activate workshop
    python -m ipykernel install --user --name workshop --display-name "Workshop (Python 3.11)"
  4. Verify with conda list (you should see numpy, pandas, matplotlib).

Step 2 (create environment): conda asks Proceed ([y]/n)? – type y and press Enter. It downloads and installs packages (takes 1-2 minutes).

Step 3 (activate and install kernel): Your prompt changes to (workshop) $. Kernel install outputs:

Installed kernelspec workshop in /rhome/<myusername>/.local/share/jupyter/kernels/workshop

Troubleshooting: - “conda: command not found” – Try module load anaconda3 or check with module avail - Kernel doesn’t appear in JupyterLab – You may need to restart JupyterLab or refresh the page

Challenge

Challenge 2: Use environment in Jupyter

  1. Launch JupyterLab through OnDemand

  2. Create a new notebook and select the “Workshop (Python 3.11)” kernel

  3. In the notebook, run:

    PYTHON

    import sys
    print(sys.executable)
    
    import numpy as np
    print(f"numpy version: {np.__version__}")
    
    import pandas as pd
    print(f"pandas version: {pd.__version__}")

Verify the Python executable path contains envs/workshop.

Expected output:

/opt/miniconda3/envs/workshop/bin/python
numpy version: 1.24.x
pandas version: 2.0.x

If sys.executable shows /usr/bin/python or /opt/miniconda3/bin/python (without /envs/workshop), you selected the wrong kernel. Go to the kernel selector (top-right of JupyterLab) and switch to “Workshop (Python 3.11)”.

Key Points
  • The kernel is the Python interpreter that maintains all variables and state
  • Restart the kernel to clear state and verify notebooks work from scratch
  • Interrupt the kernel to stop long-running cells
  • Python environments isolate packages, preventing version conflicts
  • conda is Pomona’s recommended tool for managing environments
  • Install kernels to use custom environments in Jupyter notebooks

Content from Sharing and Exporting Notebooks


Last updated on 2026-08-24 | Edit this page

Estimated time: 25 minutes

Overview

Questions

  • How can I convert notebooks to other formats?
  • How do I use version control with notebooks?
  • How do I share reproducible research?

Objectives

  • Convert notebooks to Python scripts, HTML, and PDF
  • Use version control (Git) with notebooks effectively
  • Create reproducible environment files for sharing
  • Publish and share reproducible research

Converting Notebooks to Other Formats


To Python script

Convert notebook to a standalone Python script:

BASH

# In terminal on Sagehen
jupyter nbconvert --to script my_notebook.ipynb

# Creates: my_notebook.py
# All code cells become code
# Markdown cells become comments

Then run the script:

BASH

python my_notebook.py

Or submit as a batch job:

BASH

sbatch --job-name=analysis --time=1:00:00 --cpus-per-task=4 --mem=8G script.sbatch

Where script.sbatch contains:

BASH

#!/bin/bash
module load miniconda3
conda activate analysis
python my_notebook.py

To HTML (for sharing)

Create a self-contained HTML file that looks like the notebook:

BASH

jupyter nbconvert --to html my_notebook.ipynb

# Creates: my_notebook.html
# Open in any web browser - no Jupyter needed!

Email the HTML file to colleagues. They can view all code, outputs, and visualizations without having Jupyter installed.

To PDF (for publication)

BASH

jupyter nbconvert --to pdf my_notebook.ipynb

# Creates: my_notebook.pdf

Note: PDF export requires additional dependencies. If it fails, convert to HTML first, then print to PDF from your browser.

From JupyterLab interface

You can also export from within JupyterLab:

  1. Click File then Save and Export Notebook As
  2. Choose your desired format from the submenu
JupyterLab File menu expanded showing Save and Export Notebook As submenu with options including HTML, PDF, Executable Script, and more
File menu showing Save and Export Notebook As with format options

Reproducible Environments


Exporting an environment

Share your exact environment (all packages and versions) with colleagues:

BASH

conda activate myproject

# Export to a file
conda env export > environment.yml

The file looks like:

YAML

name: myproject
channels:
  - conda-forge
  - defaults
dependencies:
  - python=3.11
  - numpy=1.24.0
  - pandas=2.0.0
  - matplotlib=3.7.0
  - pip
  - pip:
    - scikit-learn==1.3.0
    - requests==2.31.0

Recreating from a file

Someone with this file can recreate your exact environment:

BASH

conda env create -f environment.yml
conda activate myproject
conda list

Version Control with Git


Including notebooks in Git

Notebooks are JSON files, so Git can track them, but diffs aren’t pretty. Still include them:

BASH

# Initialize Git repo
git init

# Create .gitignore
cat > .gitignore << EOF
*.ipynb_checkpoints/
__pycache__/
*.pyc
data/raw/*
results/*
.DS_Store
EOF

# Add and commit
git add .
git commit -m "Initial notebook project structure"

Best practices for Git + Notebooks

Do commit: - Notebook files (yes, the .ipynb files) - environment.yml (reproducibility!) - README.md - Small data files (< 10 MB)

Don’t commit: - Large datasets (use /bigdata or external storage) - Output files (regenerate them) - Checkpoint folders (.ipynb_checkpoints/)

Before committing: 1. Restart kernel 2. Run all cells 3. Clear large outputs: Edit then Clear Outputs of All Cells 4. Save notebook 5. Then commit

Publishing Reproducible Research


Sharing on GitHub

BASH

git init
git add .
git commit -m "Initial commit"
git remote add origin https://github.com/you/project.git
git push -u origin main

Creating a DOI for citation

Use Zenodo to get a Digital Object Identifier (DOI):

  1. Go to https://zenodo.org/
  2. Login with GitHub
  3. Authorize Zenodo to access your repos
  4. Select your repo to archive
  5. Get a citable DOI like: https://doi.org/10.5281/zenodo.xxxxx
Challenge

Challenge 1: Export notebook in multiple formats

  1. In JupyterLab, create a simple notebook with a markdown title and a code cell that creates a plot

  2. Run all cells

  3. Export to Python script:

    BASH

    jupyter nbconvert --to script notebook_name.ipynb
  4. Export to HTML:

    BASH

    jupyter nbconvert --to html notebook_name.ipynb
  5. Open the .py and .html files to see how they look

Python script output shows code cells as Python code and markdown as comments:

PYTHON

# # Data Analysis Report
# This report demonstrates exporting notebooks.

import matplotlib.pyplot as plt
import numpy as np
# ... rest of code

HTML output creates a self-contained file viewable in any browser with formatted markdown, code cells, and embedded plots.

Use cases: - Script (.py): Run analysis on cluster with SLURM - HTML: Email or share with colleagues who don’t have Jupyter - PDF: Submit with research reports or publish

Challenge

Challenge 2: Export environment file

  1. In your terminal (while in the workshop environment):

    BASH

    conda activate workshop
    conda env export > ~/workshop_environment.yml
  2. Look at the file:

    BASH

    cat ~/workshop_environment.yml
  3. A collaborator can recreate your exact environment:

    BASH

    conda env create -f workshop_environment.yml

This is how you ensure reproducibility across machines!

The file contains:

YAML

name: workshop
channels:
  - conda-forge
  - defaults
dependencies:
  - python=3.11.x
  - numpy=1.24.x
  - pandas=2.0.x
  - ...
prefix: /opt/miniconda3/envs/workshop

A colleague can do conda env create -f workshop_environment.yml and conda activate workshop to get the exact same environment. This YAML file is the key to reproducible science.

Key Points
  • Convert notebooks to Python scripts for batch jobs, HTML for sharing, PDF for publication
  • Export environment.yml files so others can recreate your exact environment
  • Use Git for version control; commit notebooks, environment.yml, and documentation
  • Publish on GitHub and use Zenodo for citable DOIs
  • Always restart kernel and run all cells before sharing

Content from Best Practices


Last updated on 2026-08-24 | Edit this page

Estimated time: 25 minutes

Overview

Questions

  • How do I organize notebooks for reproducibility?
  • What are best practices for naming and documenting?
  • How do I scale notebooks to production workflows?
  • What common mistakes should I avoid?

Objectives

  • Apply organizational best practices to notebooks
  • Document notebooks for clarity and reproducibility
  • Understand when to convert notebooks to batch jobs
  • Avoid common notebook pitfalls

Organizing Notebooks for Reproducibility


Directory structure

Organize your project to separate code, data, and results:

my_analysis/
  README.md                    # Project overview
  environment.yml              # Conda environment (always include!)
  notebooks/
    01_data_loading.ipynb      # Load and explore data
    02_analysis.ipynb          # Main analysis
    03_visualization.ipynb     # Create figures
  data/
    raw/                       # Original, never modified
      experiment.csv
    processed/                 # Cleaned versions
      experiment_clean.csv
  scripts/
    functions.py               # Reusable code
  results/
    figures/
      plot.png
    tables/
      summary.csv
  ANALYSIS_LOG.md              # What you did and why

File naming conventions

# Good: Descriptive and sequential
01_data_loading.ipynb
02_exploratory_analysis.ipynb
03_statistical_tests.ipynb

# Avoid: Vague names
analysis.ipynb
notebook.ipynb
draft_v3_final_REAL.ipynb

Documentation practices

At the top of each notebook, include a markdown cell:

MARKDOWN

# Analysis of Temperature Trends

**Purpose**: Analyze temperature patterns from Sagehen weather station
**Date**: 2024-03-06
**Author**: Alice Johnson
**Data**: /bigdata/lab/<labname>/weather/sagehen_2024.csv
**Output**: Results saved to /rhome/alice/results/

## Methods

1. Load temperature data for Jan-Feb 2024
2. Calculate daily averages
3. Perform trend analysis with linear regression
4. Create visualization

## Dependencies

See environment.yml for all package versions.

Scaling Notebooks to Batch Jobs


When your notebook works well, you may want to scale it:

Interactive notebook (for development)

PYTHON

# In notebook
df = pd.read_csv('data.csv')
results = analyze(df)
plot_results(results)

Batch script (for production)

Convert to Python script and run on cluster:

BASH

#!/bin/bash
#SBATCH --job-name=analysis
#SBATCH --time=4:00:00
#SBATCH --cpus-per-task=8
#SBATCH --mem=32G
#SBATCH --partition=amd

module load miniconda3
conda activate analysis

python analysis.py

Then submit with sbatch analysis.sbatch.

Callout

Best of both worlds

  1. Develop analysis in Jupyter (interactive, exploratory)
  2. Convert to script when approach is solid
  3. Submit script as batch job (automated, scalable)
  4. Use Jupyter again to analyze batch results

Avoiding Common Mistakes


Mistake 1: Kernel state dependencies. If you define x = 5 in one cell and use x 20 cells later, that value might have changed. Keep related code together or clearly document dependencies between cells.

Mistake 2: Relative paths that break. Using pd.read_csv('data.csv') works for you but fails for collaborators. Use absolute paths like /rhome/<myusername>/project/data/data.csv or paths relative to the project root.

Mistake 3: Outdated package versions. Your notebook runs today but breaks next month when packages update. Always export conda env export > environment.yml and include it with your project.

Mistake 4: Long-running cells without checkpoints. If a cell processes a million records and fails halfway through, you lose all progress. Break large operations into chunks and save intermediate results to disk.

Callout

Sharing notebooks ethically

Before sharing, consider: - Data privacy: Remove or anonymize sensitive data - Credentials: Never include passwords or API keys - Licensing: Include license file (CC-BY-4.0 is good for research) - Acknowledgments: Credit data sources and collaborators - Reproducibility: Verify everything works before sharing

Callout

Reproducible analysis checklist

Before sharing or publishing your work: - [ ] Environment documented: environment.yml file included - [ ] Data accessible: Clear instructions for accessing data - [ ] Code runs top-to-bottom: Kernel restart then Run All Cells succeeds - [ ] Outputs explained: Markdown cells explain each analysis step - [ ] Visualizations labeled: All plots have titles, axis labels, legends - [ ] Parameters clear: Any magic numbers have explanations - [ ] Version controlled: Repository with history - [ ] README included: Instructions for reproducing analysis

Challenge

Challenge 1: Create a reproducible project

Create a complete, reproducible analysis project: 1. Create a directory structure: bash mkdir -p my_analysis/notebooks my_analysis/data/raw my_analysis/data/processed my_analysis/results/figures my_analysis/results/tables 2. Create environment.yml: bash conda env export > my_analysis/environment.yml 3. Initialize Git: bash cd my_analysis git init echo "data/raw/*" > .gitignore git add . git commit -m "Initial project structure" 4. Create a notebook in notebooks/ that loads a sample dataset and creates a visualization

After completing the steps, your project looks like:

my_analysis/
  environment.yml
  .gitignore
  notebooks/
    01_analysis.ipynb
  data/
    raw/
    processed/
  results/
    figures/
    tables/

Why this structure is reproducible: - environment.yml ensures exact package versions - Directory organization is clear and standard - .gitignore prevents committing huge data files - README documents how to set up and run everything

Challenge

Challenge 2: Test reproducibility

Ensure your notebook is truly reproducible: 1. Open your notebook in JupyterLab 2. Kernel then Restart Kernel 3. Run then Run All Cells 4. Fix any errors that appear 5. Once it runs perfectly, do steps 2-4 again to double-check

Save the notebook. It’s now verified to work from a clean state. This is the standard before sharing!

Step 1-2: Restart kernel clears all variables (simulates someone running your notebook for the first time).

Step 3: Run all cells executes every cell from top to bottom. Each cell number should increase sequentially: [1], [2], [3], etc.

Step 4: Check for errors. Common issues: - NameError: name 'x' is not defined: Variable used before being defined - FileNotFoundError: Data file path is wrong - ModuleNotFoundError: Package not installed in your environment

Why this matters: You’ve proven it works from a clean state. Someone else can run your notebook and get the same results. This is the difference between “works for me” and truly reproducible research.

Key Points
  • Organize projects with clear directory structure and naming conventions
  • Document each notebook with purpose, author, data sources, and methods
  • Scale from Jupyter to batch jobs: develop interactively, then convert to scripts
  • Avoid common mistakes: kernel state issues, relative paths, outdated packages
  • Always export environment.yml for reproducibility
  • Test reproducibility: Restart kernel and run all cells before sharing