DEV Community

Cover image for Mastering Data Analysis with Pandas: Surviving the Titanic Dataset
Piyush
Piyush

Posted on

Mastering Data Analysis with Pandas: Surviving the Titanic Dataset

Author: Piyush | Topic: Python, Data Science, Pandas

Have you ever looked at a messy spreadsheet and thought, "There has to be a better way to make sense of this?" Well, there is, and its name is Pandas 🐼.

In this hands-on guide, we're going to dive deep into the world of Data Analysis using Python. Instead of boring, abstract examples, we'll be exploring the legendary Titanic Dataset. We'll learn how to load data, uncover hidden trends, handle missing values, and answer the ultimate question: What kinds of people were most likely to survive?

Buckle up! It's time to get our hands dirty with some code. 💻


🔍 Phase 1: Understanding Our Dataset

Before we can analyze anything, we need to know what we are dealing with. Let's load up our data and take a peek under the hood!

import pandas as pd # Here, pd is a alias used to call pandas library.
Enter fullscreen mode Exit fullscreen mode
df = pd.read_csv("C:/Users/hp/Downloads/Titanic-Dataset.csv");
Enter fullscreen mode Exit fullscreen mode

Head is used to display the first rows of dataframe.

df.head(5)
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

   PassengerId  Survived  Pclass  \
0            1         0       3   
1            2         1       1   
2            3         1       3   
3            4         1       1   
4            5         0       3   

                                                Name     Sex   Age  SibSp  \
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

Tail is used to display the last rows of a DataFrame.

df.tail(5)
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

     PassengerId  Survived  Pclass                                      Name  \
886          887         0       2                     Montvila, Rev. Juozas   
887          888         1       1              Graham, Miss. Margaret Edith   
888          889         0       3  Johnston, Miss. Catherine Helen "Carrie"   
889          890         1       1                     Behr, Mr. Karl Howell   
890          891         0       3                       Dooley, Mr. Patrick   

        Sex   Age  SibSp  Parch      Ticket   Fare Cabin Embarked  
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

It is an attribute which gives the dimensions of the DataFrame. Here, the output is given in the form of tuple.

df.shape
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

(891, 12)
Enter fullscreen mode Exit fullscreen mode

Here, it is also an attribute representing the names of columns in the tuple form. OR df.columns.tolist() # Here, it will convert those columns names into Python list.

df.columns
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

Index(['PassengerId', 'Survived', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp',
       'Parch', 'Ticket', 'Fare', 'Cabin', 'Embarked'],
      dtype='object')
Enter fullscreen mode Exit fullscreen mode

It is used to tell the data type of each column in the form of pandas series(One-dimensional array capable of holding any data type).

df.dtypes
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

PassengerId      int64
Survived         int64
Pclass           int64
Name            object
Sex             object
Age            float64
SibSp            int64
Parch            int64
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

It is used to display the compact overview of the whole dataframe. Remember: If the non-null count is less than total no.of rows, the column contain missing values.

df.info()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output


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  
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

Generate descriptive statistics for numerical columns. Where 50% represent the median. statistics -> count, mean, std, min.

df.describe()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

       PassengerId    Survived      Pclass         Age       SibSp  \
count   891.000000  891.000000  891.000000  714.000000  891.000000   
mean    446.000000    0.383838    2.308642   29.699118    0.523008   
std     257.353842    0.486592    0.836071   14.526497    1.102743   
min       1.000000    0.000000    1.000000    0.420000    0.000000   
25%     223.500000    0.000000    2.000000   20.125000    0.000000   
50%     446.000000    0.000000    3.000000   28.000000    0.000000   
75%     668.500000    1.000000    3.000000   38.000000    1.000000   
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

To display both the numerical and categorical columns we use include="all". Statistics -> count, unique, top, freq

df.describe(include="all")
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

        PassengerId    Survived      Pclass                 Name   Sex  \
