Palmer Penguins Data Explorer

Explore, clean, summarize, and export Palmer Penguins data with pandas.

Introduction

30 Second Summary

Real-world datasets often arrive with blank cells hidden among hundreds of useful records. Those gaps can quietly distort a comparison when you jump straight to averages.

In this project, you will build a reproducible Jupyter notebook in JupyterLab that explores the Palmer Penguins dataset with pandas. The finished report turns incomplete raw records into a ranked species comparison with shareable CSV outputs.

What You'll Build

You will run one notebook from top to bottom to watch 344 observations become a cleaned three-row species ranking with Gentoo first.

By the end of this project, you'll have:

  • A runnable notebook that loads all 344 observations. Its output displays the first five penguin records.
  • A missing-value audit that exposes incomplete fields. The raw species table makes the measurement shortfall visible.
  • Two shareable CSV exports for use outside JupyterLab: penguins_cleaned.csv and species_summary.csv.
  • Secret Mission: Build a second summary for every observed species and island combination.

Are there any prerequisites?

You need basic Python syntax plus internet access for loading the public dataset. Your existing Anaconda installation on Windows provides the local workspace.

Before We Start

This checkpoint gives your penguin analysis a clear purpose before any hands-on work begins. The report audits missing data before calculating summaries, which prevents incomplete measurements from silently distorting the results.

Prepare Your Anaconda and JupyterLab Workspace

A reproducible report depends on a stable workspace. An isolated conda environment gives the analysis the same package versions each time you return.

Anaconda Navigator gives you a visual way to create that environment. JupyterLab gives you the notebook where pandas runs the data checks.

In this step, get ready to:
  • Create the penguins-lab environment with Python 3.11.
  • Install pandas 3.0.5 plus JupyterLab 4.6.2.
  • Verify pandas inside penguin_field_report.ipynb.
Create a project environment

A dedicated environment keeps this project's dependencies together. It also protects Anaconda's base environment from package changes.

  • Press the Windows key to open Windows search.
  • Type Anaconda Navigator into Windows search.
  • Press Enter to open Anaconda Navigator.
  • Select Environments in the left navigation pane.
  • Select Create at the bottom of the environments list.
  • Enter penguins-lab as the environment name.
  • Select Python 3.11 as the interpreter version.
  • Select Create to build the environment.

You will see penguins-lab in the environments list once creation finishes. That environment is now the isolated home for your penguin analysis.

Why use a separate environment?

Each environment can hold its own Python interpreter and package versions. This keeps one project's upgrades from changing another project's behavior.

Anaconda recommends keeping project packages outside the base environment. Your project stays easier to reproduce when its dependencies live in penguins-lab.

Install the pinned packages

A version pin records the package release used by the project. Matching the pins gives your notebook the APIs expected by every later cell.

  • Select penguins-lab in Environments.
  • Use Search Packages to find pandas.
  • Check which pandas version is installed.
  • Use Search Packages to find JupyterLab.
  • Check which JupyterLab version is installed.
  • Choose the tab that matches the package state you see.

✔️ The required versions are installed

You can see pandas 3.0.5 plus JupyterLab 4.6.2 installed in penguins-lab. Your package versions already match the project.

ⓧ I see different versions

Changing package versions can take a few minutes while Anaconda resolves dependencies. A quiet progress screen during that wait is normal.

  • Use Search Packages to find the package with the different version.
  • Select the package in the results.
  • Select Mark for specific version installation.
  • Choose 3.0.5 for pandas.
  • Choose 4.6.2 for JupyterLab.
  • Select Apply to install the selected version changes.

The package list updates when installation finishes. The changed package now shows the version required by the project.

ⓧ A package is not installed

Installing both packages can take a few minutes while Anaconda resolves their dependencies. The progress screen may sit quietly during part of that process.

  • Use Search Packages to find pandas.
  • Select pandas in the results.
  • Select Mark for specific version installation.
  • Choose 3.0.5.

