Read shape, size and ndim without parentheses. Inspect table dimensions, distinguish cells from known values and reconcile rows after cleaning.
shape, size and ndim are attributes: use them without parentheses. Attributes and indexers have a different colour from function calls in the examples.
import pandas as pd
from io import StringIO, BytesIO
students = pd.DataFrame({
"NAME": ["Ravi", "Raju", "Alex", "Ron", "King", "Jack"],
"ID": [1, 2, 3, 4, 5, 6],
"MATH": [80, 40, 70, 70, 70, 30],
"ENGLISH": [80, 70, 40, 50, 60, 30]
})
print(students)Expected output
NAME ID MATH ENGLISH
0 Ravi 1 80 80
1 Raju 2 40 70
2 Alex 3 70 40
3 Ron 4 70 50
4 King 5 70 60
5 Jack 6 30 30DataFrame.shape returns a tuple in the order rows, columns. It describes the current table, including rows with missing data.
print(students.shape)
print("Rows:", students.shape[0])
print("Columns:", students.shape[1])
rows, columns = students.shape
assert (rows, columns) == (6, 4)Expected output
(6, 4)
Rows: 6
Columns: 4size equals rows multiplied by columns. It includes missing cells; it is neither a count of known values nor a memory measurement.
print("Cells:", students.size)
print("Rows times columns:", students.shape[0] * students.shape[1])
assert students.size == 24Expected output
Cells: 24
Rows times columns: 24A DataFrame has ndim=2 even with one column. A Series has ndim=1. Selecting with a list keeps a DataFrame; selecting one column label returns a Series.
series = students["NAME"]
one_column = students[["NAME"]]
print("DataFrame ndim:", students.ndim)
print("Series shape/ndim:", series.shape, series.ndim)
print("One-column DataFrame shape/ndim:", one_column.shape, one_column.ndim)
assert series.shape == (6,)
assert one_column.shape == (6, 1)Expected output
DataFrame ndim: 2
Series shape/ndim: (6,) 1
One-column DataFrame shape/ndim: (6, 1) 2Use count() for known values and isna() for missing cells. An all-missing table still has rows, columns and cells.
table = pd.DataFrame({"A": [1.0, None, 3.0], "B": [None, 2.0, 4.0]})
known = int(table.count().sum())
missing = int(table.isna().sum().sum())
print("Shape:", table.shape, "Cells:", table.size)
print("Known cells:", known, "Missing cells:", missing)
assert table.size == known + missing == 6Expected output
Shape: (3, 2) Cells: 6
Known cells: 4 Missing cells: 2A table is empty when either axis has length zero. A table filled with missing values is not empty. Check the required columns and row count separately when validating a dataset.
for label, table in [
("No rows", pd.DataFrame(columns=["A", "B"])),
("No columns", pd.DataFrame(index=[0, 1, 2])),
("Missing values", pd.DataFrame({"A": [None, None]}))
]:
print(label, "shape:", table.shape, "size:", table.size, "ndim:", table.ndim, "empty:", table.empty)
assert not pd.DataFrame({"A": [None, None]}).emptyExpected output
No rows shape: (0, 2) size: 0 ndim: 2 empty: True
No columns shape: (3, 0) size: 0 ndim: 2 empty: True
Missing values shape: (2, 1) size: 2 ndim: 2 empty: FalseThe index is not counted as a data column or included in size. set_index() with drop=True reduces the column count. reset_index() can restore it.
indexed = students.set_index("ID")
print("Before:", students.shape, students.size)
print("After:", indexed.shape, indexed.size)
assert indexed.shape == (6, 3)
assert indexed.reset_index().shape == students.shapeExpected output
Before: (6, 4) 24
After: (6, 3) 18Each selection has its own dimensions. A preview describes only the preview; use the full table for total row counts. See head(), tail() and loc.
selected = students.loc[students["MATH"].ge(70), ["ID", "NAME", "MATH"]]
print(selected)
print("Selected shape:", selected.shape)
print("Preview shape:", selected.head(2).shape)
assert selected.shape == (4, 3)Expected output
ID NAME MATH
0 1 Ravi 80
2 3 Alex 70
3 4 Ron 70
4 5 King 70
Selected shape: (4, 3)
Preview shape: (2, 3)The embedded file runs without uploading anything. Replace StringIO(csv_text) with your file path or URL. Real dimensions depend on the actual file and reader options; do not assume a fixed row count. See read_csv().
csv_text = students.to_csv(index=False)
from_csv = pd.read_csv(StringIO(csv_text))
print("Rows:", from_csv.shape[0], "Columns:", from_csv.shape[1])
print("Cells:", from_csv.size, "Dimensions:", from_csv.ndim)
assert from_csv.shape == (6, 4)Expected output
Rows: 6 Columns: 4
Cells: 24 Dimensions: 2This in-memory workbook uses the same read_excel() call as an uploaded workbook. Excel needs an engine such as openpyxl; the example lets other cells continue if it is unavailable. See read_excel() and to_excel().
try:
import openpyxl
except ImportError:
print("Excel example requires openpyxl; continue with the CSV examples.")
else:
workbook = BytesIO()
students.to_excel(workbook, index=False, engine="openpyxl")
workbook.seek(0)
from_excel = pd.read_excel(workbook, engine="openpyxl")
print("Shape:", from_excel.shape, "Size:", from_excel.size, "ndim:", from_excel.ndim)
assert from_excel.shape == (6, 4)Expected output
Shape: (6, 4) Size: 24 ndim: 2memory_usage() reports memory rather than cell count. deep=True also inspects object data. The result depends on dtypes and content, so equal shapes can use different amounts of memory. See info() and dtypes.
usage = students.memory_usage(index=True, deep=True)
print("Memory bytes:", int(usage.sum()))
print("Cells:", students.size)
assert usage.sum() > 0Expected output
Memory bytes: 593
Cells: 24This synthetic sales table requires three named columns and at least one row. Shape alone cannot prove that the correct fields or values were loaded. Use columns to check labels and Data Cleaning to validate values.
sales = pd.DataFrame({
"order_id": [101, 102, 103, 104, 105],
"product": ["Pen", "Book", "Notebook", "Bag", "Pencil"],
"amount": ["5", "20", "bad", None, "8"]
})
required = {"order_id", "product", "amount"}
missing_columns = required - set(sales.columns)
if missing_columns:
raise ValueError("Missing columns: " + str(sorted(missing_columns)))
if sales.shape[0] == 0:
raise ValueError("No sales rows")
print("Imported shape:", sales.shape)
print("Required columns present:", not missing_columns)
assert sales.shape == (5, 3)Expected output
Imported shape: (5, 3)
Required columns present: TrueConvert amounts under a defined rule and keep unknown values in review. A smaller accepted table is expected, but every source row should be accounted for. More rows do not automatically mean more valid orders.
amount = pd.to_numeric(sales["amount"], errors="coerce")
review = sales.loc[amount.isna()].copy()
accepted = sales.loc[amount.notna()].copy()
accepted["amount"] = amount.loc[accepted.index]
print("Source:", sales.shape, "Accepted:", accepted.shape, "Review:", review.shape)
print("Review IDs:", review["order_id"].tolist())
print("Known revenue:", accepted["amount"].sum())
assert accepted.shape[0] + review.shape[0] == sales.shape[0]
assert accepted["amount"].sum() == 33Expected output
Source: (5, 3) Accepted: (3, 3) Review: (2, 3)
Review IDs: [103, 104]
Known revenue: 33.0Export the intended table with index=False, then inspect a round-trip CSV. This verifies the selected example, but does not replace schema and dtype checks for real imports. See Input and Output.
csv_output = accepted.to_csv(index=False)
loaded = pd.read_csv(StringIO(csv_output))
print(csv_output.rstrip())
print("Reloaded shape:", loaded.shape)
assert loaded.shape == accepted.shape
assert loaded["order_id"].tolist() == [101, 102, 105]
# Optional Colab export:
# accepted.to_csv("accepted_sales.csv", index=False)Expected output
order_id,product,amount
101,Pen,5.0
102,Book,20.0
105,Pencil,8.0
Reloaded shape: (3, 3)Why does shape() fail? shape is an attribute, not a function. Does size exclude missing values? No. Does len(df) count columns? It counts rows. Can a DataFrame have ndim=1? No; a selected Series has one dimension. Does a nonzero row count prove the table is usable? No; check columns, types and required values. Why did size change after indexing? Columns moved into the index are no longer counted as data cells. Practise with sample student data and NumPy arrays.
Select NAME and MATH for students with Maths marks below 50. Predict shape, size and ndim, and compare the result with selecting NAME alone as a Series.
Two students meet the condition. The two-column table has shape (2, 2), size 4 and ndim 2; its NAME Series has shape (2,), size 2 and ndim 1.
answer = students.loc[students["MATH"].lt(50), ["NAME", "MATH"]]
names = answer["NAME"]
print(answer)
print("Table:", answer.shape, answer.size, answer.ndim)
print("Series:", names.shape, names.size, names.ndim)
assert answer.shape == (2, 2) and answer.size == 4
assert names.shape == (2,) and names.ndim == 1Expected output
NAME MATH
1 Raju 40
5 Jack 30
Table: (2, 2) 4 2
Series: (2,) 2 1Open in Google Colab View on GitHub
Run the examples in order, change columns and missing values, then compare dimensions and row counts. Save your own copy to keep edits. All sample tables are included in the notebook.
Reference: Pandas DataFrame.shape documentation.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.