count    891.000000  891.000000  891.000000                  891   891   
unique          NaN         NaN         NaN                  891     2   
top             NaN         NaN         NaN  Dooley, Mr. Patrick  male   
freq            NaN         NaN         NaN                    1   577   
mean     446.000000    0.383838    2.308642                  NaN   NaN   
std      257.353842    0.486592    0.836071                  NaN   NaN   
min        1.000000    0.000000    1.000000                  NaN   NaN   
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

🎯 Phase 2: Selecting and Filtering Data

We rarely need an entire dataset at once. Usually, we want to ask specific questions, like finding passengers above a certain age, or filtering by gender. Let's see how Pandas makes this incredibly easy.

df.Age For selecting a single column data. This method is generally preferred because it is more reliable with column names containing spaces or special characters. Syntax -> df["column_name"]

df["Age"]
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

0      22.0
1      38.0
2      26.0
3      35.0
4      35.0
       ... 
886    27.0
887    19.0
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

Syntax -> df[["column1", "column2", "column3"]] Remember, here outer bracket is used to select DataFrame and inner bracket is used to select columnes.

df[["Survived", "Pclass", "Sex", "Age", "Fare"]]
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

     Survived  Pclass     Sex   Age     Fare
0           0       3    male  22.0   7.2500
1           1       1  female  38.0  71.2833
2           1       3  female  26.0   7.9250
3           1       1  female  35.0  53.1000
4           0       3    male  35.0   8.0500
..        ...     ...     ...   ...      ...
886         0       2    male  27.0  13.0000
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

iloc-> It is used to select data using integer positions/ location. Syntax -> df.iloc[row_positions, column_positions] df.iloc[0] # Shows first row. df.iloc[:5] # or [0:5] First 5 rows. df.iloc[:, 0:4] # First 4 columns.

df.iloc[0:5,0:4]
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

   PassengerId  Survived  Pclass  \
0            1         0       3   
1            2         1       1   
2            3         1       3   
3            4         1       1   
4            5         0       3   

                                                Name  
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

Here, we are using boolean filtering to select rows based on a condition. Syntax -> df[df["column"] > value] df["Age"] > 30 It will display true/false values which satisfy this condition. It will match rows according to the given condition.

df[df["Age"] > 30]
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

     PassengerId  Survived  Pclass  \
1              2         1       1   
3              4         1       1   
4              5         0       3   
6              7         0       1   
11            12         1       1   
..           ...       ...     ...   
873          874         0       3   
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

Now, we are performing boolean filtering with categorical/text columns. Syntax -> df[df["column"] == "value"]

df[df["Sex"] == "female"]
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

     PassengerId  Survived  Pclass  \
1              2         1       1   
2              3         1       3   
3              4         1       1   
8              9         1       3   
9             10         1       2   
..           ...       ...     ...   
880          881         1       2   
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

Now, we will combine multiple conditions using &(and), |(or) operators. Syntax -> df[df[(condition1) & (condition2)]]

df[(df["Sex"] == "female") & (df["Age"] > 30)]
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

     PassengerId  Survived  Pclass  \
1              2         1       1   
3              4         1       1   
11            12         1       1   
15            16         1       2   
18            19         0       3   
..           ...       ...     ...   
862          863         1       1   
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

df[(df["Pclass"] == 1) | (df["Pclass"] == 2)] without using isin() isin() function is used to check whether values belong to a set/list of specific values. Syntax -> df[df["column"].isin([value1, value2, ...])] using isin()

df[df["Pclass"].isin([1,2])]
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

     PassengerId  Survived  Pclass  \
1              2         1       1   
3              4         1       1   
6              7         0       1   
9             10         1       2   
11            12         1       1   
..           ...       ...     ...   
880          881         1       2   
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

Between() is used to check whether values fall between a specific range. Syntax -> df[df["column"].between(lower, upper)]

df[df["Age"].between(20, 30)]
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

     PassengerId  Survived  Pclass  \
0              1         0       3   
2              3         1       3   
8              9         1       3   
12            13         0       3   
23            24         1       1   
..           ...       ...     ...   
882          883         0       3   
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

