Skip to main content

Python Pandas: How to Select the Last Row in Pandas

Finding the last entry in a dataset is a common requirement-whether you're checking the latest timestamp, the most recent transaction, or simply the final record. Pandas offers several approaches, each suited to different needs.

Using iloc: the Standard Way

Python's negative indexing makes selecting the last row intuitive with iloc[-1]:

import pandas as pd

df = pd.DataFrame({
'Name': ['Alice', 'Bob', 'Charlie'],
'Age': [25, 30, 35]
})

last_row = df.iloc[-1]
print(last_row)

Output:

Name    Charlie
Age 35
Name: 2, dtype: object
note

iloc[-1] returns a Series, where column names become the index. This is ideal when you need to access individual values by column name: last_row['Name'].

Accessing Values from the Result

import pandas as pd

df = pd.DataFrame({
'Name': ['Alice', 'Bob', 'Charlie'],
'Age': [25, 30, 35],
'City': ['NYC', 'LA', 'Chicago']
})

last_row = df.iloc[-1]

# Access by column name
print(last_row['Name']) # Charlie
print(last_row['Age']) # 35

# Access by position
print(last_row.iloc[0]) # Charlie

Using tail(): Keep DataFrame Structure

When you need to preserve the DataFrame format (headers, structure, ability to chain operations), use tail(1):

import pandas as pd

df = pd.DataFrame({
'Name': ['Alice', 'Bob', 'Charlie'],
'Age': [25, 30, 35]
})

last_row_df = df.tail(1)
print(last_row_df)

Output:

      Name  Age
2 Charlie 35

When to Use tail()

import pandas as pd

df = pd.DataFrame({
'Name': ['Alice', 'Bob', 'Charlie'],
'Score': [85, 92, 78]
})

# Chaining operations
result = df.tail(1).to_dict('records')[0]
print(result)
# {'Name': 'Charlie', 'Score': 78}

# Concatenating with other DataFrames
first_and_last = pd.concat([df.head(1), df.tail(1)])
print(first_and_last)
# Name Score
# 0 Alice 85
# 2 Charlie 78

Output:

{'Name': 'Charlie', 'Score': 78}
Name Score
0 Alice 85
2 Charlie 78

Using iat: Maximum Performance

For extracting a single cell value, iat provides the fastest access:

import pandas as pd

df = pd.DataFrame({
'Name': ['Alice', 'Bob', 'Charlie'],
'Age': [25, 30, 35]
})

# Get value at [last row, first column]
last_name = df.iat[-1, 0]
print(last_name) # Charlie

# Get last age
last_age = df.iat[-1, 1]
print(last_age) # 35

Output:

Charlie
35

Performance Comparison

import pandas as pd
import timeit

df = pd.DataFrame({'A': range(100000), 'B': range(100000)})

# Compare access methods
iloc_time = timeit.timeit(lambda: df.iloc[-1, 0], number=10000)
iat_time = timeit.timeit(lambda: df.iat[-1, 0], number=10000)

print(f"iloc: {iloc_time:.4f}s")
print(f"iat: {iat_time:.4f}s")
print(f"iat is {iloc_time/iat_time:.1f}x faster")

Output:

iloc: 0.2265s
iat: 0.1475s
iat is 1.5x faster
tip

Use iat inside loops or performance-critical code where you need a single value. For general exploration, iloc is more readable and flexible.

Edge Cases

Empty DataFrame

import pandas as pd

df = pd.DataFrame(columns=['Name', 'Age'])

# iloc raises IndexError on empty DataFrame
try:
last_row = df.iloc[-1]
except IndexError:
print("DataFrame is empty!")

# tail returns empty DataFrame (no error)
last_row_df = df.tail(1)
print(f"Empty check: {last_row_df.empty}") # True

# Safe pattern
if not df.empty:
last_row = df.iloc[-1]
else:
last_row = None

Output:

DataFrame is empty!
Empty check: True

Single Row DataFrame

import pandas as pd

df = pd.DataFrame({'Name': ['Alice'], 'Age': [25]})

# All methods work the same
print(df.iloc[-1]['Name']) # Alice
print(df.tail(1)) # DataFrame with one row
print(df.iat[-1, 0]) # Alice

Output:

Alice
Name Age
0 Alice 25
Alice

Getting Multiple Last Rows

For more than just the last row:

import pandas as pd

df = pd.DataFrame({
'Date': pd.date_range('2024-01-01', periods=10),
'Value': range(10)
})

# Last 3 rows
print(df.tail(3))

# Last 3 rows using iloc
print(df.iloc[-3:])

# Every other row from the last 6
print(df.iloc[-6::2])

Output:

        Date  Value
7 2024-01-08 7
8 2024-01-09 8
9 2024-01-10 9
Date Value
7 2024-01-08 7
8 2024-01-09 8
9 2024-01-10 9
Date Value
4 2024-01-05 4
6 2024-01-07 6
8 2024-01-09 8

Practical Example: Time Series

import pandas as pd

# Stock price data
df = pd.DataFrame({
'Date': pd.date_range('2024-01-01', periods=5),
'Price': [100, 102, 99, 105, 103],
'Volume': [1000, 1200, 800, 1500, 1100]
})

# Get latest data point
latest = df.iloc[-1]
print(f"Latest price: ${latest['Price']} on {latest['Date'].date()}")

# Calculate change from previous
previous = df.iloc[-2]
change = latest['Price'] - previous['Price']
print(f"Change: ${change:+.2f}")

Output:

Latest price: $103 on 2024-01-05
Change: $-2.00

Quick Reference

MethodReturnsBest For
df.iloc[-1]SeriesGeneral access to last row
df.tail(1)DataFramePreserving structure, chaining
df.iat[-1, col]ScalarSingle value, maximum speed
df.iloc[-n:]DataFrameLast n rows

Summary

  • Use df.iloc[-1] for standard last-row access-it returns a Series that's easy to work with.
  • Use df.tail(1) when you need to maintain DataFrame structure for further operations.
  • Use df.iat[-1, col] in performance-critical scenarios where you only need a single cell value.

Always check for empty DataFrames before accessing with iloc to avoid IndexError.