teachyou.ai academy
← All posts
Claude Code

Claude Code for Data Analysis Tasks

Ira Menon · Jun 24, 2026 · 15 min read

Why Data Analysts Are Starting To Use A Coding Agent

Most data analysts learn pandas the hard way: by memorizing method names, forgetting them six weeks later, and rebuilding the same groupby-merge-pivot pattern from a Stack Overflow tab they have open in every browser session. That workflow is not wrong, but it is slow, and it puts the burden of remembering syntax on you instead of on the tool.

Claude Code changes the shape of that work. It is a terminal-based agent that can read your CSV files, write and run pandas scripts, inspect the output, catch its own mistakes, and iterate until the numbers check out — all inside your actual project folder, against your actual data. You are not copy-pasting snippets into a notebook and hoping they apply to your schema. You are describing what you want to know, and watching an agent write the code, execute it, read the error if there is one, and fix it.

This matters more for data analysis than for almost any other coding task, because data work is inherently iterative. You rarely know the right transformation on the first try. You look at a dataframe, notice a column is stored as a string when it should be a datetime, adjust, look again, notice three duplicate rows, adjust again. Claude Code is built for exactly this loop — it runs code, reads the real output (not just what it assumes the output will be), and adjusts its next step based on what actually happened. That is a fundamentally different mode of working than asking a chatbot to "write me a pandas script" and manually pasting the result into your own terminal.

This article walks through how to actually use Claude Code for data analysis: setting up a project, doing exploratory data analysis, cleaning messy real-world data, building repeatable pipelines, and generating reports — with concrete pandas code throughout.

Setting Up A Data Analysis Project With Claude Code

Claude Code works from your terminal, inside a real project directory. For data work, that means starting with a folder that has your raw files, and letting Claude Code inspect them before you ask it to do anything.

A sensible starting structure looks like this:

sales-analysis/
  data/
    raw/
      orders_2024.csv
      orders_2025.csv
      customers.csv
  notebooks/
  scripts/
  outputs/

When you open Claude Code in this folder and ask something like "look at the CSVs in data/raw and tell me what you see," it will actually read the files — checking column names, dtypes, row counts, null percentages — rather than guessing based on the filenames. This is the first meaningful difference from a plain chat-based assistant: it is grounded in your real data from the first message.

A good first prompt is deliberately open-ended:

Read the first 20 rows of each CSV in data/raw, print the dtypes,
and tell me about any columns that look like they need cleaning
before I can join these tables.

Claude Code will typically write a short inspection script like this and run it:

import pandas as pd

orders_24 = pd.read_csv("data/raw/orders_2024.csv")
orders_25 = pd.read_csv("data/raw/orders_2025.csv")
customers = pd.read_csv("data/raw/customers.csv")

for name, df in [("orders_2024", orders_24), ("orders_2025", orders_25), ("customers", customers)]:
    print(f"--- {name} ---")
    print(df.shape)
    print(df.dtypes)
    print(df.isna().mean().sort_values(ascending=False).head(10))
    print()

The value here isn't the script itself — any analyst could write this in two minutes. The value is that Claude Code runs it immediately, reads the actual printed output, and uses that to decide what to check next, without you having to relay results back and forth manually.

Exploratory Data Analysis That Actually Reads Your Data

Exploratory data analysis (EDA) is where a coding agent earns its keep, because EDA is inherently a back-and-forth conversation with the data: look, notice something odd, dig in, look again.

Suppose you have an orders dataset and you ask Claude Code:

What does the distribution of order_value look like? Are there
outliers I should know about before I compute averages?

A reasonable response involves both statistics and a visual, and Claude Code can generate both in one pass:

import pandas as pd

df = pd.read_csv("data/raw/orders_2024.csv")

print(df["order_value"].describe())

q1 = df["order_value"].quantile(0.25)
q3 = df["order_value"].quantile(0.75)
iqr = q3 - q1
lower = q1 - 1.5 * iqr
upper = q3 + 1.5 * iqr

outliers = df[(df["order_value"] < lower) | (df["order_value"] > upper)]
print(f"Outlier count: {len(outliers)} out of {len(df)} rows")
print(outliers[["order_id", "order_value", "customer_id"]].head(15))

If the outlier count comes back unexpectedly large, Claude Code doesn't just report the number — it can immediately follow up by checking whether those outliers cluster around a specific customer, date range, or product category, because that's the obvious next question a human analyst would ask. This is the iterative advantage in practice: the agent treats the printed output as new information and reasons from it, rather than sticking to a fixed script it wrote before seeing any data.