query() help us to filter DataFrame using a string expression. df.query("condition");

df.query("Age > 40")
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

     PassengerId  Survived  Pclass  \
6              7         0       1   
11            12         1       1   
15            16         1       2   
33            34         0       2   
35            36         0       1   
..           ...       ...     ...   
862          863         1       1   
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

🧹 Phase 3: Cleaning the Mess (Missing Values & Duplicates)

Real-world data is almost never clean. It has missing values, duplicates, and errors. A good data scientist knows how to clean it up before doing any serious analysis.

To check the missing values (NaN) we use isna() [R] and isnull() [Numpy] function. df.isna() function gives the output in the form of true or false. Here, we are counting the missing values.

df.isna().sum()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
Age            177
SibSp            0
Parch            0
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

Here, we will perform the sum() function again to get the total missing values in a whole dataset.

df.isna().sum().sum()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

np.int64(689)
Enter fullscreen mode Exit fullscreen mode

Now, we are going to calculate the percentage of missing values Or Alternative df.isna().mean() * 100 because mean of boolean values treats [True-> 1, False-> 0]. So, it gives the proportion of missing values.

df.isna().sum() / len(df) * 100
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

PassengerId     0.000000
Survived        0.000000
Pclass          0.000000
Name            0.000000
Sex             0.000000
Age            19.865320
SibSp           0.000000
Parch           0.000000
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

We will use fillna() function to replace missing values with the desired inputs. Syntax-> df["column"].fillna(value) or df["Age"] = df["Age"].fillna(df["Ag"].median()) Remember, fillna() will modify the series/dataframe but to change the original data we have to use inplace=True Here, it is permanently updating the values.

median_age = df["Age"].median()
df["Age"] = df["Age"].fillna(median_age)
Enter fullscreen mode Exit fullscreen mode

Now, we will drop the rows containing missing values using dropna() Remember, this creates a new DataFrame by default and does not modify the original df. It will remove rows containing missing values and to remove columns use dropna(axis=1) df_clean.shape Used to check the dimensions (rows, columns)

df_clean = df.dropna()
df_clean
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

     PassengerId  Survived  Pclass  \
1              2         1       1   
3              4         1       1   
6              7         0       1   
10            11         1       3   
11            12         1       1   
..           ...       ...     ...   
871          872         1       1   
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

We use duplicated() function which gives boolean values. It will give the number of duplicate records.

df.duplicated().sum()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

np.int64(0)
Enter fullscreen mode Exit fullscreen mode

To drop the duplicate rows we may use the function drop_duplicates() df.drop_duplicates().shape

df_no_duplicates = df.drop_duplicates()
df_no_duplicates.shape
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

(891, 12)
Enter fullscreen mode Exit fullscreen mode

📊 Phase 4: Sorting and Frequency Analysis

Who paid the highest fare? What was the most common age group? Let's sort our data and find out the frequencies of different categories.

unique() is used to return all distinct values present in a column. Syntax-> df["column"].unique()

df["Sex"].unique()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

array(['male', 'female'], dtype=object)
Enter fullscreen mode Exit fullscreen mode

Here, we are using the value_counts() to count how many time each unique value has been repeated. Syntax-> df["column"].value_counts()

df["Sex"].value_counts()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

Sex
male      577
female    314
Name: count, dtype: int64
Enter fullscreen mode Exit fullscreen mode

Here, we are calculating the percentage of male and female passengers. df["Sex"].value_counts(normalize=True) # It will return the output in the form of proportion for every unique value [Means dividing the count with the total no. of items]

df["Sex"].value_counts(normalize=True) * 100
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

Sex
male      64.758698
female    35.241302
Name: proportion, dtype: float64
Enter fullscreen mode Exit fullscreen mode

Now, we are sorting according to age in ascending order. Syntax-> df.sort_values("column") by default ascending = True. Or df.sort_values("Age", ascending = True)

df.sort_values("Age")
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

     PassengerId  Survived  Pclass                                  Name  \
