Skip to content

DS 101 · Module 1

From ESG Questions to Python Data Analysis

What question are you answering, and how far can the data take you?

Start Section 1
0 / 5 completed
Sync my progressJoin a session to keep progress across devices and share it with your instructor

Info

After you join, your completed lesson numbers are sent to your instructor together with your learner code, so they can see your progress. Use the codes your instructor gave you, and do not enter your name, company secrets or other personal data. Progress is kept with the rest of that session's data.

Learning objectives

  • Turn a vague ESG question into one that data can answer, naming its Question, Measure, Dimension, and Evidence boundary
  • Explain row, column, observation, variable, dimension, measure, metric, and unit, and tell absolute metrics apart from intensity metrics
  • Run basic Python and pandas code in a notebook in the right order, and read the error messages it produces
  • Use pandas to inspect, select, filter, and sort data, checking the result after every step
  • Complete a first Guided Lab: calculate Scope 1+2 emissions and emissions intensity, and sort observations into fact, interpretation, and needs verification

Questions and data

30 min

From ESG Questions to Data Questions

Learning objective: define what you want to answer first, then decide which data and which method you need.

Data Science begins with a question, not with Python. If the question is vague, even well-written code only produces numbers without a direction. So the first step in this course is to rewrite a broad concern as a specific question that data can actually answer.

A vague question

Is our ESG performance good? (Good by what definition? Compared with whom? In which year? Using which numbers? None of this is stated.)

Questions data can answer

Change
How did Scope 1 + 2 emissions change from 2021 to 2025?
Comparison
Which year had the highest emissions intensity?
Alternative explanation
Did emissions fall because efficiency improved, or simply because revenue fell?
Data quality
Which companies or years require further data-quality review?
Four components that turn a question into a data question
ComponentAsk yourselfExample
QuestionWhat exactly do I want to answer?How did Scope 1 + 2 emissions change from 2021 to 2025?
MeasureWhich value will I calculate or compare, and in what unit?scope1_tco2e + scope2_tco2e, in tCO2e
DimensionWill I compare by company, by year, or by another category?Look at company_name and year separately
Evidence boundaryHow far can the available data support an answer, and what can it not tell me?The data can describe how emissions changed, but cannot on its own prove why
The DS 101 learning loop
  1. Question: write down what you want to answer
  2. Data: identify the columns, units, and scope you need
  3. Python: use the minimum code necessary to work with the data
  4. Analysis: calculate metrics and compare them
  5. Verification: check formulas, units, outputs, and claims
  6. Communication: state the evidence and its limitations

Warning

Data safety: do not paste confidential institutional or company data, client data, or any personal data into an AI tool without authorization. Every exercise in this course uses the synthetic course files. If you later want to analyze real ESG or internal operational data, check your organization's data policy first.

Info

Start with the question, not the code. AI can help you break a question down, but you decide what is being asked, and you review and remain responsible for the conclusion.
  • Does it name a time range? "Recently" should become a specific span, such as 2021 to 2025.
  • Does it name a comparison? Against your own past results, or against the other sample companies?
  • Does it state the unit clearly? "Emissions" should say whether it means tCO2e or an intensity metric.
  • Does it state the boundary of the data? For example, this dataset only runs through 2025 and cannot say what happens after that.
Worked example: narrowing a question that is still too broad