A second common EDA task is checking relationships between variables before you commit to a model or a chart:

import pandas as pd

df = pd.read_csv("data/raw/orders_2024.csv")

numeric_cols = df.select_dtypes(include="number").columns
corr = df[numeric_cols].corr(numeric_only=True)
print(corr["order_value"].sort_values(ascending=False))

Asking Claude Code to explain *why* two columns are correlated, not just report the coefficient, tends to produce more useful analysis than asking a general chatbot the same question, because Claude Code can go back to the raw rows and pull specific examples that either support or complicate the correlation, rather than answering in the abstract.

EDA also includes the boring-but-critical step of checking category cardinality before you build any grouping logic on top of it. A prompt like "how many distinct values does each categorical column have, and are any of them suspiciously similar spellings of the same thing" leads to something like:

import pandas as pd

df = pd.read_csv("data/raw/orders_2024.csv")

categorical_cols = df.select_dtypes(include="object").columns

for col in categorical_cols:
    uniques = df[col].dropna().unique()
    print(f"{col}: {len(uniques)} unique values")
    if len(uniques) < 30:
        print(sorted(uniques))
    print()

This kind of check regularly surfaces things like "California", "CA", and "california " all being treated as different categories, which will quietly fracture a groupby later if nobody catches it here. Because Claude Code can print the full list of uniques and immediately reason about which ones look like duplicates, it tends to flag this before you've built anything on top of the broken column, rather than after a stakeholder asks why California shows up three times in a regional breakdown.

Cleaning Messy Real-World Data

Real datasets are inconsistent in ways that are tedious to enumerate by hand: mixed date formats, trailing whitespace in string columns, duplicate rows with slightly different casing, currency values stored as text with a dollar sign baked in. This is exactly the kind of grinding, detail-heavy work where an agent that can run code and check its own output shines.

A typical cleaning request:

The "signup_date" column has at least three different date formats
mixed together. Normalize it to ISO format and tell me how many
rows failed to parse.

Claude Code will typically reach for pd.to_datetime with error handling, rather than assuming a single format:

import pandas as pd

customers = pd.read_csv("data/raw/customers.csv")

customers["signup_date_parsed"] = pd.to_datetime(
    customers["signup_date"], errors="coerce", format="mixed"
)

failed = customers[customers["signup_date_parsed"].isna() & customers["signup_date"].notna()]
print(f"Failed to parse: {len(failed)} rows")
print(failed[["customer_id", "signup_date"]].head(10))

If some rows fail, a useful agent doesn't just leave them as NaT — it inspects the failed rows and often finds a pattern (say, a batch of rows using DD/MM/YYYY while the rest use MM/DD/YYYY), then writes a second pass to handle that specific format before falling back to NaT for anything truly broken. That's the difference between "sort of clean" data and actually clean data, and it comes from being able to look at the failures directly instead of guessing at them.

Deduplication is another place where iteration matters more than the first script:

import pandas as pd

customers = pd.read_csv("data/raw/customers.csv")

customers["email_normalized"] = customers["email"].str.strip().str.lower()

dupes = customers[customers.duplicated(subset="email_normalized", keep=False)]
print(f"Duplicate email groups: {dupes['email_normalized'].nunique()}")
print(dupes.sort_values("email_normalized")[["customer_id", "email", "signup_date"]])

A common next step is deciding which duplicate to keep — usually the most recent signup, or the one with the most complete record — and that decision genuinely depends on what the duplicates look like. Asking Claude Code to "keep the most complete row per duplicate group" produces something like:

customers["completeness"] = customers.notna().sum(axis=1)
deduped = (
    customers.sort_values("completeness", ascending=False)
    .drop_duplicates(subset="email_normalized", keep="first")
)
print(f"Rows before: {len(customers)}, after dedup: {len(deduped)}")

Handling Currency Strings And Other Text-Encoded Numbers

Another extremely common cleaning task is numeric data trapped inside strings — prices stored as "$1,204.50", percentages stored as "12.5%", or quantities stored with units like "3 units". Pandas won't aggregate these until they're converted, and doing the conversion by hand column by column is exactly the kind of repetitive task worth handing to an agent.

A prompt like "the order_value column has dollar signs and commas in it, convert it to a proper float" typically produces:

import pandas as pd

df = pd.read_csv("data/raw/orders_2024.csv", dtype={"order_value": "string"})

df["order_value_clean"] = (
    df["order_value"]
    .str.replace(r"[$,]", "", regex=True)
    .str.strip()
    .astype(float)
)

