Intuition The one core idea
Every pandas operation is matching things by their name-tag (the index) instead of by their position in line . Once you truly see that a table is just named rows glued to named columns , selecting, grouping, joining, and reshaping all become the same move — line up the labels, then act.
Before you can read a single line of the parent note, you need the vocabulary it assumes you already own . This page builds each piece from absolute zero, in the order they depend on each other. Nothing here uses a word we haven't first drawn a picture of.
Definition Value, position, and label
A value is the actual data inside a box: 30, "NY", 88.0.
A position is how far down the line a box sits, counting from zero: 0th, 1st, 2nd…
A label (also called an index label ) is a name-tag stuck onto a box: "u1", "region", 2023.
The whole subject lives on the difference between the last two. Position asks "which one is it in line?" . Label asks "what is it called?" . Look at the picture: the same three boxes, addressed two different ways.
Intuition Why labels at all?
Positions shuffle the moment you sort or filter — box "customer 1023" might be 5th today and 500th tomorrow. A name-tag never moves with the crowd . Pandas bets everything on name-tags so your data survives being reordered.
An array is a row of boxes all of the same type (all numbers, or all text), addressed only by position . This is exactly a NumPy array — pandas is built on top of it.
An array has no name-tags . It knows "the 2nd box holds 30" but not "the box called u2 holds 30" . That missing name-tag is the gap pandas fills.
An index is the ordered strip of labels running alongside an array — one name-tag per box. Think of a stack of lockers where the index is the row of name plates beneath them.
Two arrays stapled together — a values array and an index array of the same length — is the atom of all of pandas.
Intuition Why an index needs a fast lookup
When you ask for label "u2", pandas must find which position that name sits at. Scanning the whole strip would be slow. Instead the index secretly builds a hash table — a name→position dictionary — so lookup by label is (almost) instant, just like lookup by position. That is why selecting by name isn't slower than selecting by number.
A Series is precisely (values array, index array) glued together: a 1-D labelled column . You can read it as a tiny function f : label → value — feed it a name-tag, it hands back the value.
The arrow notation → just means "maps to" / "gives you". So f ( "u2" ) = 32 reads "the name-tag u2 gives the value 32" .
Worked example A Series is a partial function
s = pd.Series([ 10 , 20 , 30 ], index = [ "a" , "b" , "c" ])
s[ "b" ] # 20 → f("b") = 20
It is partial because it only answers for labels it actually has. Ask for "z" and there is no answer — a gap. Hold that word "gap"; it becomes NaN next.
NaN stands for "Not a Number" — a special marker meaning "there is no value here" . It is the value pandas drops into a box whose label had no partner. Full story: Missing Data / NaN .
Why does a missing box need its own special value instead of just 0? Because 0 is a real answer ("we measured zero sales"), while NaN means "we measured nothing at all". Confusing the two corrupts every average you compute. See the parent's alignment example, where label "z" produces NaN.
Alignment is the act of lining two Series up by their labels before combining them. Same label meets same label; leftover labels on either side become NaN.
Intuition Why alignment is the whole game
This single rule powers everything downstream. Adding Series, joining tables (merge), bucketing rows (groupby), rotating tables (pivot) — each is "first align by label, then act" . The parent note's a + b giving x→201, y→102, z→NaN is nothing but this picture. The formula it states,
( s 1 + s 2 ) [ k ] = { s 1 [ k ] + s 2 [ k ] NaN k in both otherwise
reads in plain words: "for name-tag k , if both sides own it, add; else leave a gap." The symbol [ k ] means "the box labelled k ", and k is just a stand-in for any one name-tag .
A DataFrame is many Series standing side by side, all sharing one row index , each carrying its own column label . It is a dictionary {column label → Series} where every Series shares the same strip of row name-tags.
Now you can decode the parent's one-liner: "a dictionary of aligned columns" . Dictionary = column-name → column. Aligned = every column shares the same row index. That's it.
Intuition Two axes, two kinds of name-tag
A DataFrame has row labels (the index, running down) and column labels (running across). Every cell is found by a pair of name-tags: (row label, column label). This is why .loc takes two arguments — one name per axis.
You now have enough to understand why there are three ways in .
Definition The selectors, from first principles
.loc addresses by label — "the box named u2". Slices are inclusive because a name has no "next number".
.iloc addresses by position — "the box 2nd in line ". Slices exclude the stop , like plain Python.
df["col"] grabs a whole column Series by its column label.
Common mistake "loc and iloc are interchangeable."
Why it feels right: with a default 0,1,2 index the label equals the position, so they coincide by luck.
The fix: they answer different questions — name vs line-position. The parent's steel-man shows they split the moment labels ≠ positions.
Concept
One-line meaning
Where it feeds in
Split-Apply-Combine
slice into buckets → run a function per bucket → stitch back
groupby
SQL Joins
combine two tables on a shared key column
merge
Tidy Data
one row per observation, one column per variable
pivot (long↔wide)
Hash Tables
name→position dictionary for instant lookup
why index lookup is fast
array = NumPy row of boxes
index = strip of name-tags
hash table for fast label lookup
Series = values glued to index
alignment = match by label
DataFrame = wall of aligned Series
loc iloc bracket selectors
groupby split apply combine
Read top to bottom: nothing on a lower row is usable until every arrow feeding it is understood. If any upstream box is fuzzy, the parent note will feel like magic.
Cover the right side and answer aloud. If you miss one, re-read its section above.
Difference between a value's position and its label Position = how far down the line (0,1,2…); label = the name-tag glued on, which never moves when rows are reordered.
What two arrays make a Series A values array and an index (label) array of equal length, stapled together.
What does it mean that a Series is a "partial function" It maps label → value, but only for labels it actually contains; missing labels have no answer.
What is NaN and why not just use 0 A "Not a Number" marker meaning no value here ; 0 is a real measured value, NaN means nothing was measured.
State the alignment rule in plain words Line two Series up by label; matching labels combine, unmatched labels become NaN.
What is a DataFrame in dictionary terms A dictionary {column label → Series} where every Series shares one common row index.
Why does .loc slice inclusively but .iloc exclusively .loc uses labels (a name has no "stop+1", so include the named endpoint); .iloc uses positions and follows Python's exclusive-stop rule.
Why is label lookup nearly as fast as position lookup The index builds a hash table (name→position) so finding a name-tag doesn't require scanning the whole strip.
Which foundation directly powers groupby, merge, and pivot Alignment — each is "match by label first, then act".