Pandas is now marked for installation at the project version.

  • Use Search Packages to find JupyterLab.
  • Select JupyterLab in the results.
  • Select Mark for specific version installation.
  • Choose 4.6.2.
  • Select Apply to install both packages.

The package list updates when installation finishes. Both packages are now available inside penguins-lab.

  • Use Search Packages to confirm pandas 3.0.5 is installed.
  • Use Search Packages to confirm JupyterLab 4.6.2 is installed.

Your environment now has every pinned dependency. The notebook can use the same Python and pandas behavior throughout the project.

Package versions still missing?

Confirm that penguins-lab remains selected in Environments. Searching from another environment shows that environment's packages.

Select Apply after marking each version. The package change does not begin until you apply it.

Ask for help with the package state you see.

Launch JupyterLab and verify the notebook

A Jupyter notebook keeps code beside its visible results. Your first two cells give the report a clear purpose before proving that pandas can create a table.

  • Return to Home in Anaconda Navigator.
  • Select penguins-lab from the environment dropdown.
  • Select Launch on the JupyterLab tile.

JupyterLab opens in your default browser with penguins-lab active. The file browser and Launcher give you the controls for creating the report.

  • Click the + button in the file browser.
  • Select the Python notebook option in the Launcher.
  • Right-click the new notebook file in the file browser.
  • Select Rename.
  • Enter penguin_field_report.ipynb as the notebook name.
  • Press Enter to apply the new name.

The notebook tab now shows penguin_field_report.ipynb. This file becomes the reproducible record of your analysis.

  • Change the first notebook cell to Markdown with the cell-type selector.
  • Add the report introduction by pasting this content into the Markdown cell:
# Palmer Penguins Data Explorer

This notebook checks data quality, cleans measurement fields, and compares penguin species with pandas.

How does this frame the report?

  • The heading gives the notebook a clear report title.
  • The purpose statement records the checks that make the analysis reproducible.
  • Click Run in the notebook toolbar to render the introduction.

You will see a large Palmer Penguins Data Explorer heading above the purpose statement. The notebook now reads like the beginning of an analysis report.

  • Insert a new code cell below the rendered introduction.
  • Add the setup check by pasting this code into the new cell:
import pandas as pd

pd.DataFrame({"status": ["ready"]})

What does this code do?

  • The import gives pandas the standard pd alias used throughout the report.
  • A DataFrame stores data as labelled rows and columns.
  • The status column provides a visible test that table creation works.

Before you run the setup check, predict the shape of the output pandas will display.

  • Run the setup-check cell by clicking Run in the notebook toolbar.

You will see a one-row table with ready under status. That successful table confirms pandas is running inside your project notebook.

Don't see the ready table?

Confirm that the setup check is inside a code cell. A Markdown cell displays the code as text instead of running it.

Return to Anaconda Navigator if the notebook reports a missing package. Confirm that JupyterLab was launched from penguins-lab.

Ask for help diagnosing the notebook output.

✔️ Awesome, I've got everything!

Your notebook contains the report introduction and the working setup check. Keep the successful table visible for the final checkpoint.

ⓧ I'd like to double check the full code

# Palmer Penguins Data Explorer

This notebook checks data quality, cleans measurement fields, and compares penguin species with pandas.

import pandas as pd

pd.DataFrame({"status": ["ready"]})

Your isolated notebook workspace is ready for real data. Next up, you will load the Palmer Penguins dataset and inspect its first records.

Load the Penguins and Inspect the Data

Your setup check proved that the open Jupyter notebook can run pandas inside penguins-lab. Now you can move from a one-row readiness check to real penguin records.

The public CSV becomes the penguins DataFrame. Seeing its dimensions plus first records proves that the source loaded correctly.

In this step, get ready to:
  • Add a Markdown section heading for the loading stage.
  • Load the public Palmer Penguins CSV into penguins.
  • Display the dataset dimensions plus its first five records.
Add the loading section

A Markdown heading separates this stage from the setup check above it. This keeps the notebook readable as the analysis grows.

  • Select the cell that displays the ready table.
  • Click the + button in the notebook toolbar.