803          804         1       3       Thomas, Master. Assad Alexander   
755          756         1       2             Hamalainen, Master. Viljo   
469          470         1       3         Baclini, Miss. Helene Barbara   
644          645         1       3                Baclini, Miss. Eugenie   
78            79         1       2         Caldwell, Master. Alden Gates   
..           ...       ...     ...                                   ...   
116          117         0       3                  Connors, Mr. Patrick   
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

Now, we are sorting fare price in descending order. Syntax-> df.sort_values("column", ascending=False)

df.sort_values("Fare", ascending=False)
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

     PassengerId  Survived  Pclass                                Name  \
679          680         1       1  Cardeza, Mr. Thomas Drake Martinez   
258          259         1       1                    Ward, Miss. Anna   
737          738         1       1              Lesurer, Mr. Gustave J   
88            89         1       1          Fortune, Miss. Mabel Helen   
438          439         0       1                   Fortune, Mr. Mark   
..           ...       ...     ...                                 ...   
806          807         0       1              Andrews, Mr. Thomas Jr   
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

Now, we are calculating 5 highest fares. Syntax-> df.sort_values("column", ascending = False).head(n) or can use df.nlargest(5, "Fare") [nlargest()-> sort_values() + head()]

df.sort_values("Fare", ascending=False).head(5)
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

     PassengerId  Survived  Pclass                                Name  \
679          680         1       1  Cardeza, Mr. Thomas Drake Martinez   
258          259         1       1                    Ward, Miss. Anna   
737          738         1       1              Lesurer, Mr. Gustave J   
88            89         1       1          Fortune, Miss. Mabel Helen   
438          439         0       1                   Fortune, Mr. Mark   

        Sex   Age  SibSp  Parch    Ticket      Fare        Cabin Embarked  
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

Here, we are displaying the 5 youngest passengers, or use df.nsmallest(5, "Age")

df.sort_values("Age").head(5)
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

     PassengerId  Survived  Pclass                             Name     Sex  \
803          804         1       3  Thomas, Master. Assad Alexander    male   
755          756         1       2        Hamalainen, Master. Viljo    male   
469          470         1       3    Baclini, Miss. Helene Barbara  female   
644          645         1       3           Baclini, Miss. Eugenie  female   
78            79         1       2    Caldwell, Master. Alden Gates    male   

      Age  SibSp  Parch  Ticket     Fare Cabin Embarked  
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

🧮 Phase 5: Aggregate Functions (The Big Picture)

Finally, let's crunch some numbers. Averages, minimums, maximums, and totals. This is where we extract high-level insights from our dataset.

Here, we are calculating the average age. Syntax-> df["column"].mean()

df["Age"].mean()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

np.float64(29.36158249158249)
Enter fullscreen mode Exit fullscreen mode

Now, we are calculating the median age. Here, median is the middle value when the data is arranged in order. It useful when data contains outliers.

df["Age"].median()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

np.float64(28.0)
Enter fullscreen mode Exit fullscreen mode

Now, we calculating min and max passanger age. Or df["Age"].agg(["min", "max"]) we can aggregate both the operations.

df["Age"].min()
df["Age"].max()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

np.float64(80.0)
Enter fullscreen mode Exit fullscreen mode

Now are calculating the total fare payed by each passanger. Adding all column values.

df["Fare"].sum()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

np.float64(28693.9493)
Enter fullscreen mode Exit fullscreen mode
df["Fare"].mean()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

np.float64(32.204207968574636)
Enter fullscreen mode Exit fullscreen mode

Standard deviation-> Tell how spread out the values are around the mean. Moreover, larger std-> greater var. variance = square root of std. or df["Age"].agg(["std", "var"])

df["Age"].std()
df["Age"].var()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

np.float64(169.51249827942345)
Enter fullscreen mode Exit fullscreen mode

Mode-> It is the value that occurs most frequently and its output is given in series (1D labelled array) unlike mean and median.

df["Age"].mode()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

