Lists of lists are a natural format for row-oriented data in Python. Each inner list represents one complete record, making this structure common when reading parsed files, collecting results in loops, or receiving data from functions that return rows. Pandas converts this format directly into a DataFrame, with each inner list becoming a row in the resulting table.
In this guide, you will learn how to create DataFrames from lists of lists, add column names and custom indices, handle missing data and unequal row lengths, and deal with column-oriented lists.
Standard Row-Wise Conversion
Pass the list of lists to pd.DataFrame() and specify column names with the columns parameter:
import pandas as pd
data = [
['Apple', 999, 10],
['Samsung', 899, 20],
['Google', 799, 15]
]
df = pd.DataFrame(data, columns=['Brand', 'Price', 'Qty'])
print(df)Output:
Brand Price Qty
0 Apple 999 10
1 Samsung 899 20
2 Google 799 15Each inner list maps to one row, and the columns parameter assigns meaningful names instead of the default numeric headers (0, 1, 2).
Adding a Custom Index
Replace the default sequential index with custom labels using the index parameter:
import pandas as pd
data = [
['Alice', 88],
['Bob', 92],
['Charlie', 78]
]
df = pd.DataFrame(
data,
columns=['Name', 'Score'],
index=['s1', 's2', 's3']
)
print(df)Output:
Name Score
s1 Alice 88
s2 Bob 92
s3 Charlie 78Custom indices let you access rows by meaningful labels (e.g., df.loc['s1']) rather than by position.
Handling Missing Data
When inner lists contain None, Pandas automatically converts them to NaN:
import pandas as pd
data = [
['Project A', 5000],
['Project B', None],
['Project C', 3000]
]
df = pd.DataFrame(data, columns=['Name', 'Budget'])
print(df)
print()
print(df.dtypes)Output:
Name Budget
0 Project A 5000.0
1 Project B NaN
2 Project C 3000.0
Name object
Budget float64
dtype: objectTransposing When Lists Represent Columns
Sometimes each inner list represents a column's values rather than a row's values. Use .T (transpose) to swap rows and columns:
import pandas as pd
data = [
[100, 200, 300], # Sales values
[10, 20, 30] # Units values
]
df = pd.DataFrame(data).T
df.columns = ['Sales', 'Units']
print(df)Output:
Sales Units
0 100 10
1 200 20
2 300 30Without the transpose, you would get 2 rows and 3 columns instead of the intended 3 rows and 2 columns.
Handling Ragged Lists (Unequal Lengths)
When inner lists have different numbers of elements, Pandas pads shorter rows with None to create a rectangular DataFrame:
import pandas as pd
data = [
['A', 1, 2],
['B', 3], # Missing one element
['C', 4, 5, 6] # One extra element
]
df = pd.DataFrame(data)
print(df)Output:
0 1 2 3
0 A 1 2.0 NaN
1 B 3 NaN NaN
2 C 4 5.0 6.0The resulting DataFrame has 4 columns to accommodate the longest list. Shorter lists have NaN or None in the trailing positions.
Specifying Data Types After Creation
Pandas infers data types automatically, but you often need to convert columns to more appropriate types:
import pandas as pd
data = [
['2024-01-01', 100],
['2024-01-02', 200]
]
df = pd.DataFrame(data, columns=['Date', 'Value'])
df['Date'] = pd.to_datetime(df['Date'])
df['Value'] = df['Value'].astype('Int64')
print(df.dtypes)Output:
Date datetime64[ns]
Value Int64
dtype: objectPractical Example: Building a DataFrame from Computed Results
A common pattern is generating rows in a loop and collecting them as a list of lists:
import pandas as pd
results = []
for i in range(1, 6):
name = f"Item_{i}"
price = i * 10.5
in_stock = i % 2 == 0
results.append([name, price, in_stock])
df = pd.DataFrame(results, columns=['Product', 'Price', 'In_Stock'])
print(df)Output:
Product Price In_Stock
0 Item_1 10.5 False
1 Item_2 21.0 True
2 Item_3 31.5 False
3 Item_4 42.0 True
4 Item_5 52.5 FalseQuick Reference
| Goal | Method |
|---|---|
| Standard creation | pd.DataFrame(data, columns=['X', 'Y']) |
| With custom index | pd.DataFrame(data, columns=[...], index=[...]) |
| Transpose | pd.DataFrame(data).T |
| Handle None | Automatic conversion to NaN |
| Nullable integers | .astype('Int64') after creation |
- Lists of lists are ideal for row-by-row data such as parsed logs, query results, or computed records.
- Always specify the
columnsparameter for immediate readability. - Use
.Tif your inner lists represent columns rather than rows. - Be aware that
Nonevalues convert toNaNand may cause integer columns to become floats unless you use the nullableInt64type.