You'll see a blank cell directly below the setup-check output.

  • Set the blank cell's type to Markdown using the cell-type menu.
  • Enter the report section heading by adding this Markdown:
## Load and inspect the public dataset

What Does This Markdown Do?

The ## prefix renders a level-two heading. The heading gives the loading work a clear home inside your report.

  • Render the Markdown cell by clicking Run.
  • Save penguin_field_report.ipynb using the save control in the notebook toolbar.
  • Confirm the rendered heading reads Load and inspect the public dataset.

Heading Still Looks Like Code?

  • Confirm that the cell type is set to Markdown.
  • Click Run again to render the heading.

If the heading still does not render, help me check why my Markdown cell is staying in edit mode.

Load and inspect the penguins

The loading cell uses pd.read_csv to create penguins from DATA_URL. The final three lines make the result visible inside the notebook.

  • Select the rendered loading heading.
  • Click the + button in the notebook toolbar.

You'll see a blank cell directly below the loading heading.

  • Set the blank cell's type to code using the cell-type menu.
  • Populate the blank code cell by entering the code below:
DATA_URL = "https://raw.githubusercontent.com/allisonhorst/palmerpenguins/main/inst/extdata/penguins.csv"
penguins = pd.read_csv(DATA_URL, na_values=["NA"])

print(f"Rows: {penguins.shape[0]}")
print(f"Columns: {penguins.shape[1]}")
penguins.head()

What Does This Code Do?

  • DATA_URL keeps the public dataset address in one named value.
  • pd.read_csv loads that CSV into penguins.
  • na_values=["NA"] treats matching entries as missing values for the later data-quality audit.
  • penguins.shape supplies the row count plus the column count.
  • penguins.head() displays the first five records by default.
  • Save penguin_field_report.ipynb using the save control in the notebook toolbar.

Before you run the cell, what dimensions do you expect this documented dataset to report?

  • Run the loading cell by clicking Run.

You'll see Rows: 344 followed by Columns: 8. A table underneath displays the first five penguin records.

Your first real result is live. The notebook now loads all 344 penguins from the public source.

Don't See 344 Rows?

  • Confirm that your internet connection is active before rerunning the cell.
  • Copy DATA_URL exactly as shown in the code block.
  • Rerun the setup-check cell if the notebook reports that the pandas alias is undefined.

If the loading cell still fails, help me troubleshoot why my Palmer Penguins CSV is not loading in pandas.

✔️ Awesome, I've got everything!

Your notebook is saved with the loading heading plus the working dataset cell.

ⓧ I'd like to double check the full code

# Palmer Penguins Data Explorer

This notebook checks data quality, cleans measurement fields, and compares penguin species with pandas.

import pandas as pd

pd.DataFrame({"status": ["ready"]})

## Load and inspect the public dataset

DATA_URL = "https://raw.githubusercontent.com/allisonhorst/palmerpenguins/main/inst/extdata/penguins.csv"
penguins = pd.read_csv(DATA_URL, na_values=["NA"])

print(f"Rows: {penguins.shape[0]}")
print(f"Columns: {penguins.shape[1]}")
penguins.head()

Your data is loaded in the notebook. Next, you'll find the incomplete records that could distort a species comparison.

Audit Missing Measurements

Your JupyterLab notebook now holds 344 penguin records in a pandas DataFrame. The next question is whether every record contains the measurements needed for a trustworthy species comparison.

Does every row contribute a body-mass value to a naive species summary? You will compare the counts before choosing a cleaning rule.

In this step, get ready to:
  • Audit missing values in each dataset column.
  • Compare total rows with available body-mass measurements for each species.
  • Record what the difference between the two counts reveals.
Audit the dataset columns

A column-level audit reveals where missing values occur before they disappear inside a calculation. A Markdown heading keeps this data-quality check easy to find when someone reruns your notebook.

  • Select the empty cell below the loading output.
  • Set the cell type to Markdown.
  • Add the audit heading by entering:
## Audit missing values

Why add this heading?

