Every DataFrame column is a Series, so its behaviour underlies everything you do in pandas.
pandas is imported as import pandas as pd. Its simplest structure is the Series: a one-dimensional array of values (usually backed by a NumPy array) paired with an index, a sequence of labels for those values, plus an optional name.
You can build one from a list, where the index defaults to 0, 1, 2, ..., or from a dict, where the keys become the index:
s = pd.Series({"apples": 3, "pears": 5})A Series feels like a mix of a NumPy array and a dict. Like an array, it has a dtype, supports vectorized arithmetic (s * 2), comparisons and NumPy ufuncs (np.log(s)), and has methods such as sum, mean, max, idxmax. Like a dict, you can look values up by label, test "apples" in s (which checks the index, not the values) and iterate over s.items().
Unlike NumPy, aggregations skip missing values by default: pd.Series([1, None, 3]).mean() is 2.0, not NaN.
To be explicit about how you look something up, use s.loc["apples"] for labels and s.iloc[0] for positions. Plain s[0] on a Series with a non-integer index is ambiguous; it was positional with a deprecation warning in pandas 2.x and is label-only from pandas 3.0, so avoid it.
s.index and s.values give the two halves; prefer s.to_numpy() over s.values when you need a NumPy array. Operations that return a Series keep the index, which is how labels follow your data through a computation.
Example
import numpy as np
import pandas as pd
stock = pd.Series({"apples": 3, "pears": 5, "plums": 0}, name="stock")
print(stock)
# apples 3
# pears 5
# plums 0
# Name: stock, dtype: int64
print(stock.loc["pears"], stock.iloc[0]) # 5 3
print("pears" in stock, 5 in stock) # True False (checks the index)
print(stock[stock > 0].index.tolist()) # ['apples', 'pears']
print((stock * 1.5).idxmax()) # pears
t = pd.Series([21.5, np.nan, 23.5])
print(t.mean()) # 22.5 (NaN skipped)
print(t.to_numpy()) # [21.5 nan 23.5]Remember
- A Series is values + index labels + dtype + optional name
- Vectorized maths works like NumPy; aggregations skip NaN by default
- Use
.locfor labels and.ilocfor positions intests the index, not the values
Watch out for
- Writing
s[0]and assuming it means 'first element': uses.iloc[0].