still_broken = df[df["order_value_clean"].isna() & df["order_value"].notna()]
print(f"Rows that still failed to convert: {len(still_broken)}")
print(still_broken["order_value"].unique()[:10])

If a handful of rows still fail after that first pass, it's usually because of a second inconsistency, like values recorded as "N/A" or "—" instead of a genuine number. Rather than assuming the fix is complete, a useful next instruction is simply "show me every unique value that didn't convert," which turns the cleaning step into a closed loop instead of a guess. This is a small example, but it's representative of the whole cleaning workflow: convert, check what didn't convert, fix the specific pattern, check again.

Joining, Aggregating, And Answering Business Questions

Once the data is clean, the actual analysis usually comes down to joins and groupbys — and this is where being specific about the business question pays off, because a vague prompt produces a vague aggregation.

Instead of asking "analyze sales by region," a sharper prompt is:

Join orders to customers on customer_id, then show me total
revenue and average order value per region for 2024, sorted
by total revenue descending. Flag any region with fewer than
30 orders since the average won't be reliable.

That level of specificity gets you code like this on the first pass:

import pandas as pd

orders = pd.read_csv("data/raw/orders_2024.csv", parse_dates=["order_date"])
customers = pd.read_csv("data/raw/customers.csv")

merged = orders.merge(customers[["customer_id", "region"]], on="customer_id", how="left")

summary = (
    merged.groupby("region")
    .agg(
        total_revenue=("order_value", "sum"),
        avg_order_value=("order_value", "mean"),
        order_count=("order_id", "count"),
    )
    .reset_index()
)

summary["low_sample_warning"] = summary["order_count"] < 30
summary = summary.sort_values("total_revenue", ascending=False)
print(summary.to_string(index=False))

Notice the merge uses how="left" — this preserves orders even if a customer_id is missing from the customers table, which is exactly the kind of default that matters in real analysis and is easy to get wrong when writing code quickly under time pressure. When Claude Code runs this and the row count changes unexpectedly after the merge, it will typically check for orphaned customer_id values before concluding the analysis is correct — a habit worth building whether or not you're using an agent.

For time-based questions, resampling comes up constantly:

import pandas as pd

orders = pd.read_csv("data/raw/orders_2024.csv", parse_dates=["order_date"])

monthly = (
    orders.set_index("order_date")
    .resample("ME")
    .agg(revenue=("order_value", "sum"), orders=("order_id", "count"))
)
monthly["revenue_growth_pct"] = monthly["revenue"].pct_change() * 100
print(monthly.round(2))

A related and equally common request is a cohort-style breakdown — for example, comparing behavior between customers acquired in different months. This requires combining a groupby with a pivot, which is fiddly enough by hand that it's worth letting an agent draft the first version and then refining it:

import pandas as pd

orders = pd.read_csv("data/raw/orders_2024.csv", parse_dates=["order_date"])
customers = pd.read_csv("data/raw/customers.csv", parse_dates=["signup_date"])

merged = orders.merge(customers[["customer_id", "signup_date"]], on="customer_id", how="left")
merged["signup_month"] = merged["signup_date"].dt.to_period("M")
merged["order_month"] = merged["order_date"].dt.to_period("M")
merged["months_since_signup"] = (
    (merged["order_month"] - merged["signup_month"]).apply(lambda x: x.n)
)

cohort_pivot = merged.pivot_table(
    index="signup_month",
    columns="months_since_signup",
    values="order_id",
    aggfunc="count",
    fill_value=0,
)
print(cohort_pivot)

Cohort tables like this are notoriously easy to get subtly wrong — off-by-one month offsets are the classic bug — so this is another place where asking Claude Code to print a couple of individual customer rows and manually trace them through the pivot is worth doing before trusting the aggregate.

Building Repeatable Pipelines Instead Of One-Off Scripts

A one-off analysis in a notebook is fine for a single question. But if you're going to run the same cleaning and aggregation logic every week when new data lands, a coding agent is far more useful for turning that ad hoc exploration into a script you can actually run again.

Ask Claude Code directly:

Turn the cleaning and aggregation steps we just did into a single
script called scripts/monthly_report.py that takes a CSV path as
an argument and outputs a summary CSV to outputs/.

A reasonable result:

import sys
import pandas as pd
from pathlib import Path


def clean_orders(path: str) -> pd.DataFrame:
    df = pd.read_csv(path, parse_dates=["order_date"])
    df = df.dropna(subset=["order_id", "customer_id"])
    df["order_value"] = pd.to_numeric(df["order_value"], errors="coerce")
    df = df.dropna(subset=["order_value"])
    return df