This heading separates the data-quality audit from the loading result. A clear notebook structure makes the analysis easier to rerun or share.

  • Click Run in the notebook toolbar.

You will see the heading Audit missing values rendered above a new empty cell.

Heading still looks like code?

Confirm that the heading cell uses the Markdown cell type. Run the cell again after changing its type.

Ask for help if the heading still does not render: Help me render a Markdown heading in my JupyterLab notebook.

The audit will convert each column into a missing-value count. Any count above zero identifies a field with incomplete records.

  • Select the empty cell beneath the audit heading.
  • Add the missing-value audit by entering:
missing_by_column = penguins.isna().sum()
missing_by_column

What does this code do?

  • The isna() check marks each missing entry in penguins.
  • The sum() calculation counts those marked entries for each column.
  • The missing_by_column variable keeps the audit available for later inspection.
  • Click Run in the notebook toolbar.

You will see one missing-value count for each column. At least one count is above zero.

Audit cell does not run?

Confirm that the earlier loading cell has already created penguins. Check that missing_by_column uses underscores in both lines.

Ask for help with the audit cell: Help me troubleshoot my pandas missing-value audit.

Compare rows with measured body mass

The column audit confirms that incomplete data exists. A grouped summary tests whether those gaps change the number of body-mass values available for each species.

This cell uses named aggregation to place two counts beside an average. The comparison makes the measurement gap visible.

  • Select the next empty cell beneath missing_by_column.
  • Add the raw species summary by entering:
raw_species_summary = (
    penguins.groupby("species", as_index=False)
    .agg(
        rows=("species", "size"),
        measured_body_mass=("body_mass_g", "count"),
        average_body_mass_g=("body_mass_g", "mean"),
    )
    .round(1)
)
raw_species_summary

What does this code do?

  • The groupby() operation creates one group for each value in species.
  • The agg() operation creates the three named result columns.
  • The round(1) operation keeps the average readable to one decimal place.

Before you run this cell, do you expect the two count columns to match for every species?

  • Click Run in the notebook toolbar.

You will see one row per species. For affected species, measured_body_mass is lower than rows.

That shortfall is intentional. The raw summary has exposed measurements that would otherwise disappear silently from the average.

Why do the counts differ?

The size aggregation counts every row in each species group. The count aggregation excludes missing values from body_mass_g.

Unequal values prove that some penguins are present in the dataset without a usable body-mass measurement. That evidence guides the cleaning rule in the next stage.

Summary cell does not display?

Check that each opening parenthesis has a matching closing parenthesis. Confirm that the final line contains only raw_species_summary.

Ask for help with the grouped summary: Help me troubleshoot my raw species summary.

Record the measurement gap

A reproducible analysis records why a cleaning decision is necessary. Your note will connect the unequal counts to the next stage of the report.

  • Select the empty cell below the raw species summary.
  • Set the cell type to Markdown.
  • Add the observation by entering:
The row count includes every penguin, while the body-mass count excludes missing values. The mismatch means the raw table is not ready for a measurement comparison.

Why record this observation?

The note explains what the two counts measure. It also documents why the raw table cannot support the intended measurement comparison yet.

  • Click Run in the notebook toolbar.

You will see the observation rendered beneath raw_species_summary.

Observation remains editable?

Confirm that the observation cell uses the Markdown cell type. Click Run again to render it.

Ask for help if the note still does not render: Help me render my audit observation in JupyterLab.

✔️ Awesome, I've got everything!

Your audit cells and measurement-gap observation are in place.

ⓧ I'd like to double check the full code

# Palmer Penguins Data Explorer

This notebook checks data quality, cleans measurement fields, and compares penguin species with pandas.

import pandas as pd

pd.DataFrame({"status": ["ready"]})

## Load and inspect the public dataset

DATA_URL = "https://raw.githubusercontent.com/allisonhorst/palmerpenguins/main/inst/extdata/penguins.csv"
penguins = pd.read_csv(DATA_URL, na_values=["NA"])

print(f"Rows: {penguins.shape[0]}")
print(f"Columns: {penguins.shape[1]}")
penguins.head()