A manager asks, "How is Metro Services Co. doing?" This cannot be answered directly with data: it has no comparison, time range, or metric. Narrowed down: Question (From 2023 to 2025, is Metro Services Co.'s Scope 1+2 emissions intensity higher or lower than the other three sample companies?), Measure (scope1_tco2e + scope2_tco2e, divided by revenue_m_twd), Dimension (compare by company_name and year), Evidence boundary (the data can describe relative levels, but cannot explain what caused any difference).

Check yourself

Which question is best suited to being answered directly with structured ESG data?

Which question is best suited to being answered directly with structured ESG data?

Key takeaway: Define the question before writing code. Analysis quality begins with problem framing.

30 min

Understanding ESG Datasets and Metrics

Learning objective: understand row, column, observation, variable, dimension, measure, metric, and unit.

This course uses DS101_ESG_Company_Data_Clean.csv. It is synthetic educational data: four sample companies (Alpha Tech Co., Harbor Foods Co., Evergreen Components Co., and Metro Services Co.), each with one row per year from 2020 to 2025, for 24 rows in total. The company names and values were designed for teaching and do not represent any real company.

Illustration of what the data looks like (values written for explanation only, not taken from the course file)
company_idcompany_namesectoryearrevenue_m_twdscope1_tco2escope2_tco2e
C001Alpha Tech Co.Technology2024500012003400
C001Alpha Tech Co.Technology2025520011003300
Basic vocabulary for reading a Dataset
TermMeaningIn the course data
Row / ObservationOne record, describing one observed unitOne company in one year
Column / VariableOne field that records the same characteristic for every rowFor example year or energy_mwh
DimensionA field used to describe, group, or comparecompany_name, year, sector
MeasureA numeric value you can sum, compare, or calculate withscope1_tco2e, energy_mwh, revenue_m_twd
MetricAn interpretable number defined or calculated from the dataScope 1+2 emissions, emissions intensity
UnitHow a value is measured; always read it together with the valuetCO2e, MWh, NT$ million
Scope 1, 2, and 3: this course uses conceptual definitions only
ScopeConceptHow this course uses it
Scope 1Direct emissions from the organization, such as fuel burned in its own equipmentUses scope1_tco2e
Scope 2Indirect emissions from purchased energy such as electricityUses scope2_tco2e
Scope 3Other indirect emissions across the value chain, such as suppliers and product useThe data include a scope3_tco2e column, but this foundation course does not do full Scope 3 accounting and leaves it out of the core formula

Absolute metric

  • Looks at the total, such as Scope 1+2 emissions in tCO2e
  • Answers "how much was emitted in total?"
  • Moves with company size and level of activity

Intensity metric

  • Divides the total by a base, such as emissions per NT$ million revenue
  • Answers "how much was emitted relative to the scale of operations?"
  • Changes when the denominator changes, so read the numerator and denominator together
Formulas used in this course (simplified educational examples)
Scope 1+2 emissions = scope1_tco2e + scope2_tco2e
Emissions intensity = Scope 1+2 emissions / revenue_m_twd
Unit = tCO2e per NT$ million revenue

Warning

A metric that is calculated correctly is not automatically comparable across companies. You still need to check organizational boundaries, accounting methods, units, and data completeness. The formulas in this course are simplified educational examples and are not equivalent to any specific reporting standard or official accounting method.

Info

A number without a definition and unit is not evidence. When you see "1000", ask first: 1000 of what? tCO2e, MWh, or NT$ million? For which company, and which year?
Worked example: the same numbers, two metrics

Continuing the illustration table above (for explanation only, not the actual course file values): Alpha Tech Co.'s Scope 1+2 emissions went from 1200+3400=4600 tCO2e in 2024 to 1100+3300=4400 tCO2e in 2025, so absolute emissions fell. But revenue for the same years rose from 5000 to 5200 (NT$ million), so emissions intensity moved from 4600/5000=0.92 down to 4400/5200≈0.85 (tCO2e per NT$ million revenue). Both metrics fall in the same direction, but for possibly different reasons: one looks only at the total, the other also accounts for the scale of revenue.

  • When the concern is total emissions responsibility, regulatory reporting, or a net-zero target, absolute emissions is usually the primary figure.
  • When comparing companies of different sizes, or the same company across years of changing operations, you usually need the intensity metric too.
  • Calling a drop in absolute emissions "progress" on its own can overlook a simultaneous drop in revenue.
  • Calling a drop in intensity "progress" on its own can overlook absolute emissions that are still rising.
Check yourself

A sample company's Scope 1+2 emissions fell, but its revenue also fell sharply over the same period. What is the most appropriate approach?

A sample company's Scope 1+2 emissions fell, but its revenue also fell sharply over the same period. What is the most appropriate approach?

Key takeaway: A number is only meaningful together with its definition and unit.

Notebook and pandas

35 min

Notebook and Essential Python

Learning objective: run basic Python and pandas code in a notebook, without needing to memorize syntax.

A mental model of a notebook
ElementPurpose
Markdown cellText that records your question, assumptions, explanations, and conclusions, so others can see why you did what you did
Code cellPython code that runs and carries out the analysis
OutputWhat the code produces when it runs: a table, a number, or an error message
Run orderThe notebook remembers what has already run, so the order in which cells run affects the result

Info

Run order matters. A reproducible notebook runs from top to bottom in one pass from a fresh kernel (for example with Restart & Run All). If it only works when cells are clicked in a special order, nobody else can reproduce your result.
Minimal Python: assigning values
# Use = to store a value in a variable (assignment)
company = "Alpha Tech"   # text (a string) goes in quotes
year = 2025              # numbers do not
threshold = 1000
  • Assignment: = stores a value under a name you can use later
  • Strings and numbers: text goes in quotes, such as "Technology"; numbers do not, such as 2025
  • Function calls: a name followed by parentheses, such as pd.read_csv(...)
  • Dot notation: it is enough to recognize df.head() as "do head on df"
  • Comments: text after # is not run; use it to explain what a line does
  • Reading error messages: start from the last line, which usually names the exact variable or column that caused the problem
Minimal pandas: load the data and look at the first rows
import pandas as pd

# The file must be in the same folder as the notebook
df = pd.read_csv("DS101_ESG_Company_Data_Clean.csv")
df.head()
Reading an error message

You type df["scope_1"] and the last line says KeyError: 'scope_1'. Nothing is broken: the message is telling you there is no column called scope_1. The next step is to run df.columns and look at the real column names, where you will find scope1_tco2e. An error message is a clue, not a failure.

  1. Explain line by line
    Ask AI to explain what each line of code does, not just to summarize the whole block.
  2. List the columns used
    Ask AI which columns the code uses, then compare them with df.columns or the data dictionary.
  3. State the expected output
    Ask AI how many rows and columns, or what kind of result, the code should produce, then check it against the real output.
  4. No hallucinated columns
    Tell AI to use only columns that exist in the data. If you see a column name you do not recognize, check the data dictionary before using it.

Warning

When you use an AI tool, share only the synthetic course data or column names. Do not upload confidential institutional or company data without authorization, and do not put personal or sensitive data into public or shared prompts. An AI explanation can help you understand, but it is not evidence in itself: code that runs can still be wrong.
Common error messages at a glance (read the last line first)
ErrorWhat it usually meansWhat to check first
NameErrorA name is used before it existsWhether the cell that creates that variable has already run
KeyErrorA column name does not exist in the dataThe real spelling and capitalization in df.columns
SyntaxErrorA bracket, quote, or indentation is incompleteWhether a bracket or quote is missing, usually near the reported line
TypeErrorAn operation was applied to incompatible types, such as adding text and a numberWhether that column's data type (df.dtypes) is what you expected
  • Have all the names this cell uses already been created in an earlier cell?
  • If you reopened an older notebook, did you run Restart & Run All before trusting the output?
  • When you paste someone else's code, do the column names match your own data, rather than the columns in their example?
Check yourself

You open a notebook and immediately run a cell containing df.head(). It shows NameError: name 'df' is not defined. What is the best response?

You open a notebook and immediately run a cell containing df.head(). It shows NameError: name 'df' is not defined. What is the best response?

Key takeaway: You are not being tested on memorizing Python; you are learning to understand and verify an analysis workflow.

40 min

Inspect, Select, and Filter with pandas

Learning objective: use pandas to answer "what does the data look like, which columns does it have, and which rows meet a condition?"

Five basic commands for inspecting data
df.head()
df.shape
df.columns
df.info()
df.describe()
What each command tells you
CommandQuestion it answers
df.head()What do the first five rows look like? Do the column names and values look reasonable?
df.shapeHow many rows and columns are there? (The output is (rows, columns).)
df.columnsWhich columns exist, and exactly how are they spelled?
df.info()What data type is each column, and are any values missing?
df.describe()What are the minimum, maximum, mean, and other summaries of the numeric columns? Is anything surprising?
Select columns (note the double square brackets)
df[["company_name", "year", "scope1_tco2e", "scope2_tco2e"]]
Filter rows: keep only 2025
df[df["year"] == 2025]
Multiple conditions: 2023 or later, and the Technology sector
df[(df["year"] >= 2023) & (df["sector"] == "Technology")]

Info

Three common slips: testing equality needs two equals signs, == (a single = assigns a value); combine conditions with & and wrap each condition in parentheses; text must match exactly, so "technology" and "Technology" are different values in pandas.
Sort: Scope 1 emissions from highest to lowest
df.sort_values("scope1_tco2e", ascending=False)
  • Row count: how many rows are left, and is that what you expected?
  • Selected columns: are these the columns you meant, with nothing missing or extra?
  • Filter condition: does the condition say what you meant (year, sector, greater than or greater than or equal to)?
  • Unexpected empty result: if you get 0 rows, check spelling, capitalization, and data types before concluding that "there is no data"
  • Surprising maximum or minimum: confirm the unit and definition first, then decide whether it needs verification
Three things to state for every step
  1. What went in: for example the full df, 24 rows
  2. What was done: for example keep only rows where year is 2025
  3. What remains: for example 4 rows, one per company

Warning

Code that runs can still be wrong. df[df["sector"] == "technology"] raises no error, but it returns 0 rows because the value in the data is "Technology". If you do not check the row count, you might conclude that the sector has no data.
  • df["sector"].unique(): lists the distinct values in a column, useful for spotting inconsistent spelling
  • df["sector"].value_counts(): counts how often each value appears, good for a quick look at the category breakdown
  • df["sector"].nunique(): the number of distinct values, when you do not need to see every one of them
  • df.isna().sum(): how many missing values each column has; check this before filtering so you do not undercount rows
Worked example: what went in, what was done, what remains

What went in: the full df, 24 rows. What was done: df[df["company_name"] == "Harbor Foods Co."].sort_values("year"), which keeps only Harbor Foods Co. rows and sorts them by year. What remains: you would expect 6 rows (one per year from 2020 to 2025), with year in ascending order; if the row count is not 6, the company name may be spelled inconsistently, so check with df["company_name"].unique() first.

Check yourself

Which pandas expression filters the rows where year is 2025?

Which pandas expression filters the rows where year is 2025?

Key takeaway: Every analysis step should state what went in, what was done, and what remains.

Guided Lab

45 min

Guided Lab: Your First ESG Data Analysis

Learning objective: with teacher guidance, complete a small reproducible analysis of the clean dataset and sort your observations into fact, interpretation, and needs verification.

Info

Lab question: From 2021 to 2025, how did Scope 1 + 2 emissions and emissions intensity change across the four sample companies, and which observations require further verification?
  1. Load the data
    Load DS101_ESG_Company_Data_Clean.csv.
  2. Inspect the data
    Check the shape, columns, data types, and first rows.
  3. Confirm the units
    Use the data dictionary to confirm the units of revenue_m_twd, scope1_tco2e, and scope2_tco2e.
  4. Create the Scope 1+2 column
    Create scope12_tco2e = scope1_tco2e + scope2_tco2e.
  5. Create the intensity column
    Create scope12_intensity = scope12_tco2e / revenue_m_twd, in tCO2e per NT$ million revenue.
  6. Filter the years
    Keep only 2021 to 2025, and check the row count.
  7. Sort one year
    Pick one year, sort by emissions intensity, and note that this is a single-year comparison.
  8. Organize annual values by company
    Group by company and year so the year dimension is kept, rather than collapsing every year into one number.
  9. Record three observations
    Write down, in your own words, three things you see in the output.
  10. Label the evidence
    Label each observation as Fact from data, Interpretation, or Needs verification.
  11. Write one limitation
    Write one sentence stating what this analysis cannot tell you.
Steps 1 to 5: load, inspect, and create two derived columns
import pandas as pd

df = pd.read_csv("DS101_ESG_Company_Data_Clean.csv")
df.shape
df.info()
df.head()

# Derived column: Scope 1+2 emissions (tCO2e)
df["scope12_tco2e"] = df["scope1_tco2e"] + df["scope2_tco2e"]

# Derived column: emissions intensity (tCO2e per NT$ million revenue)
df["scope12_intensity"] = df["scope12_tco2e"] / df["revenue_m_twd"]
Steps 6 to 8: filter, sort, and organize annual values by company
# Step 6: keep only 2021 to 2025
df_lab = df[(df["year"] >= 2021) & (df["year"] <= 2025)]
df_lab.shape  # expect 20 rows: 4 companies x 5 years

# Step 7: pick one year and sort by emissions intensity
df_lab[df_lab["year"] == 2025].sort_values("scope12_intensity", ascending=False)[["company_name", "year", "scope12_intensity"]]

# Step 8: group by company and year, keeping the year dimension
annual = (
    df_lab.groupby(["company_name", "year"])[["scope12_tco2e", "revenue_m_twd"]]
    .sum()
    .reset_index()
)
# Recalculate intensity from the summed numerator and denominator; never sum intensities
annual["scope12_intensity"] = annual["scope12_tco2e"] / annual["revenue_m_twd"]
annual
Label each observation by type of evidence (sentence patterns; fill in your own output)
LabelMeaningSentence pattern
Fact from dataA number read directly from the output that someone else can reproduceIn this synthetic dataset, [company]'s scope12_tco2e went from [value] in 2021 to [value] in 2025 (tCO2e).
InterpretationYour reading of the facts, with its basis and uncertainty stated[Company]'s emissions intensity fell more than its absolute emissions, which may partly reflect higher revenue rather than necessarily better emissions efficiency.
Needs verificationSomething the data cannot confirm, which needs other sources or a question to the data provider[Company]'s change in [year] is unusually large; check whether the organizational boundary or calculation method changed that year.
  • A notebook that runs again from top to bottom
  • Two derived columns: scope12_tco2e and scope12_intensity
  • At least three clearly labelled evidence statements (fact, interpretation, or needs verification)
  • At least one limitation statement
  • No causal claim that the dataset cannot support

Warning

This data can describe what changed, but it cannot on its own show why. Avoid causal claims such as "emissions fell because the company adopted renewable energy", and do not treat the results as a company ranking or investment advice. Example limitation: "This analysis uses synthetic teaching data and a simplified formula, and it does not check whether organizational boundaries and calculation methods are consistent across companies, so cross-company comparisons are for practice only."

Done

If you use AI to write or explain this code, check each point yourself: the column names exist, the formulas match the course definitions, the units are right, and the row counts are what you expect. AI can assist the analysis; responsibility for verification and the conclusion stays with you.
Common slips in this lab (use it to self-check)
SlipWhy it causes an errorHow to avoid it
Summing or averaging scope12_intensity directly after groupingIntensity is a ratio; summing or averaging it directly distorts the denominatorSum the numerator and denominator separately first, then divide again to get a correct intensity
Ranking every company from a single year aloneOne year can be skewed by a one-off event and may not represent a longer trendLook across the full 2021-2025 range before describing a trend
Writing a falling intensity directly as "the company improved"A falling intensity can simply mean revenue grew, enlarging the denominatorCite the absolute emissions figure too, and state in your limitations that the cause cannot be confirmed

Key takeaway: A good analysis produces not only numbers but also keeps the formula, units, evidence, and limitations.

What next?