DataFrame Basics
A DataFrame is pandas' two-dimensional, labeled data structure: conceptually a dictionary of Series that all share the same row index, presented as a table with rows and named columns. Each column can hold a different dtype — one might be int64, another object (strings), another datetime64[ns] — which is precisely what lets a single DataFrame represent a realistic dataset like a table of customer orders with an ID, a name, an order date, and a total price. Because a DataFrame is built from NumPy arrays under the hood, numeric columns retain NumPy's speed for vectorized computation, while the labeling layer on top provides intuitive, name-based selection and automatic alignment.
Cricket analogy: A DataFrame of a match scorecard is like a shared row index across columns for 'player' (object), 'runs' (int64), and 'strike_rate' (float64) — each column keeps its own type, but a numeric column still computes at NumPy speed under the hood.
Constructing a DataFrame
The most common construction pattern is pd.DataFrame(dict_of_lists), where dictionary keys become column names and each list becomes a column's values; all lists must be the same length. A DataFrame can also be built from a list of dictionaries (one dict per row), a 2-D NumPy array plus explicit columns= and index= arguments, or by reading directly from a file with functions like pd.read_csv. Regardless of construction method, every DataFrame has a .index (row labels), .columns (column labels), .dtypes (per-column type), and .shape (rows, columns) that together describe its structure.
Cricket analogy: pd.DataFrame({'player':[...], 'runs':[...]}) turns a dict of lists into a scorecard where keys become column headers; you could also build it from a list of per-innings dicts or read straight from a CSV, and every scorecard exposes .index, .columns, .dtypes, and .shape.
import pandas as pd
orders = pd.DataFrame({
'order_id': [1001, 1002, 1003],
'customer': ['Ada', 'Grace', 'Alan'],
'total': [59.99, 124.50, 18.25],
})
print(orders.shape) # (3, 3)
print(orders.columns) # Index(['order_id', 'customer', 'total'], dtype='object')
print(orders.dtypes)
# order_id int64
# customer object
# total float64
# dtype: object
print(orders.head(2)) # first 2 rows
print(orders.describe()) # summary statistics for numeric columns
Selecting Rows and Columns
Selecting a single column with df['col'] returns a Series; selecting a list of columns with df[['col1', 'col2']] returns a DataFrame, even if the list has only one element — the double brackets matter. Row selection by label uses .loc, and by integer position uses .iloc; both accept slices, single labels/positions, or boolean arrays, and both can select rows and columns simultaneously in one call, e.g. df.loc[0:2, ['customer', 'total']].
Cricket analogy: df['runs'] returns a bare Series of scores, but df[['runs']] returns a one-column DataFrame — the brackets matter; df.loc[0:2, ['player','runs']] grabs the first three innings by label, while df.iloc[0:2] grabs them by raw position instead.
A helpful habit when exploring an unfamiliar DataFrame: run .info() first to see column dtypes and non-null counts in one glance, then .describe() for numeric summaries, before diving into any transformation — this quickly surfaces missing values or unexpected dtypes.
df['col'] and df[['col']] look similar but return different types — a Series versus a single-column DataFrame — which changes what methods and shapes are available downstream; mixing them up is a frequent source of confusing shape errors.
- A DataFrame is a table of columns (each a Series) sharing a common row index.
- Columns can hold different dtypes; use
.dtypesto inspect them and.info()for a fuller overview. df['col']returns a Series;df[['col']]returns a single-column DataFrame — the brackets matter..locselects by label,.ilocselects by integer position; both can combine row and column selection..shape,.columns, and.indexdescribe a DataFrame's structural metadata.- DataFrames can be built from dicts of lists, lists of dicts, NumPy arrays, or read directly from files.
Practice what you learned
1. What is returned by `df[['total']]` (with double brackets) on a DataFrame with a 'total' column?
2. Which attribute gives the (rows, columns) dimensions of a DataFrame?
3. Which accessor selects DataFrame rows and columns by integer position rather than label?
4. What is the quickest way to see each column's dtype and count of non-null values at once?
5. When constructing `pd.DataFrame({'a': [1,2,3], 'b': [4,5,6]})`, what do the dictionary keys become?
Was this page helpful?
You May Also Like
What Is Pandas?
An introduction to pandas, the Python library built on NumPy for labeled, tabular data manipulation, covering its core data structures and why it dominates real-world data analysis.
Series Basics
Learn the anatomy of a pandas Series — its values, index, and name — and how label-based alignment, vectorized operations, and dtype inference make it more than a plain list.
Indexing with loc and iloc
Master the two primary pandas selection accessors — .loc for label-based indexing and .iloc for integer-position-based indexing — and the pitfalls of chained indexing.
Boolean Filtering
Learn how to filter DataFrame rows using boolean masks built from comparison and logical operators, including combining conditions and using isin and query for cleaner syntax.