def summarize(df: pd.DataFrame) -> pd.DataFrame:
    monthly = (
        df.set_index("order_date")
        .resample("ME")
        .agg(revenue=("order_value", "sum"), orders=("order_id", "count"))
    )
    monthly["avg_order_value"] = (monthly["revenue"] / monthly["orders"]).round(2)
    return monthly.reset_index()


def main():
    input_path = sys.argv[1]
    df = clean_orders(input_path)
    summary = summarize(df)

    Path("outputs").mkdir(exist_ok=True)
    out_path = Path("outputs") / "monthly_summary.csv"
    summary.to_csv(out_path, index=False)
    print(f"Wrote {len(summary)} rows to {out_path}")


if __name__ == "__main__":
    main()

This is the point where Claude Code stops being a script generator and starts acting like a junior teammate who remembers the whole conversation: it knows which cleaning steps you approved, which edge cases you flagged earlier, and it folds all of that into one maintainable file instead of a scattered notebook history. You can then ask it to add a --help message, wrap it with argparse, or add logging — incremental improvements that would normally require re-explaining context from scratch to a chat-only tool.

Validating Results Instead Of Trusting Them Blindly

The single biggest risk with any AI-assisted data work — agent or otherwise — is quietly wrong numbers. A pandas merge that silently duplicates rows, a groupby that drops nulls without you noticing, a mean computed over a column that still has outliers in it: these produce plausible-looking output that is simply incorrect.

The mitigation is the same whether a human or an agent wrote the code: check the arithmetic independently. A useful habit is asking Claude Code to sanity-check its own aggregation:

Before finalizing this, verify that the sum of revenue per region
equals the total revenue in the raw orders file. Show me the check.

That produces something like:

raw_total = orders["order_value"].sum()
summary_total = summary["total_revenue"].sum()

print(f"Raw total: {raw_total:.2f}")
print(f"Summary total: {summary_total:.2f}")
print(f"Match: {abs(raw_total - summary_total) < 0.01}")

If the totals don't match, that's usually a sign the join dropped or duplicated rows somewhere — worth catching before the number goes into a slide deck. Because Claude Code can run this check itself and see the actual mismatch, it will typically go hunting for the cause (an inner join dropping unmatched customer IDs, for instance) rather than you having to notice the discrepancy manually days later. Treat this validation step as non-negotiable, not optional — the fact that an agent wrote the code doesn't change your responsibility for the numbers being right.

A Realistic End-To-End Walkthrough

Put together, a typical Claude Code data analysis session for a business question like "did our Q3 marketing push actually increase order frequency?" tends to move through these stages in one continuous terminal session:

  1. Load and inspect the raw orders and campaign tables, checking date ranges and null rates
  2. Clean date columns and normalize customer identifiers across both tables
  3. Merge campaign exposure onto orders and compute pre/post order frequency per customer
  4. Aggregate to a summary table comparing exposed versus non-exposed customers
  5. Validate the row counts and totals against the raw files
  6. Write the final script and summary CSV to the outputs folder

What makes this materially faster than the manual equivalent isn't that the agent knows pandas better than a working analyst — it's that every step above involves running code and reading real output before deciding the next step, without you manually copying results between a browser tab and a terminal. The whole loop — write, run, read the output, adjust — happens inside one tool, on your actual files, which is the entire point of a coding agent over a plain chat interface for this kind of work.

Getting Started With Claude Code On Your Own Data

If you're new to this workflow, the learning curve isn't really about pandas — most analysts already know pandas reasonably well. The learning curve is about how to prompt a coding agent effectively: being specific about business logic, asking it to validate its own output, and knowing when to break a big question into smaller checkable steps rather than one giant request.

Start small. Point Claude Code at a single messy CSV you already understand well, and ask it to clean and summarize it before you try anything with joins across multiple tables. Get comfortable reading the code it writes — you should always be able to explain why a merge is a left join and not an inner join, even if the agent chose it for you. Treat every output number as a claim to verify, not a fact to accept, at least until you've built up trust with a particular type of task.

If you want a structured, hands-on path into this rather than piecing it together from trial and error, our Claude Code Tutorial for Beginners course on teachyou.ai walks through exactly this kind of real-world workflow — from setup, to prompting patterns that actually work, to building and validating full data pipelines — so you can go from "I know pandas" to "I can run a full analysis with an agent and trust the output" in a fraction of the time it takes to figure it out alone.