0    28.0
Name: Age, dtype: float64
Enter fullscreen mode Exit fullscreen mode

Aggregate function-> It allows multiple functions to the same column at once. Using agg() on single column.

df["Age"].agg(["count", "mean", "median", "min", "max", "std"])
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

count     891.000000
mean       29.361582
median     28.000000
min         0.420000
max        80.000000
std        13.019697
Name: Age, dtype: float64
Enter fullscreen mode Exit fullscreen mode

Now, we are using agg() on multiple columns. Syntax -> df.agg({ "column1": ["function1", "function2"], "column2": ["function3", "function4"] })

df.agg({
    "Age": ["mean", "median", "min", "max"],
    "Fare": ["mean", "sum", "min", "max"] 
})
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

              Age          Fare
mean    29.361582     32.204208
median  28.000000           NaN
min      0.420000      0.000000
max     80.000000    512.329200
sum           NaN  28693.949300
Enter fullscreen mode Exit fullscreen mode

GroupBy Operations

groupby() divide the DataFrame into groups based on a column. It helps to ans questions such as "What is the average age for each passenger clas?" Now, we are finding the average age of male and female passengers using groupby()

df.groupby("Sex")["Age"].mean()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

Sex
female    27.929936
male      30.140676
Name: Age, dtype: float64
Enter fullscreen mode Exit fullscreen mode

Now, we are calculating the survived rate for male and female passengers.

df.groupby("Sex")["Survived"].mean() * 100
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

Sex
female    74.203822
male      18.890815
Name: Survived, dtype: float64
Enter fullscreen mode Exit fullscreen mode

Now, we are calculating the number of passengers in each passenger class. size() counts the number of rows in each group. class_counts = df['Pclass'].value_counts() print(class_counts)

df.groupby("Pclass").size()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

Pclass
1    216
2    184
3    491
dtype: int64
Enter fullscreen mode Exit fullscreen mode

Here, we are calculating the average fare for each passenger class. Syntax-> df.groupby("group_column")["value_column"].mean() Remember, we can identify the group_column and the value_column based on the sentence structure.

df.groupby("Pclass")["Fare"].mean()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

Pclass
1    84.154687
2    20.662183
3    13.675550
Name: Fare, dtype: float64
Enter fullscreen mode Exit fullscreen mode

Now, we are calculating the average age for each passenger class.

df.groupby("Pclass")["Age"].mean()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

Pclass
1    36.812130
2    29.765380
3    25.932627
Name: Age, dtype: float64
Enter fullscreen mode Exit fullscreen mode

Now, we are calculating the survival rate for each passenger class.

df.groupby("Pclass")["Survived"].mean() * 100
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

Pclass
1    62.962963
2    47.282609
3    24.236253
Name: Survived, dtype: float64
Enter fullscreen mode Exit fullscreen mode

Now, we are calculating the no. of survivors in each passenger class. Remember, the difference between count() and size(). count() -> Only counts rows that contain valid data, meaning avoiding missing values. size() -> It counts every single row in a dataset. Here, we can also use sum() because survived -> 1 and unsurvived -> 0.

df.groupby("Pclass")["Survived"].size()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

Pclass
1    216
2    184
3    491
Name: Survived, dtype: int64
Enter fullscreen mode Exit fullscreen mode

Now, we have to group the passengers by both Pclass and Sex to calculate their survival rate. Now, we have to group two columns.

df.groupby(["Pclass", "Sex"])["Survived"].mean() * 100
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

Pclass  Sex   
1       female    96.808511
        male      36.885246
2       female    92.105263
        male      15.740741
3       female    50.000000
        male      13.544669
Name: Survived, dtype: float64
Enter fullscreen mode Exit fullscreen mode

Now, we are calculating mean, minimum and maximum age for each passenger class. Here, we are grouping using multiple statistics.

df.groupby("Pclass")["Age"].agg({
    "mean",
    "min",
    "max"
})
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

         min       mean   max
