Pandas Series Basics
Master pandas Series: a one-dimensional labeled array. Learn creation, indexing, and essential operations for data analysis.
Focus: understand series and basic operations
You've got your DataFrame working, but every column in it is actually a pandas Series — the building block of almost all pandas operations. If you don't understand Series, you'll find yourself fighting with your data instead of working with it. This lesson demystifies the Series, showing you how to create, inspect, and manipulate one-dimensional labeled data with confidence. By the end, you'll be able to leverage Series for fast, readable data analysis — and you'll have a solid foundation for the DataFrames that come next.
The problem this lesson solves
When you first load a CSV or Excel file into pandas, you get a DataFrame — a two-dimensional table. But if you look at any single column, you're actually looking at a Series. Many beginners treat a Series like a plain Python list and get stuck: indexing behaves differently, operations are vectorized (they run on the whole series at once), and missing values (NaN) appear out of nowhere. This confusion leads to slow loops, wrong results, and frustration.
Consider this common scenario:
import pandas as pd
# Load some data
sales = pd.DataFrame({'product': ['A', 'B', 'C'],
'revenue': [100, 200, 150]})
# Try to add 10% to each revenue value
for i in range(len(sales)):
sales['revenue'][i] = sales['revenue'][i] * 1.1
This works, but it's slow, verbose, and non-idiomatic. The pandas way is vectorized and elegant:
sales['revenue'] = sales['revenue'] * 1.1
Without understanding Series, you'll never unlock that power. This lesson closes that gap.
Core concept / mental model
Think of a Series as a labeled one-dimensional array — a single column of data with an index that labels each element. It's like a Python dictionary where the keys are the index labels and the values are the data, but with a twist: the index is ordered and can be non-unique (though it usually isn't).
- Data: any numpy data type — ints, floats, strings, booleans, even Python objects.
- Index: a sequence of labels (default: integer 0 to n-1). You can set custom labels.
- dtype: the data type of the values, inferred automatically (e.g.,
int64,float64,object).
Visualize it like a spine with labels on one side:
index → 0 100
1 200
2 150
The Series is the fundamental building block of the DataFrame. When you select a column from a DataFrame, you get a Series back. Operations on a Series are vectorized — they apply to every element in one go, using optimized C code under the hood.
How it works step by step
Here’s the logical sequence to master Series basics:
- Create a Series from a list, dictionary, or scalar.
- Inspect it — look at values, index, dtype, and size.
- Access data — by position (integer) or by label.
- Perform operations — arithmetic, comparisons, aggregation.
- Handle missing values — detect and fill or drop them.
Cause and effect: when you create a Series, pandas builds a numpy array for data and an Index object for labels. Operations use the index to align data — that’s why a Series can handle non-contiguous labels. If you do math between two Series, pandas aligns them by label, not by position, which is powerful but can surprise you if labels differ.
Hands-on walkthrough
Let’s build a Series step by step and run through the core operations.
Creating a Series
import pandas as pd
# From a list (default integer index)
s = pd.Series([10, 20, 30, 40])
print(s)
Output:
0 10
1 20
2 30
3 40
dtype: int64
You can also supply custom labels:
s_named = pd.Series([10, 20, 30], index=['a', 'b', 'c'])
print(s_named)
Output:
a 10
b 20
c 30
dtype: int64
Or from a dictionary — keys become the index:
d = {'apple': 5, 'banana': 3, 'cherry': 8}
fruit_series = pd.Series(d)
print(fruit_series)
Output:
apple 5
banana 3
cherry 8
dtype: int64
Inspecting a Series
print(s_named.values) # array([10, 20, 30])
print(s_named.index) # Index(['a', 'b', 'c'], dtype='object')
print(s_named.dtype) # int64
print(s_named.size) # 3
print(s_named.head(2)) # first 2 rows
Indexing and selection
You can access elements by position (like a list) or by label (like a dict):
# By label
print(s_named['b']) # 20
# By position
print(s_named.iloc[1]) # 20
# Slicing by label (inclusive)
print(s_named['a':'c']) # a 10, b 20, c 30
# Boolean indexing
print(s_named[s_named > 15]) # b 20, c 30
Pro tip: Use
.locfor label-based access and.ilocfor position-based access. They are explicit and avoid ambiguity.
Basic operations
Series operations are vectorized — no loops needed:
revenue = pd.Series([100, 200, 150], index=['Mon', 'Tue', 'Wed'])
costs = pd.Series([80, 120, 90], index=['Mon', 'Tue', 'Wed'])
# Arithmetic
print(revenue * 1.1) # scaling
print(revenue + costs) # element-wise addition
# Comparisons
print(revenue > 120) # boolean Series
# Aggregation
print(revenue.sum()) # 450
print(revenue.mean()) # 150.0
print(revenue.max()) # 200
Output (last few lines):
Mon 180
Tue 320
Wed 240
dtype: int64
Plus a boolean Series and the aggregate numbers.
Handling missing values
Real data is messy — you’ll get NaN.
s_missing = pd.Series([1, None, 3], index=['x', 'y', 'z'])
print(s_missing.isna()) # False, True, False
print(s_missing.fillna(0)) # replace NaN with 0
print(s_missing.dropna()) # drop NaN rows
Compare options / when to choose what
When working with a series, you have several ways to access and manipulate data. Here’s a quick comparison:
| Approach | Use case | Example | Gotcha |
|---|---|---|---|
s['label'] |
Label-based access | s['Mon'] |
KeyError if label missing |
s.iloc[i] |
Position-based access | s.iloc[1] |
Position, not label |
.loc |
Label-based slicing (inclusive) | s.loc['a':'c'] |
Slice includes end label |
| Boolean mask | Filtering by condition | s[s > 10] |
Returns a Series, not a scalar |
When to choose what:
- Use .iloc when you care about the position (e.g., first element).
- Use .loc when you have meaningful labels (e.g., dates, names).
- Use boolean masks for filtering.
- For fast arithmetic, rely on vectorized operations — avoid Python loops.
Troubleshooting & edge cases
Index misalignment — When you add two Series with different indices, pandas aligns them by label, resulting in NaN where labels don't match.
s1 = pd.Series([1, 2], index=['a', 'b'])
s2 = pd.Series([10, 20], index=['b', 'c'])
print(s1 + s2)
Output:
a NaN
b 12.0
c NaN
dtype: float64
KeyError — Accessing a label that doesn’t exist raises an error. Use .get() to avoid it:
print(s_named.get('z', 'not found'))
Wrong dtype — Mixing strings and numbers in a Series can turn everything into object, breaking math. Check dtype early.
Setting with chained indexing — In DataFrames, df['col'][i] = value can trigger a SettingWithCopyWarning. Use .loc instead.
Performance — Looping over a Series is slow. Always prefer vectorized operations or apply() for more complex logic.
What you learned & what's next
You now understand the pandas Series: how to create it, inspect it, index it, and perform basic operations. You can apply vectorized arithmetic, filter with boolean masks, and handle missing values. These skills are the foundation for all pandas data work.
Next in the track, you'll move to DataFrames — the two-dimensional structure built from Series. You'll learn how to combine multiple Series into a DataFrame, manipulate columns, and perform more complex analyses. With your Series grasp, the leap to DataFrames will feel natural.
Keep this cheat sheet in mind: a Series is a labeled array; operations are vectorized; indexes align; missing data is normal — deal with it early.
Practice recap
Create a Series of weekly temperatures (use a list and set a custom index of days). Calculate the mean, max, and min. Then filter out temperatures below 20°C using a boolean mask. Finally, add a constant of 2 to all values and print the updated Series.
Common mistakes
- Forgetting that
s['a':'c']label slicing is inclusive, unlike Python list slicing which is exclusive. - Using
s['label']when the index is a string and a number, causing unexpected KeyErrors or wrong alignment. - Looping over a Series instead of using vectorized operations, leading to slow code and missed pandas benefits.
- Ignoring index alignment when combining Series with different labels — you get NaN values instead of errors, which can silently corrupt results.
Variations
- Use a NumPy array to create a Series for faster memory access and integration with scientific libraries.
- Create a Series directly from a dictionary to leverage custom index labels without manual assignment.
- Leverage the
.apply()method for custom, element-wise transformations that can't be vectorized natively.
Real-world use cases
- Time series analysis: store daily stock prices as a Series with dates as index for fast slicing and aggregation.
- Feature engineering: extract a column from a DataFrame as a Series, transform it (e.g., scaling), and assign it back.
- Data cleaning: detect and handle missing values in a single column (e.g., sensor readings) using
isna()andfillna().
Key takeaways
- A pandas Series is a one-dimensional labeled array with an Index and a dtype.
- Operations on Series are vectorized — no Python loops needed.
- Indexing uses
.loc(labels) and.iloc(positions); slicing by label is inclusive. - Index alignment means combining Series with different labels yields NaN values.
- Always inspect the dtype and handle missing values early with
isna(),fillna(), ordropna().
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.