Mathew K Analytics

Lesson 41 · Mastering Pandas

Master Pandas Series: Advanced Slicing & Cross-Section Techniques for Data Analysis

In this lesson, we will master advanced slicing, cross-section selection, and multi-index tricks with pandas DataFrames. These skills help you analyze…

⬇ Download notebookOpen in Colab ↗

📓 Full notebook

Download .ipynb

Advanced Slicing and Cross-Section Operations in Pandas#

In this lesson, we will master advanced slicing, cross-section selection, and multi-index tricks with pandas DataFrames.

These skills help you analyze complex, real-world tables confidently.

import warnings
import numpy as np
np.random.seed(42)
warnings.filterwarnings('ignore')

Data setup (Gapminder Dataset)#

We will use the Gapminder dataset, which contains life expectancy, GDP, and population for countries over time.

import plotly.express as px
df = px.data.gapminder()
print(df.shape)
print(df.head(3))
(1704, 8)
       country continent  year  lifeExp       pop   gdpPercap iso_alpha  \
0  Afghanistan      Asia  1952   28.801   8425333  779.445314       AFG   
1  Afghanistan      Asia  1957   30.332   9240934  820.853030       AFG   
2  Afghanistan      Asia  1962   31.997  10267083  853.100710       AFG   

   iso_num  
0        4  
1        4  
2        4  
# Let's check our columns and a sample.
print(df.columns)
print(df.sample(2))
Index(['country', 'continent', 'year', 'lifeExp', 'pop', 'gdpPercap',
       'iso_alpha', 'iso_num'],
      dtype='object')
      country continent  year  lifeExp       pop    gdpPercap iso_alpha  \
1046  Myanmar      Asia  1962   45.108  23634436   388.000000       MMR   
745   Ireland    Europe  1957   68.900   2878220  5599.077872       IRL   

      iso_num  
1046      104  
745       372  

Reminder: Basic Slicing#

You can use square brackets to select columns or rows, and use .loc for label-based access and .iloc for integer-based access.

# Let's see how to slice rows and columns by index.
first_rows = df.iloc[:5, :3]
print(first_rows)
       country continent  year
0  Afghanistan      Asia  1952
1  Afghanistan      Asia  1957
2  Afghanistan      Asia  1962
3  Afghanistan      Asia  1967
4  Afghanistan      Asia  1972

Advanced Row Selection: Slicing with Conditions#

Let us pick all rows for a single country (for example, Canada) and look at their time evolution.

# Select all data for Canada only
canada_df = df[df['country'] == 'Canada']
print(canada_df.head())
    country continent  year  lifeExp       pop    gdpPercap iso_alpha  iso_num
240  Canada  Americas  1952    68.75  14785584  11367.16112       CAN      124
241  Canada  Americas  1957    69.96  17010154  12489.95006       CAN      124
242  Canada  Americas  1962    71.30  18985849  13462.48555       CAN      124
243  Canada  Americas  1967    72.13  20819767  16076.58803       CAN      124
244  Canada  Americas  1972    72.88  22284500  18970.57086       CAN      124
# Let's visualize how Canada's life expectancy has changed.
import matplotlib.pyplot as plt
plt.figure(figsize=(6,3))
plt.plot(canada_df['year'], canada_df['lifeExp'])
plt.xlabel('Year')
plt.ylabel('Life Expectancy')
plt.title("Canada's Life Expectancy Over Time")
plt.show()
No description has been provided for this image

MultiIndex for Powerful Row and Column Slicing#

The Gapminder data can be enhanced with a MultiIndex.

A MultiIndex lets you slice and dice by more than one column, for example country and year.

# Set country and year as MultiIndex
df_multi = df.set_index(['country', 'year']).sort_index()
print(df_multi.head(6))
                 continent  lifeExp       pop   gdpPercap iso_alpha  iso_num
country     year                                                            
Afghanistan 1952      Asia   28.801   8425333  779.445314       AFG        4
            1957      Asia   30.332   9240934  820.853030       AFG        4
            1962      Asia   31.997  10267083  853.100710       AFG        4
            1967      Asia   34.020  11537966  836.197138       AFG        4
            1972      Asia   36.088  13079460  739.981106       AFG        4
            1977      Asia   38.438  14880372  786.113360       AFG        4
# Slice all years for 'Canada' quickly
canada_all_years = df_multi.loc['Canada']
print(canada_all_years.head())
     continent  lifeExp       pop    gdpPercap iso_alpha  iso_num
year                                                             
1952  Americas    68.75  14785584  11367.16112       CAN      124
1957  Americas    69.96  17010154  12489.95006       CAN      124
1962  Americas    71.30  18985849  13462.48555       CAN      124
1967  Americas    72.13  20819767  16076.58803       CAN      124
1972  Americas    72.88  22284500  18970.57086       CAN      124
# Slice a range for both country and year
years = slice(1977, 2002)
sub = df_multi.loc[('Canada', years), ['lifeExp', 'gdpPercap']]
print(sub)
              lifeExp    gdpPercap
country year                      
Canada  1977    74.21  22090.88306
        1982    75.76  22898.79214
        1987    76.86  26626.51503
        1992    77.95  26342.88426
        1997    78.61  28954.92589
        2002    79.77  33328.96507