## Audit missing values

missing_by_column = penguins.isna().sum()
missing_by_column

raw_species_summary = (
    penguins.groupby("species", as_index=False)
    .agg(
        rows=("species", "size"),
        measured_body_mass=("body_mass_g", "count"),
        average_body_mass_g=("body_mass_g", "mean"),
    )
    .round(1)
)
raw_species_summary

The row count includes every penguin, while the body-mass count excludes missing values. The mismatch means the raw table is not ready for a measurement comparison.

Before the final check, which output do you think will reveal the data-quality gap most clearly?

  • Run the missing_by_column cell again by clicking Run.
  • Run the raw_species_summary cell again by clicking Run.

You will see nonzero missing-value counts. You will also see lower body-mass counts than row counts for affected species.

You caught the hidden measurement gap before cleaning the data. Your notebook now contains the evidence needed to make a defensible cleaning choice.

Your evidence is ready. Next up, you will clean only the fields required for the measurement report.

Clean Measurements and Build the Species Report

Your JupyterLab notebook now shows that the raw pandas summary contains fewer body-mass measurements than rows. That mismatch proves the raw table needs a deliberate cleaning rule before it can support the report.

Removing every row with any blank field could discard valid measurements because an unrelated field such as sex is empty. Targeted cleaning creates an analysis DataFrame from the four measurements required by your report.

In this step, get ready to:
  • Create a measurement-ready DataFrame with targeted missing-value removal.
  • Prove that all four analysis measurements are complete.
  • Build a three-row species report ranked by average body mass.
Clean the measurement fields

The dropna method accepts a subset of columns that control which rows are removed. This keeps records with complete measurements even when another field is empty.

  • Add a Markdown cell below the observation from the previous step.
  • Set the section heading by entering:
## Clean only the fields needed for measurement analysis

Why add this heading?

  • The heading separates the data-quality problem from the cleaning decision that resolves it.
  • A clear section boundary makes the notebook easier to rerun or share.
  • Click Run in the notebook toolbar to render the heading.

You will see the cleaning heading displayed below the missing-data observation.

  • Add a code cell directly below the rendered heading with this code:
MEASUREMENT_COLUMNS = [
    "bill_length_mm",
    "bill_depth_mm",
    "flipper_length_mm",
    "body_mass_g",
]

analysis_penguins = penguins.dropna(subset=MEASUREMENT_COLUMNS)
analysis_penguins[MEASUREMENT_COLUMNS].isna().sum()

What does this cleaning cell do?

  • The MEASUREMENT_COLUMNS list defines the four fields required for measurement analysis.
  • The dropna call removes rows with a missing value in any field from that list.
  • The final expression reruns isna().sum() against the cleaned measurement columns.
  • Predict what missing-value totals the four measurement columns will return.
  • Run the cell by clicking Run in the notebook toolbar.

You will see each of the four measurement columns return 0 missing values. The new analysis_penguins DataFrame is ready for a fair measurement comparison.

  • Save penguin_field_report.ipynb.

Still seeing missing measurements?

  • Confirm that the dataset-loading cell has run during this JupyterLab session.
  • Check that every value in MEASUREMENT_COLUMNS matches the column names shown in the cleaning cell.
  • Rerun the cleaning cell after correcting any spelling differences.

Help me debug why my cleaned measurement columns still contain missing values.

Build the ranked species report

Named aggregation turns each species group into one readable row with a count plus four averages. Sorting by average body mass places the heaviest species first.

  • Add a code cell below the cleaning audit with this code:
species_summary = (
    analysis_penguins.groupby("species", as_index=False)
    .agg(
        penguins=("species", "size"),
        average_body_mass_g=("body_mass_g", "mean"),
        average_flipper_length_mm=("flipper_length_mm", "mean"),
        average_bill_length_mm=("bill_length_mm", "mean"),
        average_bill_depth_mm=("bill_depth_mm", "mean"),
    )
    .sort_values("average_body_mass_g", ascending=False)
    .round(1)
)
species_summary

