So far, we have use dummy data. But, now we would like to explore data from the real world and write useful programs!
For that, we need to be able to read files containing data.
Data might be written, for example, in the commonly used Comma-Separated Values(CSV) format or some other formats.
Because all of your programs have access to the outside world through CSV files now!
Almost every software in the world (Excel or any spreadsheet software, banks, CRMs, web apps, SQL databases, Google Sheets) can export data as a CSV!
Once you have a dataset in a CSV format file, you can write small Python programs (scripts) to:
reader read the file line by
line! Which means, you can work with (very) large files
and datasets (several GB of data!). If you try to load these
huge datasets into Excel it will break! (because Excel, or any
spreadhseet, will load everything in memory at once!)For example, look at this sales_messy.csv file provided
by the sales team:
salesperson,sales_euros
John ,100
john,5000
JANE,9000
jane,1200
eve,N/A
EVE ,3000
,500
Inspect the data. Whatâs wrong here?
Whatâs wrong with them?!
Whatâs wrong here?
Proper data cleaning is often said to take up 80% of a data scientistâs or analystâs time!
Itâs a big part of the job
Following a systematic approach ensures your results are accurate and reliable.
Here are some recommandations
YYYY-mm-dd), etc.Never clean data manually by deleting rows in the source CSV file directly!
Instead, write a dedicated cleaning Python script. Once cleaned, always export the result into a new CSV file, leaving your source data untouched!
This ensures your data transformations are fully repeatable, transparent and traceable.
Keep data cleaning separate from data analysis!
If you drop or modify rows (e.g., dropping rows with missing names), itâs good practice to log somewhere how many rows were dropped so you know how much data you lost.
Pandas is a fast, powerful, and flexible open-source data analysis and manipulation library for Python, built on top of NumPy.
It allows to work with labeled data easily and intuitively. It is like bringing (Excel) spreadsheets directly into Python.
It is noticably really useful to work with CSV files (loading and writing tabular data). Indeed, pandas supports the integration with many file formats or data sources out of the box (csv, excel, sql, json, etc.).
Pandas brings two new datastructures to the table:
Each of these datastructures offer useful functionalities to work with tabular labelled data.
pip (the package manager) for nowpandasTo load the pandas package and start working with it, import the
package. The community agreed alias for pandas is
pd, so loading pandas as pd is assumed
standard practice:
import pandas as pd
# your code...In your current PyCharm project, create a text file
tickets_sold.csv containing the data from the local cinema
problem (see
Problem 1: Arrays and sales):
Adult,Children,Senior
120, 45, 30
95, 30, 25
110, 50, 40
150, 60, 35
pandas provides the read_csv() function to read data
stored as a csv file into a pandas DataFrame:
import pandas as np
dataset = pd.read_csv("tickets_sold.csv")
print(dataset)You can convert pandas DataFrame to numpy array easily with the
to_numpy() function:
import pandas as np
dataset = pd.read_csv("tickets_sold.csv")
sales = dataset.to_numpy()
print(sales, type(sales))
salesis now a numpy array! You can use everything you know about numpy!
For code examples we will use the real-world dataset of Titanic passenger data, provided by pandas and used in their tutorials.
import pandas as pd
# Load a CSV file from your local directory into a DataFrame
df = pd.read_csv('titanic.csv')Once loaded, itâs best practice to check first what the data looks like:
df.head(): Displays the first 5 rows (you can pass a
number like df.head(10)).df.info(): Shows column names, non-null counts, and
data types.df.describe(): Provides basic statistical summaries
(mean, min, max, etc.) for numerical columns.print(df.head())
print(df.info())
print(df.describe())You should always inspect your data before going further!
read_csv #As we already said earlier, real-world data is messy!
read_csv() has many built-in parameters to handle
formatting issues:
sep: Define a custom separator if itâs not a comma
(e.g., sep=â;â for semicolon-separated files).header: Specify which row number to use as column names
(e.g., header=0 or header=None).skiprows: Skip header rowsna_values: Define custom strings that should be treated
as missing/NaN values (e.g.,
na_values=['N/A', 'Unknown']).usecols: Load only specific columns to save memory
(e.g., usecols=['Name', 'Age']).index_col: Which column from your CSV file should be
used as the row labels (the index) of the DataFrame.With pandas, you donât have to handle manually corrupted or missing data like we have to do before!
titanic = pd.read_csv('titanic.csv', sep=',', usecols=['Name','Age', 'Pclass'], na_values=['N/A'])We load only 3 columns âNameâ,âAgeâ and âPclassâ
To select a single column from a DataFrame, use
bracket notation with the name of the column inside quotes. This
extracts the data as a pandas Series (a single 1D column
with a label index):
ages = titanic["Age"]
print(type(ages)) # Output: <class 'pandas.core.series.Series'>
print(ages.head())If you need to select multiple columns at the same time,
pass a list of column names inside the brackets. Note
the double brackets ([[]]):
DataFrameDataFramesubset = titanic[["Name", "Age", "Sex"]]
print(type(subset)) # Output: <class 'pandas.core.frame.DataFrame'>
print(subset.head())To filter rows based on specific conditions, you use
boolean indexing. You pass a conditional expression (predicate)
inside the DataFrame brackets, and pandas evaluates
that condition for every single row, returning only the rows where
the condition evaluates to True.
For example, to find all passengers older than 35:
above_35 = titanic[titanic["Age"] > 35]
print(above_35.head())You can combine multiple conditions using bitwise operators
(& for âANDâ, | for âORâ). Each condition
must be wrapped in parentheses:
# Passengers in 1st class who are older than 50
seniors_class1 = titanic[(titanic["Age"] > 50) & (titanic["Pclass"] == 1)].isin() #If you want to check if values belong to a specific list of
options, use the .isin() method:
# Passengers who embarked in Cherbourg ('C') or Queenstown ('Q')
port_filter = titanic[titanic["Embarked"].isin(["C", "Q"])]loc and iloc #Besides bracket notation, pandas provides two indexers to select rows and columns at the same time:
.loc[]: selection by label (index
values and column names).iloc[]: selection by integer position
(0-based, like Python lists)Both use the syntax df.loc[rows, columns].
To use labels, the DataFrame needs a meaningful
index. We can choose one at loading time with
index_col parameter:
titanic = pd.read_csv('titanic.csv', index_col='PassengerId')
print(titanic.index) # 1, 2, 3, ... 891Without
index_col, pandas creates a default index0, 1, 2, .... In that case, labels and positions are identical, which is a frequent source of confusion.
loc: selection by label #titanic.loc[1] # one row (returns a Series)
titanic.loc[[1, 5, 10]] # several rows (returns a DataFrame)
titanic.loc[1:5] # rows 1 to 5 (end INCLUDED)
titanic.loc[1, "Name"] # one value
titanic.loc[1:5, ["Name", "Age"]] # rows and columns
titanic.loc[:, "Name":"Sex"] # all rows, columns from Name to Sexiloc: selection by
position #titanic.iloc[0] # first row
titanic.iloc[[0, 4, 9]] # rows at positions 0, 4 and 9
titanic.iloc[0:5] # positions 0 to 4 (end EXCLUDED)
titanic.iloc[0, 3] # first row, 4th column
titanic.iloc[0:5, [2, 3]] # rows 0 to 4, columns at positions 2 and 3
titanic.iloc[-1] # last rowRemember:
ilocmeans index location
loc vs iloc #The two indexers do not treat the end of a slice the same way:
titanic.loc[1:3] # PassengerId 1, 2, 3 --> 3 rows (end included)
titanic.iloc[1:3] # positions 1, 2 --> 2 rows (end excluded, as we have seen with range)loc with conditions #loc accepts the boolean conditions seen previously, and
lets you choose the columns to keep:
# Select only passenger names and ages of passengers older than 35
titanic.loc[titanic["Age"] > 35, ["Name", "Age"]]
# Select only passenger names of female passengers in 1st class
titanic.loc[(titanic["Pclass"] == 1) & (titanic["Sex"] == "female"), "Name"]loc vs
iloc #loc |
iloc |
|
|---|---|---|
| Selection by | labels | integer positions |
| Slice end | included | excluded |
| Accepts a boolean condition | yes | only with a list/array (not a Series) |
loc when you know names or when
filtering with conditionsiloc when you need positions
(first rows, last row, âthe 3rd columnâ)Prefer labels (
loc) whenever possible because the code will stay correct if the column order changes
Pandas provides many built-in statistical methods to quickly analyze your data without writing manual loops!
These methods are applied directly to a DataFrame column
(aka a Series) and automatically ignore missing (NaN)
values:
import pandas as pd
titanic = pd.read_csv('titanic.csv')
# Average age of all passengers
print("Mean Age:", titanic["Age"].mean())
# Median age
print("Median Age:", titanic["Age"].median())
# Total number of survivors (since Survived is 1 or 0)
print("Total Survivors:", titanic["Survived"].sum())
# Minimum and maximum fare paid
print("Lowest Fare:", titanic["Fare"].min())
print("Highest Fare:", titanic["Fare"].max())To order your rows based on the values of one or more columns, use .sort_values():
by: Specifies the column name (or a list of column
names) to sort by.ascending: Set to True (default) for lowest-to-highest,
or False for highest-to-lowest.# Sort passengers by age in ascending order (youngest first)
youngest_first = titanic.sort_values(by="Age")
print(youngest_first[["Name", "Age"]].head())
# Sort passengers by fare in descending order (highest price first)
expensive_first = titanic.sort_values(by="Fare", ascending=False)
print(expensive_first[["Name", "Fare"]].head())Because Pandas methods return a Series or a DataFrame, you can chain multiple operations together in a single readable line:
# Find the name of the Oldest survivor
oldest_survivor = (
titanic[titanic["Survived"] == 1]
.sort_values(by="Age", ascending=False)
.iloc[0][["Name", "Age"]]
)print(âYoungest survivor:â, youngest_survivor)
Write Python instructions (directly in the console) with pandas to find the:
We suppose that passengerâs data are stored in the DataFrame
titanic.
#1. List of survivors
titanic[titanic["Survived"] == 1]
#2. Names of survivors
titanic.loc[titanic["Survived"] == 1, "Name"]
#3. Number of survivors
titanic["Survived"].sum()
#4. Number of survivors who were female
((titanic["Survived"] == 1) & (titanic["Sex"] == "female")).sum()
#5. Average survivor age
titanic.loc[titanic["Survived"] == 1, "Age"].mean()
#6. Median survivor age
titanic.loc[titanic["Survived"] == 1, "Age"].median()
#7. Name and age of the youngest survivor
titanic[titanic["Survived"] == 1].sort_values("Age").iloc[0][["Name", "Age"]]
#8. Name and age of the youngest survivor
titanic[titanic["Survived"] == 1].sort_values(by="Age")[["Name", "Age", "Pclass"]].head(5)value_counts() #To count how many times each unique value appears in a
column, use .value_counts():
# Count passengers by class
passenger_counts = titanic["Pclass"].value_counts()
print(passenger_counts)To get percentages instead of raw counts,
pass normalize=True to the function (it rescales counts
between 0 and 1) and multiply by 100:
# Get the percentage split of passengers by sex
passenger_pct = titanic["Sex"].value_counts(normalize=True) * 100
print(passenger_pct)groupby() #To summarize data by categories, use .groupby(). It
follows the Split-Apply-Combine pattern:
.mean(),
.sum(), or .count()) to each group.# Calculate the average age for each passenger class
avg_age_by_class = titanic.groupby("Pclass")["Age"].mean()
print(avg_age_by_class)
# Calculate average survival rate (%) by class
survival_by_class = titanic.groupby("Pclass")["Survived"].mean() * 100
print(survival_by_class)Why
.mean()for survival? SinceSurvivedcontains binary values (1for survived,0for died), taking the mean calculates the exact proportion of survivors!
Pandas comes with built-in plotting capabilities powered by
Matplotlib, making it effortless to turn your
DataFrames and Series into plots with
a
single method call: .plot()
import pandas as pd
import matplotlib.pyplot as plt # import!
# Plot a simple bar chart from a value count
titanic["Sex"].value_counts().plot(kind="bar", color=["#3498db", "#e74c3c"])
plt.title("Titanic Passengers by Sex")
plt.xlabel("Sex")
plt.ylabel("Count")
plt.show()Everything you know about Matplotlib can be reused!
To render plots with pandas, always import
matplotlib.pyplot!
You can change the type of visualization by modifying the
kind parameter:
kind="bar": Vertical bar chart (ideal for comparing
categories)kind="barh": Horizontal bar chartkind="hist": Histogram (ideal for showing distributions
of continuous variables like Age)# Plot an age distribution histogram with 20 bins
titanic["Age"].plot(kind="hist", bins=20, color="#2ecc71", edgecolor="white")
plt.title("Age Distribution of Titanic Passengers")
plt.xlabel("Age")
plt.ylabel("Frequency")
plt.show()Always use
plt.show()at the end of your plotting block to display the canvas window!
Follow the How do I read and write tabular data? tutorial
You can also check 10 minutes to pandas, a short introduction to pandas, geared for new users
To handle CSV files, we can use the built-in
csv module.
Create the file sample.csv with the
following content:
salesperson,sales_euros
john,100
john,5000
jane,9000
jane,1200
eve,100
eve,3000
import csv
with open('sample.csv', newline='') as csvfile:
#'reader' is an iterable over the file "rows"
reader = csv.reader(csvfile, delimiter=',', quotechar='|')
# iterate over the reader to access each row, one by one
# Skip the header row
header = next(reader)
for row in reader:
#Each row is a list of strings (one column = one string)
print(row[0], row[1])In Python,
with open('sample.csv', newline='') as csvfile:is a context manager. It handles the setup and teardown of resources, in this case, opening and closing a file.
reader = csv.reader(csvfile, delimiter=',', quotechar='|')csvfile: name of the file open with the
with instructiondelimiter: character to delimit columns. Comma by
default (,)quotechar: defines the character used to wrap fields
that contain special characters (such as commas or newlines).import csv
# open a new file with 'w' (write permission)
with open('total.csv', 'w', newline='') as csvfile:
writer = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL)
writer.writerow(["Some data"] * 5 + ["Other data"])
writer.writerow(["Some data"] * 2 + ["Extra data"] * 3)Check the
total.csvfile in your project
clean_dataset.py (using the built-in csv module) to clean
the dataset stored in sales_messy.csv and output the clean
dataset into clean_sales.csv. Drop lines with incomplete
data and standardize text in lowercase.analyze_dataset.py that reads the clean CSV file
clean_sales.csv and aggregates the total sales by person
(like before).# clean_dataset.py
import csv
cleaned_rows = []
with open('sales_messy.csv', mode='r', encoding='utf-8') as infile:
reader = csv.reader(infile)
header = next(reader) # Skip header
for row in reader:
name = row[0]
sales_str = row[1]
# Skip rows with missing or empty names
if not name or not name.strip():
continue
# Clean the name: remove extra spaces and lowercase it
person = name.strip().lower()
# Skip rows with invalid sales numbers (like 'N/A')
if not sales_str.strip().isdigit():
continue
cleaned_rows.append([person, sales_str])
# 2. Write clean rows into a new CSV file sales_clean.csv
with open('sales_clean.csv', mode='w', newline='', encoding='utf-8') as outfile:
writer = csv.writer(outfile)
writer.writerow(header)
writer.writerows(cleaned_rows)
print('Cleaning complete! Saved to sales_clean.csv')# analyze_dataset.py
import csv
sales_by_person = {}
# Read the clean file generated by Program 1
with open('sales_clean.csv', mode='r', encoding='utf-8') as file:
reader = csv.reader(file)
header = next(reader)
for row in reader:
person = row[0]
sales = int(row[1])
if person in sales_by_person:
sales_by_person[person] += sales
else:
sales_by_person[person] = sales
print('Total Sales by person:', sales_by_person)Working manually with real-world data requires a lot of work and precautions!
Maybe we can use some Python library to help us?
(Pandas enters the room)