First look at a dataset

First lesson of the Exploratory Data Analysis course, free to read: about 15 minutes, 3 exercises and 3 quiz questions. This is the catalogue's base lesson; in the student space, it is rewritten for your profile.

Objectives

  • Profile a DataFrame (dimensions, types, preview, statistical summary)
  • Fix wrongly imported column types (numbers, dates)
  • Formulate analysis questions before computing

Data profilingpandas types

Exploratory data analysis (EDA) means understanding a dataset before modeling it or drawing conclusions from it: its structure, quality, distributions and relationships. This is the step where you discover the pitfalls… and the first leads.

Start with the questions and the grain

Before coding, two questions:

  1. What do we want to know? “Which regions and categories drive revenue? Is there seasonality?”
  2. What does a row represent? An order, an order line, a customer, a day? This grain determines every calculation: summing order line amounts does not mean the same thing as counting orders.

The basic review

import pandas as pd

ventes = pd.DataFrame({
    "commande_id": [101, 102, 103, 104],
    "date": ["2026-01-03", "2026-01-04", "2026-01-04", "2026-01-10"],
    "region": ["Nord", "Sud", "Nord", "Est"],
    "quantite": [2, 1, 3, 1],
    "montant": ["39,90", "12,50", "59,85", "24,00"],   # decimal comma: read as text
})

print(ventes.shape)        # (4, 5): 4 rows, 5 columns
print(ventes.head())       # preview of the first rows
print(ventes.dtypes)       # type of each column
ventes.info()              # types, non-null values, memory
print(ventes.describe())   # statistics of the numeric columns
print(ventes.nunique())    # number of distinct values per column

Reading the results carefully:

  • montant is of type str: the decimal comma prevented it from being read as a number. Since pandas 3, text has the str type by default (previously object).
  • date is also text: you cannot sort or group by month correctly.
  • commande_id is numeric, but it is an identifier: its mean in describe() makes no sense. Checking that nunique() == number of rows confirms that it does identify each row.

Fixing the types

ventes["montant"] = pd.to_numeric(ventes["montant"].str.replace(",", ".", regex=False))
ventes["date"] = pd.to_datetime(ventes["date"], errors="coerce")
print(ventes.dtypes)   # montant float64, date datetime64

errors="coerce" turns values that cannot be converted into missing values (NaT for dates) instead of raising an error. Always check how many values were lost:

print(ventes["date"].isna().sum())

Selecting by type

numeriques = ventes.select_dtypes(include="number").columns.tolist()
autres = ventes.select_dtypes(exclude="number").columns.tolist()
print(ventes.describe(include="all"))   # summary of all columns

Keeping track of your observations

Keep a running list: “montant imported as text”, “only 3 regions”, “period from January to March”. This log will become the basis of your final summary.

Key takeaways

  • Business questions and grain first.
  • shape, dtypes, info, describe, nunique: the starting profile.
  • Fix the types, then check the values lost in conversion.

Exercises

Solutions and tests stay on the server. In the student space, your code is run and checked, and the tutor gives hints before the solution.

  1. CodeDifficulty 2Python in the browser2 hints

    Profile function

    Write profil(df) that returns a dictionary with the keys n_lignes, n_colonnes, numeriques (list of numeric columns) and cle_unique (True if the commande_id column has as many distinct values as there are rows).

    import pandas as pd
    
    ventes = pd.DataFrame({
        'commande_id': [101, 102, 103, 104, 105, 106, 107, 108],
        'date': ['2026-01-03', '2026-01-04', '2026-01-04', '2026-01-10', '2026-02-02', '2026-02-03', '2026-02-15', '2026-03-01'],
        'region': ['Nord', 'Sud', 'Nord', 'Est', 'Sud', 'Nord', 'Est', 'Sud'],
        'categorie': ['jeux', 'livres', 'jeux', 'deco', 'livres', 'deco', 'jeux', 'livres'],
        'quantite': [2, 1, 3, 1, 2, 4, 1, 2],
        'montant': ['39,90', '12,50', '59,85', '24,00', '25,00', '96,00', '19,95', '25,00'],
    })
    
    def profil(df):
        pass
  2. CodeDifficulty 2Python in the browser2 hints

    Fix the types

    Convert montant to a number (replace the comma with a period) and date to datetime with errors='coerce'. Then store in nb_dates_invalides the number of dates that were not converted and in ca_total the sum of the amounts.

    import pandas as pd
    
    ventes = pd.DataFrame({
        'date': ['2026-01-03', '2026-01-04', '2026-02-30', '2026-03-01'],
        'montant': ['39,90', '12,50', '59,85', '24,00'],
    })
    
    # conversions puis nb_dates_invalides et ca_total
  3. Multiple choiceDifficulty 11 hint

    The grain of the data

    A file contains one row per ordered product: order 12 appears 3 times (3 products). To count the number of orders, what should you use?

    • len(df)
    • df['commande_id'].nunique()
    • df['commande_id'].sum()
    • df.shape[1]

End-of-lesson quiz

  1. Since pandas 3, what is the default type of a text column?

    • object
    • str
    • category
  2. What does pd.to_datetime(s, errors=‘coerce’) do with an invalid date?

    • It raises an error
    • It replaces it with NaT
    • It removes it from the series
  3. By default, describe() summarizes:

    • All columns
    • The numeric columns
    • The text columns

The rest of the course

  1. Missing values and duplicates18 min
  2. Univariate analysis: distributions and categories18 min
  3. Bivariate analysis: crossing variables20 min
  4. Anomalies, change over time and the EDA summary20 min

Exploratory Data Analysis · Intermediate · 3 h 1 min