What does this report cell do?

  • The groupby call creates one group for each species.
  • The named aggregations calculate the group size plus average body mass, flipper length, bill length, and bill depth.
  • The sort_values call ranks the groups from highest to lowest average body mass.
  • The round(1) call keeps each average readable with one decimal place.
  • Predict which species will appear at the top of the ranked report.
  • Run the cell by clicking Run in the notebook toolbar.

You will see a three-row species_summary table with Gentoo first by average body mass. Each average is rounded to one decimal place.

That closes the data-quality loop. Your species comparison now uses complete measurements from the exact fields the report needs.

  • Save penguin_field_report.ipynb.

Report missing rows or the wrong ranking?

  • Confirm that the report starts from analysis_penguins.
  • Check that average_body_mass_g is the column passed to sort_values.
  • Confirm that ascending=False is present in the sorting line.

Help me debug why my species summary is incomplete or sorted incorrectly.

Your notebook now contains the targeted cleaning workflow plus the ranked species report.

  • Select the second tab if you want to compare your notebook with the complete version.

✔️ Awesome, I've got everything!

Your cleaning audit and ranked species report are both in place.

ⓧ I'd like to double check the full code

# Palmer Penguins Data Explorer

This notebook checks data quality, cleans measurement fields, and compares penguin species with pandas.

import pandas as pd

pd.DataFrame({"status": ["ready"]})

## Load and inspect the public dataset

DATA_URL = "https://raw.githubusercontent.com/allisonhorst/palmerpenguins/main/inst/extdata/penguins.csv"
penguins = pd.read_csv(DATA_URL, na_values=["NA"])

print(f"Rows: {penguins.shape[0]}")
print(f"Columns: {penguins.shape[1]}")
penguins.head()

## Audit missing values

missing_by_column = penguins.isna().sum()
missing_by_column

raw_species_summary = (
    penguins.groupby("species", as_index=False)
    .agg(
        rows=("species", "size"),
        measured_body_mass=("body_mass_g", "count"),
        average_body_mass_g=("body_mass_g", "mean"),
    )
    .round(1)
)
raw_species_summary

The row count includes every penguin, while the body-mass count excludes missing values. The mismatch means the raw table is not ready for a measurement comparison.

## Clean only the fields needed for measurement analysis

MEASUREMENT_COLUMNS = [
    "bill_length_mm",
    "bill_depth_mm",
    "flipper_length_mm",
    "body_mass_g",
]

analysis_penguins = penguins.dropna(subset=MEASUREMENT_COLUMNS)
analysis_penguins[MEASUREMENT_COLUMNS].isna().sum()

species_summary = (
    analysis_penguins.groupby("species", as_index=False)
    .agg(
        penguins=("species", "size"),
        average_body_mass_g=("body_mass_g", "mean"),
        average_flipper_length_mm=("flipper_length_mm", "mean"),
        average_bill_length_mm=("bill_length_mm", "mean"),
        average_bill_depth_mm=("bill_depth_mm", "mean"),
    )
    .sort_values("average_body_mass_g", ascending=False)
    .round(1)
)
species_summary

Your cleaned measurement table now supports a ranked species comparison. Next, you will export the cleaned records plus the report as shareable CSV files.

Export Shareable Results

Your cleaning workflow now produces a complete measurement table plus a ranked species report inside your Jupyter notebook. Those results are still tied to the live JupyterLab session.

A reusable analysis needs outputs that open outside the notebook. In this step, pandas writes both DataFrames to CSV files that can be inspected or shared.

In this step, get ready to:
  • Add an export section to penguin_field_report.ipynb.
  • Export both analysis tables as local CSV files.
  • Inspect each file to confirm its contents.
Add the export section

A Markdown heading gives the output stage a clear place in the report. It keeps the notebook readable when someone runs it from top to bottom.

  • Insert a Markdown cell directly below the species_summary output using the notebook toolbar.
  • Enter this report heading in the new cell:
## Export the cleaned data and species report

Why Add This Heading?

