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
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
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
| Method | Returns | Best For |
|---|---|---|
df.iloc[-1] | Series | General access to last row |
df.tail(1) | DataFrame | Preserving structure, chaining |
df.iat[-1, col] | Scalar | Single value, maximum speed |
df.iloc[-n:] | DataFrame | Last 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.