EduFlow

Section 2 · pandas Foundations

Loading and Inspecting a CSV

18 min3 min readPython exercise

Lesson at a glance

Load a CSV into a DataFrame with read_csv and run a five-step inspection routine (shape, head, info, describe, value_counts) that catches problems before they reach your numbers.

  1. 1Step 1: load it, then check shape and head()
  2. 2Step 2: info() shows types and blanks
  3. 3Step 3: describe() summarises numbers
  4. 4Step 4: value_counts() on the text columns
  5. 5Step 5: check what you are about to assume

No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.

The head of growth sends you orders.csv and asks for “a quick revenue number”. The fastest way to give a wrong answer is to load the file and sum a column straight away. Every experienced analyst does the same thing first: look at the data. Not the whole thing, a few deliberate checks that take thirty seconds and catch most problems before they reach a slide.

Step 1: load it, then check shape and head()

Python
import pandas as pd

orders = pd.read_csv("orders.csv")
print(orders.shape)
print(orders.head().to_string())

read_csv reads the header row as column names, guesses each column’s type, and returns a DataFrame. head() shows the first five rows (head(10) shows ten, tail() the last ones, sample(5) five at random, which is better at surfacing oddities than the first rows). .to_string() prints every column instead of hiding the middle ones behind ....

2,066 rows and 7 columns. Before going further, say out loud what you expect: one row per order, an order id, the customer, a date, a status, a channel, an optional coupon and a shipping fee. The checks below confirm or refute each expectation.

Step 2: info() shows types and blanks

Python
import pandas as pd

orders = pd.read_csv("orders.csv")
orders.info()

info() prints by itself, no print() needed. Read it column by column. Non-Null Count tells you how many rows have a value: coupon_code has 388 of 2,066, so most orders used no coupon. Dtype tells you what pandas made of each column: the ids are int64, the fee is float64, and everything else is object, meaning text.

That includes order_date. Stored as text, dates sort correctly only because this file happens to write them year first, and they cannot answer “which month?”. Ask read_csv to parse them as they load:

Python
import pandas as pd

orders = pd.read_csv("orders.csv", parse_dates=["order_date"])
print(orders.dtypes)
print(orders["order_date"].min(), "to", orders["order_date"].max())

Now the column is datetime64[ns], and min() and max() confirm the data runs from 2 January 2024 to 30 December 2025. Checking the date range of every new file is a habit worth forming: a missing month or a stray date in 1970 shows up immediately.

Step 3: describe() summarises numbers

Python
import pandas as pd

orders = pd.read_csv("orders.csv")
print(orders.describe().round(2))

describe() gives count, mean, standard deviation, minimum, quartiles and maximum for every numeric column. Shipping fees run from 0 to 9.99 with a median of 4.99: plausible. But it also dutifully reports a mean customer_id of 300.47, which means nothing. Ids are labels that happen to be numbers. Read describe() only for columns where arithmetic makes sense, and use it to spot impossible values: negative quantities, prices of zero, a maximum a hundred times the median.

Step 4: value_counts() on the text columns

Python
import pandas as pd

orders = pd.read_csv("orders.csv")
print(orders["status"].value_counts())
print(orders["coupon_code"].value_counts(dropna=False))
print(orders["channel"].value_counts(normalize=True).round(3))

value_counts() lists every distinct value with its count, most common first. It is how you find typos ("Web" next to "web"), unexpected categories, and how big each group is. dropna=False includes the missing values as their own row, and normalize=True gives shares instead of counts: 55% of orders came through the web.

Notice that 105 orders were cancelled. Whether revenue should include them is a business decision, not a coding one, and you will make it explicitly in Section 4. Inspection is where those decisions first become visible.

Step 5: check what you are about to assume

Finally, turn your expectations into one-line checks:

Python
import pandas as pd

orders = pd.read_csv("orders.csv")
customers = pd.read_csv("customers.csv")
print(orders["order_id"].is_unique)            # one row per order?
print(orders["customer_id"].nunique(), "customers ordered")
print(len(customers), "customers in total")
print(customers["referred_by"].dtype)

The order id is unique, so one row really is one order. 515 of 600 customers have ordered at least once; the other 85 signed up and never bought, which matters later when you compute “revenue per customer”. And referred_by loaded as float64 even though it holds customer ids: most customers were not referred, the blanks became NaN, and in classic pandas NaN is a float, so the whole column became one.

In the exercise you run the routine on customers.csv. Next you learn to pull out exactly the rows and columns you need with .loc and .iloc.

Key takeaways

  • pd.read_csv("file.csv") loads a table; parse_dates=[...] turns date columns into real datetimes as they load.
  • Run the same routine on every new file - shape, head(), info(), describe(), then value_counts() on the text columns.
  • info() shows each column’s dtype and non-null count, which reveals missing values and numbers stored as text.
  • An integer column that contains blanks loads as float64, because classic NaN is a float.
  • Check the facts you are about to rely on, such as an id being unique, with code like orders["order_id"].is_unique.

Further reading

Check your understanding

3 questions

Question 1 of 3

info() reports coupon_code 388 non-null object for a table of 2,066 rows. What does that tell you?

Question 2 of 3

describe() shows mean 300.47 for customer_id. How should you read that?

Question 3 of 3

In customers.csv, referred_by holds customer ids but loads as float64. Why?

Practice

Python exercise

Inspect the customers table

Load customers.csv into customers, parsing signup_date as a date while loading. Then answer five questions with code: n_rows (number of rows), n_countries (number of distinct countries), top_country (the country with the most customers), missing_referrals (how many rows have no referred_by), and first_signup (the earliest signup date).

Data files: customers.csv

import pandas as pd

# Load customers.csv with signup_date parsed as a date
customers = None

n_rows = None             # number of customers (rows)
n_countries = None        # how many distinct countries
top_country = None        # the country with the most customers
missing_referrals = None  # how many rows have no referred_by value
first_signup = None       # the earliest signup_date
Loading the editor…

Ctrl/⌘ + Enter to run · Runs in a sandbox in your browser. Nothing leaves your device.

(no output)

Checks

  • customers has 600 rows and 9 columns
  • signup_date is a datetime column
  • 600 rows and 8 countries
  • The top country is the United States
  • 503 customers have no referral
  • The first signup was on 2024-01-01