This heading makes the notebook's output stage easy to find. Anyone rerunning the analysis can quickly see where the reusable files are created.

  • Run the Markdown cell by clicking Run.

You’ll see Export the cleaned data and species report rendered as a section heading below the species table.

  • Save penguin_field_report.ipynb.

Heading Still Looks Like Code?

  • Confirm that the cell type is Markdown.
  • Run the cell again after changing its type.

Help me render the export heading as Markdown in JupyterLab.

Prepare the final notebook cell

A CSV export captures the current state of a DataFrame in a separate file. The index=False option keeps the generated row index out of each export.

  • Insert a code cell directly below the rendered export heading using the notebook toolbar.
  • Write the export logic in the new cell by entering this code:
analysis_penguins.to_csv("penguins_cleaned.csv", index=False)
species_summary.to_csv("species_summary.csv", index=False)

print("Created penguins_cleaned.csv and species_summary.csv")

What Does This Code Do?

  • analysis_penguins.to_csv("penguins_cleaned.csv", index=False) writes the cleaned row-level table to penguins_cleaned.csv.
  • species_summary.to_csv("species_summary.csv", index=False) writes the ranked species table to species_summary.csv.
  • index=False leaves the notebook’s generated row index out of both files.
  • print("Created penguins_cleaned.csv and species_summary.csv") confirms that both exports have finished.

Before you run the export cell, which two file names do you expect to appear in the file browser?

  • Run the export cell by clicking Run.

You’ll see Created penguins_cleaned.csv and species_summary.csv below the cell.

  • Check the file browser beside your notebook.

You’ll see penguins_cleaned.csv in the file browser. You’ll also see species_summary.csv beside it.

  • Save penguin_field_report.ipynb.

Don’t See Both CSV Files?

  • Select the export cell.
  • Click Run.
  • Refresh the file browser if both file names still do not appear.

Help me find the CSV exports in JupyterLab.

✔️ Awesome, I’ve got everything!

Your notebook now contains the export section plus the cell that creates both CSV files.

ⓧ I’d like to double check the full code

# Palmer Penguins Data Explorer

This notebook checks data quality, cleans measurement fields, and compares penguin species with pandas.

import pandas as pd

pd.DataFrame({"status": ["ready"]})

## Load and inspect the public dataset

DATA_URL = "https://raw.githubusercontent.com/allisonhorst/palmerpenguins/main/inst/extdata/penguins.csv"
penguins = pd.read_csv(DATA_URL, na_values=["NA"])

print(f"Rows: {penguins.shape[0]}")
print(f"Columns: {penguins.shape[1]}")
penguins.head()

## Audit missing values

missing_by_column = penguins.isna().sum()
missing_by_column

raw_species_summary = (
    penguins.groupby("species", as_index=False)
    .agg(
        rows=("species", "size"),
        measured_body_mass=("body_mass_g", "count"),
        average_body_mass_g=("body_mass_g", "mean"),
    )
    .round(1)
)
raw_species_summary

The row count includes every penguin, while the body-mass count excludes missing values. The mismatch means the raw table is not ready for a measurement comparison.

## Clean only the fields needed for measurement analysis

MEASUREMENT_COLUMNS = [
    "bill_length_mm",
    "bill_depth_mm",
    "flipper_length_mm",
    "body_mass_g",
]

analysis_penguins = penguins.dropna(subset=MEASUREMENT_COLUMNS)
analysis_penguins[MEASUREMENT_COLUMNS].isna().sum()

species_summary = (
    analysis_penguins.groupby("species", as_index=False)
    .agg(
        penguins=("species", "size"),
        average_body_mass_g=("body_mass_g", "mean"),
        average_flipper_length_mm=("flipper_length_mm", "mean"),
        average_bill_length_mm=("bill_length_mm", "mean"),
        average_bill_depth_mm=("bill_depth_mm", "mean"),
    )
    .sort_values("average_body_mass_g", ascending=False)
    .round(1)
)
species_summary

## Export the cleaned data and species report

analysis_penguins.to_csv("penguins_cleaned.csv", index=False)
species_summary.to_csv("species_summary.csv", index=False)

