Skip to content

Create your first Python project with pixi

This tutorial builds a weather data analysis tool that reads CSV data, computes statistics with pandas, and produces a chart with matplotlib. Every dependency comes from conda-forge and is managed by pixi.

Prerequisites

Install pixi following the official installation instructions. No separate Python install is required.

Create the project

pixi init weather_analysis
cd weather_analysis

This creates a directory with a pixi.toml file that stores project metadata, dependencies, and task definitions:

pixi.toml
[workspace]
name = "weather_analysis"
channels = ["conda-forge"]
platforms = ["osx-arm64"]

The channels field tells pixi where to fetch packages. conda-forge is a community-maintained, openly licensed package repository with thousands of scientific Python packages.

The platforms value reflects your current machine. Pixi sets this automatically.

Note

You may also see a pixi.lock file. This lockfile pins the exact version of every package pixi installs. Commit it to version control so collaborators reproduce the same environment.

Add dependencies

pixi add python pandas matplotlib

This does three things:

  1. Resolves compatible versions of Python, pandas, matplotlib, and all their transitive dependencies from conda-forge.
  2. Installs everything into a .pixi/ directory inside the project (a project-local environment, not a global one).
  3. Updates pixi.lock with the exact versions.

The pixi.toml now includes:

pixi.toml
[dependencies]
python = ">=3.13.3,<4"
pandas = ">=2.2.3,<3"
matplotlib = ">=3.10.1,<4"

The version bounds reflect whichever releases are current when you run the command.

Tip

The .pixi/ directory contains the full environment (Python interpreter, all packages). It can be large. Add .pixi/ to .gitignore and let collaborators recreate it with pixi install.

Create sample data

Create a data directory and add a CSV file:

mkdir data

Create data/weather.csv with this content:

data/weather.csv
date,city,temp_high,temp_low,precipitation_mm,humidity_pct
2025-01-01,Portland,8,2,12.5,82
2025-01-02,Portland,7,1,0.0,65
2025-01-03,Portland,9,3,8.3,78
2025-01-04,Portland,6,-1,15.2,88
2025-01-05,Portland,10,4,0.0,60
2025-01-01,Phoenix,18,5,0.0,25
2025-01-02,Phoenix,20,7,0.0,22
2025-01-03,Phoenix,22,8,0.0,20
2025-01-04,Phoenix,19,6,2.1,35
2025-01-05,Phoenix,21,7,0.0,23

Write the analysis script

Create analyze.py:

analyze.py
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
from pathlib import Path

matplotlib.use("Agg")


def load_weather_data(path):
    df = pd.read_csv(path, parse_dates=["date"])
    df["temp_range"] = df["temp_high"] - df["temp_low"]
    return df


def summarize_by_city(df):
    return df.groupby("city").agg(
        avg_high=("temp_high", "mean"),
        avg_low=("temp_low", "mean"),
        total_precip=("precipitation_mm", "sum"),
        avg_humidity=("humidity_pct", "mean"),
    ).round(1)


def plot_temperature_comparison(df, output_path):
    fig, ax = plt.subplots(figsize=(8, 4))
    for city, group in df.groupby("city"):
        ax.plot(group["date"], group["temp_high"], marker="o", label=f"{city} high")
        ax.plot(group["date"], group["temp_low"], marker="s", label=f"{city} low",
                linestyle="--", alpha=0.6)
    ax.set_ylabel("Temperature (°C)")
    ax.set_title("Daily Temperatures by City")
    ax.legend()
    fig.tight_layout()
    fig.savefig(output_path, dpi=150)
    print(f"Chart saved to {output_path}")
    plt.close(fig)


def main():
    data_dir = Path(__file__).parent / "data"
    df = load_weather_data(data_dir / "weather.csv")

    summary = summarize_by_city(df)
    print("Weather Summary by City:")
    print(summary)
    print()

    plot_temperature_comparison(df, data_dir / "temperatures.png")


if __name__ == "__main__":
    main()

Run the script

pixi run python analyze.py

Expected output:

Weather Summary by City:
         avg_high  avg_low  total_precip  avg_humidity
city
Phoenix      20.0      6.6           2.1          25.0
Portland      8.0      1.8          36.0          74.6

Chart saved to data/temperatures.png

pixi run activates the project environment and executes the command. You never need to activate or deactivate environments manually.

Add a named task

Instead of typing pixi run python analyze.py every time, define a task in pixi.toml:

pixi.toml
[tasks]
analyze = "python analyze.py"

Now run the analysis with:

pixi run analyze

Tasks are useful for longer commands, pipelines, and giving collaborators discoverable entry points into the project.

Add a dev dependency and write a test

Add pytest as a dependency:

pixi add pytest

Create test_analyze.py:

test_analyze.py
import pandas as pd
from analyze import load_weather_data, summarize_by_city
from pathlib import Path


def test_load_weather_data():
    path = Path(__file__).parent / "data" / "weather.csv"
    df = load_weather_data(path)
    assert "temp_range" in df.columns
    assert len(df) == 10


def test_summarize_by_city():
    df = pd.DataFrame({
        "city": ["A", "A", "B"],
        "temp_high": [20, 22, 10],
        "temp_low": [10, 12, 5],
        "precipitation_mm": [0.0, 5.0, 10.0],
        "humidity_pct": [50, 60, 70],
    })
    summary = summarize_by_city(df)
    assert summary.loc["A", "avg_high"] == 21.0
    assert summary.loc["B", "total_precip"] == 10.0

Add a test task and run it:

pixi.toml
[tasks]
analyze = "python analyze.py"
test = "pytest"
pixi run test

Final project structure

    • pixi.toml
    • pixi.lock
    • analyze.py
    • test_analyze.py
    • .gitignore
      • weather.csv

Commit pixi.toml, pixi.lock, your source files, and data. The .pixi/ directory is local and should stay in .gitignore.

Next steps

Last updated on

Please submit corrections and feedback...