Cross-section with .xs()#

The .xs() function makes it easy to get all rows at a specific inner level across the index.

# Get all countries' statistics in 2002 as a cross-section
stats_2002 = df_multi.xs(2002, level='year')
print(stats_2002.head())
            continent  lifeExp       pop    gdpPercap iso_alpha  iso_num
country                                                                 
Afghanistan      Asia   42.129  25268405   726.734055       AFG        4
Albania        Europe   75.651   3508512  4604.211737       ALB        8
Algeria        Africa   70.994  31287142  5288.040382       DZA       12
Angola         Africa   41.003  10866106  2773.287312       AGO       24
Argentina    Americas   74.340  38331121  8797.640716       ARG       32
# You can do .xs() for countries too.
canada_xs = df_multi.xs('Canada', level='country')
print(canada_xs.head())
     continent  lifeExp       pop    gdpPercap iso_alpha  iso_num
year                                                             
1952  Americas    68.75  14785584  11367.16112       CAN      124
1957  Americas    69.96  17010154  12489.95006       CAN      124
1962  Americas    71.30  18985849  13462.48555       CAN      124
1967  Americas    72.13  20819767  16076.58803       CAN      124
1972  Americas    72.88  22284500  18970.57086       CAN      124

Slicing Ranges with MultiIndex#

You can slice ranges of outer levels by tuples.

For example, select all American countries between 1987 and 1992.

# Find the American countries
americas = df['continent'] == 'Americas'
countries = df.loc[americas, 'country'].unique()

# Get their data in MultiIndex for years 1987-1992
americas_slice = df_multi.loc[(countries, slice(1987, 1992)), :]
print(americas_slice.head())
               continent  lifeExp        pop    gdpPercap iso_alpha  iso_num
country   year                                                              
Argentina 1987  Americas   70.774   31620918  9139.671389       ARG       32
          1992  Americas   71.868   33958947  9308.418710       ARG       32
Bolivia   1987  Americas   57.251    6156369  2753.691490       BOL       68
          1992  Americas   59.957    6893451  2961.699694       BOL       68
Brazil    1987  Americas   65.205  142938076  7807.095818       BRA       76
# Quick analysis: see mean GDP per capita for those years and countries.
result = americas_slice.groupby('country')['gdpPercap'].mean().sort_values(ascending=False)
print(result.head())
country
United States    30944.141325
Canada           26484.699645
Puerto Rico      13461.464510
Venezuela        10308.755479
Argentina         9224.045050
Name: gdpPercap, dtype: float64

Cross-Section Slicing: Columns Too#

Pandas allows you to select ranges or sets of columns along with MultiIndex rows.

# Pick just population and lifeExp for the same slice
cols = ['pop', 'lifeExp']
americas_basic = americas_slice[cols]
print(americas_basic.head())
                      pop  lifeExp
country   year                    
Argentina 1987   31620918   70.774
          1992   33958947   71.868
Bolivia   1987    6156369   57.251
          1992    6893451   59.957
Brazil    1987  142938076   65.205
# Resetting index: go back to flat DataFrame for charting or export
flat = americas_basic.reset_index()
print(flat.head())
     country  year        pop  lifeExp
0  Argentina  1987   31620918   70.774
1  Argentina  1992   33958947   71.868
2    Bolivia  1987    6156369   57.251
3    Bolivia  1992    6893451   59.957
4     Brazil  1987  142938076   65.205

Mini-Project: Cross-Section Analysis on Gapminder#

Pick any two countries and compare their life expectancy over time using MultiIndex slicing.

# Select countries
c1 = input("Enter the first country: ")
c2 = input("Enter the second country: ")
subset = df_multi.loc[([c1, c2], slice(None)), 'lifeExp'].reset_index()
plt.figure(figsize=(8,4))
for country in [c1, c2]:
    plt.plot(subset[subset['country'] == country]['year'], subset[subset['country'] == country]['lifeExp'], label=country)
plt.xlabel('Year')
plt.ylabel('Life Expectancy')
plt.title('Country Comparison: Life Expectancy Over Time')
plt.legend()
plt.show()
No description has been provided for this image

Best Practices for Advanced Slicing#

  • Use MultiIndex only if needed for complex grouping.
  • Document your slices with comments for clarity.
  • Reset indices when exporting for others.
  • Test your edge cases (empty slices, missing labels).
# Troubleshooting: KeyError when slicing MultiIndex
try:
    missing = df_multi.loc[('Atlantis', 1999)]
except KeyError:
    print("KeyError: That combination does not exist!")
    
KeyError: That combination does not exist!

Challenge#

Try selecting all African countries for any one year, sort by life expectancy, and plot the results.

Recap: What Did You Learn?#

  • Slicing by rows and columns with loc and iloc.
  • Creating and using MultiIndex for advanced cross-sectioning.
  • Analyzing complex data slices over levels.

Keep practicing advanced slicing for real-world data!

Subscribe for more pandas mastery tutorials!#

Comment below with your favorite slicing trick or topics you want next.

Found this useful?

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