print("Created penguins_cleaned.csv and species_summary.csv")

How to Read This Reference

This reference mirrors every notebook cell in order. The final section contains the Markdown heading followed by the export cell.

Inspect the exported files

Opening each export confirms that the files contain the intended level of detail. This check also proves that the results work outside the live notebook output.

Before you open the files, how do you expect the detailed records to differ from the three-row summary?

  • Open penguins_cleaned.csv from JupyterLab’s file browser.

You’ll see the cleaned penguin records with the original dataset columns. Every row has values for bill_length_mm, bill_depth_mm, flipper_length_mm, and body_mass_g.

  • Open species_summary.csv from JupyterLab’s file browser.

You’ll see one row per species with the penguin count plus rounded average measurements. Gentoo appears first because the source summary is sorted by average_body_mass_g in descending order.

Well done. Your cleaned penguin records plus ranked species findings now exist outside the live notebook.

Secret mission

Compare Species Across Islands

This mission adds island as a second grouping dimension. You will rank each species's observed island groups by average body mass. The finished report is exported to species_island_summary.csv.

Clean Up Your Resources

Clean Up Your Resources

Choose whether to keep your local resources available, pause your work for later, or delete the project. Every resource in this project is local to your Windows computer, so leaving it in place adds no usage charges.

Resources you used:

  • The penguins-lab Python 3.11 environment in Anaconda Navigator, with pandas 3.0.5 plus JupyterLab 4.6.2.
  • The penguin_field_report.ipynb notebook.
  • The penguins_cleaned.csv, species_summary.csv, plus species_island_summary.csv exports.

Keep everything running

No action is needed. Choose this if you plan to rerun the report or extend the analysis.

  • Your penguins-lab environment stays available in Anaconda Navigator.
  • Your penguin_field_report.ipynb notebook remains ready to run in JupyterLab.
  • Your three CSV exports remain beside the notebook for inspection or sharing.

Pause - I'll come back to this later

Shut down the live notebook session to free local memory. Your environment and files remain available for later.

  • Save the open penguin_field_report.ipynb notebook by selecting the save icon in the notebook toolbar.
  • Close the browser tab running JupyterLab.
  • Close Anaconda Navigator.
  • Launch JupyterLab from penguins-lab when you are ready to continue.

Delete - I don't want to use this again

Removing the whole project can feel drastic. These steps target the penguins-lab environment plus the four named project files.

Keep a Copy First

Any file you may want later needs a copy outside the project folder before you delete it.

Prepare the local files for deletion:

  • Save the open penguin_field_report.ipynb notebook by selecting the save icon in the notebook toolbar.
  • Close the browser tab running JupyterLab.
  • Press the Windows key to open search.
  • Type File Explorer and press Enter to open it.
  • Navigate to the folder that contains penguin_field_report.ipynb.

Delete the local project files:

  • Delete penguin_field_report.ipynb from that folder.
  • Delete penguins_cleaned.csv from that folder.
  • Delete species_summary.csv from that folder.
  • Delete species_island_summary.csv from that folder.
  • Confirm the four project files no longer appear in the folder.

Remove the project environment:

  • Switch back to Anaconda Navigator.
  • Select Environments.
  • Select the penguins-lab environment.
  • Select Remove to delete the selected environment.
  • Confirm the environment removal in the dialog.
  • Confirm that penguins-lab no longer appears under Environments.

Nice Work!

Nice Work!

You made it! Your reproducible Palmer Penguins field report now turns raw observations into defensible species and island comparisons.

You've learned how to:

  • Build a reproducible Jupyter notebook that loads a public dataset into a pandas DataFrame with 344 rows and 8 columns.
  • Expose incomplete measurements with a missing-data audit. Apply targeted cleaning to the four columns required for measurement analysis.
  • Create a ranked species report with rounded measurement averages. Export the cleaned records and report as shareable CSV files.
  • Secret Mission: Compare every observed species and island combination in island_summary. Export the result as species_island_summary.csv.

Ready to quiz yourself?