Pclass                       
1       0.92  36.812130  80.0
2       0.67  29.765380  70.0
3       0.42  25.932627  74.0
Enter fullscreen mode Exit fullscreen mode

Now, we are calculating the mean, median and maximum fare for each passenger class.

df.groupby("Pclass")["Age"].agg({
    "mean",
    "median",
    "max"
})
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

             mean   max  median
Pclass                         
1       36.812130  80.0    35.0
2       29.765380  70.0    28.0
3       25.932627  74.0    28.0
Enter fullscreen mode Exit fullscreen mode

Part G-> Creating and Transforming columns

transform() is used when you want a group-level calculation but want the result returned for every original row. Syntax-> df["new_column"] = df.groupby("group_column")["value_column"].transform("mean")

df["Class_Mean_Age"] = df.groupby("Pclass")["Age"].transform("mean")
df[["Pclass", "Age", "Class_Mean_Age"]].head()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

   Pclass   Age  Class_Mean_Age
0       3  22.0       25.932627
1       1  38.0       36.812130
2       3  26.0       25.932627
3       1  35.0       36.812130
4       3  35.0       25.932627
Enter fullscreen mode Exit fullscreen mode

Now, we have to create a new age column with categories such as child, teenager, young adult, adult and senior. Here, we can use pd.cut() to divide the Age values into meaningful categories. Syntax-> df["Age_Group"] = pd.cut( df["Age"], bin=[...], # It is used to define the boundaries. labels=[...] # It is used to define the category names. )

df["Age_Group"] = pd.cut(
    df["Age"],
    bins=[0, 12, 19, 29, 50, float("inf")],
    labels=["Child", "Teenager", "Young Adult", "Adult", "Senior"]
)
df[["Age", "Age_Group"]].head(10)
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

    Age    Age_Group
0  22.0  Young Adult
1  38.0        Adult
2  26.0  Young Adult
3  35.0        Adult
4  35.0        Adult
5  28.0  Young Adult
6  54.0       Senior
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

Now, we have to count the number of passengers in each age group. Here, we have used value_count() to check how many passengers fall into each category.

df["Age_Group"].value_counts().sort_index()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

Age_Group
Child           69
Teenager        95
Young Adult    397
Adult          266
Senior          64
Name: count, dtype: int64
Enter fullscreen mode Exit fullscreen mode

Now, we are going to use apply(), classify passengers as Minor or Adult. apply() allows you to apply a function to each value in a series. Syntax-> df["new_column"] = df["column"].apply( lambda x: ... ) Here, we can also use pd.cut alongside groupby which is more effective and fast.

df["Age_Status"] = df["Age"].apply(
    lambda age: "Minor" if age < 18 else "Adult"
)
df[["Age", "Age_Status"]].head(10)
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

    Age Age_Status
0  22.0      Adult
1  38.0      Adult
2  26.0      Adult
3  35.0      Adult
4  35.0      Adult
5  28.0      Adult
6  54.0      Adult
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

Now, we will use map() to convert the survival value. map() is particularly convenient when you have a dictionary mapping. There is a difference between map() and apply(), map() only modify one column and want to use a dictionary lookup, whereas apply() write a custom function that depends on multiple columns at once.

survival_map = {
    0: "Did Not Survive",
    1: "Survived"
}

df["Survival_Status"] = df["Survived"].map(survival_map)
df[["Survived", "Survival_Status"]].head()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

   Survived  Survival_Status
0         0  Did Not Survive
1         1         Survived
2         1         Survived
3         1         Survived
4         0  Did Not Survive
Enter fullscreen mode Exit fullscreen mode

Now, we are going to see the use of replace() function. Syntax-> df["column"].replace({ "old": "new" })

df["Sex"] = df["Sex"].replace({
    "male": "Male",
    "female": "Female"
})
df["Sex"].unique()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

array(['Male', 'Female'], dtype=object)
Enter fullscreen mode Exit fullscreen mode

Part H -> Correlation and Crosstab

Correlation-> It measures how strongly two numerical variables are related. Here, we have used the numeric_only which will select only numeric values, we need it because our DataFrame contain categorical columns such as Sex.

df.corr(numeric_only=True)
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

                PassengerId  Survived    Pclass       Age     SibSp     Parch  \
PassengerId        1.000000 -0.005007 -0.035144  0.034212 -0.057527 -0.001652   
Survived          -0.005007  1.000000 -0.338481 -0.064910 -0.035322  0.081629   
Pclass            -0.035144 -0.338481  1.000000 -0.339898  0.083081  0.018443   
Age                0.034212 -0.064910 -0.339898  1.000000 -0.233296 -0.172482   
SibSp             -0.057527 -0.035322  0.083081 -0.233296  1.000000  0.414838   
Parch             -0.001652  0.081629  0.018443 -0.172482  0.414838  1.000000   
Fare               0.012658  0.257307 -0.549500  0.096688  0.159651  0.216225   
... [Data Truncated for Readability]
Enter fullscreen mode Exit fullscreen mode

Now, we have to find the relationship between Pclass, Fare, Age and Survived Syntax-> df[["col1", "col2", "col3"]].corr() It will create a matrix where each variable is compared with every other variable.

df[["Pclass", "Fare", "Age", "Survived"]].corr()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

            Pclass      Fare       Age  Survived
Pclass    1.000000 -0.549500 -0.339898 -0.338481
Fare     -0.549500  1.000000  0.096688  0.257307
Age      -0.339898  0.096688  1.000000 -0.064910
Survived -0.338481  0.257307 -0.064910  1.000000
Enter fullscreen mode Exit fullscreen mode

pd.crosstab() creates a frequency table showing the relationship between categorical variables. The core difference is that correlation measures the mathematical direction and strength of a relationship between numbers, while a crosstab counts frequencies and overlaps between categories. Syntax-> pd.crosstab(df["row_column"], df["column_column"])

pd.crosstab(df["Sex"], df["Survived"])
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

Survived    0    1
Sex               
Female     81  233
Male      468  109
Enter fullscreen mode Exit fullscreen mode

Now, we are going to create a percentage crosstab using normalize = "index" which convert raw counts to row-wise percentages (proportions). Syntax-> pd.crosstab( df["row_column"], df["column_column"], normalize="index" ) * 100

pd.crosstab(
    df["Sex"],
    df["Survived"],
    normalize="index"
) * 100
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

Survived          0          1
Sex                           
Female    25.796178  74.203822
Male      81.109185  18.890815
Enter fullscreen mode Exit fullscreen mode

Now, we are going to create the crosstable for passenger class vs survival status. OR pd.crosstab( df["Pclass"], df["Survived"] )

pd.crosstab(
    df["Pclass"],
    df["Survival_Status"]
)
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

Survival_Status  Did Not Survive  Survived
Pclass                                    
1                             80       136
2                             97        87
3                            372       119
Enter fullscreen mode Exit fullscreen mode

Part I-> Pivot Tables

Pivot table-> It is basically a summarized table that groups data acros multiple dimensions. pd.pivot_table() creates a summarized table by specifying index, columns, values and aggfunc. Syntax -> pd.pivot_table( df, # Define the dataset first to use for create table. index="row_group", columns="column_group", values="value_column", aggfunc="mean" )

pd.pivot_table(
    df,
    index="Pclass",
    columns="Sex",
    values="Survived",
    aggfunc="mean"
)
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

Sex       Female      Male
Pclass                    
1       0.968085  0.368852
2       0.921053  0.157407
3       0.500000  0.135447
Enter fullscreen mode Exit fullscreen mode

Now, we have to create a pivot table showing the average fare by passenger class and sex.

pd.pivot_table(
    df,
    index="Pclass",
    columns="Sex",
    values="Fare",
    aggfunc="mean"
)
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

Sex         Female       Male
Pclass                       
1       106.125798  67.226127
2        21.970121  19.741782
3        16.118810  12.661633
Enter fullscreen mode Exit fullscreen mode

Now, we are creating a pivot table showing the average age by passenger class and sex.

pd.pivot_table(
    df,
    index="Pclass",
    columns="Sex",
    values="Age",
    aggfunc="mean"
)
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

Sex        Female       Male
Pclass                      
1       33.978723  38.995246
2       28.703947  30.512315
3       23.572917  26.911873
Enter fullscreen mode Exit fullscreen mode

Now, we are going to create a pivot table showing the number of passengers by class and sex.

pd.pivot_table(
    df, 
    index="Pclass",
    columns="Sex",
    aggfunc="size"
)
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

Sex     Female  Male
Pclass              
1           94   122
2           76   108
3          144   347
Enter fullscreen mode Exit fullscreen mode

Now, we are creating the pivot table showing the mean, median, and maximum fare according to the passenger class and sex. Syntax-> pd.pivot_table( df, index="group1", columns="group2", values="column", aggfunc=["function1", "function2", "function3"] )

pd.pivot_table(
    df,
    index="Pclass",
    columns="Sex",
    values="Fare",
    aggfunc=["mean", "median", "max"]
)
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

              mean               median                max          
Sex         Female       Male    Female     Male    Female      Male
Pclass                                                              
1       106.125798  67.226127  82.66455  41.2625  512.3292  512.3292
2        21.970121  19.741782  22.00000  13.0000   65.0000   73.5000
3        16.118810  12.661633  12.47500   7.9250   69.5500   69.5500
Enter fullscreen mode Exit fullscreen mode

margins=True adds on All rows and/or column containing the overall aggregate. Basically, margins shows the mean(average) of those entire groups or rows because aggfunc = mean.

pd.pivot_table(
    df,
    index="Pclass",
    columns="Sex",
    values="Survived",
    aggfunc="mean",
    margins=True
)
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

Sex       Female      Male       All
Pclass                              
1       0.968085  0.368852  0.629630
2       0.921053  0.157407  0.472826
3       0.500000  0.135447  0.242363
All     0.742038  0.188908  0.383838
Enter fullscreen mode Exit fullscreen mode

Now, we are going to compare the survival rates of male and female passengers across the three passenger classes.

survival_pivot = pd.pivot_table(
    df, 
    index="Pclass",
    columns="Sex",
    values="Survived",
    aggfunc="mean"
)
survival_pivot
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

Sex       Female      Male
Pclass                    
1       0.968085  0.368852
2       0.921053  0.157407
3       0.500000  0.135447
Enter fullscreen mode Exit fullscreen mode

groupby() vs pivot_table() groupby-> df.groupby(["Pclass", "Sex"])["Survived"].mean(), produces grouped results, often with a MultiIndex. pivot_table-> pd.pivot_table( df, index="Pclass", columns="Sex", values="Survived", aggfunc="mean" ) Produces a more table/matrix-like layout. Both can answer similar analytical questions, but pivot tables are particularly convenient when you want one grouping variable as rows and another as columns.

Three Final Observations

Survival by Sex Survival by Pclass Average Fare by Pclass

df.groupby("Sex")["Survived"].mean() * 100

df.groupby("Pclass")["Survived"].mean() * 100

df.groupby("Pclass")["Fare"].mean()
Enter fullscreen mode Exit fullscreen mode

👀 Click to view output

Pclass
1    84.154687
2    20.662183
3    13.675550
Name: Fare, dtype: float64
Enter fullscreen mode Exit fullscreen mode

🚀 Wrapping Up

And there you have it! We've taken a raw CSV file and turned it into actionable insights using nothing but Python and Pandas. We learned how to:

  • Load & Inspect data effortlessly.
  • Slice & Filter to find exactly what we need.
  • Clean the inevitable mess of real-world datasets.
  • Aggregate & Sort to reveal hidden trends.

[!TIP]
Keep Practicing! The best way to master Pandas is to find a dataset you are passionate about (sports, gaming, finance) and start exploring. The sky is the limit!

Happy Coding! 🎈

Top comments (0)