Mathew K Analytics

Lesson 51 · Mastering Pandas

Step-by-Step Guide to Exploratory Data Analysis (EDA) Using Python and Pandas

Welcome! In this lesson, we will explore real data and master pandas for EDA. We will analyze the world-famous Titanic dataset to find insights for survival…

⬇ Download notebookOpen in Colab ↗

📓 Full notebook

Download .ipynb

Hands-On Exploratory Data Analysis (EDA) Project in Pandas#

Welcome! In this lesson, we will explore real data and master pandas for EDA.

We will analyze the world-famous Titanic dataset to find insights for survival analysis and data-driven storytelling.

You will learn data loading, cleaning, exploration, aggregation, and more!

Lets get started.

import warnings
warnings.filterwarnings("ignore")

# Data setup (Titanic Dataset)
import pandas as pd
import numpy as np
np.random.seed(42)
url = 'https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv'
df = pd.read_csv(url)
print(df.shape)
print(df.head(3))
(891, 12)
   PassengerId  Survived  Pclass  \
0            1         0       3   
1            2         1       1   
2            3         1       3   

                                                Name     Sex   Age  SibSp  \
0                            Braund, Mr. Owen Harris    male  22.0      1   
1  Cumings, Mrs. John Bradley (Florence Briggs Th...  female  38.0      1   
2                             Heikkinen, Miss. Laina  female  26.0      0   

   Parch            Ticket     Fare Cabin Embarked  
0      0         A/5 21171   7.2500   NaN        S  
1      0          PC 17599  71.2833   C85        C  
2      0  STON/O2. 3101282   7.9250   NaN        S  

Understanding the Data Structure#

Lets quickly learn about the columns or features in our Titanic dataset.

Each row tells us about a unique passenger and their attributes, such as:

  • PassengerId
  • Survived (1=Yes, 0=No)
  • Pclass (ticket class)
  • Name
  • Sex
  • Age
  • SibSp (siblings/spouses aboard)
  • Parch (parents/children aboard)
  • Ticket
  • Fare
  • Cabin
  • Embarked (port of embarkation)
# Look at column names and data types
print(df.dtypes)
PassengerId      int64
Survived         int64
Pclass           int64
Name            object
Sex             object
Age            float64
SibSp            int64
Parch            int64
Ticket          object
Fare           float64
Cabin           object
Embarked        object
dtype: object
# Basic data info: missing values, non-null counts
print(df.info())
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   PassengerId  891 non-null    int64  
 1   Survived     891 non-null    int64  
 2   Pclass       891 non-null    int64  
 3   Name         891 non-null    object 
 4   Sex          891 non-null    object 
 5   Age          714 non-null    float64
 6   SibSp        891 non-null    int64  
 7   Parch        891 non-null    int64  
 8   Ticket       891 non-null    object 
 9   Fare         891 non-null    float64
 10  Cabin        204 non-null    object 
 11  Embarked     889 non-null    object 
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB
None
# Checking for missing values directly
print(df.isnull().sum())
PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
Age            177
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          687
Embarked         2
dtype: int64

Quick Data Cleaning Steps#

Handling missing values is a core EDA task.

Lets decide what to do with them, especially for major columns like 'Age' and 'Cabin'.

Option 1: Remove rows or Option 2: Fill in with statistics (like median age).

Lets clean the 'Age' column by filling missing values with the median.

# Fill missing Age with median age
median_age = df['Age'].median()
df['Age'] = df['Age'].fillna(median_age)
print('Missing Age values:', df['Age'].isnull().sum())
Missing Age values: 0
# Fill missing 'Embarked' (port) with the most common port
most_common_port = df['Embarked'].mode()[0]
df['Embarked'] = df['Embarked'].fillna(most_common_port)
print('Missing Embarked values:', df['Embarked'].isnull().sum())
Missing Embarked values: 0
# Drop the 'Cabin' column (too many missing values)
df = df.drop('Cabin', axis=1)
print(df.columns)
Index(['PassengerId', 'Survived', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp',
       'Parch', 'Ticket', 'Fare', 'Embarked'],
      dtype='object')

Exploring Patterns: Summary Statistics#

Summary statistics help us quickly understand the range of values in our data.

Lets look at the basics like mean, median, min, max for numeric columns.

# Describe numeric columns
print(df.describe())
       PassengerId    Survived      Pclass         Age       SibSp  \
count   891.000000  891.000000  891.000000  891.000000  891.000000   
mean    446.000000    0.383838    2.308642   29.361582    0.523008   
std     257.353842    0.486592    0.836071   13.019697    1.102743   
min       1.000000    0.000000    1.000000    0.420000    0.000000   
25%     223.500000    0.000000    2.000000   22.000000    0.000000   
50%     446.000000    0.000000    3.000000   28.000000    0.000000   
75%     668.500000    1.000000    3.000000   35.000000    1.000000   
max     891.000000    1.000000    3.000000   80.000000    8.000000   

            Parch        Fare  
count  891.000000  891.000000  
mean     0.381594   32.204208  
std      0.806057   49.693429  
min      0.000000    0.000000  
25%      0.000000    7.910400  
50%      0.000000   14.454200  
75%      0.000000   31.000000  
max      6.000000  512.329200  
# What percent survived overall?
survival_rate = df['Survived'].mean() * 100
print(f"Survival rate: {survival_rate:.2f}%")
Survival rate: 38.38%
# Survival rate by gender
print(df.groupby('Sex')['Survived'].mean())
Sex
female    0.742038
male      0.188908
Name: Survived, dtype: float64
# Survival rate by passenger class
print(df.groupby('Pclass')['Survived'].mean())
Pclass
1    0.629630
2    0.472826
3    0.242363
Name: Survived, dtype: float64

Filtering and Conditional Selection#

Lets learn to pick rows that match certain conditions, like all children, or people who embarked from a specific port.

This skill is essential for quick hypothesis testing.

# Show all passengers under age 12 (children)
children = df[df['Age'] < 12]
print(children[['Name', 'Age', 'Survived']].head())
                                        Name  Age  Survived
7             Palsson, Master. Gosta Leonard  2.0         0
10           Sandstrom, Miss. Marguerite Rut  4.0         1
16                      Rice, Master. Eugene  2.0         0
24             Palsson, Miss. Torborg Danira  8.0         0
43  Laroche, Miss. Simonne Marie Anne Andree  3.0         1
# Passengers who paid high fares (above 100)
rich = df[df['Fare'] > 100]
print(rich[['Name', 'Fare', 'Survived']].head())
                                               Name      Fare  Survived
27                   Fortune, Mr. Charles Alexander  263.0000         0
31   Spencer, Mrs. William Augustus (Marie Eugenie)  146.5208         1
88                       Fortune, Miss. Mabel Helen  263.0000         1
118                        Baxter, Mr. Quigg Edmond  247.5208         0
195                            Lurette, Miss. Elise  146.5208         1

Aggregation and GroupBy: Average Age by Class#

Lets discover more by grouping passengers and computing aggregate statistics.

We will find the typical age and survival chance for each class.

# Average age and survival by class
print(df.groupby('Pclass')[['Age', 'Survived']].mean())
              Age  Survived
Pclass                     
1       36.812130  0.629630
2       29.765380  0.472826
3       25.932627  0.242363
# Number of survivors by port of embarkation
print(df.groupby('Embarked')['Survived'].sum())
Embarked
C     93
Q     30
S    219
Name: Survived, dtype: int64

Pivot Tables: Survival by Class and Gender#

A pivot table can show patterns across two or more variables at once.

Lets create a table to spot differences in survival rates by both class and gender.

# Create a pivot table of survival rates by class and sex
pivot = df.pivot_table(index='Pclass', columns='Sex', values='Survived', aggfunc='mean')
print(pivot)
Sex       female      male
Pclass                    
1       0.968085  0.368852
2       0.921053  0.157407
3       0.500000  0.135447

Simple Data Visualization: Survival by Age#

Visualizations help us see trends and relationships that numbers alone may hide.

Lets plot a histogram of ages for survivors and non-survivors side by side.

import matplotlib.pyplot as plt
plt.figure(figsize=(8,4))
df[df['Survived']==1]['Age'].hist(alpha=0.6, bins=20, label='Survived')
df[df['Survived']==0]['Age'].hist(alpha=0.6, bins=20, label='Did not survive')
plt.legend()
plt.xlabel('Age')
plt.ylabel('Number of passengers')
plt.title('Age Distribution by Survival')
plt.show()
No description has been provided for this image

Mini Project: Your Turn#

Lets combine your new skills into a simple EDA 'mini-project' question:

Question: Did traveling alone or with family make a difference for survival?

You can use the 'SibSp' and 'Parch' columns to define if a person was alone (both zero) or with family (either above zero).

Lets try to answer this step by step!

# Add a column 'Alone' (True if no siblings/spouses and no parents/children)
df['Alone'] = (df['SibSp'] == 0) & (df['Parch'] == 0)
print(df[['SibSp', 'Parch', 'Alone']].head())
   SibSp  Parch  Alone
0      1      0  False
1      1      0  False
2      0      0   True
3      1      0  False
4      0      0   True
# Compare survival for people alone vs with family
print(df.groupby('Alone')['Survived'].mean())
Alone
False    0.505650
True     0.303538
Name: Survived, dtype: float64

Common Problems in Pandas and How to Fix Them#

  • Typos in column names can cause errors.
  • Writing 'df[Age]' instead of 'df["Age"]' looks for a variable, not a column.
  • Chaining many methods at once can sometimes return a copy, not the real DataFrame.

If something is not working, check your column names and try printing out interim results.

# Practice: Try renaming a column
df = df.rename(columns={'Fare': 'TicketFare'})
print(df.columns)
Index(['PassengerId', 'Survived', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp',
       'Parch', 'Ticket', 'TicketFare', 'Embarked', 'Alone'],
      dtype='object')
# Practice: Try summarizing with value_counts
print(df['Embarked'].value_counts())
Embarked
S    646
C    168
Q     77
Name: count, dtype: int64
# Quick user challenge: input() for a guessHow many passengers traveled solo?
guess = int(input("Guess: How many Titanic passengers were alone? "))
answer = df['Alone'].sum()
print(f"You guessed: {guess}. The actual answer is {answer}!")
You guessed: 250. The actual answer is 537!

Review and Next Steps#

Great job! You loaded, cleaned, explored, grouped, visualized, and asked questions about real Titanic data.

Keep practicing: Try similar steps with new datasets, or repeat with more questions of your own.

Thanks for joining us. For more learning, subscribe for more videos and try out the code on your own!

Found this useful?

All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.